From a8305ba541273a2df61405352a30b585bd4359d4 Mon Sep 17 00:00:00 2001 From: Gondolf <145931259+vGondolf@users.noreply.github.com> Date: Mon, 3 Feb 2025 21:41:49 +0100 Subject: [PATCH 1/3] Create fortigate.sh Deploy certificates to FortiGate firewall --- deploy/fortigate.sh | 104 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 deploy/fortigate.sh diff --git a/deploy/fortigate.sh b/deploy/fortigate.sh new file mode 100644 index 00000000..e5b80a57 --- /dev/null +++ b/deploy/fortigate.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env sh +# Script to deploy a certificate to FortiGate via API and set it as the current web GUI certificate. +# +# REQUIRED: +# export FGT_HOST="fortigate_hostname-or-ip" +# export FGT_TOKEN="fortigate_api_token" +# +# OPTIONAL: +# export FGT_PORT="10443" # Custom HTTPS port (defaults to 443 if not set) +# +# Run `acme.sh --deploy -d example.com --deploy-hook fortigate --insecure` to use this script. +# '--insecure' is required to allow acme.sh to connect to the FortiGate API over HTTPS without a pre-existing valid certificate. +# + +# Function to parse response from the firewall +parse_response() { + status=$(echo "$1" | grep -o '"status":[ ]*"[^"]*"' | sed 's/"status":[ ]*"\([^"]*\)"/\1/') + error_code=$(echo "$1" | grep -o '"error":[ ]*[-0-9]*' | sed 's/"error":[ ]*\([-0-9]*\)/\1/') + http_status=$(echo "$1" | grep -o '"http_status":[ ]*[0-9]*' | sed 's/"http_status":[ ]*\([0-9]*\)/\1/') + + if [ "$status" != "success" ]; then + _err "FortiGate error: HTTP $http_status, Code $error_code" + return 1 + else + _debug "Operation successful." + return 0 + fi +} + +# Function to deploy certificate to firewall +deployer() { + cert_base64=$(cat "$_cfullchain" | _base64) + key_base64=$(cat "$_ckey" | _base64) + + payload=$(cat < Date: Tue, 4 Feb 2025 14:19:09 +0100 Subject: [PATCH 2/3] Update fortigate.sh Workaround for issue with duplicate certificate names --- deploy/fortigate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/fortigate.sh b/deploy/fortigate.sh index e5b80a57..6852d4b1 100644 --- a/deploy/fortigate.sh +++ b/deploy/fortigate.sh @@ -71,7 +71,7 @@ EOF # Main function fortigate_deploy() { - _cdomain=$(echo "$1" | sed 's/*/WILDCARD_/g') + _cdomain="$(echo "$1" | sed 's/*/WILDCARD_/g')_$(date -u +"%Y-%m-%d")" # Append date to certname to avoid conflicts _ckey="$2" _cfullchain="$5" From cda5b4db6ff030d0b17ef7c7d4086790014e4f11 Mon Sep 17 00:00:00 2001 From: Gondolf <145931259+vGondolf@users.noreply.github.com> Date: Tue, 4 Feb 2025 14:29:04 +0100 Subject: [PATCH 3/3] Update fortigate.sh --- deploy/fortigate.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/deploy/fortigate.sh b/deploy/fortigate.sh index 6852d4b1..169f5d8a 100644 --- a/deploy/fortigate.sh +++ b/deploy/fortigate.sh @@ -8,18 +8,17 @@ # OPTIONAL: # export FGT_PORT="10443" # Custom HTTPS port (defaults to 443 if not set) # +# This script is intended for use as an acme.sh deploy hook. +# # Run `acme.sh --deploy -d example.com --deploy-hook fortigate --insecure` to use this script. # '--insecure' is required to allow acme.sh to connect to the FortiGate API over HTTPS without a pre-existing valid certificate. -# # Function to parse response from the firewall parse_response() { status=$(echo "$1" | grep -o '"status":[ ]*"[^"]*"' | sed 's/"status":[ ]*"\([^"]*\)"/\1/') - error_code=$(echo "$1" | grep -o '"error":[ ]*[-0-9]*' | sed 's/"error":[ ]*\([-0-9]*\)/\1/') - http_status=$(echo "$1" | grep -o '"http_status":[ ]*[0-9]*' | sed 's/"http_status":[ ]*\([0-9]*\)/\1/') if [ "$status" != "success" ]; then - _err "FortiGate error: HTTP $http_status, Code $error_code" + _err "Operation failed. Deploy with --insecure if current certificate is invalid. Try deploying with --debug to troubleshoot." return 1 else _debug "Operation successful." @@ -80,7 +79,6 @@ fortigate_deploy() { return 1 fi - # Handle environment variables for var in FGT_HOST FGT_TOKEN FGT_PORT; do if [ "$(eval echo \$$var)" ]; then _debug "Detected ENV variable $var. Saving to file."