Files
acme.sh/deploy/nginx_proxy_manager.sh
invario 2f8a5535dc Add deploy hook for nginx proxy manager and NPM plus (#7267)
* Add deploy hook for nginx proxy manager and npmplus

Signed-off-by: invario <67800603+invario@users.noreply.github.com>

* rename to nginx-proxy-manager
use _post instead of raw curl
support both npmplus and nginx API
create certificate if it doesn't already exist
json_encode values
evaluate and report server return code

Signed-off-by: invario <67800603+invario@users.noreply.github.com>

* - removed jq dependency, replaced with _egrep_o
- fix _secure_debug output for _npm_token
- remove newline from _npm_certname_json
- remove unused variables
- escape backslashes in passwords
- unset _H1 before returning
- tested on nginx proxy manager (image jc21/nginx-proxy-manager:latest)
- tested on NPMplus (image zoeyvid/npmplus:2026-07-24-r1)

Signed-off-by: invario <67800603+invario@users.noreply.github.com>

* add "requiresTotp" check for 2FA enablement (npmplus:develop branch)

Signed-off-by: invario <67800603+invario@users.noreply.github.com>

* remove api/schema detection and infer API based on token location

Signed-off-by: invario <67800603+invario@users.noreply.github.com>

* replaced `[[:space:]]`
evaluate `requires_2fa` and `requires_Totp` separately
unset `_H1` on additional return for failure
prefix `nl` `_boundary` `_payload`
add `_npm_payload_format` to strip backslashes, encode for JSON, and remove newlines

Signed-off-by: invario <67800603+invario@users.noreply.github.com>

* remove backslash handling from _npm_payload_format
use _utc_date instead of date

Signed-off-by: invario <67800603+invario@users.noreply.github.com>

---------

Signed-off-by: invario <67800603+invario@users.noreply.github.com>
2026-10-09 17:20:39 +02:00

208 lines
9.3 KiB
Bash

#!/usr/bin/env sh
# Deploy hook to deploy certificate and key to nginx proxy manager/NPMPlus using
# REST API. Separately uploading the intermediate for nginx proxy manager is
# not necessary as the "fullchain" is always used for the certificate.
#
# Environment variables:
#
# DEPLOY_NPM_HOST - example.com, 192.168.1.1, etc. (REQUIRED)
# Host/IP address of NPM admin panel
# DEPLOY_NPM_PORT - 80, 443, 8443, etc. (REQUIRED)
# Port number of NPM admin panel
# DEPLOY_NPM_PROTOCOL - http/https (REQUIRED)
# Protocol to connect to NPM admin panel. https is
# STRONGLY recommended
# DEPLOY_NPM_CERTNUM - 1, 2, 3, etc. (OPTIONAL)
# the ID number of the custom certificate to overwrite.
# Find it in the NPM admin panel under certificates.
# Must already exist.
# DEPLOY_NPM_USER - admin, npmuser, etc. (REQUIRED)
# Authorized user for NPM. Must have permission
# to manage certificates.
# DEPLOY_NPM_PASSWORD - mysecurepassword, mysecretpass, etc. (REQUIRED)
# Authorized user's password for NPM 2FA is not supported
# since neither nginx proxy manager nor NPMplus support
# permanent API tokens. Either disable 2FA for the
# administrator account, or (better option) create a new user
# with 2FA disabled, and grant this user permissions for
# managing certificates.
# DEPLOY_NPM_CERTNAME My Custom Certificate, Cool Certificate Name, etc. (OPTIONAL)
# Name of custom certificate to create. This is only used
# to create a certificate the first time. It is IGNORED
# once DEPLOY_NPM_CERTNUM is set. If neither certificate
# ID number and name are provided, a default name of
# "acme.sh custom certificate" with a timestamp suffix will be
# used
#
######## Public functions #####################
#domain keyfile certfile cafile fullchain
nginx_proxy_manager_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_cpfx="$6"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_debug _cpfx "$_cpfx"
_getdeployconf DEPLOY_NPM_HOST
_getdeployconf DEPLOY_NPM_PORT
_getdeployconf DEPLOY_NPM_PROTOCOL
_getdeployconf DEPLOY_NPM_CERTNUM
_getdeployconf DEPLOY_NPM_USER
_getdeployconf DEPLOY_NPM_PASSWORD
_getdeployconf DEPLOY_NPM_CERTNAME
if [ -z "$DEPLOY_NPM_HOST" ]; then
_err "DEPLOY_NPM_HOST must be defined"
return 1
fi
if [ -z "$DEPLOY_NPM_PORT" ]; then
_err "DEPLOY_NPM_PORT must be defined"
return 1
fi
if [ -z "$DEPLOY_NPM_CERTNUM" ]; then
_info "DEPLOY_NPM_CERTNUM not provided, a new certificate will be created."
if [ -z "$DEPLOY_NPM_CERTNAME" ]; then
DEPLOY_NPM_CERTNAME="acme.sh custom certificate - $(_utc_date | tr ' ' '_' | tr -d -- ':')"
_info "DEPLOY_NPM_CERTNAME not set. Using default:"
_info DEPLOY_NPM_CERTNAME "$DEPLOY_NPM_CERTNAME"
fi
fi
if [ "$DEPLOY_NPM_PROTOCOL" != "https" ] && [ "$DEPLOY_NPM_PROTOCOL" != "http" ]; then
_err "DEPLOY_NPM_PROTOCOL must be either http or https"
return 1
fi
if [ -z "$DEPLOY_NPM_USER" ]; then
_err "DEPLOY_NPM_USER must be defined"
return 1
fi
if [ -z "$DEPLOY_NPM_PASSWORD" ]; then
_err "DEPLOY_NPM_PASSWORD must be defined"
return 1
fi
# extract and return response code
_npm_response_code() {
_npm_code="$(_egrep_o <"$HTTP_HEADER" "^HTTP[^ ]* .*$" | cut -d " " -f 2-100 | tr -d "\f\n")"
printf '%s\n' "$_npm_code" | _egrep_o "^[0-9][0-9]*"
}
# strip backslashes, encode for JSON, and remove newlines
_npm_payload_format() {
_npm_payload_out="$(printf "%s\n" "$1" | _json_encode)"
printf '%s' "${_npm_payload_out%\\n}"
}
_debug DEPLOY_NPM_USER "$DEPLOY_NPM_USER"
_secure_debug DEPLOY_NPM_PASSWORD "$DEPLOY_NPM_PASSWORD"
_npm_user_json="$(_npm_payload_format "$DEPLOY_NPM_USER")"
_npm_password_json="$(_npm_payload_format "$DEPLOY_NPM_PASSWORD")"
_debug _npm_user_json "$_npm_user_json"
_secure_debug _npm_password_json "$_npm_password_json"
_info "Authenticating and fetching temporary API token"
_npm_token_raw="$(_post '{"identity":"'"$_npm_user_json"'","secret":"'"$_npm_password_json"'"}' "${DEPLOY_NPM_PROTOCOL}://${DEPLOY_NPM_HOST}:${DEPLOY_NPM_PORT}/api/tokens" "" "POST" "application/json")"
_secure_debug _npm_token_raw "$_npm_token_raw"
_npm_requires2fa="$(printf '%s' "$_npm_token_raw" | _egrep_o '"requires_2fa" *: *[^,}]*' | cut -d : -f 2 | tr -d ' ')"
_npm_requiresTotp="$(printf '%s' "$_npm_token_raw" | _egrep_o '"requiresTotp" *: *[^,}]*' | cut -d : -f 2 | tr -d ' ')"
if [ "$_npm_requires2fa" = "true" ] || [ "$_npm_requiresTotp" = "true" ]; then
_err "2FA/MFA is enabled, deployment is not supported. Disable it or create a user with 2FA/MFA disabled and update your configuration."
return 1
fi
_npm_token_code="$(_npm_response_code)"
_debug _npm_token_code "$_npm_token_code"
if [ "$_npm_token_code" != "200" ]; then
_err "Failed to retrieve a token, server response code: $_npm_token_code"
return 1
fi
# first look for a token in JSON
_npm_type="Nginx Proxy Manager API"
_npm_token="$(printf '%s' "$_npm_token_raw" | _egrep_o '"token" *: *"[^"]*"' | cut -d '"' -f 4)"
if [ "$_npm_token" = "" ]; then
_debug "Token not found in JSON (nginx proxy manager API), checking HTTP header"
# look for a token in the header
_npm_type="NPMplus API"
_npm_token="$(grep <"$HTTP_HEADER" -i "^Set-Cookie: *__Host-Http-token=" | _tail_n 1 | _egrep_o "__Host-Http-token=[^;]*" | _head_n 1 | cut -d'=' -f2-)"
if [ "$_npm_token" = "" ]; then
_err "Token not found in HTTP header (NPMplus API). Unable to obtain a token."
return 1
fi
fi
_info "Detected API type: $_npm_type"
_secure_debug _npm_token "$_npm_token"
_info "API token retrieved."
if [ "$_npm_type" = "Nginx Proxy Manager API" ]; then
_H1="Authorization: Bearer $_npm_token"
elif [ "$_npm_type" = "NPMplus API" ]; then
_H1="Cookie: __Host-Http-token=$_npm_token"
fi
export _H1
_secure_debug _H1 "$_H1"
_npm_nl="\0015\0012"
# Create new certificate if a certificate ID number was not provided
if [ -z "$DEPLOY_NPM_CERTNUM" ]; then
_npm_certname_json="$(_npm_payload_format "$DEPLOY_NPM_CERTNAME")"
_npm_create_result="$(_post '{"provider":"other","nice_name":"'"$_npm_certname_json"'"}' "$DEPLOY_NPM_PROTOCOL://$DEPLOY_NPM_HOST:$DEPLOY_NPM_PORT/api/nginx/certificates" "" "POST" "application/json")"
_debug _npm_create_result "$_npm_create_result"
_npm_create_code="$(_npm_response_code)"
if [ "$_npm_create_code" != "201" ]; then
_err "Failed to create new certificate, server response code: $_npm_create_code"
unset _H1
return 1
fi
DEPLOY_NPM_CERTNUM="$(printf '%s' "$_npm_create_result" | _egrep_o '"id" *: *[^,}]*' | cut -d : -f 2 | tr -d ' "')"
_info "Created certificate ID number $DEPLOY_NPM_CERTNUM"
fi
_info "Uploading certificates into entry $DEPLOY_NPM_CERTNUM"
_npm_form_boundary="--------------------------$(_utc_date | tr -d -- '-: ')"
_npm_post_payload="--$_npm_form_boundary${_npm_nl}Content-Disposition: form-data; name=\"certificate_key\"; filename=\"$(basename "$_ckey")\"${_npm_nl}Content-Type: application/octet-stream${_npm_nl}${_npm_nl}$(cat "$_ckey")\0012"
_npm_post_payload="$_npm_post_payload${_npm_nl}--$_npm_form_boundary${_npm_nl}Content-Disposition: form-data; name=\"certificate\"; filename=\"$(basename "$_cfullchain")\"${_npm_nl}Content-Type: application/octet-stream${_npm_nl}${_npm_nl}$(cat "$_cfullchain")\0012"
_npm_post_payload="$_npm_post_payload${_npm_nl}--$_npm_form_boundary--${_npm_nl}"
_npm_post_payload="$(printf "%b_" "$_npm_post_payload")"
_npm_post_payload="${_npm_post_payload%_}"
_secure_debug _npm_post_payload "$_npm_post_payload"
_npm_upload_result="$(_post "$_npm_post_payload" "${DEPLOY_NPM_PROTOCOL}://${DEPLOY_NPM_HOST}:${DEPLOY_NPM_PORT}/api/nginx/certificates/${DEPLOY_NPM_CERTNUM}/upload" "" "POST" "multipart/form-data; boundary=${_npm_form_boundary}")"
unset _H1
_secure_debug _npm_upload_result "$_npm_upload_result"
_npm_upload_code="$(_npm_response_code)"
_debug _npm_upload_code "$_npm_upload_code"
case "$_npm_upload_code" in
"404")
_err "Failed to upload certificates, server response code: $_npm_upload_code"
_err "Check that certificate number $DEPLOY_NPM_CERTNUM exists."
return 1
;;
"200")
_info "Certificate uploaded successfully"
;;
*)
_err "Failed to upload certificates, server response code: $_npm_upload_code"
return 1
;;
esac
_info "$(__green "Deployed successfully")"
_savedeployconf DEPLOY_NPM_HOST "$DEPLOY_NPM_HOST"
_savedeployconf DEPLOY_NPM_PORT "$DEPLOY_NPM_PORT"
_savedeployconf DEPLOY_NPM_PROTOCOL "$DEPLOY_NPM_PROTOCOL"
_savedeployconf DEPLOY_NPM_CERTNUM "$DEPLOY_NPM_CERTNUM"
_savedeployconf DEPLOY_NPM_USER "$DEPLOY_NPM_USER"
_savedeployconf DEPLOY_NPM_PASSWORD "$DEPLOY_NPM_PASSWORD" "base64"
return 0
}