Hello. I'd like to share information about installing an SSL LE (Let's Encrypt) certificate using CF (Cloudflare) DNS for a Pi-hole web server, based on how I did it. Perhaps this option will be useful to someone. P.S. I spent a lot of time searching for information, trying, and making mistakes, but nothing worked out.
Let's assume you already have the server and Pi-hole properly installed and configured, and you have SSH access (e.g., via PuTTY), but you want to add an auto-updating certificate to it. All steps were performed via PuTTY, and I had already added a Local DNS record in Pi-hole for the domain beforehand.
Obtaining a Cloudflare API Token for Certbot
Before starting the installation, you will need to generate a Cloudflare API token with the necessary permissions to manage DNS records. This token will allow Certbot to automatically pass domain validation.
- Log in to your Cloudflare dashboard.
- Navigate to "My Profile" in the top right corner.
- Select the "API Tokens" tab.
- Click the "Create Token" button.
- Find the "API Token Templates" section and choose the "Edit DNS records" template, or create a "Create Custom Token".
- If you are creating a custom token, set the following permissions:
- Permissions:
- Zone → DNS → Edit
- Zone → DNS → Read
- Zone Resources: Include → Specific zone → Select your domain for which you are requesting the certificate. It is recommended to restrict the token to only the necessary domain for security.
- Permissions:
- Give the token a descriptive name, for example, "Certbot DNS CF".
- Click "Continue to summary", then "Create Token".
- Important: Copy the generated token. It will be shown only once. Store it securely; you will need it to create the credentials file in the following steps. Treat it like a password.
Installation and Configuration
Now that you have the token, you can proceed with the installation and configuration:
- Install Certbot and the Cloudflare plugin:
sudo apt update sudo apt install certbot python3-certbot-dns-cloudflare -y
- Create a directory for secrets and the credentials file:
sudo mkdir -p ~/.secrets sudo nano ~/.secrets/cloudflare.ini
- Add the content to
cloudflare.ini
and save (using the token copied in the previous step):dns_cloudflare_api_token = your_copied_token
(To save in nano: Ctrl+O, Enter, Ctrl+X)
- Set strict permissions for the credentials file:
sudo chmod 600 ~/.secrets/cloudflare.ini
- Request the certificate (replace
your.domain.com
with your actual domain):sudo certbot certonly \ --dns-cloudflare \ --dns-cloudflare-credentials ~/.secrets/cloudflare.ini \ -d your.domain.com \ -d *.your.domain.com # Optional, for a Wildcard certificate
- Change the ports that the Pihole-FTL web server will use:
sudo nano /etc/pihole/pihole.toml
Change the value for the
port
parameter. Find the[webserver]
section and change or add the lineport = "127.0.0.1:8080,[::1]:8080"
.[webserver] port = "127.0.0.1:8080,[::1]:8080"
(To save in nano: Ctrl+O, Enter, Ctrl+X)
- Restart pihole-FTL:
systemctl restart pihole-FTL
- Install nginx. Follow the instructions for your OS on the page:
https://nginx.org/en/linux_packages.html
(This usually involves adding a repository and then installing via
sudo apt update && sudo apt install nginx
) - Create a folder for generating the DH Key and generate the key:
sudo mkdir /etc/nginx/ssl sudo openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
- Create and save the site configuration for Nginx (replace
your.domain.com
with your actual domain):sudo nano /etc/nginx/conf.d/your.domain.com.conf
server { listen 80; server_name your.domain.com; # Replace with your domain return 301 https://$host$request_uri; }
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name your.domain.com; # Replace with your domainadd_header X-Robots-Tag "noindex, nofollow"; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Cross-Origin-Resource-Policy "same-origin" always; add_header Cross-Origin-Opener-Policy "same-origin" always; add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), fullscreen=(), payment=(), interest-cohort=()" always; ssl_certificate /etc/letsencrypt/live/your.domain.com/fullchain.pem; # Replace with path to your certificate (your domain) ssl_certificate_key /etc/letsencrypt/live/your.domain.com/privkey.pem; # Replace with path to your private key (your domain) ssl_dhparam /etc/nginx/ssl/dhparam.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers 'ECDHE+AESGCM:CHACHA20:DHE+AESGCM:!aNULL:!eNULL:!LOW:!SSLv2:!SSLv3:!MD5:!RC4:!3DES'; ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1:x448; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_session_tickets off; ssl_early_data off; ssl_buffer_size 8k; location ~ /\. { deny all; } location = / { return 301 /admin/; } location / { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header Upgrade $http_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Connection "upgrade"; proxy_hide_header X-Powered-By; proxy_hide_header Server; proxy_http_version 1.1; proxy_buffering off; # proxy_ssl_verify off; # Usually not needed when proxying to localhost HTTP proxy_connect_timeout 10s; proxy_send_timeout 30s; proxy_read_timeout 30s; }
}
(To save in nano: Ctrl+O, Enter, Ctrl+X)
- Check the nginx configuration:
sudo nginx -t
- Reload nginx:
sudo systemctl reload nginx
- After these steps, the Pi-hole web interface opens via the domain using the LE certificate for me.