From ed72b090af3fce68195b7ab3d605d70831fca45d Mon Sep 17 00:00:00 2001 From: Keith Chiem Date: Wed, 18 Oct 2023 20:32:39 -0700 Subject: [PATCH 1/8] deploy hook for Ruckus ZoneDirector / Unleashed --- deploy/ruckus.sh | 110 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100755 deploy/ruckus.sh diff --git a/deploy/ruckus.sh b/deploy/ruckus.sh new file mode 100755 index 00000000..d16f40e4 --- /dev/null +++ b/deploy/ruckus.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash + +# Here is a script to deploy cert to Ruckus Zone Director/Unleashed. +# +# Adapted from: +# https://ms264556.net/pages/PfSenseLetsEncryptToRuckus +# +# ```sh +# acme.sh --deploy -d ruckus.example.com --deploy-hook ruckus +# ``` +# +# Then you need to set the environment variables for the +# deploy script to work. +# +# ```sh +# export RUCKUS_HOST=ruckus.example.com +# export RUCKUS_USER=myruckususername +# export RUCKUS_PASS=myruckuspassword +# +# acme.sh --deploy -d ruckus.example.com --deploy-hook ruckus +# ``` +# +# returns 0 means success, otherwise error. + +######## Public functions ##################### + +#domain keyfile certfile cafile fullchain +ruckus_deploy() { + _cdomain="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + _cfullchain="$5" + _err_code=0 + + _debug _cdomain "$_cdomain" + _debug _ckey "$_ckey" + _debug _ccert "$_ccert" + _debug _cca "$_cca" + _debug _cfullchain "$_cfullchain" + + _getdeployconf RUCKUS_HOST + _getdeployconf RUCKUS_USER + _getdeployconf RUCKUS_PASS + + if [ -z "$RUCKUS_HOST" ]; then + _debug "Using _cdomain as RUCKUS_HOST, please set if not correct." + RUCKUS_HOST="$_cdomain" + fi + + if [ -z "$RUCKUS_USER" ]; then + _err "Need to set the env variable RUCKUS_USER" + return 1 + fi + + if [ -z "$RUCKUS_PASS" ]; then + _err "Need to set the env variable RUCKUS_PASS" + return 1 + fi + + _savedeployconf RUCKUS_HOST "$RUCKUS_HOST" + _savedeployconf RUCKUS_USER "$RUCKUS_USER" + _savedeployconf RUCKUS_PASS "$RUCKUS_PASS" + + _debug RUCKUS_HOST "$RUCKUS_HOST" + _debug RUCKUS_USER "$RUCKUS_USER" + _debug RUCKUS_PASS "$RUCKUS_PASS" + + COOKIE_JAR=$(mktemp) + cleanup() { + rm $COOKIE_JAR + } + trap cleanup EXIT + + LOGIN_URL=$(curl https://$RUCKUS_HOST -ksSLo /dev/null -w '%{url_effective}') + _debug LOGIN_URL "$LOGIN_URL" + + XSS=$(curl -ksSic $COOKIE_JAR $LOGIN_URL -d username=$RUCKUS_USER -d password="$RUCKUS_PASS" -d ok='Log In' | awk '/^HTTP_X_CSRF_TOKEN:/ { print $2 }' | tr -d '\040\011\012\015') + _debug XSS "$XSS" + + if [ -n "$XSS" ]; then + _info "Authentication successful" + else + _err "Authentication failed" + return 1 + fi + + BASE_URL=$(dirname $LOGIN_URL) + CONF_ARGS="-ksSo /dev/null -b $COOKIE_JAR -c $COOKIE_JAR" + UPLOAD="$CONF_ARGS $BASE_URL/_upload.jsp?request_type=xhr" + CMD="$CONF_ARGS $BASE_URL/_cmdstat.jsp" + + REPLACE_CERT_AJAX='' + CERT_REBOOT_AJAX='' + + _info "Uploading certificate" + curl $UPLOAD -H "X-CSRF-Token: $XSS" -F "u=@$_ccert" -F action=uploadcert -F callback=uploader_uploadcert || return 1 + + _info "Uploading private key" + curl $UPLOAD -H "X-CSRF-Token: $XSS" -F "u=@$_ckey" -F action=uploadprivatekey -F callback=uploader_uploadprivatekey || return 1 + + _info "Replacing certificate" + curl $CMD -H "X-CSRF-Token: $XSS" --data-raw "$REPLACE_CERT_AJAX" || return 1 + + _info "Rebooting" + curl $CMD -H "X-CSRF-Token: $XSS" --data-raw "$CERT_REBOOT_AJAX" || return 1 + + return 0 +} + From b6a77e0231923ab13a31c00c73e2727ec2adb070 Mon Sep 17 00:00:00 2001 From: ms264556 <29752086+ms264556@users.noreply.github.com> Date: Sun, 10 Nov 2024 22:12:38 +1300 Subject: [PATCH 2/8] Ruckus - use _get() and _post() --- deploy/ruckus.sh | 134 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 99 insertions(+), 35 deletions(-) diff --git a/deploy/ruckus.sh b/deploy/ruckus.sh index d16f40e4..cbd5e353 100755 --- a/deploy/ruckus.sh +++ b/deploy/ruckus.sh @@ -1,9 +1,8 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh -# Here is a script to deploy cert to Ruckus Zone Director/Unleashed. -# -# Adapted from: -# https://ms264556.net/pages/PfSenseLetsEncryptToRuckus +# Here is a script to deploy cert to Ruckus ZoneDirector / Unleashed. +# +# Public domain, 2024, Tony Rielly # # ```sh # acme.sh --deploy -d ruckus.example.com --deploy-hook ruckus @@ -13,11 +12,11 @@ # deploy script to work. # # ```sh -# export RUCKUS_HOST=ruckus.example.com +# export RUCKUS_HOST=myruckus.example.com # export RUCKUS_USER=myruckususername # export RUCKUS_PASS=myruckuspassword # -# acme.sh --deploy -d ruckus.example.com --deploy-hook ruckus +# acme.sh --deploy -d myruckus.example.com --deploy-hook ruckus # ``` # # returns 0 means success, otherwise error. @@ -66,45 +65,110 @@ ruckus_deploy() { _debug RUCKUS_USER "$RUCKUS_USER" _debug RUCKUS_PASS "$RUCKUS_PASS" - COOKIE_JAR=$(mktemp) - cleanup() { - rm $COOKIE_JAR - } - trap cleanup EXIT - - LOGIN_URL=$(curl https://$RUCKUS_HOST -ksSLo /dev/null -w '%{url_effective}') - _debug LOGIN_URL "$LOGIN_URL" - - XSS=$(curl -ksSic $COOKIE_JAR $LOGIN_URL -d username=$RUCKUS_USER -d password="$RUCKUS_PASS" -d ok='Log In' | awk '/^HTTP_X_CSRF_TOKEN:/ { print $2 }' | tr -d '\040\011\012\015') - _debug XSS "$XSS" + export HTTPS_INSECURE=1 + export ACME_HTTP_NO_REDIRECTS=1 + + _info Discovering the login URL + _get "https://$RUCKUS_HOST" >/dev/null + _login_url="$(_response_header 'Location')" + if [ -n "$_login_url" ]; then + _login_path=$(echo "$_login_url" | sed 's|https\?://[^/]\+||') + if [ -z "$_login_path" ]; then + # redirect was to a different host + _get "$_login_url" >/dev/null + _login_url="$(_response_header 'Location')" + fi + fi - if [ -n "$XSS" ]; then - _info "Authentication successful" - else - _err "Authentication failed" + if [ -z "${_login_url}" ]; then + _err "Connection failed: couldn't find login page." return 1 fi + + _base_url=$(dirname "$_login_url") + _login_page=$(basename "$_login_url") - BASE_URL=$(dirname $LOGIN_URL) - CONF_ARGS="-ksSo /dev/null -b $COOKIE_JAR -c $COOKIE_JAR" - UPLOAD="$CONF_ARGS $BASE_URL/_upload.jsp?request_type=xhr" - CMD="$CONF_ARGS $BASE_URL/_cmdstat.jsp" + if [ "$_login_page" = "index.html" ]; then + _err "Connection temporarily unavailable: Unleashed Rebuilding." + return 1 + fi - REPLACE_CERT_AJAX='' - CERT_REBOOT_AJAX='' + if [ "$_login_page" = "wizard.jsp" ]; then + _err "Connection failed: Setup Wizard not complete." + return 1 + fi + + _info Login + _username_encoded="$(printf "%s" "$RUCKUS_USER" | _url_encode)" + _password_encoded="$(printf "%s" "$RUCKUS_PASS" | _url_encode)" + _login_query="$(printf "%s" "username=${_username_encoded}&password=${_password_encoded}&ok=Log+In")" + _post "$_login_query" "$_login_url" >/dev/null + + _login_code="$(_response_code)" + if [ "$_login_code" = "200" ]; then + _err "Login failed: incorrect credentials." + return 1 + fi + + _info Collect Session Cookie + _H1="Cookie: $(_response_cookie)" + export _H1 + _info Collect CSRF Token + _H2="X-CSRF-Token: $(_response_header 'HTTP_X_CSRF_TOKEN')" + export _H2 _info "Uploading certificate" - curl $UPLOAD -H "X-CSRF-Token: $XSS" -F "u=@$_ccert" -F action=uploadcert -F callback=uploader_uploadcert || return 1 - + _post_upload "uploadcert" "$_cfullchain" + _info "Uploading private key" - curl $UPLOAD -H "X-CSRF-Token: $XSS" -F "u=@$_ckey" -F action=uploadprivatekey -F callback=uploader_uploadprivatekey || return 1 + _post_upload "uploadprivatekey" "$_ckey" _info "Replacing certificate" - curl $CMD -H "X-CSRF-Token: $XSS" --data-raw "$REPLACE_CERT_AJAX" || return 1 + _replace_cert_ajax='' + _post "$_replace_cert_ajax" "$_base_url/_cmdstat.jsp" >/dev/null + + info "Rebooting" + _cert_reboot_ajax='' + _post "$_cert_reboot_ajax" "$_base_url/_cmdstat.jsp" >/dev/null + + return 0 +} + +_response_code() { + < "$HTTP_HEADER" _egrep_o "^HTTP[^ ]* .*$" | cut -d " " -f 2-100 | tr -d "\f\n" | _egrep_o "^[0-9]*" +} - _info "Rebooting" - curl $CMD -H "X-CSRF-Token: $XSS" --data-raw "$CERT_REBOOT_AJAX" || return 1 +_response_header() { + < "$HTTP_HEADER" grep -i "^$1:" | cut -d ':' -f 2- | tr -d "\r\n\t " +} - return 0 +_response_cookie() { + _response_header 'Set-Cookie' | awk -F';' '{for(i=1;i<=NF;i++) if (tolower($i) !~ /(path|domain|expires|max-age|secure|httponly|samesite)/) printf "%s; ", $i}' | sed 's/; $//' } +_post_upload() { + _post_action="$1" + _post_file="$2" + _post_url="$3" + + _post_boundary="----FormBoundary$(date "+%s%N")" + + _post_data="$({ + printf -- "--%s\r\n" "$_post_boundary" + printf -- "Content-Disposition: form-data; name=\"u\"; filename=\"%s\"\r\n" "$_post_action" + printf -- "Content-Type: application/octet-stream\r\n\r\n" + printf -- "%s\r\n" "$(cat "$_post_file")" + + printf -- "--%s\r\n" "$_post_boundary" + printf -- "Content-Disposition: form-data; name=\"action\"\r\n\r\n" + printf -- "%s\r\n" "$_post_action" + + printf -- "--%s\r\n" "$_post_boundary" + printf -- "Content-Disposition: form-data; name=\"callback\"\r\n\r\n" + printf -- "%s\r\n" "uploader_$_post_action" + + printf -- "--%s--\r\n\r\n" "$_post_boundary" + })" + + _post "$_post_data" "$_base_url/_upload.jsp?request_type=xhr" "" "" "multipart/form-data; boundary=$_post_boundary" >/dev/null +} From 717802611afb4d3e36c2aa2796b013355a0643f7 Mon Sep 17 00:00:00 2001 From: ms264556 <29752086+ms264556@users.noreply.github.com> Date: Sun, 10 Nov 2024 22:43:57 +1300 Subject: [PATCH 3/8] remove dead code --- deploy/ruckus.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/deploy/ruckus.sh b/deploy/ruckus.sh index cbd5e353..def8197d 100755 --- a/deploy/ruckus.sh +++ b/deploy/ruckus.sh @@ -149,8 +149,7 @@ _response_cookie() { _post_upload() { _post_action="$1" _post_file="$2" - _post_url="$3" - + _post_boundary="----FormBoundary$(date "+%s%N")" _post_data="$({ From 0cc74b7cfe910d6961cd225e70dfaba884a418b4 Mon Sep 17 00:00:00 2001 From: ms264556 <29752086+ms264556@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:50:51 +1300 Subject: [PATCH 4/8] fix insecure password debug and _info typo --- deploy/ruckus.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/ruckus.sh b/deploy/ruckus.sh index def8197d..d83675bb 100755 --- a/deploy/ruckus.sh +++ b/deploy/ruckus.sh @@ -63,7 +63,7 @@ ruckus_deploy() { _debug RUCKUS_HOST "$RUCKUS_HOST" _debug RUCKUS_USER "$RUCKUS_USER" - _debug RUCKUS_PASS "$RUCKUS_PASS" + _secure_debug RUCKUS_PASS "$RUCKUS_PASS" export HTTPS_INSECURE=1 export ACME_HTTP_NO_REDIRECTS=1 @@ -127,7 +127,7 @@ ruckus_deploy() { _replace_cert_ajax='' _post "$_replace_cert_ajax" "$_base_url/_cmdstat.jsp" >/dev/null - info "Rebooting" + _info "Rebooting" _cert_reboot_ajax='' _post "$_cert_reboot_ajax" "$_base_url/_cmdstat.jsp" >/dev/null From e98e7a232ffa70d37bc4af6260e754a5a5060b98 Mon Sep 17 00:00:00 2001 From: ms264556 <29752086+ms264556@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:27:36 +1300 Subject: [PATCH 5/8] Fix info logging --- deploy/ruckus.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deploy/ruckus.sh b/deploy/ruckus.sh index d83675bb..3b147c25 100755 --- a/deploy/ruckus.sh +++ b/deploy/ruckus.sh @@ -68,7 +68,7 @@ ruckus_deploy() { export HTTPS_INSECURE=1 export ACME_HTTP_NO_REDIRECTS=1 - _info Discovering the login URL + _info "Discovering the login URL" _get "https://$RUCKUS_HOST" >/dev/null _login_url="$(_response_header 'Location')" if [ -n "$_login_url" ]; then @@ -98,7 +98,7 @@ ruckus_deploy() { return 1 fi - _info Login + _info "Login" _username_encoded="$(printf "%s" "$RUCKUS_USER" | _url_encode)" _password_encoded="$(printf "%s" "$RUCKUS_PASS" | _url_encode)" _login_query="$(printf "%s" "username=${_username_encoded}&password=${_password_encoded}&ok=Log+In")" @@ -110,10 +110,10 @@ ruckus_deploy() { return 1 fi - _info Collect Session Cookie + _info "Collect Session Cookie" _H1="Cookie: $(_response_cookie)" export _H1 - _info Collect CSRF Token + _info "Collect CSRF Token" _H2="X-CSRF-Token: $(_response_header 'HTTP_X_CSRF_TOKEN')" export _H2 From 38c41b72d6acc0edfe6d7a1fa072fe16a1505ff5 Mon Sep 17 00:00:00 2001 From: ms264556 <29752086+ms264556@users.noreply.github.com> Date: Thu, 14 Nov 2024 07:16:38 +1300 Subject: [PATCH 6/8] fix acme.sh PR shfmt failure --- deploy/ruckus.sh | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/deploy/ruckus.sh b/deploy/ruckus.sh index 3b147c25..b4249472 100755 --- a/deploy/ruckus.sh +++ b/deploy/ruckus.sh @@ -1,7 +1,7 @@ #!/usr/bin/env sh # Here is a script to deploy cert to Ruckus ZoneDirector / Unleashed. -# +# # Public domain, 2024, Tony Rielly # # ```sh @@ -84,20 +84,20 @@ ruckus_deploy() { _err "Connection failed: couldn't find login page." return 1 fi - + _base_url=$(dirname "$_login_url") _login_page=$(basename "$_login_url") - if [ "$_login_page" = "index.html" ]; then + if [ "$_login_page" = "index.html" ]; then _err "Connection temporarily unavailable: Unleashed Rebuilding." return 1 fi - if [ "$_login_page" = "wizard.jsp" ]; then + if [ "$_login_page" = "wizard.jsp" ]; then _err "Connection failed: Setup Wizard not complete." return 1 fi - + _info "Login" _username_encoded="$(printf "%s" "$RUCKUS_USER" | _url_encode)" _password_encoded="$(printf "%s" "$RUCKUS_PASS" | _url_encode)" @@ -109,7 +109,7 @@ ruckus_deploy() { _err "Login failed: incorrect credentials." return 1 fi - + _info "Collect Session Cookie" _H1="Cookie: $(_response_cookie)" export _H1 @@ -119,27 +119,27 @@ ruckus_deploy() { _info "Uploading certificate" _post_upload "uploadcert" "$_cfullchain" - + _info "Uploading private key" _post_upload "uploadprivatekey" "$_ckey" _info "Replacing certificate" _replace_cert_ajax='' _post "$_replace_cert_ajax" "$_base_url/_cmdstat.jsp" >/dev/null - + _info "Rebooting" _cert_reboot_ajax='' _post "$_cert_reboot_ajax" "$_base_url/_cmdstat.jsp" >/dev/null - + return 0 } _response_code() { - < "$HTTP_HEADER" _egrep_o "^HTTP[^ ]* .*$" | cut -d " " -f 2-100 | tr -d "\f\n" | _egrep_o "^[0-9]*" + _egrep_o <"$HTTP_HEADER" "^HTTP[^ ]* .*$" | cut -d " " -f 2-100 | tr -d "\f\n" | _egrep_o "^[0-9]*" } _response_header() { - < "$HTTP_HEADER" grep -i "^$1:" | cut -d ':' -f 2- | tr -d "\r\n\t " + grep <"$HTTP_HEADER" -i "^$1:" | cut -d ':' -f 2- | tr -d "\r\n\t " } _response_cookie() { @@ -149,9 +149,9 @@ _response_cookie() { _post_upload() { _post_action="$1" _post_file="$2" - + _post_boundary="----FormBoundary$(date "+%s%N")" - + _post_data="$({ printf -- "--%s\r\n" "$_post_boundary" printf -- "Content-Disposition: form-data; name=\"u\"; filename=\"%s\"\r\n" "$_post_action" From 2bb5fbdee549f6f1baacd2e7cc3cd8f1a4c4fc48 Mon Sep 17 00:00:00 2001 From: ms264556 <29752086+ms264556@users.noreply.github.com> Date: Thu, 14 Nov 2024 07:21:19 +1300 Subject: [PATCH 7/8] Remove HTTPS_INSECURE --- deploy/ruckus.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/deploy/ruckus.sh b/deploy/ruckus.sh index b4249472..1bfa6bd6 100755 --- a/deploy/ruckus.sh +++ b/deploy/ruckus.sh @@ -65,7 +65,6 @@ ruckus_deploy() { _debug RUCKUS_USER "$RUCKUS_USER" _secure_debug RUCKUS_PASS "$RUCKUS_PASS" - export HTTPS_INSECURE=1 export ACME_HTTP_NO_REDIRECTS=1 _info "Discovering the login URL" From 4232923641479da186a21009cd1aae9617801da4 Mon Sep 17 00:00:00 2001 From: ms264556 <29752086+ms264556@users.noreply.github.com> Date: Fri, 15 Nov 2024 12:39:41 +1300 Subject: [PATCH 8/8] Remove awk usage and refuse redirect to new host --- deploy/ruckus.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deploy/ruckus.sh b/deploy/ruckus.sh index 1bfa6bd6..f62e2fc0 100755 --- a/deploy/ruckus.sh +++ b/deploy/ruckus.sh @@ -74,8 +74,8 @@ ruckus_deploy() { _login_path=$(echo "$_login_url" | sed 's|https\?://[^/]\+||') if [ -z "$_login_path" ]; then # redirect was to a different host - _get "$_login_url" >/dev/null - _login_url="$(_response_header 'Location')" + _err "Connection failed: redirected to a different host. Configure Unleashed with a Preferred Master or Management Interface." + return 1 fi fi @@ -142,7 +142,7 @@ _response_header() { } _response_cookie() { - _response_header 'Set-Cookie' | awk -F';' '{for(i=1;i<=NF;i++) if (tolower($i) !~ /(path|domain|expires|max-age|secure|httponly|samesite)/) printf "%s; ", $i}' | sed 's/; $//' + _response_header 'Set-Cookie' | sed 's/;.*//' } _post_upload() {