Day 21 - DNS-01 Challenge
Using the DNS-01 challenge with Certbot and Cloudflare is an excellent approach to obtain an SSL certificate without opening port 80, as it verifies domain ownership via DNS TXT records rather than HTTP. This is particularly useful for your single-node Kubernetes setup on a VPS without a load balancer, as it avoids the need to expose ports like 80 or 443 during certificate issuance. Below, I’ll explain how to use Certbot with the certbot-dns-cloudflare plugin to obtain a certificate using the DNS-01 challenge, based on the provided documentation and tailored to your Kubernetes environment.
Why DNS-01 Challenge?
- No Port 80 Required: Unlike the HTTP-01 challenge, DNS-01 doesn’t require a web server or open ports (80 or 443). It proves domain ownership by adding a TXT record to your DNS.
- Wildcard Support: DNS-01 is required for wildcard certificates (e.g.,
*.example.com). - Security: It’s ideal for internal or firewalled setups, as no external access to your server is needed during validation.[](https://letsencrypt.org/docs/challenge-types/)
Prerequisites
- A domain managed by Cloudflare with DNS records configured.
- A Cloudflare API token with DNS edit permissions (preferred over Global API Key for security).
- Certbot and the
certbot-dns-cloudflareplugin installed on your VPS. kubectlaccess to your single-node Kubernetes cluster.- Your application running in Kubernetes with a NodePort or ClusterIP service.
Step-by-Step Guide
1. Install Certbot and the Cloudflare Plugin
Run these commands on your VPS (assuming a Debian/Ubuntu-based system):
sudo apt update
sudo apt install certbot python3-certbot-dns-cloudflareFor other distributions, refer to the Certbot instructions or install via pip:
sudo pip3 install certbot certbot-dns-cloudflareVerify installation:
certbot --version2. Create a Cloudflare API Token
Following the Certbot DNS Cloudflare documentation, create an API token:
- Log into your Cloudflare dashboard.
- Go to My Profile > API Tokens.
- Click Create Token and use the Edit zone DNS template.
- Set permissions:
- Zone: DNS - Edit (for all zones or select your specific domain).
- Save and copy the generated token.
Alternatively, you can use a Global API Key (less secure):
- Go to My Profile > API Keys.
- Copy the Global API Key.
3. Configure Cloudflare Credentials
Create a credentials file to store your Cloudflare API token or key, as specified in the documentation:
sudo mkdir -p /root/.secrets
sudo touch /root/.secrets/cloudflare.ini
sudo chmod 600 /root/.secrets/cloudflare.iniEdit the file:
sudo nano /root/.secrets/cloudflare.iniAdd one of the following, depending on your choice:
dns_cloudflare_api_token = YOUR_API_TOKEN_HERE- Using API Token (recommended):
dns_cloudflare_email = your-cloudflare-email@example.com
dns_cloudflare_api_key = YOUR_GLOBAL_API_KEY_HERE- Using Global API Key:
Save and exit. The documentation emphasizes securing this file, so ensure it’s only readable by root.
4. Run Certbot with DNS-01 Challenge
Use Certbot to request a certificate for your domain. Replace example.com with your domain. If you want a wildcard certificate, include *.example.com.
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
--preferred-challenges dns-01 \
-d example.com \
-d *.example.comWhat happens:
- Certbot contacts Let’s Encrypt, which provides a token.
- The
certbot-dns-cloudflareplugin uses your API token to create a TXT record at_acme-challenge.example.com. - Let’s Encrypt verifies the TXT record.
- The plugin removes the TXT record after validation.
- The certificate is saved to
/etc/letsencrypt/live/example.com/.
Key Notes:
- No port 80 needed: The DNS-01 challenge relies entirely on DNS, so port 80 can remain closed.[](https://mytechiethoughts.com/linux/free-ssl-certificates-using-cloudflare-dns-validation-and-certbot/)[](https://letsencrypt.org/docs/challenge-types/)
- Propagation delay: The
--dns-cloudflare-propagation-secondsoption can be added (e.g.,--dns-cloudflare-propagation-seconds 30) if DNS updates are slow.[](https://mytechiethoughts.com/linux/free-ssl-certificates-using-cloudflare-dns-validation-and-certbot/) - Wildcard certificates: Including
*.example.comrequires DNS-01, and this command supports it.[](https://labzilla.io/blog/cloudflare-certbot)
If you encounter errors (e.g., “Incorrect TXT record” or “Invalid request headers”), check:
- The API token has Edit permissions for DNS.[](https://github.com/NginxProxyManager/nginx-proxy-manager/issues/3063)
- The domain is correctly configured in Cloudflare.
- The credentials file is correctly formatted and readable.
5. Integrate the Certificate into Kubernetes
As described in the previous response, you can use the certificate in your Kubernetes application or with a reverse proxy like Nginx.
kubectl create secret tls my-app-tls \
--cert=/etc/letsencrypt/live/example.com/fullchain.pem \
--key=/etc/letsencrypt/live/example.com/privkey.pem- Create a Kubernetes Secret: Copy the certificate files to a secure location and create a secret:
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
type: NodePort
ports:
- port: 443
targetPort: 8443
nodePort: 30443
selector:
app: my-app- Option 1: Application-Level SSL: Mount the secret in your application pod and configure your app to use HTTPS (e.g., on port 8443). Update your NodePort service to expose the HTTPS port:
Update your deployment to mount the secret:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-app-image
ports:
- containerPort: 8443
volumeMounts:
- name: tls
mountPath: /etc/tls
readOnly: true
volumes:
- name: tls
secret:
secretName: my-app-tls- Option 2: Nginx Reverse Proxy:
Deploy an Nginx pod to terminate SSL and proxy to your app. Use a ConfigMap for Nginx configuration and mount the TLS secret, as shown in the previous response.
Example Nginx ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
data:
nginx.conf: |
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/certs/tls.crt;
ssl_certificate_key /etc/nginx/certs/tls.key;
location / {
proxy_pass http://my-app-service:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}Deploy Nginx and expose it via NodePort, as detailed previously.
6. Automate Certificate Renewal
Certificates from Let’s Encrypt expire every 90 days. The certbot-dns-cloudflare plugin supports automatic renewal.[](https://certbot.eff.org/faq)
sudo certbot renew --dry-run- Test renewal:
- Schedule renewal:
Certbot typically sets up a cron job or systemd timer to run certbot renew twice daily. Verify it:
sudo systemctl status certbot.timerIf not enabled, create a cron job:
sudo crontab -eAdd:
0 0 * * * certbot renew --quiet && kubectl create secret tls my-app-tls --cert=/etc/letsencrypt/live/example.com/fullchain.pem --key=/etc/letsencrypt/live/example.com/privkey.pem --dry-run=client -o yaml | kubectl apply -f -This renews the certificate and updates the Kubernetes secret. Restart your pods if needed:
kubectl rollout restart deployment my-app
kubectl rollout restart deployment nginx7. Map Domain to NodePort (Optional)
To access your app at https://example.com without specifying the NodePort (e.g., 30443), set up a reverse proxy on the VPS host:
sudo apt install nginx- Install Nginx:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://<VPS_PUBLIC_IP>:30443;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}- Configure Nginx to forward traffic from port 443 to the NodePort:
Save to /etc/nginx/sites-available/example.com, enable, and restart Nginx:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx- Ensure your domain’s A record points to the VPS’s public IP.
Verification
openssl s_client -connect example.com:443- Access your app at
https://example.comorhttps://<VPS_PUBLIC_IP>:30443. - Check the certificate with:
- Verify DNS TXT records are cleaned up after issuance (they should be removed automatically by the plugin).[](https://labzilla.io/blog/cloudflare-certbot)
Answers to Your Questions
Yes, the DNS-01 challenge does not require port 80 or 443 to be open. It uses DNS TXT records for validation, making it ideal for your setup where you may not want to expose these ports. The certbot-dns-cloudflare plugin handles all DNS operations via Cloudflare’s API.[](https://mytechiethoughts.com/linux/free-ssl-certificates-using-cloudflare-dns-validation-and-certbot/)[](https://www.reddit.com/r/selfhosted/comments/tvwr2u/howtodealwithopenport80torenew/)- Can I run it without opening port 80?
The Certbot DNS Cloudflare documentation is accurate and supports this process. It details:
- Using the provided documentation:
- Setting up credentials (
cloudflare.ini). - Using
--dns-cloudflareand--preferred-challenges dns-01. - Automatic TXT record creation and cleanup.
- Renewal automation with the plugin.
Additional Notes
Security: Keep the cloudflare.ini file secure (permissions 600). Use API tokens instead of Global API Keys for better scoping.[](https://www.nodinrogers.com/post/2022-03-10-certbot-cloudflare-docker/)
Wildcard Certificates: If you only need specific subdomains, list them explicitly (e.g., -d app.example.com -d api.example.com) to reduce the attack surface compared to a wildcard.[](https://www.reddit.com/r/selfhosted/comments/16og5nx/letsencryptdns01challengeorrootca/)
Troubleshooting:
- If Certbot fails with “unauthorized” or “incorrect TXT record,” verify your API token permissions and DNS zone settings.[](https://community.letsencrypt.org/t/unable-to-generate-a-wildcard-certificate-using-cloudflare-dns-challenge/205336)
- Check DNS propagation with
dig _acme-challenge.example.com TXT. - Use
--dry-runto test without hitting Let’s Encrypt rate limits.
Cloudflare Proxy: If Cloudflare’s proxy is enabled, ensure SSL/TLS is set to “Full (strict)” to avoid issues with Let’s Encrypt validation.[](https://community.letsencrypt.org/t/will-cloudflare-proxy-block-certbot-challenge/191879)
Example Workflow Summary
- Install Certbot and
certbot-dns-cloudflare. - Create a Cloudflare API token and store it in
/root/.secrets/cloudflare.ini. - Run Certbot with the DNS-01 challenge to obtain a certificate.
- Create a Kubernetes Secret and configure your app or Nginx to use the certificate.
- Expose the app via NodePort (e.g., 30443 for HTTPS).
- Set up a host-level Nginx proxy to map
example.com:443to the NodePort. - Automate renewal with a cron job.
This approach ensures you can secure your Kubernetes app with SSL without opening port 80, fully leveraging the DNS-01 challenge and Cloudflare’s API. If you need help with specific errors or configurations, let me know!