From bde2444b172537e62ff2645efda27306c5041397 Mon Sep 17 00:00:00 2001 From: Ruben Date: Wed, 7 May 2025 10:56:26 +0200 Subject: [PATCH 1/2] Fetch PEM from certwarden API --- certman.sh | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/certman.sh b/certman.sh index 3a41137..d13f460 100755 --- a/certman.sh +++ b/certman.sh @@ -87,8 +87,8 @@ download_and_verify_cert() { local key_api_key=$3 local temp_cert="$TEMP_DIR/$domain.crt" local temp_key="$TEMP_DIR/$domain.key" - local temp_cert_pem="$TEMP_DIR/$domain.cert.pem" - local temp_key_pem="$TEMP_DIR/$domain.key.pem" + local temp_pem="$TEMP_DIR/$domain.pem" + echo -e "${BLUE}Processing certificate for $domain${NC}" @@ -106,18 +106,19 @@ download_and_verify_cert() { return 1 fi + # Download fullchain PEM file + if ! curl -s -fL -o "$temp_pem" -H "X-API-Key: $cert_api_key.$key_api_key" \ + "https://$CERTWARDEN_SERVER/certwarden/api/v1/download/privatecertchains/$domain"; then + echo -e "${RED}Failed to download fullchain PEM file for $domain${NC}" + return 1 + fi + # Verify files are not empty - if [ ! -s "$temp_cert" ] || [ ! -s "$temp_key" ]; then + if [ ! -s "$temp_cert" ] || [ ! -s "$temp_key" ] || [ ! -s "$temp_pem" ]; then echo -e "${RED}Downloaded files are empty for $domain${NC}" return 1 fi - # Create PEM files if requested - if [ "$FULLCHAIN_PEM" = "true" ]; then - cat "$temp_cert" > "$temp_cert_pem" - cat "$temp_key" > "$temp_key_pem" - fi - # Validate certificate and key match local cert_fingerprint cert_fingerprint=$(openssl x509 -in "$temp_cert" -noout -pubkey | @@ -126,12 +127,21 @@ download_and_verify_cert() { local key_fingerprint key_fingerprint=$(openssl pkey -in "$temp_key" -pubout -outform DER 2>/dev/null | openssl dgst -sha256) + local pem_fingerprint + pem_fingerprint=$(openssl x509 -in "$temp_pem" -noout -pubkey | + openssl pkey -pubin -outform DER 2>/dev/null | + openssl dgst -sha256) if [ "$cert_fingerprint" != "$key_fingerprint" ]; then echo -e "${RED}Certificate and key do not match for $domain${NC}" return 1 fi + if [[ "$cert_fingerprint" != "$pem_fingerprint" ]]; then + echo -e "${RED}Certificate and PEM file do not match for $domain${NC}" + return 1 + fi + return 0 } @@ -139,19 +149,17 @@ install_certificate() { local domain=$1 local final_cert="$CERT_PATH/$domain.crt" local final_key="$KEY_PATH/$domain.key" - local final_cert_pem="$CERT_PATH/$domain.cert.pem" - local final_key_pem="$KEY_PATH/$domain.key.pem" + local final_pem="$KEY_PATH/$domain.pem" local temp_cert="$TEMP_DIR/$domain.crt" local temp_key="$TEMP_DIR/$domain.key" - local temp_cert_pem="$TEMP_DIR/$domain.cert.pem" - local temp_key_pem="$TEMP_DIR/$domain.key.pem" + local temp_pem="$TEMP_DIR/$domain.pem" local needs_reload=0 # Check if certificate needs updating if [ "$FORCE_UPDATE" = "true" ]; then needs_reload=1 - elif [ "$FULLCHAIN_PEM" = "true" ] && [ -f "$final_cert_pem" ]; then - if ! cmp -s "$final_cert_pem" "$temp_cert_pem"; then + elif [ "$FULLCHAIN_PEM" = "true" ] && [ -f "$final_pem" ]; then + if ! cmp -s "$final_pem" "$temp_cert_pem"; then needs_reload=1 fi elif [ -f "$final_cert" ]; then @@ -165,11 +173,11 @@ install_certificate() { # Install new certificate and key if [ $needs_reload -eq 1 ]; then if [ "$FULLCHAIN_PEM" = "true" ]; then - if ! cp -f "$temp_cert_pem" "$final_cert_pem" || ! cp -f "$temp_key_pem" "$final_key_pem"; then - echo -e "${RED}Failed to install PEM files for $domain${NC}" + if ! cp -f "$temp_pem" "$final_pem"; then + echo -e "${RED}Failed to install PEM file for $domain${NC}" return 1 fi - local files=("$final_cert_pem" "$final_key_pem") + local files=("$final_pem") else if ! cp -f "$temp_cert" "$final_cert" || ! cp -f "$temp_key" "$final_key"; then echo -e "${RED}Failed to install certificate files for $domain${NC}" From 47db5038ac7f16287c18368dd9d6ec7736e0b871 Mon Sep 17 00:00:00 2001 From: Ruben Date: Wed, 7 May 2025 13:04:49 +0200 Subject: [PATCH 2/2] Cleaned up before merge --- README.md | 74 ++++++++++++++++------------------------------------- certman.sh | 6 ++--- example.env | 15 +++++------ 3 files changed, 31 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index d6d4fba..241f605 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,10 @@ A bash script for managing SSL/TLS certificates through the Certwarden API. This - Download and verify certificates and private keys from Certwarden server - Automatic installation with proper permissions and ownership -- Certificate and key pair validation -- Service reload after certificate updates - Certificate expiration monitoring - Interactive menu-driven interface -- Silent mode for automated operations - Force update option for certificate renewals -- Proper error handling and logging - Support for multiple certificates -- Secure temporary file handling ## Prerequisites @@ -25,26 +20,26 @@ The script requires the following dependencies: ## Installation -1. Clone this repository: +1. Clone this repository into `/opt/certman/`: ```bash -git clone -cd certman +git clone https://forge.dmz.skyfritt.net/Skyfritt/certman.git --depth=1 /opt/certman && cd certman ``` -2. Create a `.env` file with your configuration: +2. Rename `example.env` to `.env` and modify as needed: ```bash # Server Configuration CERTWARDEN_SERVER="certwarden.dmz.skyfritt.net:443" # Certificate Paths -CERT_PATH="/etc/forgejo" -KEY_PATH="/etc/forgejo" +CERT_PATH="/etc/certs" +KEY_PATH="/etc/certs" TEMP_PATH="/tmp/certman" +FULLCHAIN_PEM="true" # Service Configuration -SERVICE_NAME="forgejo" -CERT_OWNER="git" -CERT_GROUP="git" +SERVICE_NAME="nginx" +CERT_OWNER="www-data" +CERT_GROUP="www-data" CERT_PERMISSIONS="644" KEY_PERMISSIONS="600" @@ -55,11 +50,6 @@ CERTIFICATES='[ "domain": "example-one.com", "cert_api_key": "your_cert_api_key", "key_api_key": "your_key_api_key" - }, - { - "domain": "example-two.com", - "cert_api_key": "your_cert_api_key", - "key_api_key": "your_key_api_key" } ]' ``` @@ -67,20 +57,20 @@ CERTIFICATES='[ ## Usage ### Interactive Mode -Run the script without any arguments: +Run the script without any arguments ```bash ./certman.sh ``` -This will present a menu with the following options: -1. Process all certificates -2. List installed certificates -3. Check certificate expiration -4. Force update all certificates -5. Exit +### Cron Configuration +Add these lines to your crontab for automated certificate management: +```cron +@reboot sleep 15 && /path/to/certman.sh --silent +2 2 * * 7 /opt/certman/certman.sh --silent +``` -### Automated Mode -Run the script with the `--silent` flag for automated operations: +### Silent Mode +Run the script with the `--silent` flag for a silent certificate check. It will use the options set in the `.env` file. ```bash ./certman.sh --silent ``` @@ -91,21 +81,10 @@ Use the `--force` flag to force certificate updates regardless of current status ./certman.sh --force ``` -Flags can be combined: +### Disable fetching fullchain PEM +Use the `--disable-pem` to only fetch the .key and .crt: ```bash -./certman.sh --silent --force -``` - -### Fullchain PEM -Use the `--fullchain-pem` flag to combine certificate and private key into a single PEM file: -```bash -./certman.sh --fullchain-pem - -### Cron Configuration -Add these lines to your crontab for automated certificate management: -```cron -@reboot sleep 15 && /path/to/certman.sh --silent -5 4 * * 2 /path/to/certman.sh --silent +./certman.sh --disable-pem ``` ## Environment Variables @@ -116,19 +95,10 @@ Add these lines to your crontab for automated certificate management: | CERT_PATH | Directory for certificate storage | Yes | | KEY_PATH | Directory for private key storage | Yes | | TEMP_PATH | Temporary directory for downloads | Yes | -| FULLCHAIN_PEM | Optional: Combine cert and key into single PEM file | No | +| FULLCHAIN_PEM | Enabled by default | No | | SERVICE_NAME | Service to reload after certificate updates | Yes | | CERT_OWNER | User owner for certificate files | Yes | | CERT_GROUP | Group owner for certificate files | Yes | | CERT_PERMISSIONS | Certificate file permissions | Yes | | KEY_PERMISSIONS | Private key file permissions | Yes | | CERTIFICATES | JSON array of certificate configurations | Yes | - -## Security Considerations - -- Store the script and `.env` file in a secure location with restricted permissions -- Use appropriate permissions for certificate and key files -- Keep API keys secure and rotate them periodically -- Run the script as a user with appropriate privileges -- Temporary files are automatically cleaned up using secure practices -- Certificate and key pairs are validated before installation diff --git a/certman.sh b/certman.sh index d13f460..25ee48a 100755 --- a/certman.sh +++ b/certman.sh @@ -26,7 +26,7 @@ load_env || exit 1 AUTO_MODE="false" FORCE_UPDATE="false" -FULLCHAIN_PEM="${FULLCHAIN_PEM:-false}" +FULLCHAIN_PEM="${FULLCHAIN_PEM:-true}" while [[ $# -gt 0 ]]; do case $1 in @@ -38,8 +38,8 @@ while [[ $# -gt 0 ]]; do FORCE_UPDATE="true" shift ;; - --fullchain-pem) - FULLCHAIN_PEM="true" + --disable-pem) + FULLCHAIN_PEM="false" shift ;; *) diff --git a/example.env b/example.env index 51bdff7..266a3e9 100644 --- a/example.env +++ b/example.env @@ -1,19 +1,19 @@ CERTWARDEN_SERVER="certwarden.dmz.skyfritt.net:443" # Certificate Paths -CERT_PATH="/etc/forgejo" -KEY_PATH="/etc/forgejo" +CERT_PATH="/etc/certs" +KEY_PATH="/etc/certs" TEMP_PATH="/tmp/certman" -# FULLCHAIN_PEM=true # Service Configuration -SERVICE_NAME="forgejo" -CERT_OWNER="git" -CERT_GROUP="git" +SERVICE_NAME="nginx" +CERT_OWNER="www-data" +CERT_GROUP="www-data" CERT_PERMISSIONS="644" KEY_PERMISSIONS="600" # Certificate Configurations (JSON format) +# Add as many or few domains as you need (but remember to add or delete the JSON comma!) CERTIFICATES='[ { "domain": "example.com", @@ -21,6 +21,3 @@ CERTIFICATES='[ "key_api_key": "your_key_api_key" } ]' - -# Optional: Auto mode configuration -AUTO_MODE="false"