From c41eda0fd48acd6acd7d89d964afac1ca1957e51 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Mon, 9 Oct 2023 11:25:08 +0000 Subject: [PATCH] [ci skip] update tls auto renew script to use technitium --- .drone.yml | 38 ++--- modules/kubernetes/setup_tls_secret/renew.sh | 159 +++++++++++------- modules/kubernetes/setup_tls_secret/renew2.sh | 48 ++++++ 3 files changed, 168 insertions(+), 77 deletions(-) create mode 100755 modules/kubernetes/setup_tls_secret/renew2.sh diff --git a/.drone.yml b/.drone.yml index e64f2d21..c8259d98 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,11 +1,11 @@ kind: pipeline type: kubernetes name: default -concurrency: +concurrency: limit: 1 trigger: event: - - push + - push steps: - name: Prepare terraform files @@ -29,7 +29,7 @@ steps: commands: - "apk update && apk add openssh-client git git-crypt" - "mkdir ~/.ssh && ssh-keyscan -H github.com >> ~/.ssh/known_hosts" - - 'chmod 400 secrets/deploy_key' + - "chmod 400 secrets/deploy_key" - "git add ." - "git remote set-url origin git@github.com:ViktorBarzin/infra.git" - "git commit -m 'Drone CI deploy commit [CI SKIP]' || echo 'No changes'" @@ -41,13 +41,13 @@ steps: from_secret: slack_webhook channel: general when: - status: [ success, failure ] + status: [success, failure] --- kind: pipeline type: kubernetes name: build-cli -concurrency: +concurrency: limit: 1 trigger: event: @@ -55,16 +55,16 @@ trigger: - push steps: -- name: Build image - image: plugins/docker - settings: - username: "viktorbarzin" - password: - from_secret: dockerhub_password - repo: viktorbarzin/infra - dockerfile: cli/Dockerfile - context: cli - auto_tag: true + - name: Build image + image: plugins/docker + settings: + username: "viktorbarzin" + password: + from_secret: dockerhub_password + repo: viktorbarzin/infra + dockerfile: cli/Dockerfile + context: cli + auto_tag: true --- kind: secret @@ -77,9 +77,9 @@ type: kubernetes name: renew-tls-certificate trigger: event: - - cron + - cron cron: - - renew-tls-certificate + - renew-tls-certificate steps: - name: Prepare terraform files @@ -98,13 +98,13 @@ steps: # Install terraform cli - "curl https://releases.hashicorp.com/terraform/0.14.6/terraform_0.14.6_linux_amd64.zip | gzip -d > /usr/local/bin/terraform && chmod 775 /usr/local/bin/terraform" - "terraform init" - - "./modules/kubernetes/setup_tls_secret/renew.sh" + - "./modules/kubernetes/setup_tls_secret/renew2.sh" - name: Commit updated certificates image: alpine commands: - "apk update && apk add openssh-client git git-crypt" - "mkdir ~/.ssh && ssh-keyscan -H github.com >> ~/.ssh/known_hosts" - - 'chmod 400 secrets/deploy_key' + - "chmod 400 secrets/deploy_key" - "git add ." - "git remote set-url origin git@github.com:ViktorBarzin/infra.git" - "git commit -m 'Drone CI Update TLS Certificates Commit' || echo 'No changes'" diff --git a/modules/kubernetes/setup_tls_secret/renew.sh b/modules/kubernetes/setup_tls_secret/renew.sh index da3ef88a..410cea6a 100755 --- a/modules/kubernetes/setup_tls_secret/renew.sh +++ b/modules/kubernetes/setup_tls_secret/renew.sh @@ -4,75 +4,118 @@ set timeout -1 set le_dir "/tmp/le/" set config_dir "$le_dir/out/config" set pwd [pwd] +set technitium_token "REDACTED_TECHNITIUM_TOKEN" -spawn certbot certonly --manual --preferred-challenge=dns --email me@viktorbarzin.me --server https://acme-v02.api.letsencrypt.org/directory --agree-tos --manual-public-ip-logging-ok -d *.viktorbarzin.me -d viktorbarzin.me --config-dir $config_dir --work-dir $le_dir/workdir --logs-dir $le_dir/logsdir --no-eff-email +# contents for certbot-auth +set auth_contents {#!/usr/bin/env sh + # Generate API token from DNS web console + API_TOKEN="REDACTED_TECHNITIUM_TOKEN" -set prompt "$" -set dns_file "$pwd/modules/kubernetes/bind/extra/viktorbarzin.me" -# expect -re "Please deploy a DNS TXT record under the name" { -expect -re "Press Enter to Continue" { - set challenge [ exec sh -c "echo '$expect_out(buffer)' | tail -n 4 | head -n 1" ] - set dns_record "_acme-challenge IN TXT \"$challenge\"" - puts "\nChallenge: '$challenge'" - # send \x03 - puts "Dns file: '$dns_file'" + # Create challenge TXT record + curl "http://technitium-web.technitium.svc.cluster.local:5380/api/zones/records/add?token=$API_TOKEN&domain=_acme-challenge.\$CERTBOT_DOMAIN&type=TXT&ttl=60&text=\$CERTBOT_VALIDATION" - # Check if dns record is not already present - try { - set results [exec grep -q $dns_record $dns_file] - set status 0 - } trap CHILDSTATUS {results options} { - set status [lindex [dict get $options -errorcode] 2] - } - if {$status != 0} { - exec echo $dns_record | tee -a $dns_file - puts "Teed into file" - } else { - puts "DNS record '$dns_record' already in file" - } + # Sleep to make sure the change has time to propagate from primary to secondary name servers + sleep 25 } +spawn /bin/sh +send "echo \"$auth_contents\" > /root/certbot-auth.sh \r" +send "chmod 700 /root/certbot-auth.sh \r" +send "cat /root/certbot-auth.sh \r" +send "exit \r" -send -- "\r" -# Do the same for the 2nd dns record -expect -re "\[a-zA-Z0-9_-\]{43}" { - set challenge $expect_out(0,string) - # set challenge [ exec sh -c "echo $expect_out(0, buffer) | tail -n 8 | head -n 1" ] - set dns_record1 "_acme-challenge IN TXT \"$challenge\"" - puts "Challenge: '$challenge'" - puts "Dns record: '$dns_record1'" - puts "Dns file: '$dns_file'" +# Contents for certbot-cleanup +set cleanup_contents {#!/usr/bin/env sh + exit 0 # DEBUG: TODO: Remove me + # Generate API token from DNS web console + API_TOKEN="REDACTED_TECHNITIUM_TOKEN" - # Check if dns record is not already present - try { - set results [exec grep -q $dns_record1 $dns_file] - set status 0 - } trap CHILDSTATUS {results options} { - set status [lindex [dict get $options -errorcode] 2] - } - if {$status != 0} { - exec echo $dns_record1 | tee -a $dns_file - puts "Teed into file" - } else { - puts "DNS record '$dns_record1' already in file" - } + # Delete challenge TXT record + curl "http://technitium-web.technitium.svc.cluster.local:5380/api/zones/records/delete?token=$API_TOKEN&domain=_acme-challenge.\$CERTBOT_DOMAIN&type=TXT&text=\$CERTBOT_VALIDATION" } +spawn /bin/sh +send "echo \"$cleanup_contents\" > /root/certbot-cleanup.sh \r" +send "chmod 700 /root/certbot-cleanup.sh \r" +send "exit \r" -# Force deployment recreation -exec terraform taint module.kubernetes_cluster.module.bind.module.bind-public-deployment.kubernetes_deployment.bind -# Apply changes to configmap and redeploy -exec >@stdout 2>@stderr terraform apply -auto-approve -target=module.kubernetes_cluster.module.bind +exit 0 +spawn certbot certonly --manual --preferred-challenges=dns --email me@viktorbarzin.me --server https://acme-v02.api.letsencrypt.org/directory --agree-tos --manual-auth-hook /root/certbot-auth.sh --config-dir $config_dir --work-dir $le_dir/workdir --logs-dir $le_dir/logsdir --no-eff-email --manual-cleanup-hook /root/certbot-cleanup.sh -d viktorbarzin.me -d *.viktorbarzin.me -# Wait for deployment update -# TODO: better to use k8s api. What we want is `kubectl rollout status deployment -l app=bind-public` as a curl -# exec bash -c 'while [[ $(kubectl get pods -l app=bind-public -o \'jsonpath={..status.conditions[\?(\@.type=="Ready")].status}\') != "True" ]]; do echo "waiting pod..." && sleep 1; done' -exec >@stdout echo 'Waiting for redeployment of bind...' -exec sleep 10 +############# Old way of auth -send -- "\r" +# spawn certbot certonly --manual --preferred-challenge=dns --email me@viktorbarzin.me --server https://acme-v02.api.letsencrypt.org/directory --agree-tos -d *.viktorbarzin.me -d viktorbarzin.me --config-dir $config_dir --work-dir $le_dir/workdir --logs-dir $le_dir/logsdir --no-eff-email -# Clean up -exec sed -i "s/$dns_record//g" "$dns_file" -exec sed -i "s/$dns_record1//g" "$dns_file" +# set prompt "$" +# set dns_file "$pwd/modules/kubernetes/bind/extra/viktorbarzin.me" +# # expect -re "Please deploy a DNS TXT record under the name" { +# expect -re "Press Enter to Continue" { +# set challenge [ exec sh -c "echo '$expect_out(buffer)' | tail -n 4 | head -n 1" ] +# set dns_record "_acme-challenge IN TXT \"$challenge\"" +# puts "\nChallenge: '$challenge'" +# # send \x03 +# puts "Dns file: '$dns_file'" + +# # Check if dns record is not already present +# try { +# set results [exec grep -q $dns_record $dns_file] +# set status 0 +# } trap CHILDSTATUS {results options} { +# set status [lindex [dict get $options -errorcode] 2] +# } +# if {$status != 0} { +# exec echo $dns_record | tee -a $dns_file +# puts "Teed into file" +# } else { +# puts "DNS record '$dns_record' already in file" +# } +# } + +# send -- "\r" +# # Do the same for the 2nd dns record +# expect -re "\[a-zA-Z0-9_-\]{43}" { +# set challenge $expect_out(0,string) +# # set challenge [ exec sh -c "echo $expect_out(0, buffer) | tail -n 8 | head -n 1" ] +# set dns_record1 "_acme-challenge IN TXT \"$challenge\"" +# puts "Challenge: '$challenge'" +# puts "Dns record: '$dns_record1'" +# puts "Dns file: '$dns_file'" + +# # Check if dns record is not already present +# try { +# set results [exec grep -q $dns_record1 $dns_file] +# set status 0 +# } trap CHILDSTATUS {results options} { +# set status [lindex [dict get $options -errorcode] 2] +# } +# if {$status != 0} { +# exec echo $dns_record1 | tee -a $dns_file +# puts "Teed into file" +# } else { +# puts "DNS record '$dns_record1' already in file" +# } +# } + +# # Force deployment recreation +# # exec terraform taint module.kubernetes_cluster.module.bind.module.bind-public-deployment.kubernetes_deployment.bind +# exec terraform taint module.kubernetes_cluster.module.technitium.kubernetes_deployment.technitium +# # set current_time [clock seconds] +# # set formatted_time [clock format $current_time -format "+%Y-%m-%dT%TZ"] +# # exec curl -X PATCH https://10.0.20.100:6443/apis/apps/v1/namespaces/technitium/deployments/technitium -H \"Authorization:Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)\" -H \"Content-Type:application/strategic-merge-patch+json\" -k -d '{\"spec\": {\"template\": {\"metadata\": { \"annotations\": {\"kubectl.kubernetes.io/restartedAt\": \"'$(date +%Y-%m-%dT%TZ)'\" }}}}}' +# # exec curl -X PATCH https://10.0.20.100:6443/apis/apps/v1/namespaces/technitium/deployments/technitium -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" -H "Content-Type: application/strategic-merge-patch+json" -k -d "{\"spec\": {\"template\": {\"metadata\": { \"annotations\": {\"kubectl.kubernetes.io/restartedAt\": \"$formatted_time\" }}}}}" +# # exec terraform taint module.kubernetes_cluster.module.technitium.module.technitium.kubernetes_deployment.technitium +# # Apply changes to configmap and redeploy +# exec >@stdout 2>@stderr terraform apply -auto-approve -target=module.kubernetes_cluster.module.technitium + +# # Wait for deployment update +# # TODO: better to use k8s api. What we want is `kubectl rollout status deployment -l app=bind-public` as a curl +# # exec bash -c 'while [[ $(kubectl get pods -l app=bind-public -o \'jsonpath={..status.conditions[\?(\@.type=="Ready")].status}\') != "True" ]]; do echo "waiting pod..." && sleep 1; done' +# exec >@stdout echo 'Waiting for redeployment of technitium...' +# exec sleep 10 + +# send -- "\r" + +# # Clean up +# exec sed -i "s/$dns_record//g" "$dns_file" +# exec sed -i "s/$dns_record1//g" "$dns_file" # Success expect ".*Congratulations!" diff --git a/modules/kubernetes/setup_tls_secret/renew2.sh b/modules/kubernetes/setup_tls_secret/renew2.sh new file mode 100755 index 00000000..bc9624d7 --- /dev/null +++ b/modules/kubernetes/setup_tls_secret/renew2.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env sh + +set -e + + +export le_dir="/tmp/le/" +export config_dir="$le_dir/out/config" +export technitium_token="REDACTED_TECHNITIUM_TOKEN" +export certbot_auth="$le_dir/certbot_auth.sh" +export certbot_cleanup="$le_dir/certbot_cleanup.sh" + +mkdir $le_dir +cat << EOF > $certbot_auth +#!/usr/bin/env sh +# Generate API token from DNS web console +API_TOKEN="REDACTED_TECHNITIUM_TOKEN" + +# Create challenge TXT record +curl "http://technitium-web.technitium.svc.cluster.local:5380/api/zones/records/add?token=\$API_TOKEN&domain=_acme-challenge.\$CERTBOT_DOMAIN&type=TXT&ttl=60&text=\$CERTBOT_VALIDATION" + +# Sleep to make sure the change has time to propagate from primary to secondary name servers +sleep 25 +EOF + +chmod 700 $certbot_auth +cat $certbot_auth + + +cat << EOF > $certbot_cleanup +#!/usr/bin/env sh +# Generate API token from DNS web console +API_TOKEN="REDACTED_TECHNITIUM_TOKEN" + +# Delete challenge TXT record +curl "http://technitium-web.technitium.svc.cluster.local:5380/api/zones/records/delete?token=\$API_TOKEN&domain=_acme-challenge.\$CERTBOT_DOMAIN&type=TXT&text=\$CERTBOT_VALIDATION" +EOF + +chmod 700 $certbot_cleanup +cat $certbot_cleanup + + +certbot certonly --manual --preferred-challenges=dns --email me@viktorbarzin.me --server https://acme-v02.api.letsencrypt.org/directory --agree-tos --manual-auth-hook $certbot_auth --config-dir $config_dir --work-dir $le_dir/workdir --logs-dir $le_dir/logsdir --no-eff-email --manual-cleanup-hook $certbot_cleanup -d viktorbarzin.me -d *.viktorbarzin.me + +exec cp --remove-destination $config_dir/live/viktorbarzin.me/fullchain.pem ./secrets +exec cp --remove-destination $config_dir/live/viktorbarzin.me/privkey.pem ./secrets + +echo "Done renewing cert. Output certificates stored in ./secrets\n" +ls ./secrets