From 4cbda69a6b1b5c43654464900ae6e1774746dce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Fri, 26 Apr 2024 12:24:17 +0200 Subject: [PATCH] Add support for proxy domain validation to tls_cert resource --- .../kosmos-base/resources/tls_cert_for.rb | 20 +++++-- .../default/gandi_dns_certbot_hook.sh.erb | 57 ++++++++++++------- .../templates/gandi_dns_certbot_hook.sh.erb | 11 ++-- .../kosmos-mastodon/recipes/nginx.rb | 1 + 4 files changed, 59 insertions(+), 30 deletions(-) diff --git a/site-cookbooks/kosmos-base/resources/tls_cert_for.rb b/site-cookbooks/kosmos-base/resources/tls_cert_for.rb index 02e73d2..b1f92fc 100644 --- a/site-cookbooks/kosmos-base/resources/tls_cert_for.rb +++ b/site-cookbooks/kosmos-base/resources/tls_cert_for.rb @@ -3,6 +3,7 @@ provides :tls_cert_for property :domain, [String, Array], name_property: true property :auth, [String, NilClass], default: nil +property :acme_domain, [String, NilClass], default: nil default_action :create @@ -17,13 +18,22 @@ action :create do case new_resource.auth when "gandi_dns" - gandi_api_data_bag_item = data_bag_item('credentials', 'gandi_api_5apps') + gandi_api_credentials = data_bag_item('credentials', 'gandi_api_5apps') hook_path = "/root/gandi_dns_certbot_hook.sh" + hook_auth_command = "#{hook_path} auth" + hook_cleanup_command = "#{hook_path} cleanup" + + if new_resource.acme_domain + hook_auth_command += " #{new_resource.acme_domain}" + hook_cleanup_command += " #{new_resource.acme_domain}" + end + template hook_path do cookbook "kosmos-base" - variables gandi_api_key: gandi_api_data_bag_item["key"] - mode 0770 + variables access_token: gandi_api_credentials["access_token"] + mode 0700 + sensitive true end # Generate a Let's Encrypt cert (only if no cert has been generated before). @@ -34,8 +44,8 @@ action :create do --preferred-challenges dns \ --manual-public-ip-logging-ok \ --agree-tos \ - --manual-auth-hook '#{hook_path} auth' \ - --manual-cleanup-hook '#{hook_path} cleanup' \ + --manual-auth-hook '#{hook_auth_command}' \ + --manual-cleanup-hook '#{hook_cleanup_command}' \ --email ops@kosmos.org \ #{node.run_list.roles.include?("openresty_proxy") ? '--deploy-hook /etc/letsencrypt/renewal-hooks/post/openresty' : nil } \ #{domains.map {|d| "-d #{d}" }.join(" ")} diff --git a/site-cookbooks/kosmos-base/templates/default/gandi_dns_certbot_hook.sh.erb b/site-cookbooks/kosmos-base/templates/default/gandi_dns_certbot_hook.sh.erb index 4c59a11..2a5f0eb 100755 --- a/site-cookbooks/kosmos-base/templates/default/gandi_dns_certbot_hook.sh.erb +++ b/site-cookbooks/kosmos-base/templates/default/gandi_dns_certbot_hook.sh.erb @@ -1,21 +1,16 @@ #!/usr/bin/env bash -# - set -euf -o pipefail # ************** USAGE ************** # -# Example usage (with this hook file saved in /root/): +# Example usage: # -# sudo su - # certbot certonly --manual --preferred-challenges dns --manual-public-ip-logging-ok --agree-tos -d "5apps.com" -d muc.5apps.com -d "xmpp.5apps.com" \ # --manual-auth-hook "/root/letsencrypt_hook.sh auth" --manual-cleanup-hook "/root/letsencrypt_hook.sh cleanup" # -# This hook requires configuration, continue reading. -# # ************** CONFIGURATION ************** # -# GANDI_API_KEY: Your Gandi Live API key +# ACCESS_TOKEN: Your Gandi Live API key # # PROVIDER_UPDATE_DELAY: # How many seconds to wait after updating your DNS records. This may be required, @@ -25,10 +20,16 @@ set -euf -o pipefail # # Defaults to 30 seconds. # -GANDI_API_KEY="<%= @gandi_api_key %>" +# VALIDATION_DOMAIN: +# Domain to create ACME DNS entries on. Use this when redirecting ACME subdomains +# from the original domain to a proxy validation domain that we control. +# +ACCESS_TOKEN="<%= @access_token %>" PROVIDER_UPDATE_DELAY=10 +VALIDATION_DOMAIN="${2:-}" regex='.*\.(.*\..*)' + if [[ $CERTBOT_DOMAIN =~ $regex ]] then DOMAIN="${BASH_REMATCH[1]}" @@ -36,25 +37,41 @@ else DOMAIN="${CERTBOT_DOMAIN}" fi +if [[ -n "$VALIDATION_DOMAIN" ]] +then + if [[ $VALIDATION_DOMAIN =~ $regex ]] + then + ACME_BASE_DOMAIN="${BASH_REMATCH[1]}" + else + echo "Validation domain has to be a subdomain, but it is not: \"${VALIDATION_DOMAIN}\"" + exit 1 + fi + ACME_DOMAIN="${CERTBOT_DOMAIN}.${VALIDATION_DOMAIN}" +else + ACME_BASE_DOMAIN="${DOMAIN}" + ACME_DOMAIN="_acme-challenge.${CERTBOT_DOMAIN}" +fi + # To be invoked via Certbot's --manual-auth-hook function auth { - curl -s -D- -H "Content-Type: application/json" \ - -H "X-Api-Key: ${GANDI_API_KEY}" \ - -d "{\"rrset_name\": \"_acme-challenge.${CERTBOT_DOMAIN}.\", - \"rrset_type\": \"TXT\", - \"rrset_ttl\": 3600, - \"rrset_values\": [\"${CERTBOT_VALIDATION}\"]}" \ - "https://dns.api.gandi.net/api/v5/domains/${DOMAIN}/records" + curl -s -D- \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + -d "{\"rrset_name\": \"${ACME_DOMAIN}.\", + \"rrset_type\": \"TXT\", + \"rrset_ttl\": 300, + \"rrset_values\": [\"${CERTBOT_VALIDATION}\"]}" \ + "https://api.gandi.net/v5/livedns/domains/${ACME_BASE_DOMAIN}/records" - - sleep ${PROVIDER_UPDATE_DELAY} + sleep ${PROVIDER_UPDATE_DELAY} } # To be invoked via Certbot's --manual-cleanup-hook function cleanup { - curl -s -X DELETE -H "Content-Type: application/json" \ - -H "X-Api-Key: ${GANDI_API_KEY}" \ - https://dns.api.gandi.net/api/v5/domains/${DOMAIN}/records/_acme-challenge.${CERTBOT_DOMAIN}./TXT + curl -s -X DELETE \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + "https://api.gandi.net/v5/livedns/domains/${ACME_BASE_DOMAIN}/records/${ACME_DOMAIN}./TXT" } HANDLER=$1; shift; diff --git a/site-cookbooks/kosmos-ejabberd/templates/gandi_dns_certbot_hook.sh.erb b/site-cookbooks/kosmos-ejabberd/templates/gandi_dns_certbot_hook.sh.erb index 7bf1a84..2a5f0eb 100755 --- a/site-cookbooks/kosmos-ejabberd/templates/gandi_dns_certbot_hook.sh.erb +++ b/site-cookbooks/kosmos-ejabberd/templates/gandi_dns_certbot_hook.sh.erb @@ -3,17 +3,14 @@ set -euf -o pipefail # ************** USAGE ************** # -# Example usage (with this hook file saved in /root/): +# Example usage: # -# sudo su - # certbot certonly --manual --preferred-challenges dns --manual-public-ip-logging-ok --agree-tos -d "5apps.com" -d muc.5apps.com -d "xmpp.5apps.com" \ # --manual-auth-hook "/root/letsencrypt_hook.sh auth" --manual-cleanup-hook "/root/letsencrypt_hook.sh cleanup" # -# This hook requires configuration, continue reading. -# # ************** CONFIGURATION ************** # -# GANDI_API_KEY: Your Gandi Live API key +# ACCESS_TOKEN: Your Gandi Live API key # # PROVIDER_UPDATE_DELAY: # How many seconds to wait after updating your DNS records. This may be required, @@ -23,6 +20,10 @@ set -euf -o pipefail # # Defaults to 30 seconds. # +# VALIDATION_DOMAIN: +# Domain to create ACME DNS entries on. Use this when redirecting ACME subdomains +# from the original domain to a proxy validation domain that we control. +# ACCESS_TOKEN="<%= @access_token %>" PROVIDER_UPDATE_DELAY=10 VALIDATION_DOMAIN="${2:-}" diff --git a/site-cookbooks/kosmos-mastodon/recipes/nginx.rb b/site-cookbooks/kosmos-mastodon/recipes/nginx.rb index 405be3a..bcb1d83 100644 --- a/site-cookbooks/kosmos-mastodon/recipes/nginx.rb +++ b/site-cookbooks/kosmos-mastodon/recipes/nginx.rb @@ -34,6 +34,7 @@ end tls_cert_for server_name do auth "gandi_dns" + acme_domain "letsencrypt.kosmos.org" action :create end