The Challenge
If you’ve ever hosted a website with a provider that doesn’t offer API access for DNS management, you’ve likely encountered challenges with automating SSL certificate generation - particularly for wildcard certificates. This is a common issue when you need to secure multiple subdomains but can’t automatically verify domain ownership through DNS challenges.
The Solution: Using a Secondary Domain
A clever workaround for this limitation is to leverage a secondary domain hosted with a provider that does support API access. Here’s how it works:
- Purchase a secondary domain from a provider with API support (like Hetzner)
- Set up DNS CNAME records to redirect the ACME challenge verification
- Use
acme.sh
to automate certificate generation
How It Works
When requesting a wildcard certificate for *.primary-domain.com
, Let’s Encrypt requires verification through a TXT record at _acme-challenge.primary-domain.com
. Instead of placing the TXT record directly there, we:
-
Create a CNAME record on the primary domain:
_acme-challenge.primary-domain.com IN CNAME _acme-challenge.secondary-domain.com
-
Let the automation script place the TXT record on the secondary domain where we have API access
-
When Let’s Encrypt checks the challenge, it follows the CNAME and finds the verification record
Automated Implementation
Here’s a comprehensive script that automates the entire process:
#!/bin/bash
# Configuration
PRIMARY_DOMAIN="myblog.technology"
SECONDARY_DOMAIN="myblog-alternative.technology"
ADMIN_EMAIL="admin@myblog.technology"
CERT_DIR="/etc/ssl/private"
SERVICE_RESTART_CMD="systemctl restart nginx"
# Function to check if acme.sh is installed
check_acme_sh() {
if ! command -v acme.sh &> /dev/null; then
echo "acme.sh not found. Installing..."
curl https://get.acme.sh | sh
source ~/.bashrc
fi
}
# Function to validate environment
validate_environment() {
if [ -z "$HETZNER_TOKEN" ]; then
echo "Error: HETZNER_TOKEN environment variable not set"
echo "Please set it using: export HETZNER_TOKEN=your_token_here"
exit 1
fi
}
# Function to generate and deploy certificate
generate_certificate() {
local domain="*.$PRIMARY_DOMAIN"
local challenge_domain="$SECONDARY_DOMAIN"
echo "Generating certificate for $domain using $challenge_domain for verification..."
acme.sh --issue \
--server letsencrypt \
-d "$domain" \
--dns dns_hetzner \
--challenge-alias "$challenge_domain" \
-m "$ADMIN_EMAIL" \
--renew-hook "$0 deploy" || {
echo "Certificate generation failed"
exit 1
}
}
# Function to deploy certificate
deploy_certificate() {
local cert_path="$HOME/.acme.sh/*.$PRIMARY_DOMAIN"
echo "Deploying certificates to $CERT_DIR..."
# Create directory if it doesn't exist
mkdir -p "$CERT_DIR"
# Copy certificates
cp "$cert_path/fullchain.cer" "$CERT_DIR/fullchain.pem"
cp "$cert_path/*.tech-tales.blog.key" "$CERT_DIR/privkey.pem"
# Set permissions
chmod 644 "$CERT_DIR/fullchain.pem"
chmod 600 "$CERT_DIR/privkey.pem"
# Restart services
echo "Restarting services..."
$SERVICE_RESTART_CMD
}
# Main execution
main() {
case "$1" in
"generate")
check_acme_sh
validate_environment
generate_certificate
;;
"deploy")
deploy_certificate
;;
*)
echo "Usage: $0 {generate|deploy}"
echo " generate: Generate new certificate"
echo " deploy: Deploy existing certificate"
exit 1
;;
esac
}
main "$@"
Prerequisites
- A primary domain where you want to use the wildcard certificate
- A secondary domain with API access for DNS management (e.g., Hetzner)
acme.sh
installed on your system- Hetzner API token (for this example)
Setting Up
-
First, set up your Hetzner API token:
export HETZNER_TOKEN=your_token_here
-
Add the CNAME record to your primary domain’s DNS:
_acme-challenge.tech-tales.blog IN CNAME _acme-challenge.tech-tales-alternative.blog
-
Save the script above as
ssl-cert-manager.sh
and make it executable:chmod +x ssl-cert-manager.sh
Usage
To generate a new certificate:
./ssl-cert-manager.sh generate
The script will handle:
- Checking for acme.sh installation
- Validating the environment
- Generating the certificate using DNS challenge
- Deploying the certificate to the specified location
- Restarting necessary services
Automation Tips
-
Add the script to cron for automatic renewal:
0 0 1 * * /path/to/ssl-cert-manager.sh generate
-
Store the Hetzner token in a secure environment file:
echo "export HETZNER_TOKEN=your_token_here" > ~/.ssl-cert-env chmod 600 ~/.ssl-cert-env
-
Source the environment file in your cron job:
0 0 1 * * source ~/.ssl-cert-env && /path/to/ssl-cert-manager.sh generate
Troubleshooting
If you encounter issues:
- Check the Let’s Encrypt logs in ~/.acme.sh/acme.sh.log
- Verify DNS propagation using dig or nslookup
- Ensure the HETZNER_TOKEN has sufficient permissions
- Test with Let’s Encrypt staging environment first by modifying the --server parameter
Security Considerations
- Keep your API token secure and regularly rotate it
- Use restricted permissions for certificate files
- Monitor certificate expiration dates
- Maintain backup copies of certificates
Remember to replace the example domains, email addresses, and paths with your actual values before using the script.
This solution provides a robust way to automate wildcard certificate generation even when your primary domain host doesn’t provide API access for DNS management.