Skip to content

Instantly share code, notes, and snippets.

@Adrian-Grimm
Created August 24, 2025 11:34
Show Gist options
  • Select an option

  • Save Adrian-Grimm/336346f3e37e4843bd8e04b9802e6573 to your computer and use it in GitHub Desktop.

Select an option

Save Adrian-Grimm/336346f3e37e4843bd8e04b9802e6573 to your computer and use it in GitHub Desktop.
Script to fetch the Cert from an NGINX Proxy Manager instance
#!/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"
@Adrian-Grimm
Copy link
Author

e.g.: ./ngix-pm-cert-download.sh -h 'https://domain.tld' -u 'email@domain.tlc' -p '#########' -n '*.domain.tld' -o ~/target-folder-for-certs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment