Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
- ADD_CHAIN138_TO_LEDGER_LIVE: Ledger form done; public code review repo bis-innovations/LedgerLive; init/push commands - CONTRACT_DEPLOYMENT_RUNBOOK: Chain 138 gas price 1 gwei, 36-addr check, TransactionMirror workaround - CONTRACT_*: AddressMapper, MirrorManager deployed 2026-02-12; 36-address on-chain check - NEXT_STEPS_FOR_YOU: Ledger done; steps completable now (no LAN); run-completable-tasks-from-anywhere - MASTER_INDEX, OPERATOR_OPTIONAL, SMART_CONTRACTS_INVENTORY_SIMPLE: updates - LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE: bis-innovations/LedgerLive reference Co-authored-by: Cursor <cursoragent@cursor.com>
204 lines
8.8 KiB
Bash
Executable File
204 lines
8.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Request SSL certificates for proxy hosts in NPMplus that do NOT already have one.
|
||
# Skips hosts with certificate_id set to avoid duplicate/inactive cert spam.
|
||
# Uses .env for NPM_URL, NPM_EMAIL, NPM_PASSWORD when run from repo root.
|
||
# Optional args: PROXMOX_HOST CONTAINER_ID NPM_URL NPM_EMAIL NPM_PASSWORD SSL_EMAIL
|
||
# Env: FIRST_ONLY=1 or FIRST_ONLY=true – request cert for only the first host without one (verify before running for rest).
|
||
# Cleanup guide: docs/04-configuration/NPMPLUS_TLS_CLEANUP.md
|
||
|
||
set -euo pipefail
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||
|
||
# Preserve NPM credentials from environment so "export NPM_PASSWORD=...; ./script" works
|
||
_orig_npm_url="${NPM_URL:-}"
|
||
_orig_npm_email="${NPM_EMAIL:-}"
|
||
_orig_npm_password="${NPM_PASSWORD:-}"
|
||
# Load .env (set +u so values with $ in them don't trigger unbound variable)
|
||
if [ -f "$PROJECT_ROOT/.env" ]; then
|
||
set +u
|
||
set -a
|
||
# shellcheck source=/dev/null
|
||
source "$PROJECT_ROOT/.env" 2>/dev/null || true
|
||
set +a
|
||
set -u
|
||
[ -n "$_orig_npm_url" ] && NPM_URL="$_orig_npm_url"
|
||
[ -n "$_orig_npm_email" ] && NPM_EMAIL="$_orig_npm_email"
|
||
[ -n "$_orig_npm_password" ] && NPM_PASSWORD="$_orig_npm_password"
|
||
fi
|
||
|
||
# Colors
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m'
|
||
|
||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||
log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
|
||
log_error() { echo -e "${RED}[✗]${NC} $1"; }
|
||
|
||
PROXMOX_HOST="${1:-192.168.11.11}"
|
||
CONTAINER_ID="${2:-10233}"
|
||
# Default .167: NPMplus (VMID 10233) at 192.168.11.167:81; set NPM_URL in .env or pass as 3rd arg to override
|
||
NPM_URL="${3:-${NPM_URL:-https://192.168.11.167:81}}"
|
||
NPM_EMAIL="${4:-${NPM_EMAIL:-admin@example.org}}"
|
||
NPM_PASSWORD="${5:-${NPM_PASSWORD:-}}"
|
||
if [ -z "$NPM_PASSWORD" ]; then
|
||
log_error "NPM_PASSWORD is required. Set it in .env or pass as 5th argument"
|
||
exit 1
|
||
fi
|
||
SSL_EMAIL="${6:-${SSL_EMAIL:-nsatoshi2007@hotmail.com}}"
|
||
|
||
echo ""
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo "🔒 NPMplus Certificate Request"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo ""
|
||
|
||
# Authenticate (use jq to build JSON so password is safely escaped)
|
||
log_info "Authenticating to NPMplus API..."
|
||
AUTH_JSON=$(jq -n --arg identity "$NPM_EMAIL" --arg secret "$NPM_PASSWORD" '{identity:$identity,secret:$secret}')
|
||
TOKEN_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/tokens" \
|
||
-H "Content-Type: application/json" \
|
||
-d "$AUTH_JSON")
|
||
|
||
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token // empty' 2>/dev/null || echo "")
|
||
|
||
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
|
||
ERROR_MSG=$(echo "$TOKEN_RESPONSE" | jq -r '.error.message // "Unknown error"' 2>/dev/null || echo "Unknown error")
|
||
log_error "Failed to authenticate: $ERROR_MSG"
|
||
log_info "Response: $TOKEN_RESPONSE"
|
||
log_info ""
|
||
log_info "Trying to reset password or check credentials..."
|
||
exit 1
|
||
fi
|
||
|
||
log_success "Authenticated successfully"
|
||
echo ""
|
||
|
||
# Get all proxy hosts
|
||
log_info "Fetching proxy hosts..."
|
||
PROXY_HOSTS_JSON=$(curl -s -k -X GET "$NPM_URL/api/nginx/proxy-hosts" \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
-H "Content-Type: application/json")
|
||
|
||
PROXY_COUNT=$(echo "$PROXY_HOSTS_JSON" | jq -r 'length' 2>/dev/null || echo "0")
|
||
log_info "Found $PROXY_COUNT proxy hosts"
|
||
echo ""
|
||
|
||
if [ "$PROXY_COUNT" = "0" ]; then
|
||
log_warn "No proxy hosts found"
|
||
exit 0
|
||
fi
|
||
|
||
# Build list of hosts that need a certificate (id|domain, one per line)
|
||
NEED_CERT_LIST=$(echo "$PROXY_HOSTS_JSON" | jq -r '.[] | select(.certificate_id == null or .certificate_id == 0) | "\(.id)|\(.domain_names[0] // "")"' 2>/dev/null | while IFS='|' read -r id domain; do
|
||
[ -z "$domain" ] || [ "$domain" = "null" ] && continue
|
||
echo "$domain" | grep -q "test.*example.com" && continue
|
||
echo "${id}|${domain}"
|
||
done)
|
||
|
||
NEED_CERT_COUNT=$(echo "$NEED_CERT_LIST" | grep -c . 2>/dev/null || echo "0")
|
||
if [ "$NEED_CERT_COUNT" = "0" ]; then
|
||
log_success "No proxy hosts need a certificate (all have one)."
|
||
exit 0
|
||
fi
|
||
|
||
# FIRST_ONLY: process only the first host (verify renewal/working before adding rest)
|
||
FIRST_ONLY="${FIRST_ONLY:-0}"
|
||
if [ "$FIRST_ONLY" = "1" ] || [ "$FIRST_ONLY" = "true" ] || [ "$FIRST_ONLY" = "yes" ]; then
|
||
NEED_CERT_LIST=$(echo "$NEED_CERT_LIST" | head -n 1)
|
||
log_warn "FIRST_ONLY=1 – processing only the first host without a cert. Verify renewal date and that it works, then run without FIRST_ONLY for the rest."
|
||
echo ""
|
||
fi
|
||
|
||
# Try to get DNS (Cloudflare) credential_id so we use same method as UI (DNS challenge)
|
||
CREDENTIAL_ID=""
|
||
for path in "/api/nginx/letsencrypt-credentials" "/api/letsencrypt-credentials"; do
|
||
CRED_JSON=$(curl -s -k -X GET "$NPM_URL$path" -H "Authorization: Bearer $TOKEN" 2>/dev/null || echo "[]")
|
||
if echo "$CRED_JSON" | jq -e 'type == "array" and length > 0' >/dev/null 2>&1; then
|
||
CREDENTIAL_ID=$(echo "$CRED_JSON" | jq -r '.[0].id // .[0].credential_id // empty' 2>/dev/null)
|
||
[ -n "$CREDENTIAL_ID" ] && [ "$CREDENTIAL_ID" != "null" ] && break
|
||
fi
|
||
done
|
||
if [ -n "$CREDENTIAL_ID" ] && [ "$CREDENTIAL_ID" != "null" ]; then
|
||
log_info "Using DNS challenge (credential_id: $CREDENTIAL_ID)"
|
||
else
|
||
log_info "No DNS credential found – request will use Let's Encrypt defaults (HTTP or NPM default). Add Cloudflare credential in NPM UI for DNS."
|
||
fi
|
||
echo ""
|
||
|
||
# Process each host that needs a cert
|
||
success_count=0
|
||
fail_count=0
|
||
|
||
while IFS='|' read -r host_id domain; do
|
||
[ -z "$host_id" ] || [ -z "$domain" ] && continue
|
||
|
||
log_info "Processing: $domain (Host ID: $host_id)"
|
||
|
||
# Request certificate. NPM API accepts only domain_names + provider (extra keys cause "must NOT have additional properties").
|
||
# For DNS (Cloudflare) and correct expiry, request certs in NPM UI: Hosts → host → SSL → Request new SSL Certificate → DNS Challenge, Cloudflare.
|
||
log_info " Requesting SSL certificate..."
|
||
CERT_RESPONSE=$(curl -s -k -X POST "$NPM_URL/api/nginx/certificates" \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d "$(jq -n --arg domain "$domain" '{ domain_names: [$domain], provider: "letsencrypt" }')")
|
||
|
||
NEW_CERT_ID=$(echo "$CERT_RESPONSE" | jq -r '.id // empty' 2>/dev/null || echo "")
|
||
|
||
if [ -z "$NEW_CERT_ID" ] || [ "$NEW_CERT_ID" = "null" ]; then
|
||
ERROR=$(echo "$CERT_RESPONSE" | jq -r '.error.message // .error // "Unknown error"' 2>/dev/null || echo "Unknown error")
|
||
log_warn " Certificate request failed: $ERROR"
|
||
log_info " Certificate may be processing in background"
|
||
fail_count=$((fail_count + 1))
|
||
else
|
||
log_success " Certificate requested (ID: $NEW_CERT_ID)"
|
||
|
||
# Update proxy host to use certificate
|
||
log_info " Assigning certificate to proxy host..."
|
||
UPDATE_RESPONSE=$(curl -s -k -X PUT "$NPM_URL/api/nginx/proxy-hosts/$host_id" \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d "{
|
||
\"certificate_id\": $NEW_CERT_ID,
|
||
\"ssl_forced\": true,
|
||
\"http2_support\": true,
|
||
\"hsts_enabled\": true,
|
||
\"hsts_subdomains\": true
|
||
}")
|
||
|
||
UPDATE_ID=$(echo "$UPDATE_RESPONSE" | jq -r '.id // empty' 2>/dev/null || echo "")
|
||
|
||
if [ -n "$UPDATE_ID" ] && [ "$UPDATE_ID" != "null" ]; then
|
||
log_success " ✓ Certificate assigned to proxy host"
|
||
success_count=$((success_count + 1))
|
||
else
|
||
ERROR=$(echo "$UPDATE_RESPONSE" | jq -r '.error.message // .error // "Unknown error"' 2>/dev/null || echo "Unknown error")
|
||
log_warn " Failed to assign certificate: $ERROR"
|
||
fail_count=$((fail_count + 1))
|
||
fi
|
||
fi
|
||
|
||
echo ""
|
||
sleep 2 # Rate limiting
|
||
done <<< "$NEED_CERT_LIST"
|
||
|
||
# Summary
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
log_info "Summary:"
|
||
log_success " Successful: $success_count"
|
||
log_warn " Failed: $fail_count"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo ""
|
||
|
||
log_info "Note: Certificate requests may take a few minutes to process"
|
||
log_info "Check NPMplus UI to verify certificate status"
|
||
if [ "$FIRST_ONLY" = "1" ] || [ "$FIRST_ONLY" = "true" ] || [ "$FIRST_ONLY" = "yes" ]; then
|
||
log_info "After verifying renewal date and that the cert works: run ./scripts/request-npmplus-certificates.sh (no FIRST_ONLY) for the remaining hosts."
|
||
fi
|
||
echo ""
|