diff --git a/README.md b/README.md index be6a83c..0a8f5e6 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ # UniFi OS Server SSL Import Script -A script to automatically import and update SSL certificates for your UniFi OS server (`uosserver`) from multiple certificate providers. The script supports both **Let's Encrypt via Certbot** and **acme.sh**, with DNS challenge support for all out-of-the-box DNS providers. The script stops the UniFi controller, replaces its TLS key and certificate files with the latest certs, sets correct permissions, and then restarts the controller — ensuring your UniFi server always has a valid SSL certificate. +A script to automatically import and update SSL certificates for your UniFi OS server (`uosserver`) from multiple certificate providers. The script supports **Let's Encrypt via Certbot**, **acme.sh**, and **Caddy**, with DNS challenge support for all out-of-the-box DNS providers. The script stops the UniFi controller, replaces its TLS key and certificate files with the latest certs, sets correct permissions, and then restarts the controller — ensuring your UniFi server always has a valid SSL certificate. --- ## Features -- **Multiple Certificate Providers**: Support for both Certbot and acme.sh +- **Multiple Certificate Providers**: Support for Certbot, acme.sh, and Caddy - **DNS Challenge Support**: Works with Cloudflare and all supported acme.sh DNS providers - **Smart Updates**: Automatically detects if the certificate changed and updates only if needed - **Command Line Options**: - `--force` to reinstall certificate even if unchanged - `--verbose` for detailed logs and command output - - `--provider=certbot|acme` to specify certificate provider + - `--provider=certbot|acme|caddy` to specify certificate provider - `--dns=cloudflare` to specify DNS provider (for acme.sh) - **Safety Features**: Creates backups of existing key and cert files before updating - **Comprehensive Logging**: Logs actions to `/var/log/unifi-ssl-import.log` @@ -107,6 +107,32 @@ acme.sh --issue --dns dns_hetzner -d your.domain.com --keylength 4096 **Important**: UniFi OS Server only supports RSA certificates, so always use `--keylength 2048` (or higher RSA key lengths) with acme.sh. ECC certificates are not supported. +### Option 3: Caddy Server + +If you're using Caddy as your reverse proxy, it automatically generates and manages Let's Encrypt certificates. This option allows you to import existing certificates that Caddy has already generated, without needing to generate new ones. + +#### Prerequisites + +- Caddy server must be installed and configured +- Caddy must have already generated certificates for your domain +- Certificates should be located at: `/var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/your.domain.com/` + +#### Caddy Configuration Example + +Your Caddyfile should include your UniFi domain: + +```caddyfile +your.domain.com { + reverse_proxy localhost:443 { + transport http { + tls_insecure_skip_verify + } + } +} +``` + +After Caddy generates certificates, you can use this script to import them into UniFi OS. + --- ## Install the Import Script @@ -135,7 +161,7 @@ Look for and modify the following configuration variables: # Domain Name: UNIFI_HOSTNAME="unifi.example.com" -# Certificate Provider: "certbot" or "acme" +# Certificate Provider: "certbot", "acme", or "caddy" CERT_PROVIDER="certbot" # DNS Provider (for acme.sh): "cloudflare", "hetzner", etc. @@ -168,6 +194,9 @@ You can override the configuration using command line arguments: # Use acme.sh with Cloudflare DNS /usr/local/bin/unifi-osserver-ssl-import.sh --provider=acme --dns=cloudflare +# Use Caddy certificates +/usr/local/bin/unifi-osserver-ssl-import.sh --provider=caddy + # Force certificate reinstallation /usr/local/bin/unifi-osserver-ssl-import.sh --force @@ -179,7 +208,7 @@ You can override the configuration using command line arguments: ``` ### Available Options: -- `--provider=certbot|acme` – Specify certificate provider +- `--provider=certbot|acme|caddy` – Specify certificate provider - `--dns=cloudflare|hetzner` – Specify DNS provider (for acme.sh only) - `--verbose` – Show detailed output of what the script is doing - `--force` – Force reimport of certificate even if it hasn't changed @@ -218,6 +247,15 @@ acme.sh automatically installs its own cron job for renewal. You only need to ad 5 */12 * * * root /usr/local/bin/unifi-osserver-ssl-import.sh --provider=acme --dns=hetzner >> /home/import_log.txt 2>&1 ``` +### For Caddy users: + +Caddy automatically renews certificates. You only need to add the import script: + +```cron +# Check for certificate updates twice a day +5 */12 * * * root /usr/local/bin/unifi-osserver-ssl-import.sh --provider=caddy >> /home/import_log.txt 2>&1 +``` + ### Provider-agnostic approach: You can also set the provider in the script configuration and use: diff --git a/unifi-osserver-ssl-import b/unifi-osserver-ssl-import old mode 100644 new mode 100755 index a0fc0b2..d099e61 --- a/unifi-osserver-ssl-import +++ b/unifi-osserver-ssl-import @@ -5,7 +5,7 @@ #Domain Name: UNIFI_HOSTNAME="unifi.example.com" -#Certificate Provider: "certbot" or "acme" +#Certificate Provider: "certbot", "acme", or "caddy" CERT_PROVIDER="certbot" #DNS Provider (for acme.sh): "cloudflare", "hetzner", etc. @@ -14,6 +14,7 @@ DNS_PROVIDER="cloudflare" #Configuration paths: ACME_HOME="/root/.acme.sh" CERTBOT_CONFIG_DIR="/etc/letsencrypt" +CADDY_DATA_DIR="/var/lib/caddy/.local/share/caddy" #Force Mode: FORCE=false @@ -34,7 +35,7 @@ for arg in "$@"; do --dns=*) DNS_PROVIDER="${arg#*=}" ;; *) echo "Unknown argument: $arg" - echo "Usage: $0 [--force] [--verbose] [--provider=certbot|acme] [--dns=cloudflare|hetzner]" + echo "Usage: $0 [--force] [--verbose] [--provider=certbot|acme|caddy] [--dns=cloudflare|hetzner]" exit 1 ;; esac @@ -75,6 +76,19 @@ get_cert_dir() { exit 1 fi ;; + "caddy") + # Caddy stores certificates in subdirectories by ACME server and domain + local caddy_cert_dir="${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/${UNIFI_HOSTNAME}" + + if [[ -d "$caddy_cert_dir" ]]; then + echo "$caddy_cert_dir" + else + echo "❌ No Caddy certificate directory found for ${UNIFI_HOSTNAME}" >&2 + echo " Expected directory: $caddy_cert_dir" >&2 + echo " Make sure Caddy has generated certificates for this domain" >&2 + exit 1 + fi + ;; *) echo "❌ Unknown certificate provider: $CERT_PROVIDER" >&2 exit 1 @@ -106,6 +120,13 @@ get_cert_files() { exit 1 fi ;; + "caddy") + # Caddy stores certificates as domain.crt and domain.key + CERT_FILE="${cert_dir}/${UNIFI_HOSTNAME}.crt" + KEY_FILE="${cert_dir}/${UNIFI_HOSTNAME}.key" + # Caddy's .crt file already contains the full certificate chain + CHAIN_FILE="" + ;; esac } @@ -125,9 +146,16 @@ validate_provider_config() { exit 1 fi ;; + "caddy") + if [[ ! -d "$CADDY_DATA_DIR" ]]; then + echo "❌ Caddy data directory not found: $CADDY_DATA_DIR" + echo " Please make sure Caddy is installed and has generated certificates" + exit 1 + fi + ;; *) echo "❌ Unsupported certificate provider: $CERT_PROVIDER" - echo " Supported providers: certbot, acme" + echo " Supported providers: certbot, acme, caddy" exit 1 ;; esac @@ -168,7 +196,13 @@ if [[ "$CERT_PROVIDER" == "certbot" ]]; then echo "❌ Missing certificate files. Aborting." exit 1 fi +elif [[ "$CERT_PROVIDER" == "caddy" ]]; then + if [[ ! -f "$CERT_FILE" ]]; then + echo "❌ Missing certificate file: $CERT_FILE. Aborting." + exit 1 + fi else + # acme.sh provider if [[ ! -f "$CERT_FILE" ]]; then echo "❌ Missing certificate file: $CERT_FILE. Aborting." exit 1 @@ -180,6 +214,7 @@ echo "✅ All required files found." if [[ "$CERT_PROVIDER" == "certbot" ]]; then CURRENT_SUM=$(cat "$KEY_FILE" "$CERT_FILE" "$CHAIN_FILE" | md5sum | awk '{print $1}') else + # For acme.sh and caddy, only use key and cert files CURRENT_SUM=$(cat "$KEY_FILE" "$CERT_FILE" | md5sum | awk '{print $1}') fi echo "Current cert bundle MD5: $CURRENT_SUM" @@ -229,7 +264,7 @@ if [[ "$CERT_PROVIDER" == "certbot" ]]; then cat "$CERT_FILE" "$CHAIN_FILE" > "$DEST_CERT" 2>/dev/null fi else - # For acme.sh, fullchain.cer already contains both cert and chain + # For acme.sh and caddy, the cert file already contains the full chain run_command cp -v "$CERT_FILE" "$DEST_CERT" fi echo "✅"