From 7aaf4432d4068ec10dff5447d5843957f729de0a Mon Sep 17 00:00:00 2001 From: William Lallemand Date: Thu, 30 Nov 2023 11:49:54 +0100 Subject: [PATCH 01/16] haproxy: sanitize the PEM in the deploy script Sanitize the PEM of the haproxy deploy script by removing the '\n', this way it could be injected directly over the CLI. --- deploy/haproxy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index d638abb8..6c1b4a68 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -147,7 +147,7 @@ haproxy_deploy() { # Create a temporary PEM file _temppem="$(_mktemp)" _debug _temppem "${_temppem}" - cat "${_ccert}" "${_cca}" "${_ckey}" >"${_temppem}" + cat "${_ccert}" "${_cca}" "${_ckey}" | grep . >"${_temppem}" _ret="$?" # Check that we could create the temporary file From 0f7be905004fba8b6d4ec59354dd623ce8aa5c33 Mon Sep 17 00:00:00 2001 From: William Lallemand Date: Thu, 16 Mar 2023 17:24:24 +0100 Subject: [PATCH 02/16] haproxy: deploy script can update existing certificate over stats socket Since version 2.2, HAProxy is able to update dynamically certificates, without a reload. This patch uses socat to push the certificate into HAProxy in order to achieve hot update. With this method, reloading is not required. This should be used only to update an existing certificate in haproxy. 2 new variables are available: - DEPLOY_HAPROXY_HOT_UPDATE="yes" update over the stats socket instead of reloading - DEPLOY_HAPROXY_STATS_SOCKET="UNIX:/run/haproxy/admin.sock" set the path on the stats socket. --- deploy/haproxy.sh | 80 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 8 deletions(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index 6c1b4a68..f6ddd7b9 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -36,6 +36,14 @@ # Note: This functionality requires HAProxy was compiled against # a version of OpenSSL that supports this. # +# export DEPLOY_HAPROXY_HOT_UPDATE="yes" +# export DEPLOY_HAPROXY_STATS_SOCKET="UNIX:/run/haproxy/admin.sock" +# +# OPTIONAL: Deploy the certificate over the HAProxy stats socket without +# needing to reload HAProxy. Default is "no". +# +# Require the socat binary. DEPLOY_HAPROXY_STATS_SOCKET variable uses the socat +# address format. ######## Public functions ##################### @@ -53,6 +61,8 @@ haproxy_deploy() { DEPLOY_HAPROXY_BUNDLE_DEFAULT="no" DEPLOY_HAPROXY_ISSUER_DEFAULT="no" DEPLOY_HAPROXY_RELOAD_DEFAULT="true" + DEPLOY_HAPROXY_HOT_UPDATE_DEFAULT="no" + DEPLOY_HAPROXY_STATS_SOCKET_DEFAULT="UNIX:/run/haproxy/admin.sock" _debug _cdomain "${_cdomain}" _debug _ckey "${_ckey}" @@ -118,6 +128,26 @@ haproxy_deploy() { Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD_DEFAULT}" fi + # HOT_UPDATE is optional. If not provided then assume "${DEPLOY_HAPROXY_HOT_UPDATE_DEFAULT}" + _getdeployconf DEPLOY_HAPROXY_HOT_UPDATE + _debug2 DEPLOY_HAPROXY_HOT_UPDATE "${DEPLOY_HAPROXY_HOT_UPDATE}" + if [ -n "${DEPLOY_HAPROXY_HOT_UPDATE}" ]; then + Le_Deploy_haproxy_hot_update="${DEPLOY_HAPROXY_HOT_UPDATE}" + _savedomainconf Le_Deploy_haproxy_hot_update "${Le_Deploy_haproxy_hot_update}" + elif [ -z "${Le_Deploy_haproxy_hot_update}" ]; then + Le_Deploy_haproxy_hot_update="${DEPLOY_HAPROXY_HOT_UPDATE_DEFAULT}" + fi + + # STATS_SOCKET is optional. If not provided then assume "${DEPLOY_HAPROXY_STATS_SOCKET_DEFAULT}" + _getdeployconf DEPLOY_HAPROXY_STATS_SOCKET + _debug2 DEPLOY_HAPROXY_STATS_SOCKET "${DEPLOY_HAPROXY_STATS_SOCKET}" + if [ -n "${DEPLOY_HAPROXY_STATS_SOCKET}" ]; then + Le_Deploy_haproxy_stats_socket="${DEPLOY_HAPROXY_STATS_SOCKET}" + _savedomainconf Le_Deploy_haproxy_stats_socket "${Le_Deploy_haproxy_stats_socket}" + elif [ -z "${Le_Deploy_haproxy_stats_socket}" ]; then + Le_Deploy_haproxy_stats_socket="${DEPLOY_HAPROXY_STATS_SOCKET_DEFAULT}" + fi + # Set the suffix depending if we are creating a bundle or not if [ "${Le_Deploy_haproxy_bundle}" = "yes" ]; then _info "Bundle creation requested" @@ -142,6 +172,7 @@ haproxy_deploy() { _issuer="${_pem}.issuer" _ocsp="${_pem}.ocsp" _reload="${Le_Deploy_haproxy_reload}" + _statssock="${Le_Deploy_haproxy_stats_socket}" _info "Deploying PEM file" # Create a temporary PEM file @@ -265,15 +296,48 @@ haproxy_deploy() { fi fi - # Reload HAProxy - _debug _reload "${_reload}" - eval "${_reload}" - _ret=$? - if [ "${_ret}" != "0" ]; then - _err "Error code ${_ret} during reload" - return ${_ret} + if [ "${Le_Deploy_haproxy_hot_update}" = "yes" ]; then + # Update certificate over HAProxy stats socket. + _info "Update the certificate over HAProxy stats socket." + if _exists socat; then + _socat_cert_cmd="echo 'show ssl cert' | socat '${_statssock}' - | grep -q '^${_pem}$'" + _debug _socat_cert_cmd "${_socat_cert_cmd}" + eval "${_socat_cert_cmd}" + _ret=$? + if [ "${_ret}" != "0" ]; then + _err "Couldn't find '${_pem}' in haproxy 'show ssl cert'" + return "${_ret}" + fi + _socat_cert_set_cmd="echo -e 'set ssl cert ${_pem} <<\n$(cat "${_pem}")\n' | socat '${_statssock}' - | grep -q 'Transaction created'" + _debug _socat_cert_set_cmd "${_socat_cert_set_cmd}" + eval "${_socat_cert_set_cmd}" + _ret=$? + if [ "${_ret}" != "0" ]; then + _err "Can't update '${_pem}' in haproxy" + return "${_ret}" + fi + _socat_cert_commit_cmd="echo 'commit ssl cert ${_pem}' | socat '${_statssock}' - | grep -q '^Success!$'" + _debug _socat_cert_commit_cmd "${_socat_cert_commit_cmd}" + eval "${_socat_cert_commit_cmd}" + _ret=$? + if [ "${_ret}" != "0" ]; then + _err "Can't commit '${_pem}' in haproxy" + return ${_ret} + fi + else + _err "'socat' is not available, couldn't update over stats socket" + fi else - _info "Reload successful" + # Reload HAProxy + _debug _reload "${_reload}" + eval "${_reload}" + _ret=$? + if [ "${_ret}" != "0" ]; then + _err "Error code ${_ret} during reload" + return ${_ret} + else + _info "Reload successful" + fi fi return 0 From 98a7a01dbb85ec7e8ca2fa60497b6f7db70add98 Mon Sep 17 00:00:00 2001 From: William Lallemand Date: Thu, 6 Apr 2023 16:14:44 +0200 Subject: [PATCH 03/16] haproxy: deploy script can add a new certificate over the stats socket DEPLOY_HAPROXY_HOT_UPDATE="yes" now allows to add a new certificate within HAProxy instead of updating an existing one. In order to work, the ${DEPLOY_HAPROXY_PEM_PATH} value must be used as a parameter to the "crt" keyword in the haproxy configuration. The patch uses the following commands over HAProxy stats socket: - show ssl cert - new ssl cert - set ssl cert - commit ssl cert - add ssl crt-list --- deploy/haproxy.sh | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index f6ddd7b9..b4c021d5 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -298,15 +298,35 @@ haproxy_deploy() { if [ "${Le_Deploy_haproxy_hot_update}" = "yes" ]; then # Update certificate over HAProxy stats socket. - _info "Update the certificate over HAProxy stats socket." if _exists socat; then + # look for the certificate on the stats socket, to chose between updating or creating one _socat_cert_cmd="echo 'show ssl cert' | socat '${_statssock}' - | grep -q '^${_pem}$'" _debug _socat_cert_cmd "${_socat_cert_cmd}" eval "${_socat_cert_cmd}" _ret=$? if [ "${_ret}" != "0" ]; then - _err "Couldn't find '${_pem}' in haproxy 'show ssl cert'" - return "${_ret}" + _newcert="1" + _info "Creating new certificate '${_pem}' over HAProxy stats socket." + # certificate wasn't found, it's a new one. We should check if the crt-list exists and creates/inserts the certificate. + _socat_crtlist_show_cmd="echo 'show ssl crt-list' | socat '${_statssock}' - | grep -q '^${Le_Deploy_haproxy_pem_path}$'" + _debug _socat_crtlist_show_cmd "${_socat_crtlist_show_cmd}" + eval "${_socat_crtlist_show_cmd}" + _ret=$? + if [ "${_ret}" != "0" ]; then + _err "Couldn't find '${Le_Deploy_haproxy_pem_path}' in haproxy 'show ssl crt-list'" + return "${_ret}" + fi + # create a new certificate + _socat_new_cmd="echo 'new ssl cert ${_pem}' | socat '${_statssock}' - | grep -q 'New empty'" + _debug _socat_new_cmd "${_socat_new_cmd}" + eval "${_socat_new_cmd}" + _ret=$? + if [ "${_ret}" != "0" ]; then + _err "Couldn't create '${_pem}' in haproxy" + return "${_ret}" + fi + else + _info "Update existing certificate '${_pem}' over HAProxy stats socket." fi _socat_cert_set_cmd="echo -e 'set ssl cert ${_pem} <<\n$(cat "${_pem}")\n' | socat '${_statssock}' - | grep -q 'Transaction created'" _debug _socat_cert_set_cmd "${_socat_cert_set_cmd}" @@ -324,6 +344,17 @@ haproxy_deploy() { _err "Can't commit '${_pem}' in haproxy" return ${_ret} fi + if [ "${_newcert}" = "1" ]; then + # if this is a new certificate, it needs to be inserted into the crt-list` + _socat_cert_add_cmd="echo 'add ssl crt-list ${Le_Deploy_haproxy_pem_path} ${_pem}' | socat '${_statssock}' - | grep -q 'Success!'" + _debug _socat_cert_add_cmd "${_socat_cert_add_cmd}" + eval "${_socat_cert_add_cmd}" + _ret=$? + if [ "${_ret}" != "0" ]; then + _err "Can't update '${_pem}' in haproxy" + return "${_ret}" + fi + fi else _err "'socat' is not available, couldn't update over stats socket" fi From 36fc3210967c839884bea8e2f90a4bdf180c89a2 Mon Sep 17 00:00:00 2001 From: William Lallemand Date: Thu, 30 Nov 2023 15:22:51 +0100 Subject: [PATCH 04/16] haproxy: use the master CLI for hot update DEPLOY_HAPROXY_MASTER_CLI allows to use the HAProxy master CLI instead of a stats socket for DEPLOY_HAPROXY_HOT_UPDATE="yes" The syntax of the master CLI is slightly different, a prefix with the process number need to be added before any command. This patch uses ${_cmdpfx} in front of every socat commands which is filled when the master CLI is used. --- deploy/haproxy.sh | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index b4c021d5..ef7fe45e 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -44,6 +44,11 @@ # # Require the socat binary. DEPLOY_HAPROXY_STATS_SOCKET variable uses the socat # address format. +# +# export DEPLOY_HAPROXY_MASTER_CLI="UNIX:/run/haproxy-master.sock" +# +# OPTIONAL: To use the master CLI with DEPLOY_HAPROXY_HOT_UPDATE="yes" instead +# of a stats socket, use this variable. ######## Public functions ##################### @@ -54,6 +59,7 @@ haproxy_deploy() { _ccert="$3" _cca="$4" _cfullchain="$5" + _cmdpfx="" # Some defaults DEPLOY_HAPROXY_PEM_PATH_DEFAULT="/etc/haproxy" @@ -148,6 +154,16 @@ haproxy_deploy() { Le_Deploy_haproxy_stats_socket="${DEPLOY_HAPROXY_STATS_SOCKET_DEFAULT}" fi + # MASTER_CLI is optional. No defaults are used. When the master CLI is used, + # all commands are sent with a prefix. + _getdeployconf DEPLOY_HAPROXY_MASTER_CLI + _debug2 DEPLOY_HAPROXY_MASTER_CLI "${DEPLOY_HAPROXY_MASTER_CLI}" + if [ -n "${DEPLOY_HAPROXY_MASTER_CLI}" ]; then + Le_Deploy_haproxy_stats_socket="${DEPLOY_HAPROXY_MASTER_CLI}" + _savedomainconf Le_Deploy_haproxy_stats_socket "${Le_Deploy_haproxy_stats_socket}" + _cmdpfx="@1 " # command prefix used for master CLI only. + fi + # Set the suffix depending if we are creating a bundle or not if [ "${Le_Deploy_haproxy_bundle}" = "yes" ]; then _info "Bundle creation requested" @@ -297,18 +313,25 @@ haproxy_deploy() { fi if [ "${Le_Deploy_haproxy_hot_update}" = "yes" ]; then - # Update certificate over HAProxy stats socket. + # set the socket name for messages + if [ -n "${_cmdpfx}" ]; then + _socketname="master CLI" + else + _socketname="stats socket" + fi + + # Update certificate over HAProxy stats socket or master CLI. if _exists socat; then # look for the certificate on the stats socket, to chose between updating or creating one - _socat_cert_cmd="echo 'show ssl cert' | socat '${_statssock}' - | grep -q '^${_pem}$'" + _socat_cert_cmd="echo '${_cmdpfx}show ssl cert' | socat '${_statssock}' - | grep -q '^${_pem}$'" _debug _socat_cert_cmd "${_socat_cert_cmd}" eval "${_socat_cert_cmd}" _ret=$? if [ "${_ret}" != "0" ]; then _newcert="1" - _info "Creating new certificate '${_pem}' over HAProxy stats socket." + _info "Creating new certificate '${_pem}' over HAProxy ${_socketname}." # certificate wasn't found, it's a new one. We should check if the crt-list exists and creates/inserts the certificate. - _socat_crtlist_show_cmd="echo 'show ssl crt-list' | socat '${_statssock}' - | grep -q '^${Le_Deploy_haproxy_pem_path}$'" + _socat_crtlist_show_cmd="echo '${_cmdpfx}show ssl crt-list' | socat '${_statssock}' - | grep -q '^${Le_Deploy_haproxy_pem_path}$'" _debug _socat_crtlist_show_cmd "${_socat_crtlist_show_cmd}" eval "${_socat_crtlist_show_cmd}" _ret=$? @@ -317,7 +340,7 @@ haproxy_deploy() { return "${_ret}" fi # create a new certificate - _socat_new_cmd="echo 'new ssl cert ${_pem}' | socat '${_statssock}' - | grep -q 'New empty'" + _socat_new_cmd="echo '${_cmdpfx}new ssl cert ${_pem}' | socat '${_statssock}' - | grep -q 'New empty'" _debug _socat_new_cmd "${_socat_new_cmd}" eval "${_socat_new_cmd}" _ret=$? @@ -326,9 +349,9 @@ haproxy_deploy() { return "${_ret}" fi else - _info "Update existing certificate '${_pem}' over HAProxy stats socket." + _info "Update existing certificate '${_pem}' over HAProxy ${_socketname}." fi - _socat_cert_set_cmd="echo -e 'set ssl cert ${_pem} <<\n$(cat "${_pem}")\n' | socat '${_statssock}' - | grep -q 'Transaction created'" + _socat_cert_set_cmd="echo -e '${_cmdpfx}set ssl cert ${_pem} <<\n$(cat "${_pem}")\n' | socat '${_statssock}' - | grep -q 'Transaction created'" _debug _socat_cert_set_cmd "${_socat_cert_set_cmd}" eval "${_socat_cert_set_cmd}" _ret=$? @@ -336,7 +359,7 @@ haproxy_deploy() { _err "Can't update '${_pem}' in haproxy" return "${_ret}" fi - _socat_cert_commit_cmd="echo 'commit ssl cert ${_pem}' | socat '${_statssock}' - | grep -q '^Success!$'" + _socat_cert_commit_cmd="echo '${_cmdpfx}commit ssl cert ${_pem}' | socat '${_statssock}' - | grep -q '^Success!$'" _debug _socat_cert_commit_cmd "${_socat_cert_commit_cmd}" eval "${_socat_cert_commit_cmd}" _ret=$? @@ -346,7 +369,7 @@ haproxy_deploy() { fi if [ "${_newcert}" = "1" ]; then # if this is a new certificate, it needs to be inserted into the crt-list` - _socat_cert_add_cmd="echo 'add ssl crt-list ${Le_Deploy_haproxy_pem_path} ${_pem}' | socat '${_statssock}' - | grep -q 'Success!'" + _socat_cert_add_cmd="echo '${_cmdpfx}add ssl crt-list ${Le_Deploy_haproxy_pem_path} ${_pem}' | socat '${_statssock}' - | grep -q 'Success!'" _debug _socat_cert_add_cmd "${_socat_cert_add_cmd}" eval "${_socat_cert_add_cmd}" _ret=$? @@ -356,7 +379,7 @@ haproxy_deploy() { fi fi else - _err "'socat' is not available, couldn't update over stats socket" + _err "'socat' is not available, couldn't update over ${_socketname}" fi else # Reload HAProxy From e09d45c84475f3d1e223c51787fd35c9fe0bada8 Mon Sep 17 00:00:00 2001 From: William Lallemand Date: Fri, 1 Dec 2023 15:29:18 +0100 Subject: [PATCH 05/16] haproxy; don't use '*' in the filename for wildcard domain By default acme.sh uses the '*' character in the filename for wildcard. That can be confusing within HAProxy since the * character in front of a filename in the stat socket is used to specified an uncommitted transaction. This patch replace the '*' by a '_' in the filename. This is only done when using the default filename, the name can still be forced with an asterisk. --- deploy/haproxy.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index ef7fe45e..4b6ca0e1 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -102,6 +102,11 @@ haproxy_deploy() { _savedomainconf Le_Deploy_haproxy_pem_name "${Le_Deploy_haproxy_pem_name}" elif [ -z "${Le_Deploy_haproxy_pem_name}" ]; then Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}" + # We better not have '*' as the first character + if [ "${Le_Deploy_haproxy_pem_name%%"${Le_Deploy_haproxy_pem_name#?}"}" = '*' ]; then + # removes the first characters and add a _ instead + Le_Deploy_haproxy_pem_name="_${Le_Deploy_haproxy_pem_name#?}" + fi fi # BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}" From 122dfa12acea1e6f378a407c4a61da3ece186023 Mon Sep 17 00:00:00 2001 From: Tim Dery Date: Tue, 30 Jan 2024 15:51:55 -0800 Subject: [PATCH 06/16] add imdsv2 support to dns_aws --- dnsapi/dns_aws.sh | 63 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index bb0682a7..e3b8e28b 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -207,6 +207,65 @@ _use_container_role() { } _use_instance_role() { + # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html + # https://aws.amazon.com/blogs/security/get-the-full-benefits-of-imdsv2-and-disable-imdsv1-across-your-aws-infrastructure/ + _url="http://169.254.169.254/latest/meta-data/" + _response=$(curl --write-out "%{http_code}\n" -s -HEAD $_url) + _debug "_response" "$_response" + if [ "$_response" -eq "401" ]; then + _use_imdsv2_instance_role + else + _use_imdsv1_instance_role + fi +} + +_use_imdsv2_instance_role() { + _request_token_url="http://169.254.169.254/latest/api/token" + _instance_role_url="http://169.254.169.254/latest/meta-data/iam" + _request_token="$(curl -s -X PUT "$_request_token_url" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")" + _debug "_request_token" "$_request_token" + if [ -z "$_request_token" ]; then + _debug "Unable to fetch IMDSv2 token from instance metadata" + return 1 + fi + _instance_role_name="$(curl -s -H "X-aws-ec2-metadata-token: $_request_token" http://169.254.169.254/latest/meta-data/iam/security-credentials/)" + _debug "_instance_role_name" "$_instance_role_name" + if [ -z "$_instance_role_name" ]; then + _debug "Unable to fetch instance role name from instance metadata" + return 1 + fi + _use_metadata_imdsv2 "http://169.254.169.254/latest/meta-data/iam/security-credentials/$_instance_role_name" "$_request_token" +} + +_use_metadata_imdsv2() { + _aws_creds="$( + curl -s -H "X-aws-ec2-metadata-token: $2" "$1" | + _normalizeJson | + tr '{,}' '\n' | + while read -r _line; do + _key="$(echo "${_line%%:*}" | tr -d '"')" + _value="${_line#*:}" + _debug3 "_key" "$_key" + _secure_debug3 "_value" "$_value" + case "$_key" in + AccessKeyId) echo "AWS_ACCESS_KEY_ID=$_value" ;; + SecretAccessKey) echo "AWS_SECRET_ACCESS_KEY=$_value" ;; + Token) echo "AWS_SESSION_TOKEN=$_value" ;; + esac + done | + paste -sd' ' - + )" + _secure_debug "_aws_creds" "$_aws_creds" + + if [ -z "$_aws_creds" ]; then + return 1 + fi + + eval "$_aws_creds" + _using_role=true +} + +_use_imdsv1_instance_role() { _url="http://169.254.169.254/latest/meta-data/iam/security-credentials/" _debug "_url" "$_url" if ! _get "$_url" true 1 | _head_n 1 | grep -Fq 200; then @@ -215,10 +274,10 @@ _use_instance_role() { fi _aws_role=$(_get "$_url" "" 1) _debug "_aws_role" "$_aws_role" - _use_metadata "$_url$_aws_role" + _use_metadata_imdsv1 "$_url$_aws_role" } -_use_metadata() { +_use_metadata_imdsv1() { _aws_creds="$( _get "$1" "" 1 | _normalizeJson | From 7da9a45c6151150b16e34d4fabdfc5d8c181d294 Mon Sep 17 00:00:00 2001 From: Tim Dery Date: Wed, 31 Jan 2024 15:39:08 -0800 Subject: [PATCH 07/16] combined functions for cleaner code --- dnsapi/dns_aws.sh | 80 +++++++++++------------------------------------ 1 file changed, 19 insertions(+), 61 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index e3b8e28b..7a5ad4b1 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -209,75 +209,33 @@ _use_container_role() { _use_instance_role() { # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html # https://aws.amazon.com/blogs/security/get-the-full-benefits-of-imdsv2-and-disable-imdsv1-across-your-aws-infrastructure/ - _url="http://169.254.169.254/latest/meta-data/" - _response=$(curl --write-out "%{http_code}\n" -s -HEAD $_url) - _debug "_response" "$_response" - if [ "$_response" -eq "401" ]; then - _use_imdsv2_instance_role - else - _use_imdsv1_instance_role + _instance_role_name_url="http://169.254.169.254/latest/meta-data/iam/security-credentials/" + #_response=$(curl --write-out "%{http_code}\n" -s -HEAD $_url) + if _get "$_instance_role_name_url" true 1 | _head_n 1 | grep -Fq 401; then + _debug "Using IMDSv2" + _token_url="http://169.254.169.254/latest/api/token" + export _H1="X-aws-ec2-metadata-token-ttl-seconds: 21600" + _token="$(_post "" "$_token_url" "" "PUT")" + _debug "_token" "$_token" + if [ -z "$_token" ]; then + _debug "Unable to fetch IMDSv2 token from instance metadata" + return 1 + fi + export _H1="X-aws-ec2-metadata-token: $_token" fi -} -_use_imdsv2_instance_role() { - _request_token_url="http://169.254.169.254/latest/api/token" - _instance_role_url="http://169.254.169.254/latest/meta-data/iam" - _request_token="$(curl -s -X PUT "$_request_token_url" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")" - _debug "_request_token" "$_request_token" - if [ -z "$_request_token" ]; then - _debug "Unable to fetch IMDSv2 token from instance metadata" + if ! _get "$_instance_role_name_url" true 1 | _head_n 1 | grep -Fq 200; then + _debug "Unable to fetch IAM role from instance metadata" return 1 fi - _instance_role_name="$(curl -s -H "X-aws-ec2-metadata-token: $_request_token" http://169.254.169.254/latest/meta-data/iam/security-credentials/)" + _instance_role_name=$(_get "$_instance_role_name_url" "" 1) _debug "_instance_role_name" "$_instance_role_name" - if [ -z "$_instance_role_name" ]; then - _debug "Unable to fetch instance role name from instance metadata" - return 1 - fi - _use_metadata_imdsv2 "http://169.254.169.254/latest/meta-data/iam/security-credentials/$_instance_role_name" "$_request_token" -} -_use_metadata_imdsv2() { - _aws_creds="$( - curl -s -H "X-aws-ec2-metadata-token: $2" "$1" | - _normalizeJson | - tr '{,}' '\n' | - while read -r _line; do - _key="$(echo "${_line%%:*}" | tr -d '"')" - _value="${_line#*:}" - _debug3 "_key" "$_key" - _secure_debug3 "_value" "$_value" - case "$_key" in - AccessKeyId) echo "AWS_ACCESS_KEY_ID=$_value" ;; - SecretAccessKey) echo "AWS_SECRET_ACCESS_KEY=$_value" ;; - Token) echo "AWS_SESSION_TOKEN=$_value" ;; - esac - done | - paste -sd' ' - - )" - _secure_debug "_aws_creds" "$_aws_creds" - - if [ -z "$_aws_creds" ]; then - return 1 - fi - - eval "$_aws_creds" - _using_role=true -} - -_use_imdsv1_instance_role() { - _url="http://169.254.169.254/latest/meta-data/iam/security-credentials/" - _debug "_url" "$_url" - if ! _get "$_url" true 1 | _head_n 1 | grep -Fq 200; then - _debug "Unable to fetch IAM role from instance metadata" - return 1 - fi - _aws_role=$(_get "$_url" "" 1) - _debug "_aws_role" "$_aws_role" - _use_metadata_imdsv1 "$_url$_aws_role" + _use_metadata "$_instance_role_name_url$_instance_role_name" "$_token" } -_use_metadata_imdsv1() { +_use_metadata() { + export _H1="X-aws-ec2-metadata-token: $2" _aws_creds="$( _get "$1" "" 1 | _normalizeJson | From bd247c35f2303a6ff8635dc378be52cdd9a09d04 Mon Sep 17 00:00:00 2001 From: Tim Dery Date: Wed, 31 Jan 2024 15:48:44 -0800 Subject: [PATCH 08/16] remove comments --- dnsapi/dns_aws.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 7a5ad4b1..c87a7012 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -210,7 +210,6 @@ _use_instance_role() { # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html # https://aws.amazon.com/blogs/security/get-the-full-benefits-of-imdsv2-and-disable-imdsv1-across-your-aws-infrastructure/ _instance_role_name_url="http://169.254.169.254/latest/meta-data/iam/security-credentials/" - #_response=$(curl --write-out "%{http_code}\n" -s -HEAD $_url) if _get "$_instance_role_name_url" true 1 | _head_n 1 | grep -Fq 401; then _debug "Using IMDSv2" _token_url="http://169.254.169.254/latest/api/token" From b9157e29cb803e39448cf0ecd68f56e81076be5b Mon Sep 17 00:00:00 2001 From: Tim Dery Date: Wed, 31 Jan 2024 15:52:59 -0800 Subject: [PATCH 09/16] spacing cleanup --- dnsapi/dns_aws.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index c87a7012..855100aa 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -207,15 +207,14 @@ _use_container_role() { } _use_instance_role() { - # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html - # https://aws.amazon.com/blogs/security/get-the-full-benefits-of-imdsv2-and-disable-imdsv1-across-your-aws-infrastructure/ _instance_role_name_url="http://169.254.169.254/latest/meta-data/iam/security-credentials/" + if _get "$_instance_role_name_url" true 1 | _head_n 1 | grep -Fq 401; then _debug "Using IMDSv2" _token_url="http://169.254.169.254/latest/api/token" export _H1="X-aws-ec2-metadata-token-ttl-seconds: 21600" _token="$(_post "" "$_token_url" "" "PUT")" - _debug "_token" "$_token" + _secure_debug3 "_token" "$_token" if [ -z "$_token" ]; then _debug "Unable to fetch IMDSv2 token from instance metadata" return 1 @@ -227,9 +226,9 @@ _use_instance_role() { _debug "Unable to fetch IAM role from instance metadata" return 1 fi + _instance_role_name=$(_get "$_instance_role_name_url" "" 1) _debug "_instance_role_name" "$_instance_role_name" - _use_metadata "$_instance_role_name_url$_instance_role_name" "$_token" } From 22374b81de4ccd73abb89af5ba01a8f8d7bb4a49 Mon Sep 17 00:00:00 2001 From: Tim Dery Date: Wed, 31 Jan 2024 16:02:45 -0800 Subject: [PATCH 10/16] delete a cr to force a workflow run --- dnsapi/dns_aws.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 855100aa..a29cbd1e 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -145,7 +145,6 @@ dns_aws_rm() { fi _sleep 1 return 1 - } #################### Private functions below ################################## From 48e4e41e05353fc470204b86b0135186311ba581 Mon Sep 17 00:00:00 2001 From: Tim Dery Date: Wed, 31 Jan 2024 17:32:56 -0800 Subject: [PATCH 11/16] add cr to force a new gh actions run --- dnsapi/dns_aws.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index a29cbd1e..1df5b21b 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -229,6 +229,7 @@ _use_instance_role() { _instance_role_name=$(_get "$_instance_role_name_url" "" 1) _debug "_instance_role_name" "$_instance_role_name" _use_metadata "$_instance_role_name_url$_instance_role_name" "$_token" + } _use_metadata() { From 2cbdf274b151178e89943706581512d3d5faa7ca Mon Sep 17 00:00:00 2001 From: Scruel Tao Date: Wed, 28 Feb 2024 18:30:06 +0800 Subject: [PATCH 12/16] feat(config_migrate): always remove domain old key & replace old value by new value --- acme.sh | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/acme.sh b/acme.sh index 9c5b1481..80a94530 100755 --- a/acme.sh +++ b/acme.sh @@ -2393,16 +2393,21 @@ _readdomainconf() { #_migratedomainconf oldkey newkey base64encode _migratedomainconf() { - _old_key="$1" - _new_key="$2" - _b64encode="$3" - _value=$(_readdomainconf "$_old_key") - if [ -z "$_value" ]; then - return 1 # oldkey is not found - fi - _savedomainconf "$_new_key" "$_value" "$_b64encode" - _cleardomainconf "$_old_key" - _debug "Domain config $_old_key has been migrated to $_new_key" +  _old_key="$1" +  _new_key="$2" +  _b64encode="$3" +  _old_value=$(_readdomainconf "$_old_key") +  _cleardomainconf "$_old_key" +  if [ -z "$_old_value" ]; then +    return 1 # migrated failed: old value is empty +  fi +  _new_value=$(_readdomainconf "$_new_key") +  if [ -n "$_new_value" ]; then +    _debug "Domain config new key exists, old key $_old_key='$_old_value' has been removed." +    return 1 # migrated failed: old value replaced by new value +  fi +  _savedomainconf "$_new_key" "$_old_value" "$_b64encode" +  _debug "Domain config $_old_key has been migrated to $_new_key." } #_migratedeployconf oldkey newkey base64encode From 79640f6b7d3f58e52a9c7a6c22244c8648e15a80 Mon Sep 17 00:00:00 2001 From: Scruel Tao Date: Wed, 28 Feb 2024 20:02:24 +0800 Subject: [PATCH 13/16] replace wired space symbol --- acme.sh | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/acme.sh b/acme.sh index 80a94530..1b6b299d 100755 --- a/acme.sh +++ b/acme.sh @@ -2393,21 +2393,21 @@ _readdomainconf() { #_migratedomainconf oldkey newkey base64encode _migratedomainconf() { -  _old_key="$1" -  _new_key="$2" -  _b64encode="$3" -  _old_value=$(_readdomainconf "$_old_key") -  _cleardomainconf "$_old_key" -  if [ -z "$_old_value" ]; then -    return 1 # migrated failed: old value is empty -  fi -  _new_value=$(_readdomainconf "$_new_key") -  if [ -n "$_new_value" ]; then -    _debug "Domain config new key exists, old key $_old_key='$_old_value' has been removed." -    return 1 # migrated failed: old value replaced by new value -  fi -  _savedomainconf "$_new_key" "$_old_value" "$_b64encode" -  _debug "Domain config $_old_key has been migrated to $_new_key." + _old_key="$1" + _new_key="$2" + _b64encode="$3" + _old_value=$(_readdomainconf "$_old_key") + _cleardomainconf "$_old_key" + if [ -z "$_old_value" ]; then + return 1 # migrated failed: old value is empty + fi + _new_value=$(_readdomainconf "$_new_key") + if [ -n "$_new_value" ]; then + _debug "Domain config new key exists, old key $_old_key='$_old_value' has been removed." + return 1 # migrated failed: old value replaced by new value + fi + _savedomainconf "$_new_key" "$_old_value" "$_b64encode" + _debug "Domain config $_old_key has been migrated to $_new_key." } #_migratedeployconf oldkey newkey base64encode From 0bf87bf4afedbdc25bbd836f3b6f3d5bde40b042 Mon Sep 17 00:00:00 2001 From: Harald Kapper <4014716+hknet@users.noreply.github.com> Date: Mon, 11 Mar 2024 00:44:53 +0100 Subject: [PATCH 14/16] dns-record TTL set to 300 reduce TTL for the TXT record from 3600 to 300 to have an easier way to replicate changes for the dns-verification in case multiple submissions for a specific record/domain are done within an hour. --- dnsapi/dns_kappernet.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_kappernet.sh b/dnsapi/dns_kappernet.sh index 0a8951cb..e9ea0a4d 100644 --- a/dnsapi/dns_kappernet.sh +++ b/dnsapi/dns_kappernet.sh @@ -41,7 +41,7 @@ dns_kappernet_add() { _debug _domain "DOMAIN: $_domain" _info "Trying to add TXT DNS Record" - data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%223600%22%2C%22prio%22%3A%22%22%7D" + data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%22300%22%2C%22prio%22%3A%22%22%7D" if _kappernet_api GET "action=new&subject=$_domain&data=$data"; then if _contains "$response" "{\"OK\":true"; then @@ -81,7 +81,7 @@ dns_kappernet_rm() { _saveaccountconf_mutable KAPPERNETDNS_Secret "$KAPPERNETDNS_Secret" _info "Trying to remove the TXT Record: $fullhostname containing $txtvalue" - data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%223600%22%2C%22prio%22%3A%22%22%7D" + data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%22300%22%2C%22prio%22%3A%22%22%7D" if _kappernet_api GET "action=del&subject=$fullhostname&data=$data"; then if _contains "$response" "{\"OK\":true"; then return 0 From b2c6b9a320a1c729bc35091a43ee5414f822da5a Mon Sep 17 00:00:00 2001 From: Tim Dery Date: Mon, 11 Mar 2024 10:33:14 -0700 Subject: [PATCH 15/16] attempt _use_metadata fix from j-c-m --- dnsapi/dns_aws.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 1df5b21b..27923b64 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -239,7 +239,7 @@ _use_metadata() { _normalizeJson | tr '{,}' '\n' | while read -r _line; do - _key="$(echo "${_line%%:*}" | tr -d '"')" + _key="$(echo "${_line%%:*}" | tr -d '\"')" _value="${_line#*:}" _debug3 "_key" "$_key" _secure_debug3 "_value" "$_value" From 2728d2aa6eec98338affaa92adf93bc692bd3fcf Mon Sep 17 00:00:00 2001 From: neil Date: Mon, 18 Mar 2024 21:09:49 +0100 Subject: [PATCH 16/16] fix format --- deploy/haproxy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh index 4b6ca0e1..c8491d92 100644 --- a/deploy/haproxy.sh +++ b/deploy/haproxy.sh @@ -373,7 +373,7 @@ haproxy_deploy() { return ${_ret} fi if [ "${_newcert}" = "1" ]; then - # if this is a new certificate, it needs to be inserted into the crt-list` + # if this is a new certificate, it needs to be inserted into the crt-list` _socat_cert_add_cmd="echo '${_cmdpfx}add ssl crt-list ${Le_Deploy_haproxy_pem_path} ${_pem}' | socat '${_statssock}' - | grep -q 'Success!'" _debug _socat_cert_add_cmd "${_socat_cert_add_cmd}" eval "${_socat_cert_add_cmd}"