Created
August 24, 2025 11:34
-
-
Save Adrian-Grimm/336346f3e37e4843bd8e04b9802e6573 to your computer and use it in GitHub Desktop.
Script to fetch the Cert from an NGINX Proxy Manager instance
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/sh | |
| set -eu | |
| API_HOST="" | |
| API_USER="" | |
| API_PASS="" | |
| CERT_NAME="" | |
| CERT_ID="" | |
| OUT_DIR="" | |
| CURL_INSECURE="" | |
| usage() { | |
| echo "Usage:" | |
| echo " $(basename "$0") -h <api_base_url> -u <email> -p <password> (-n <cert_name> | -i <cert_id>) -o <output_dir> [--insecure]" | |
| exit 1 | |
| } | |
| # --- Arguments --- | |
| while [ $# -gt 0 ]; do | |
| case "$1" in | |
| -h|--host) API_HOST=$2; shift 2;; | |
| -u|--user) API_USER=$2; shift 2;; | |
| -p|--pass) API_PASS=$2; shift 2;; | |
| -n|--name) CERT_NAME=$2; shift 2;; | |
| -i|--id) CERT_ID=$2; shift 2;; | |
| -o|--out) OUT_DIR=$2; shift 2;; | |
| --insecure) CURL_INSECURE="-k"; shift;; | |
| -\?|--help) usage;; | |
| *) echo "Unknown argument: $1"; usage;; | |
| esac | |
| done | |
| [ -n "$API_HOST" ] || usage | |
| [ -n "$API_USER" ] || usage | |
| [ -n "$API_PASS" ] || usage | |
| [ -n "$OUT_DIR" ] || usage | |
| [ -n "$CERT_NAME" ] || [ -n "$CERT_ID" ] || usage | |
| # --- Tilde-Expansion --- | |
| case "$OUT_DIR" in | |
| ~) OUT_DIR=$HOME ;; | |
| ~/*) OUT_DIR=$HOME/${OUT_DIR#~/} ;; | |
| esac | |
| # --- Dependencies --- | |
| for cmd in curl jq unzip sed; do | |
| command -v "$cmd" >/dev/null 2>&1 || { echo "❌ Missing dependency: $cmd"; exit 1; } | |
| done | |
| # API-Basis normalisieren | |
| API_HOST="${API_HOST%/}" | |
| case "$API_HOST" in | |
| */api) API_BASE="$API_HOST" ;; | |
| *) API_BASE="$API_HOST/api" ;; | |
| esac | |
| # --- Helper: JSON-String fix --- | |
| clean_json() { | |
| # portable replacement: escape/mask control signs | |
| # 1. Backslashes escaping | |
| # 2. CR/TAB in \r/\t transformation | |
| # 3. linebreak (!) in \n escape | |
| sed 's/\\/\\\\/g' | sed 's/\r/\\r/g' | sed 's/ /\\t/g' | tr '\n' '\r' | sed 's/\r/\\n/g' | |
| } | |
| # --- 1) Login --- | |
| LOGIN_JSON=$(jq -cn --arg identity "$API_USER" --arg secret "$API_PASS" \ | |
| '{identity:$identity, secret:$secret}') | |
| resp=$(curl -sS $CURL_INSECURE -w "\n%{http_code}" \ | |
| -H "Content-Type: application/json" -X POST "$API_BASE/tokens" \ | |
| -d "$LOGIN_JSON") | |
| AUTH_BODY=$(printf '%s' "$resp" | sed '$d') | |
| AUTH_CODE=$(printf '%s' "$resp" | tail -n1) | |
| if [ "$AUTH_CODE" != "200" ]; then | |
| echo "❌ Authentication failed (HTTP $AUTH_CODE)" | |
| echo "$AUTH_BODY" | |
| exit 1 | |
| fi | |
| JWT=$(printf '%s' "$AUTH_BODY" | jq -r '.token // empty') | |
| [ -n "$JWT" ] && [ "$JWT" != "null" ] || { echo "❌ No token in response"; exit 1; } | |
| AUTH_HEADER="Authorization: Bearer $JWT" | |
| # --- 2) Cert-ID search --- | |
| if [ -n "$CERT_NAME" ] && [ -z "$CERT_ID" ]; then | |
| resp=$(curl -sS $CURL_INSECURE -w "\n%{http_code}" \ | |
| -H "$AUTH_HEADER" "$API_BASE/nginx/certificates") | |
| CERTS_RAW=$(printf '%s' "$resp" | sed '$d') | |
| CERTS_CODE=$(printf '%s' "$resp" | tail -n1) | |
| if [ "$CERTS_CODE" != "200" ]; then | |
| echo "❌ Certificate list failed (HTTP $CERTS_CODE)" | |
| echo "$CERTS_RAW" | |
| exit 1 | |
| fi | |
| CERTS_BODY=$(printf '%s' "$CERTS_RAW" | clean_json) | |
| CERT_ID=$(printf '%s' "$CERTS_BODY" | jq -r --arg n "$CERT_NAME" ' | |
| .[]? | | |
| select( | |
| (.nice_name == $n) | |
| or ((.domain_names // []) | any(. == $n)) | |
| or ((.domain_names // []) | | |
| any(test("^" + ($n | gsub("\\."; "\\\\.") | gsub("\\*"; ".*")) + "$"))) | |
| ) | .id' | head -n1) | |
| [ -n "$CERT_ID" ] || { echo "❌ No certificate found for '$CERT_NAME'"; exit 1; } | |
| fi | |
| # --- 3) cert-details --- | |
| resp=$(curl -sS $CURL_INSECURE -w "\n%{http_code}" \ | |
| -H "$AUTH_HEADER" "$API_BASE/nginx/certificates/$CERT_ID") | |
| CERT_RAW=$(printf '%s' "$resp" | sed '$d') | |
| CERT_CODE=$(printf '%s' "$resp" | tail -n1) | |
| if [ "$CERT_CODE" != "200" ]; then | |
| echo "❌ Failed to fetch certificate (HTTP $CERT_CODE)" | |
| echo "$CERT_RAW" | |
| exit 1 | |
| fi | |
| CERT_DETAIL=$(printf '%s' "$CERT_RAW" | clean_json) | |
| CERT_PEM=$(printf '%s' "$CERT_DETAIL" | jq -r '.meta.certificate // empty') | |
| KEY_PEM=$(printf '%s' "$CERT_DETAIL" | jq -r '.meta.certificate_key // empty') | |
| mkdir -p "$OUT_DIR" | |
| umask 077 | |
| # --- 4) safe --- | |
| if [ -n "$CERT_PEM" ] && [ "$CERT_PEM" != "null" ] && \ | |
| [ -n "$KEY_PEM" ] && [ "$KEY_PEM" != "null" ]; then | |
| printf "%s\n" "$CERT_PEM" > "$OUT_DIR/fullchain.pem" | |
| printf "%s\n" "$KEY_PEM" > "$OUT_DIR/privkey.pem" | |
| else | |
| TMP_ZIP=$(mktemp) | |
| dlresp=$(curl -sS $CURL_INSECURE -w "\n%{http_code}" \ | |
| -H "$AUTH_HEADER" \ | |
| "$API_BASE/nginx/certificates/$CERT_ID/download" -o "$TMP_ZIP") | |
| DL_CODE=$(printf '%s' "$dlresp" | tail -n1) | |
| if [ "$DL_CODE" != "200" ]; then | |
| echo "❌ Download failed (HTTP $DL_CODE)" | |
| rm -f "$TMP_ZIP" | |
| exit 1 | |
| fi | |
| unzip -o -j "$TMP_ZIP" -d "$OUT_DIR" >/dev/null | |
| rm -f "$TMP_ZIP" | |
| # unify and cleanup names | |
| for f in "$OUT_DIR"/fullchain*.pem; do [ -f "$f" ] && cp -f "$f" "$OUT_DIR/fullchain.pem"; done | |
| for f in "$OUT_DIR"/privkey*.pem; do [ -f "$f" ] && cp -f "$f" "$OUT_DIR/privkey.pem" && chmod 600 "$OUT_DIR/privkey.pem"; done | |
| for f in "$OUT_DIR"/cert*.pem; do [ -f "$f" ] && cp -f "$f" "$OUT_DIR/cert.pem"; done | |
| for f in "$OUT_DIR"/chain*.pem; do [ -f "$f" ] && cp -f "$f" "$OUT_DIR/chain.pem"; done | |
| fi | |
| echo "✅ Certificate exported to: $OUT_DIR" | |
| ls -l "$OUT_DIR" |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
e.g.:
./ngix-pm-cert-download.sh -h 'https://domain.tld' -u 'email@domain.tlc' -p '#########' -n '*.domain.tld' -o ~/target-folder-for-certs