|
6 | 6 | # NPM api https://github.com/NginxProxyManager/nginx-proxy-manager/tree/develop/backend/schema |
7 | 7 | # https://github.com/NginxProxyManager/nginx-proxy-manager/tree/develop/backend/schema/components |
8 | 8 |
|
9 | | -VERSION="3.0.5" |
| 9 | +VERSION="3.0.6" |
10 | 10 |
|
11 | 11 | ################################# |
12 | 12 | # This script allows you to manage Nginx Proxy Manager via the API. It provides |
@@ -205,6 +205,7 @@ LIST_CERT_ALL=false |
205 | 205 | CERT_SHOW=false |
206 | 206 | CERT_GENERATE=false |
207 | 207 | CERT_DELETE=false |
| 208 | +CERT_DOWNLOAD=false |
208 | 209 | CERT_DOMAIN="" |
209 | 210 | CERT_EMAIL="" |
210 | 211 | DNS_PROVIDER="" |
@@ -595,6 +596,7 @@ show_help() { |
595 | 596 | echo -e " --cert-list List ALL SSL certificates" |
596 | 597 | echo -e " --cert-show ${COLOR_CYAN}domain${CoR} Or ${COLOR_CYAN}🆔${CoR} List SSL certificates filtered by [domain name] (${COLOR_YELLOW}JSON${CoR})${CoR}" |
597 | 598 | echo -e " --cert-delete ${COLOR_CYAN}domain${CoR} Or ${COLOR_CYAN}🆔${CoR} Delete Certificate for the given '${COLOR_YELLOW}domain${CoR}'" |
| 599 | + echo -e " --cert-download ${COLOR_CYAN}🆔${CoR} ${COLOR_CYAN}[output_dir]${CoR} ${COLOR_CYAN}[cert_name]${CoR} Download certificate as ZIP with fallback support" |
598 | 600 |
|
599 | 601 | echo -e " --cert-generate ${COLOR_CYAN}domain${CoR} ${COLOR_CYAN}[email]${CoR} Generate Let's Encrypt Certificate or others Providers.${CoR}" |
600 | 602 | echo -e " • ${COLOR_YELLOW}Standard domains:${CoR} example.com, sub.example.com" |
@@ -670,6 +672,8 @@ examples_cli() { |
670 | 672 |
|
671 | 673 | echo -e "${COLOR_GREY} # List all certificates${CoR}" |
672 | 674 | echo -e " $0 --cert-list" |
| 675 | + echo -e "${COLOR_GREY} # Download certificate as ZIP${CoR}" |
| 676 | + echo -e " $0 --cert-download 123" |
673 | 677 | echo -e "${COLOR_GREY} # Generate SSL certificate${CoR}" |
674 | 678 | echo -e " $0 --cert-generate example.com --cert-email admin@example.com" |
675 | 679 |
|
@@ -1398,6 +1402,185 @@ list_cert_all() { |
1398 | 1402 | echo -e " • ${COLOR_RED}Expired${CoR} : ${COLOR_RED}$EXPIRED_CERTS${CoR}\n" |
1399 | 1403 | } |
1400 | 1404 |
|
| 1405 | +################################ |
| 1406 | +# Function to download certificate as ZIP with fallback support |
| 1407 | +cert_download() { |
| 1408 | + local cert_id="$1" |
| 1409 | + local output_dir="${2:-./certificates}" |
| 1410 | + local cert_name="${3:-certificate_$cert_id}" |
| 1411 | + |
| 1412 | + check_token_notverbose |
| 1413 | + |
| 1414 | + if [ -z "$cert_id" ]; then |
| 1415 | + echo -e "\n ⛔ ${COLOR_RED}ERROR: Certificate ID is required${CoR}" |
| 1416 | + echo -e " Usage: " |
| 1417 | + echo -e " ${COLOR_ORANGE}$0 --cert-download <cert_id> [output_dir] [cert_name]${CoR}" |
| 1418 | + echo -e " ${COLOR_ORANGE}$0 --cert-download 123 ./certs mydomain${CoR}\n" |
| 1419 | + exit 1 |
| 1420 | + fi |
| 1421 | + |
| 1422 | + # Create output directory |
| 1423 | + mkdir -p "$output_dir" |
| 1424 | + |
| 1425 | + echo -e "\n 🔒 Downloading certificate (ID: ${COLOR_YELLOW}$cert_id${CoR})" |
| 1426 | + echo -e " Output directory: ${COLOR_CYAN}$output_dir${CoR}" |
| 1427 | + echo -e " Certificate name: ${COLOR_CYAN}$cert_name${CoR}" |
| 1428 | + |
| 1429 | + # Get certificate metadata first |
| 1430 | + local CERT_META |
| 1431 | + CERT_META=$(curl -s -X GET "$BASE_URL/nginx/certificates/$cert_id" \ |
| 1432 | + -H "Authorization: Bearer $(cat "$TOKEN_FILE")") |
| 1433 | + |
| 1434 | + if [ -z "$CERT_META" ] || ! echo "$CERT_META" | jq empty 2>/dev/null; then |
| 1435 | + echo -e " ⛔ ${COLOR_RED}Error: Unable to retrieve certificate metadata${CoR}" |
| 1436 | + exit 1 |
| 1437 | + fi |
| 1438 | + |
| 1439 | + # Check if certificate exists |
| 1440 | + if echo "$CERT_META" | jq -e '.error' >/dev/null; then |
| 1441 | + echo -e " ⛔ ${COLOR_RED}Certificate not found with ID: $cert_id${CoR}" |
| 1442 | + exit 1 |
| 1443 | + fi |
| 1444 | + |
| 1445 | + # Extract certificate information |
| 1446 | + local domain_names=$(echo "$CERT_META" | jq -r '.domain_names | join(", ")') |
| 1447 | + local provider=$(echo "$CERT_META" | jq -r '.provider // "Unknown"') |
| 1448 | + local expires_on=$(echo "$CERT_META" | jq -r '.expires_on // "N/A"') |
| 1449 | + |
| 1450 | + echo -e " Domain(s): ${COLOR_YELLOW}$domain_names${CoR}" |
| 1451 | + echo -e " Provider: ${COLOR_YELLOW}$provider${CoR}" |
| 1452 | + echo -e " Expires: ${COLOR_YELLOW}$expires_on${CoR}" |
| 1453 | + |
| 1454 | + # Try new API first (JSON format) |
| 1455 | + echo -e "\n 🔄 Trying new API format..." |
| 1456 | + local CERT_CONTENT |
| 1457 | + CERT_CONTENT=$(curl -s -X GET "$BASE_URL/nginx/certificates/$cert_id/certificates" \ |
| 1458 | + -H "Authorization: Bearer $(cat "$TOKEN_FILE")" \ |
| 1459 | + -H "Accept: application/json") |
| 1460 | + |
| 1461 | + if [ -n "$CERT_CONTENT" ] && echo "$CERT_CONTENT" | jq empty 2>/dev/null; then |
| 1462 | + # Check if we got valid certificate data |
| 1463 | + local cert_data=$(echo "$CERT_CONTENT" | jq -r '.certificate // empty') |
| 1464 | + local private_data=$(echo "$CERT_CONTENT" | jq -r '.private // empty') |
| 1465 | + |
| 1466 | + if [ -n "$cert_data" ] && [ -n "$private_data" ]; then |
| 1467 | + echo -e " ✅ ${COLOR_GREEN}New API format successful${CoR}" |
| 1468 | + |
| 1469 | + # Save certificate files |
| 1470 | + echo "$cert_data" > "$output_dir/${cert_name}.crt" |
| 1471 | + echo "$private_data" > "$output_dir/${cert_name}.key" |
| 1472 | + chmod 600 "$output_dir/${cert_name}.key" |
| 1473 | + |
| 1474 | + # Save intermediate certificate if available |
| 1475 | + local intermediate_data=$(echo "$CERT_CONTENT" | jq -r '.intermediate // empty') |
| 1476 | + if [ -n "$intermediate_data" ]; then |
| 1477 | + echo "$intermediate_data" > "$output_dir/${cert_name}.chain.crt" |
| 1478 | + fi |
| 1479 | + |
| 1480 | + # Save metadata |
| 1481 | + echo "$CERT_META" | jq '.' > "$output_dir/${cert_name}_metadata.json" |
| 1482 | + |
| 1483 | + # Create ZIP file |
| 1484 | + local zip_file="$output_dir/${cert_name}_certificate.zip" |
| 1485 | + cd "$output_dir" || exit 1 |
| 1486 | + zip -q "$zip_file" "${cert_name}.crt" "${cert_name}.key" "${cert_name}_metadata.json" |
| 1487 | + [ -f "${cert_name}.chain.crt" ] && zip -q "$zip_file" "${cert_name}.chain.crt" |
| 1488 | + cd - > /dev/null || exit 1 |
| 1489 | + |
| 1490 | + echo -e " ✅ ${COLOR_GREEN}Certificate downloaded successfully${CoR}" |
| 1491 | + echo -e " Files created:" |
| 1492 | + echo -e " • ${COLOR_CYAN}${cert_name}.crt${CoR} (Certificate)" |
| 1493 | + echo -e " • ${COLOR_CYAN}${cert_name}.key${CoR} (Private Key)" |
| 1494 | + [ -f "$output_dir/${cert_name}.chain.crt" ] && echo -e " • ${COLOR_CYAN}${cert_name}.chain.crt${CoR} (Chain)" |
| 1495 | + echo -e " • ${COLOR_CYAN}${cert_name}_metadata.json${CoR} (Metadata)" |
| 1496 | + echo -e " • ${COLOR_CYAN}${cert_name}_certificate.zip${CoR} (ZIP Archive)" |
| 1497 | + |
| 1498 | + return 0 |
| 1499 | + fi |
| 1500 | + fi |
| 1501 | + |
| 1502 | + # Fallback to old API (ZIP download) |
| 1503 | + echo -e " ⚠️ ${COLOR_YELLOW}New API failed, trying legacy ZIP download...${CoR}" |
| 1504 | + |
| 1505 | + local TMP_DIR=$(mktemp -d) |
| 1506 | + local zip_file="$TMP_DIR/certificate.zip" |
| 1507 | + |
| 1508 | + # Download ZIP file |
| 1509 | + local download_response |
| 1510 | + download_response=$(curl -s -w "%{http_code}" -X GET "$BASE_URL/nginx/certificates/$cert_id/download" \ |
| 1511 | + -H "Authorization: Bearer $(cat "$TOKEN_FILE")" \ |
| 1512 | + -o "$zip_file") |
| 1513 | + |
| 1514 | + local http_code="${download_response: -3}" |
| 1515 | + |
| 1516 | + if [ "$http_code" = "200" ] && [ -f "$zip_file" ] && [ -s "$zip_file" ]; then |
| 1517 | + echo -e " ✅ ${COLOR_GREEN}Legacy ZIP download successful${CoR}" |
| 1518 | + |
| 1519 | + # Extract ZIP file |
| 1520 | + if command -v unzip >/dev/null 2>&1; then |
| 1521 | + unzip -q "$zip_file" -d "$TMP_DIR" |
| 1522 | + |
| 1523 | + # Find certificate files with wildcards (supporting cert8.pem, cert9.pem, etc.) |
| 1524 | + local cert_file=$(find "$TMP_DIR" -name "cert*.pem" | head -1) |
| 1525 | + local key_file=$(find "$TMP_DIR" -name "privkey*.pem" | head -1) |
| 1526 | + local chain_file=$(find "$TMP_DIR" -name "chain*.pem" | head -1) |
| 1527 | + local fullchain_file=$(find "$TMP_DIR" -name "fullchain*.pem" | head -1) |
| 1528 | + |
| 1529 | + if [ -n "$cert_file" ] && [ -n "$key_file" ]; then |
| 1530 | + # Copy files to output directory |
| 1531 | + cp "$cert_file" "$output_dir/${cert_name}.crt" |
| 1532 | + cp "$key_file" "$output_dir/${cert_name}.key" |
| 1533 | + chmod 600 "$output_dir/${cert_name}.key" |
| 1534 | + |
| 1535 | + # Copy chain file if available |
| 1536 | + if [ -n "$chain_file" ]; then |
| 1537 | + cp "$chain_file" "$output_dir/${cert_name}.chain.crt" |
| 1538 | + fi |
| 1539 | + |
| 1540 | + # Copy fullchain if available |
| 1541 | + if [ -n "$fullchain_file" ]; then |
| 1542 | + cp "$fullchain_file" "$output_dir/${cert_name}.fullchain.crt" |
| 1543 | + fi |
| 1544 | + |
| 1545 | + # Save metadata |
| 1546 | + echo "$CERT_META" | jq '.' > "$output_dir/${cert_name}_metadata.json" |
| 1547 | + |
| 1548 | + # Create new ZIP file with standardized names |
| 1549 | + local final_zip="$output_dir/${cert_name}_certificate.zip" |
| 1550 | + cd "$output_dir" || exit 1 |
| 1551 | + zip -q "$final_zip" "${cert_name}.crt" "${cert_name}.key" "${cert_name}_metadata.json" |
| 1552 | + [ -f "${cert_name}.chain.crt" ] && zip -q "$final_zip" "${cert_name}.chain.crt" |
| 1553 | + [ -f "${cert_name}.fullchain.crt" ] && zip -q "$final_zip" "${cert_name}.fullchain.crt" |
| 1554 | + cd - > /dev/null || exit 1 |
| 1555 | + |
| 1556 | + echo -e " ✅ ${COLOR_GREEN}Certificate downloaded successfully (legacy method)${CoR}" |
| 1557 | + echo -e " Files created:" |
| 1558 | + echo -e " • ${COLOR_CYAN}${cert_name}.crt${CoR} (Certificate)" |
| 1559 | + echo -e " • ${COLOR_CYAN}${cert_name}.key${CoR} (Private Key)" |
| 1560 | + [ -f "$output_dir/${cert_name}.chain.crt" ] && echo -e " • ${COLOR_CYAN}${cert_name}.chain.crt${CoR} (Chain)" |
| 1561 | + [ -f "$output_dir/${cert_name}.fullchain.crt" ] && echo -e " • ${COLOR_CYAN}${cert_name}.fullchain.crt${CoR} (Full Chain)" |
| 1562 | + echo -e " • ${COLOR_CYAN}${cert_name}_metadata.json${CoR} (Metadata)" |
| 1563 | + echo -e " • ${COLOR_CYAN}${cert_name}_certificate.zip${CoR} (ZIP Archive)" |
| 1564 | + |
| 1565 | + # Cleanup |
| 1566 | + rm -rf "$TMP_DIR" |
| 1567 | + return 0 |
| 1568 | + else |
| 1569 | + echo -e " ⛔ ${COLOR_RED}Error: Could not find certificate files in ZIP${CoR}" |
| 1570 | + fi |
| 1571 | + else |
| 1572 | + echo -e " ⛔ ${COLOR_RED}Error: unzip command not found${CoR}" |
| 1573 | + fi |
| 1574 | + else |
| 1575 | + echo -e " ⛔ ${COLOR_RED}Error: Failed to download certificate ZIP (HTTP $http_code)${CoR}" |
| 1576 | + fi |
| 1577 | + |
| 1578 | + # Cleanup on failure |
| 1579 | + rm -rf "$TMP_DIR" |
| 1580 | + echo -e " ⛔ ${COLOR_RED}Certificate download failed${CoR}" |
| 1581 | + exit 1 |
| 1582 | +} |
| 1583 | + |
1401 | 1584 | # Verify Cloudflare API Key validity |
1402 | 1585 | verify_cloudflare_api_key() { |
1403 | 1586 | local api_key="$1" |
@@ -4153,6 +4336,36 @@ while [[ "$#" -gt 0 ]]; do |
4153 | 4336 | fi |
4154 | 4337 | ;; |
4155 | 4338 | --cert-list) LIST_CERT_ALL=true; shift;; |
| 4339 | + --cert-download) |
| 4340 | + shift |
| 4341 | + if [ $# -eq 0 ] || [[ "$1" == -* ]]; then |
| 4342 | + echo -e "\n ⛔ ${COLOR_RED}The --cert-download option requires a certificate ID.${CoR}" |
| 4343 | + echo -e "\n ${COLOR_CYAN}Usage:${CoR}" |
| 4344 | + echo -e " ${COLOR_ORANGE}$0 --cert-download <cert_id> [output_dir] [cert_name]${CoR}" |
| 4345 | + echo -e "\n ${COLOR_CYAN}Examples:${CoR}" |
| 4346 | + echo -e " ${COLOR_GREEN}$0 --cert-download 240${CoR}" |
| 4347 | + echo -e " ${COLOR_GREEN}$0 --cert-download 240 ./certs${CoR}" |
| 4348 | + echo -e " ${COLOR_GREEN}$0 --cert-download 240 ./certs mydomain${CoR}" |
| 4349 | + echo -e "\n ${COLOR_YELLOW}💡 Tips:${CoR}" |
| 4350 | + echo -e " • Use ${COLOR_GREEN}--cert-list${CoR} to see all certificates and their IDs" |
| 4351 | + echo -e " • Supports both new API (JSON) and legacy API (ZIP) with automatic fallback" |
| 4352 | + echo -e " • Creates standardized certificate files (.crt, .key, .chain.crt) and ZIP archive\n" |
| 4353 | + exit 1 |
| 4354 | + fi |
| 4355 | + CERT_DOWNLOAD=true |
| 4356 | + CERT_ID="$1" |
| 4357 | + shift |
| 4358 | + # Optional output directory |
| 4359 | + if [ $# -gt 0 ] && [[ "$1" != -* ]]; then |
| 4360 | + CERT_OUTPUT_DIR="$1" |
| 4361 | + shift |
| 4362 | + fi |
| 4363 | + # Optional certificate name |
| 4364 | + if [ $# -gt 0 ] && [[ "$1" != -* ]]; then |
| 4365 | + CERT_NAME="$1" |
| 4366 | + shift |
| 4367 | + fi |
| 4368 | + ;; |
4156 | 4369 | --access-list) ACCESS_LIST=true; shift;; |
4157 | 4370 | --access-list-show) |
4158 | 4371 | ACCESS_LIST_SHOW=true |
@@ -4267,6 +4480,8 @@ elif [ "$CERT_SHOW" = true ]; then |
4267 | 4480 | cert_show "$search_term" |
4268 | 4481 | elif [ "$LIST_CERT_ALL" = true ]; then |
4269 | 4482 | list_cert_all |
| 4483 | +elif [ "$CERT_DOWNLOAD" = true ]; then |
| 4484 | + cert_download "$CERT_ID" "${CERT_OUTPUT_DIR:-./certificates}" "${CERT_NAME:-certificate_$CERT_ID}" |
4270 | 4485 |
|
4271 | 4486 | elif [ "$ACCESS_LIST" = true ]; then |
4272 | 4487 | access_list |
|
0 commit comments