286 lines
7.1 KiB
Markdown
286 lines
7.1 KiB
Markdown
### **Securing Your Critical Server: Starting/Stopping `acme-dns` & Opening/Closing Port 53 with Certbot**
|
||
|
||
On a **critical server**, leaving **port 53 (DNS)** open unnecessarily is a **security risk**. DNS servers are frequent targets for attacks (e.g., DDoS amplification, DNS hijacking).
|
||
|
||
The best practice is to **only open port 53 and run `acme-dns` *while* Certbot is executing a DNS challenge**, then immediately **close port 53 and stop `acme-dns`** afterward.
|
||
|
||
Below is a **complete, production‑ready solution** using **`systemd`** to automate this safely.
|
||
|
||
---
|
||
|
||
#### **Why Systemd?**
|
||
Systemd allows you to define dependencies, pre/post actions, and clean lifecycle management — perfect for this use case!
|
||
|
||
---
|
||
|
||
## ✅ Step-by-Step Implementation
|
||
|
||
### **1. Modify `acme-dns.service` (DO NOT enable it at boot!)**
|
||
|
||
We **disable** `acme-dns` from starting automatically. It will **only run when Certbot needs it**.
|
||
|
||
Edit the service file:
|
||
|
||
```bash
|
||
sudo nano /etc/systemd/system/acme-dns.service
|
||
```
|
||
|
||
Replace its content with this **(security‑hardened version)**:
|
||
|
||
```ini
|
||
#/etc/systemd/system/acme-dns.service
|
||
[Unit]
|
||
Description=Limited DNS server for ACME DNS challenges
|
||
After=network.target
|
||
# ONLY start when explicitly requested (never at boot)
|
||
RefuseManualStart=no # Allow manual start (for testing)
|
||
# DO NOT start on boot!
|
||
[Service]
|
||
# Run as a NON-ROOT user (HIGHLY RECOMMENDED!)
|
||
User=acme-dns # Create this user first (see Step 2)
|
||
Group=acme-dns
|
||
|
||
# Prevent the service from gaining extra privileges
|
||
PrivateTmp=yes
|
||
ProtectSystem=strict
|
||
ProtectHome=yes
|
||
NoNewPrivileges=yes
|
||
RestrictAddressFamilies=AF_INET AF_INET6 # Only allow IPv4/IPv6
|
||
# Bind to port 53 WITHOUT root (requires kernel >= 4.11)
|
||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||
|
||
ExecStart=/usr/local/bin/acme-dns -c /etc/acme-dns/config.cfg
|
||
|
||
Restart=on-failure
|
||
RestartSec=5s
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target # BUT WE WILL DISABLE THIS!
|
||
```
|
||
|
||
> ⚠️ **Critical:** **DO NOT enable this service!**
|
||
> Run: `sudo systemctl disable acme-dns.service`
|
||
|
||
---
|
||
|
||
### **2. Create a Dedicated Non-Root User for `acme-dns`**
|
||
|
||
```bash
|
||
sudo useradd --system --no-create-home --shell /usr/sbin/nologin acme-dns
|
||
sudo mkdir -p /var/lib/acme-dns /etc/acme-dns
|
||
sudo chown -R acme-dns:acme-dns /var/lib/acme-dns /etc/acme-dns
|
||
```
|
||
|
||
Ensure your **`/etc/acme-dns/config.cfg`** has correct permissions:
|
||
|
||
```bash
|
||
sudo chown acme-dns:acme-dns /etc/acme-dns/config.cfg
|
||
sudo chmod 600 /etc/acme-dns/config.cfg
|
||
```
|
||
|
||
---
|
||
|
||
### **3. Create a NEW Systemd Service: `certbot-acme-dns.service`**
|
||
|
||
This service **orchestrates**:
|
||
1. Open UDP port 53 in `ufw`
|
||
2. Start `acme-dns.service`
|
||
3. Run **Certbot**
|
||
4. Stop `acme-dns.service`
|
||
5. **Close UDP port 53**
|
||
|
||
Create the file:
|
||
|
||
```bash
|
||
sudo nano /etc/systemd/system/certbot-acme-dns.service
|
||
```
|
||
|
||
Paste the following **(replace `<yoursite>` with your domain)**:
|
||
|
||
```ini
|
||
#/etc/systemd/system/certbot-acme-dns.service
|
||
[Unit]
|
||
Description=Run Certbot with acme-dns (opens/closes port 53)
|
||
After=network.target network-online.target
|
||
Wants=network-online.target
|
||
|
||
# Start ONLY when triggered (by timer or manual)
|
||
RefuseManualStart=no
|
||
|
||
[Service]
|
||
# ----- OPEN PORT 53 & START acme-dns BEFORE Certbot -----
|
||
ExecStartPre=/usr/sbin/ufw allow 53/udp
|
||
ExecStartPre=/usr/bin/systemctl start acme-dns.service
|
||
|
||
# ----- RUN CERTBOT -----
|
||
# • Use 'renew' for automatic renewals
|
||
# • Add domains as needed
|
||
ExecStart=/usr/bin/certbot renew \
|
||
--quiet \
|
||
--post-hook "/usr/bin/systemctl restart nginx" # <-- CHANGE "nginx" to "apache2" if you use Apache
|
||
|
||
# ----- STOP acme-dns & CLOSE PORT 53 AFTER Certbot FINISHES (EVEN ON FAILURE!) -----
|
||
ExecStopPost=/usr/bin/systemctl stop acme-dns.service
|
||
ExecStopPost=/usr/sbin/ufw delete allow 53/udp
|
||
|
||
# Critical: Clean up even if Certbot FAILS!
|
||
Restart=on-failure
|
||
RestartSec=10
|
||
|
||
# Never run this service manually! It's triggered by the timer.
|
||
RefuseManualStart=yes
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
> ✅ **`ExecStopPost` runs *after* the service stops — even if Certbot fails!**
|
||
> This guarantees port 53 is **always closed**.
|
||
|
||
---
|
||
|
||
### **4. Create a Timer to Run Renewals Weekly**
|
||
|
||
Create `/etc/systemd/system/certbot-acme-dns.timer`:
|
||
|
||
```bash
|
||
sudo nano /etc/systemd/system/certbot-acme-dns.timer
|
||
```
|
||
|
||
```ini
|
||
#/etc/systemd/system/certbot-acme-dns.timer
|
||
[Unit]
|
||
Description=Weekly Certbot Renewal (with acme-dns)
|
||
|
||
[Timer]
|
||
# Wait 5 minutes after boot (to avoid race conditions)
|
||
OnBootSec=5min
|
||
|
||
# Run every week
|
||
OnUnitActiveSec=1w
|
||
|
||
# Randomize delay ±30 min to avoid "thundering herd"
|
||
RandomizedDelaySec=1800s
|
||
|
||
[Install]
|
||
WantedBy=timers.target
|
||
```
|
||
|
||
**Enable & Start the Timer:**
|
||
|
||
```bash
|
||
sudo systemctl enable --now certbot-acme-dns.timer
|
||
```
|
||
|
||
✅ **Now Certbot will run automatically every week — and port 53 is ONLY open for a few seconds!**
|
||
|
||
---
|
||
|
||
### **5. Test It!**
|
||
|
||
#### 🔍 Check Timer Status
|
||
```bash
|
||
systemctl status certbot-acme-dns.timer
|
||
```
|
||
|
||
#### 🔧 Dry-Run Renewal (NO real changes!)
|
||
```bash
|
||
sudo certbot renew --dry-run
|
||
```
|
||
|
||
#### ▶️ Manually Trigger the Service (for testing)
|
||
```bash
|
||
sudo systemctl start certbot-acme-dns.service
|
||
```
|
||
|
||
**Watch the logs:**
|
||
```bash
|
||
journalctl -u certbot-acme-dns.service -f
|
||
```
|
||
|
||
You should see:
|
||
1. `ufw allow 53/udp`
|
||
2. `systemctl start acme-dns.service`
|
||
3. Certbot running
|
||
4. `systemctl stop acme-dns.service`
|
||
5. `ufw delete allow 53/udp`
|
||
|
||
✅ **Port 53 is closed again!**
|
||
|
||
Verify UFW after the run:
|
||
```bash
|
||
sudo ufw status verbose
|
||
```
|
||
You should **NOT** see `53/udp` allowed.
|
||
|
||
---
|
||
|
||
## 📌 Additional Security Tips
|
||
|
||
1. **Firewall Lockdown**
|
||
Ensure **no other service** listens on port 53!
|
||
```bash
|
||
sudo ss -tulpn | grep ':53'
|
||
```
|
||
Only `acme-dns` should appear — **and only when the service is running**.
|
||
|
||
2. **`acme-dns` Configuration Hardening**
|
||
In `/etc/acme-dns/config.cfg`:
|
||
|
||
```ini
|
||
[general]
|
||
debug = false # <<-- TURN OFF DEBUG IN PRODUCTION!
|
||
listen = ":53"
|
||
protocol = "udp"
|
||
|
||
[api]
|
||
disable_registration = true # <<-- PREVENT NEW ACCOUNTS!
|
||
tls = "none" # API runs on localhost:8081 (safe)
|
||
```
|
||
|
||
3. **Backups**
|
||
Backup:
|
||
* `/etc/acme-dns/config.cfg`
|
||
* `/var/lib/acme-dns/acme-dns.db`
|
||
* Your Let's Encrypt certificates (`/etc/letsencrypt`)
|
||
|
||
Use `cron` to backup daily.
|
||
|
||
---
|
||
|
||
## ❓ What About Manual Certificate Issuance (e.g., adding a new domain)?
|
||
|
||
Use this **one‑liner** (it auto-opens/closes port 53):
|
||
|
||
```bash
|
||
sudo systemctl start certbot-acme-dns.service --no-block
|
||
```
|
||
|
||
Or create a helper script (`/usr/local/bin/certbot-with-acme-dns`):
|
||
|
||
```bash
|
||
#!/usr/bin/env bash
|
||
sudo systemctl start certbot-acme-dns.service --no-block "$@"
|
||
```
|
||
|
||
Make it executable:
|
||
```bash
|
||
sudo chmod +x /usr/local/bin/certbot-with-acme-dns
|
||
```
|
||
|
||
Then run:
|
||
```bash
|
||
sudo certbot-with-acme-dns certonly \
|
||
-d newdomain.com \
|
||
--manual \
|
||
--manual-auth-hook /etc/letsencrypt/acme-dns-auth.py \
|
||
--preferred-challenges dns
|
||
```
|
||
|
||
The script will handle opening/closing port 53 automatically!
|
||
|
||
---
|
||
|
||
💚 **You’re now running a critical server with MINIMAL exposure!**
|
||
Port 53 is **only open for seconds** while Certbot runs — drastically reducing your attack surface. 🎯
|