Enabling HTTPS for your Pi-hole Web Interface with LE and CF DNS

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.

  1. Log in to your Cloudflare dashboard.
  2. Navigate to "My Profile" in the top right corner.
  3. Select the "API Tokens" tab.
  4. Click the "Create Token" button.
  5. Find the "API Token Templates" section and choose the "Edit DNS records" template, or create a "Create Custom Token".
  6. 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.
  7. Give the token a descriptive name, for example, "Certbot DNS CF".
  8. Click "Continue to summary", then "Create Token".
  9. 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:

  1. Install Certbot and the Cloudflare plugin:
    sudo apt update
    sudo apt install certbot python3-certbot-dns-cloudflare -y
  2. Create a directory for secrets and the credentials file:
    sudo mkdir -p ~/.secrets
    sudo nano ~/.secrets/cloudflare.ini
  3. 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)

  4. Set strict permissions for the credentials file:
    sudo chmod 600 ~/.secrets/cloudflare.ini
  5. 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
  6. 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 line port = "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)

  7. Restart pihole-FTL:
    systemctl restart pihole-FTL
  8. 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)

  9. 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
  10. 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 domain

    add_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)

  11. Check the nginx configuration:
    sudo nginx -t
  12. Reload nginx:
    sudo systemctl reload nginx
  13. After these steps, the Pi-hole web interface opens via the domain using the LE certificate for me.