Complete acme-dns Configuration with SSL Certificate Installation
For ssl.anytrust.ch on Ubuntu
This comprehensive guide covers the complete setup of acme-dns with proper SSL certificate installation, from DNS preparation through production deployment.
Prerequisites and DNS Setup
1. DNS Records Preparation
Before starting, ensure these DNS records are configured in your main domain's DNS zone (anytrust.ch):
# A record for your acme-dns server ssl.anytrust.ch. IN A YOUR_SERVER_PUBLIC_IP # NS record to delegate subdomain ssl.anytrust.ch. IN NS ssl.anytrust.ch. # Optional: IPv6 support ssl.anytrust.ch. IN AAAA YOUR_IPv6_ADDRESS
Verify DNS propagation:
# Check A record dig A ssl.anytrust.ch @8.8.8.8 # Check NS record dig NS ssl.anytrust.ch @1.1.1.1 # Verify from multiple locations host ssl.anytrust.ch nslookup ssl.anytrust.ch
2. System Preparation
# Update system sudo apt update && sudo apt upgrade -y # Install required packages sudo apt install -y \ golang-go \ git \ certbot \ python3-certbot \ sqlite3 \ dnsutils \ net-tools \ ufw \ acl # Check if port 53 is in use (common conflict with systemd-resolved) sudo netstat -tulpn | grep :53 # If systemd-resolved is using port 53, disable it sudo systemctl disable systemd-resolved sudo systemctl stop systemd-resolved # Alternative: modify systemd-resolved to not bind port 53 sudo sed -i 's/#DNSStubListener=yes/DNSStubListener=no/' /etc/systemd/resolved.conf sudo systemctl restart systemd-resolved
acme-dns Installation
3. Build and Install acme-dns
# Create working directory mkdir -p ~/build && cd ~/build # Clone repository git clone https://github.com/joohoi/acme-dns cd acme-dns # Build the binary go build # Install binary sudo mv acme-dns /usr/local/bin/ sudo chmod +x /usr/local/bin/acme-dns # Verify installation /usr/local/bin/acme-dns -h
4. Create System User and Directories
# Create dedicated system user sudo adduser --system --gecos "acme-dns Service" \ --disabled-password --group --home /var/lib/acme-dns acme-dns # Create required directories sudo mkdir -p /etc/acme-dns sudo mkdir -p /var/lib/acme-dns sudo mkdir -p /var/lib/acme-dns/cert sudo mkdir -p /var/log/acme-dns # Set ownership sudo chown -R acme-dns:acme-dns /var/lib/acme-dns sudo chown -R acme-dns:acme-dns /var/log/acme-dns sudo chown root:acme-dns /etc/acme-dns
SSL Certificate Acquisition
5. Initial Certificate Request with Certbot
Method A: Using HTTP-01 Challenge (Recommended for initial setup)
# Stop any service using port 80 sudo systemctl stop nginx apache2 2>/dev/null # Request certificate using standalone mode sudo certbot certonly \ --standalone \ --preferred-challenges http \ -d ssl.anytrust.ch \ --email admin@anytrust.ch \ --agree-tos \ --no-eff-email \ --non-interactive # Certificate will be saved to: # /etc/letsencrypt/live/ssl.anytrust.ch/fullchain.pem # /etc/letsencrypt/live/ssl.anytrust.ch/privkey.pem
Method B: Using DNS-01 Challenge (If port 80 is not available)
# Request certificate with manual DNS challenge sudo certbot certonly \ --manual \ --preferred-challenges dns \ -d ssl.anytrust.ch \ --email admin@anytrust.ch \ --agree-tos \ --no-eff-email # Follow the prompts to add TXT record to your DNS # Add: _acme-challenge.ssl.anytrust.ch TXT "provided-value" # Wait for DNS propagation before continuing
6. Verify Certificate Installation
# Check certificate details sudo openssl x509 -in /etc/letsencrypt/live/ssl.anytrust.ch/fullchain.pem \ -text -noout | grep -A 2 "Subject:" # Verify certificate dates sudo openssl x509 -in /etc/letsencrypt/live/ssl.anytrust.ch/fullchain.pem \ -text -noout | grep -A 2 "Validity" # Test certificate chain sudo openssl verify -CAfile /etc/letsencrypt/live/ssl.anytrust.ch/chain.pem \ /etc/letsencrypt/live/ssl.anytrust.ch/cert.pem
acme-dns Configuration with SSL
7. Create Configuration File
Create /etc/acme-dns/config.cfg:
[general] # DNS interface settings listen = ":53" protocol = "both" domain = "ssl.anytrust.ch" nsname = "ssl.anytrust.ch" nsadmin = "admin.anytrust.ch" records = [ "ssl.anytrust.ch. A YOUR_SERVER_PUBLIC_IP", "ssl.anytrust.ch. NS ssl.anytrust.ch.", ] debug = false [database] # Database backend settings engine = "sqlite3" connection = "/var/lib/acme-dns/acme-dns.db" [api] # API interface settings with SSL ip = "0.0.0.0" port = "443" # Direct TLS configuration using Let's Encrypt certificates tls = "cert" tls_cert_privkey = "/var/lib/acme-dns/cert/privkey.pem" tls_cert_fullchain = "/var/lib/acme-dns/cert/fullchain.pem" # API settings corsorigins = ["*"] use_header = false header_name = "X-Forwarded-For" disable_registration = false [logconfig] # Logging configuration loglevel = "info" logtype = "file" logformat = "text" logfile = "/var/log/acme-dns/acme-dns.log"
8. Set Up Certificate Management
Create certificate copy script for acme-dns access:
# Create certificate deployment script sudo tee /usr/local/bin/deploy-acme-dns-certs.sh << 'EOF' #!/bin/bash # Certificate source and destination CERT_SRC="/etc/letsencrypt/live/ssl.anytrust.ch" CERT_DST="/var/lib/acme-dns/cert" # Copy certificates cp -L "$CERT_SRC/fullchain.pem" "$CERT_DST/fullchain.pem" cp -L "$CERT_SRC/privkey.pem" "$CERT_DST/privkey.pem" # Set permissions chown acme-dns:acme-dns "$CERT_DST"/*.pem chmod 640 "$CERT_DST"/*.pem # Restart acme-dns if running if systemctl is-active --quiet acme-dns; then systemctl restart acme-dns echo "acme-dns restarted with new certificates" fi echo "Certificates deployed successfully" EOF # Make script executable sudo chmod +x /usr/local/bin/deploy-acme-dns-certs.sh # Run initial deployment sudo /usr/local/bin/deploy-acme-dns-certs.sh
Set up automatic renewal hook:
# Create renewal hook for certbot sudo tee /etc/letsencrypt/renewal-hooks/deploy/01-acme-dns.sh << 'EOF' #!/bin/bash # This script runs after successful certificate renewal if [[ "$RENEWED_DOMAINS" == *"ssl.anytrust.ch"* ]]; then /usr/local/bin/deploy-acme-dns-certs.sh logger -t certbot-renewal "acme-dns certificates renewed and deployed" fi EOF sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/01-acme-dns.sh
9. Configure Systemd Service
# Create systemd service file sudo tee /etc/systemd/system/acme-dns.service << 'EOF' [Unit] Description=Limited DNS server with RESTful HTTP API for ACME challenges Documentation=https://github.com/joohoi/acme-dns After=network-online.target Wants=network-online.target [Service] Type=simple User=acme-dns Group=acme-dns ExecStart=/usr/local/bin/acme-dns -c /etc/acme-dns/config.cfg Restart=on-failure RestartSec=5s # Security hardening NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ProtectHome=true ReadWritePaths=/var/lib/acme-dns /var/log/acme-dns ReadOnlyPaths=/etc/acme-dns # Network capabilities AmbientCapabilities=CAP_NET_BIND_SERVICE CapabilityBoundingSet=CAP_NET_BIND_SERVICE # Resource limits LimitNOFILE=65536 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF # Reload systemd sudo systemctl daemon-reload
Firewall Configuration
10. Configure UFW Firewall
# Configure default policies sudo ufw default deny incoming sudo ufw default allow outgoing # Allow SSH (adjust port if needed) sudo ufw allow 22/tcp comment 'SSH' # Allow DNS sudo ufw allow 53/tcp comment 'DNS TCP' sudo ufw allow 53/udp comment 'DNS UDP' # Allow HTTPS for API sudo ufw allow 443/tcp comment 'HTTPS API' # Allow HTTP for certificate renewal sudo ufw allow 80/tcp comment 'HTTP Certbot renewal' # Enable firewall sudo ufw --force enable # Check status sudo ufw status verbose
Service Startup and Testing
11. Start acme-dns Service
# Set correct permissions on config sudo chmod 640 /etc/acme-dns/config.cfg sudo chown root:acme-dns /etc/acme-dns/config.cfg # Enable and start service sudo systemctl enable acme-dns.service sudo systemctl start acme-dns.service # Check service status sudo systemctl status acme-dns.service # View logs sudo journalctl -u acme-dns -f
12. Test SSL Connection
# Test HTTPS API endpoint curl -v https://ssl.anytrust.ch/health # Test SSL certificate echo | openssl s_client -connect ssl.anytrust.ch:443 -servername ssl.anytrust.ch 2>/dev/null | \ openssl x509 -noout -text | grep -A 2 "Subject:" # Test with SSL Labs (optional) echo "Visit: https://www.ssllabs.com/ssltest/analyze.html?d=ssl.anytrust.ch"
13. Test DNS Functionality
# Test DNS resolution dig @ssl.anytrust.ch SOA ssl.anytrust.ch # Register a test account curl -X POST https://ssl.anytrust.ch/register \ -H "Content-Type: application/json" | python3 -m json.tool # Save the output! You'll get credentials like: # { # "username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", # "password": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", # "fulldomain": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.ssl.anytrust.ch", # "subdomain": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # }
Certificate Renewal Automation
14. Set Up Automatic Renewal
# Test renewal process sudo certbot renew --dry-run # Check certbot timer is active sudo systemctl status certbot.timer # If timer doesn't exist, create cron job echo "0 3 * * * /usr/bin/certbot renew --quiet --post-hook '/usr/local/bin/deploy-acme-dns-certs.sh'" | \ sudo crontab - # Manual renewal test sudo certbot renew --force-renewal --cert-name ssl.anytrust.ch
Production Security Hardening
15. Security Enhancements
# After initial setup, disable public registration sudo sed -i 's/disable_registration = false/disable_registration = true/' \ /etc/acme-dns/config.cfg sudo systemctl restart acme-dns # Set up fail2ban for API protection (optional) sudo apt install fail2ban # Create fail2ban filter sudo tee /etc/fail2ban/filter.d/acme-dns.conf << 'EOF' [Definition] failregex = ^.*Failed authentication attempt from <HOST>.*$ ignoreregex = EOF # Create jail configuration sudo tee /etc/fail2ban/jail.d/acme-dns.conf << 'EOF' [acme-dns] enabled = true port = 443 filter = acme-dns logpath = /var/log/acme-dns/acme-dns.log maxretry = 5 bantime = 3600 EOF sudo systemctl restart fail2ban
16. Monitoring and Maintenance
# Create monitoring script sudo tee /usr/local/bin/check-acme-dns.sh << 'EOF' #!/bin/bash # Check service status if ! systemctl is-active --quiet acme-dns; then echo "CRITICAL: acme-dns service is not running" systemctl start acme-dns exit 2 fi # Check API availability if ! curl -sf https://ssl.anytrust.ch/health > /dev/null 2>&1; then echo "WARNING: acme-dns API not responding" exit 1 fi # Check certificate expiry CERT_FILE="/var/lib/acme-dns/cert/fullchain.pem" EXPIRY_DATE=$(openssl x509 -in "$CERT_FILE" -noout -enddate | cut -d= -f2) EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s) CURRENT_EPOCH=$(date +%s) DAYS_LEFT=$(( ($EXPIRY_EPOCH - $CURRENT_EPOCH) / 86400 )) if [ $DAYS_LEFT -lt 7 ]; then echo "WARNING: Certificate expires in $DAYS_LEFT days" exit 1 fi echo "OK: acme-dns operational, cert valid for $DAYS_LEFT days" exit 0 EOF sudo chmod +x /usr/local/bin/check-acme-dns.sh # Add to cron for regular checks echo "*/5 * * * * /usr/local/bin/check-acme-dns.sh" | sudo crontab -l | sudo crontab -
Backup Configuration
17. Set Up Backups
# Create backup script sudo tee /usr/local/bin/backup-acme-dns.sh << 'EOF' #!/bin/bash BACKUP_DIR="/var/backups/acme-dns" TIMESTAMP=$(date +%Y%m%d_%H%M%S) # Create backup directory mkdir -p "$BACKUP_DIR" # Backup database sqlite3 /var/lib/acme-dns/acme-dns.db \ ".backup $BACKUP_DIR/acme-dns-$TIMESTAMP.db" # Backup configuration tar czf "$BACKUP_DIR/config-$TIMESTAMP.tar.gz" \ /etc/acme-dns/ \ /var/lib/acme-dns/cert/ # Keep only last 30 days find "$BACKUP_DIR" -type f -mtime +30 -delete echo "Backup completed: $BACKUP_DIR/*-$TIMESTAMP.*" EOF sudo chmod +x /usr/local/bin/backup-acme-dns.sh # Schedule daily backups echo "0 2 * * * /usr/local/bin/backup-acme-dns.sh" | sudo crontab -l | sudo crontab -
Client Configuration
18. Configure ACME Clients to Use acme-dns
For domains using this acme-dns server:
- Register with acme-dns:
curl -X POST https://ssl.anytrust.ch/register
- Create CNAME record in your domain's DNS:
_acme-challenge.yourdomain.com. CNAME xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.ssl.anytrust.ch.
- Use with certbot:
# Install acme-dns authenticator wget https://github.com/joohoi/acme-dns-certbot-joohoi/raw/master/acme-dns-auth.py chmod +x acme-dns-auth.py # Request certificate certbot certonly --manual \ --manual-auth-hook ./acme-dns-auth.py \ --preferred-challenges dns \ --debug-challenges \ -d yourdomain.com
Troubleshooting
19. Common Issues and Solutions
Port 53 Already in Use:
# Find what's using port 53 sudo lsof -i :53 sudo netstat -tulpn | grep :53 # If systemd-resolved, disable DNS stub sudo systemctl stop systemd-resolved sudo rm /etc/resolv.conf echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf
SSL Certificate Issues:
# Check certificate permissions ls -la /var/lib/acme-dns/cert/ # Verify certificate readability sudo -u acme-dns openssl x509 -in /var/lib/acme-dns/cert/fullchain.pem -text -noout # Check service logs sudo journalctl -u acme-dns --since "1 hour ago"
API Not Accessible:
# Check if service is listening sudo ss -tulpn | grep 443 # Test local connection curl -k https://localhost/health # Check firewall sudo ufw status numbered
DNS Resolution Failures:
# Test DNS server directly dig @localhost ssl.anytrust.ch dig @ssl.anytrust.ch test.ssl.anytrust.ch TXT # Check zone delegation dig +trace ssl.anytrust.ch
20. Verification Checklist
- DNS A and NS records properly configured
- SSL certificate obtained and valid
- acme-dns service running on ports 53 and 443
- Firewall rules configured
- API accessible via HTTPS
- Certificate auto-renewal configured
- Monitoring and backups in place
- Test registration successful
- DNS queries responding correctly
Maintenance Commands Reference
# Service management sudo systemctl status/start/stop/restart acme-dns # View logs sudo journalctl -u acme-dns -f tail -f /var/log/acme-dns/acme-dns.log # Certificate management sudo certbot certificates sudo certbot renew --dry-run sudo /usr/local/bin/deploy-acme-dns-certs.sh # Test endpoints curl https://ssl.anytrust.ch/health dig @ssl.anytrust.ch test.ssl.anytrust.ch # Backup sudo /usr/local/bin/backup-acme-dns.sh # Monitoring sudo /usr/local/bin/check-acme-dns.sh
This completes the comprehensive setup of acme-dns with SSL on ssl.anytrust.ch. The service is now ready for production use with automated certificate management, monitoring, and backup systems in place.