From bb2f6cc9f8459bb061d430f92cb7a09672aa696a Mon Sep 17 00:00:00 2001 From: Ioannis Tsouvalas Date: Thu, 7 Sep 2023 11:59:59 +0000 Subject: [PATCH 1/2] allows stateless acme cert renewal [Features] * Introduces haproxy-acme add-on which allows for stateless cert renewal on acme generated certificates. @itsouvalas --- hooks/addon | 104 +++++++++++++++++++++++++++++-- hooks/blueprint | 13 ++++ overlay/routing/haproxy-acme.yml | 15 +++++ 3 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 overlay/routing/haproxy-acme.yml diff --git a/hooks/addon b/hooks/addon index caa81083..26d5aaf9 100755 --- a/hooks/addon +++ b/hooks/addon @@ -1,6 +1,18 @@ #!/bin/bash set -eu +# Automatically set to 'true' when called by blueprint which removes all echo +# messages and uses default values for the answers +: "${SILENT_MODE:=false}" + +info() { echo -e "\n\e[32m$*\e[0m"; } + +header() { + local token="=" + local line=$(printf "${token}%.0s" {1..80}) + info "\n${line}\n${token} $*\n${line}" +} + list() { echo "The following addons are defined:" echo @@ -17,6 +29,10 @@ list() { echo echo " stratos Deploy Stratos, the Cloud Foundry web console." echo + echo " acme-client Install and configure the acme-client for stateless" + echo " crtificate renewal - preequisite for enabling" + echo " the '- haproxy-acme' feature" + echo } login() { @@ -47,10 +63,86 @@ login() { cf target } +installacme(){ + email="$(git config --global user.email)" + if [[ $SILENT_MODE != true ]]; then + header "Installing acme.sh shell script" + + prompt_for email line --default "$email" \ + 'Type your email address' + fi + curl -s https://get.acme.sh | sh -s email=$email > /dev/null 2>&1 +} + + + +registeracme(){ + if [[ $SILENT_MODE != true ]]; then + header "Registering acme client and setting vault credential" + + lab_targets=($(safe targets --json 2>/dev/null | jq -r '.[].name')) + + echo -e "Available targets: ${lab_targets[*]}" + current_target=$(safe target --json 2>/dev/null | jq -r '.name') + + echo -e "You are currently targeting: '${current_target}'" + prompt_for change_target boolean --default false \ + "Would you like to use another target?" + + if [[ $change_target == "true" ]]; then + prompt_for new_target line 'Type your target' + safe target "$new_target" + fi + fi + current_thumbprint=$(safe get "$GENESIS_EXODUS_MOUNT$GENESIS_ENVIRONMENT/acme/acme:thumbprint" 2>/dev/null || true) + + if [[ -n $current_thumbprint ]]; then + if [[ $SILENT_MODE != true ]]; then + prompt_for update_thumbprint boolean --default false \ + "You have already set your thumbprint to '${current_thumbprint}'. Would you like to update it?" + else + update_thumbprint=false + fi + else + update_thumbprint=true + fi + + + if [[ $update_thumbprint == true ]]; then + thumbprint=$(~/.acme.sh/acme.sh --register-account | awk -F"'" '/ACCOUNT_THUMBPRINT=/{print $2}') + if [[ $SILENT_MODE != true ]]; then + echo -e "\n$GENESIS_EXODUS_MOUNT$GENESIS_ENVIRONMENT/acme/acme is being set with the following value:" + fi + safe set "$GENESIS_EXODUS_MOUNT$GENESIS_ENVIRONMENT/acme/acme" thumbprint="$thumbprint" + else + if [[ $SILENT_MODE != true ]]; then + echo "Skipping thumbprint update." + fi + fi +} + +setup_acme(){ + if [[ -x ~/.acme.sh/acme.sh ]]; then + acme_version=$(~/.acme.sh/acme.sh -v 2>/dev/null || true) + url=$(echo "$acme_version" | grep -Eo 'https?://[^ ]+' || true) + version=$(echo "$acme_version" | grep -Eo 'v[0-9]+\.[0-9]+\.[0-9]+' || true) + fi + if [[ -n $version ]]; then + if [[ $SILENT_MODE != true ]]; then + echo "You are currently using acme.sh '${version}' from '${url}'" + fi + else + installacme + fi + registeracme + +} + + case $GENESIS_ADDON_SCRIPT in - (list) list ; exit 0 ;; + (list) list ;; - (login) login ; exit 0 ;; + (login) login ;; (remigrate) # Migrate the secrets @@ -90,9 +182,13 @@ case $GENESIS_ADDON_SCRIPT in (smoketest) "$GENESIS_BOSH_COMMAND" -e "$BOSH_ENVIRONMENT" -d "$BOSH_DEPLOYMENT" run-errand smoke_tests ;; - + + (acme-client) + setup_acme + ;; + (*) run_extended_addon "$@" ;; # This will run the addon script in the # $GENESIS_ADDON_SCRIPT file, if it exists. # Ex: hooks/addon-stratos esac - +exit 0 \ No newline at end of file diff --git a/hooks/blueprint b/hooks/blueprint index 90b6e00c..3ecb6ae6 100755 --- a/hooks/blueprint +++ b/hooks/blueprint @@ -448,6 +448,9 @@ features::validate() { (gcp-use-access-key) features+=( "$want" ) ;; + (haproxy-acme) + features+=( "$want" ) + ;; (enable-service-discovery|ssh-proxy-on-routers|no-tcp-routers) features+=( "$want" ) ;; @@ -820,6 +823,16 @@ features::process() { want_feature "small-footprint" && manifests+=( "overlay/routing/haproxy-small-footprint.yml" ) + + if want_feature "haproxy-acme"; then + manifests+=( "overlay/routing/haproxy-acme.yml" ) + ( + GENESIS_ADDON_SCRIPT=acme-client + SILENT_MODE=true + source hooks/addon + ) + fi + fi done diff --git a/overlay/routing/haproxy-acme.yml b/overlay/routing/haproxy-acme.yml new file mode 100644 index 00000000..b1e1eefe --- /dev/null +++ b/overlay/routing/haproxy-acme.yml @@ -0,0 +1,15 @@ +--- +meta: + ocfp: + certs: + acme_thumbprint: (( vault $GENESIS_EXODUS_MOUNT genesis.env "/acme/acme:thumbprint" )) +instance_groups: +- name: haproxy + jobs: + - name: haproxy + properties: + ha_proxy: + frontend_config: | + acl is_acme_challenge path_beg /.well-known/acme-challenge/ + http-request return status 200 content-type text/plain lf-string "%[path,field(-1,/)].%[env(ACCOUNT_THUMBPRINT)]\n" if is_acme_challenge + global_config: (( concat "setenv ACCOUNT_THUMBPRINT '" meta.ocfp.certs.acme_thumbprint "'")) \ No newline at end of file From 46502e5917d035c7a60d7c34e5a2be993d5123cc Mon Sep 17 00:00:00 2001 From: Ioannis Tsouvalas Date: Mon, 9 Oct 2023 08:34:19 +0000 Subject: [PATCH 2/2] update vault path for thumbprint [Chores] * Use single location for all cert management configs @itsouvalas --- overlay/routing/haproxy-acme.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overlay/routing/haproxy-acme.yml b/overlay/routing/haproxy-acme.yml index b1e1eefe..1db6489e 100644 --- a/overlay/routing/haproxy-acme.yml +++ b/overlay/routing/haproxy-acme.yml @@ -2,7 +2,7 @@ meta: ocfp: certs: - acme_thumbprint: (( vault $GENESIS_EXODUS_MOUNT genesis.env "/acme/acme:thumbprint" )) + acme_thumbprint: (( vault $GENESIS_EXODUS_MOUNT genesis.env "/acme/settings/cf-cert-management:acme_thumbprint" )) instance_groups: - name: haproxy jobs: