DNS over TLSをnginxで設定

2025年7月25日

pi-holeをスマホで使用したいが、最近のAndroidではプライベートDNSの機能でドメイン名でDNSサーバを指定するのが主流のようである。
調べてみると Nginx の機能で実現できそうなので設定してみた。

参考にさせてもらったサイトはこちら
https://varunsridharan.hashnode.dev/configure-pi-hole-with-dns-over-tls-private-dns

nginx を稼働させるのはクラウドで借りている CentOS Stream release 9 として、このサーバではすでに let’s encrypt の処理と各サイトへの振り分けを nginx で行っている。

問題点としては、参考サイトでも Step 6 で提示している「他の設定は全部削除」とあるとおり、nginx が http{} と stream{} を同時に扱えないか、同時に扱うための設定が一般に知られていない状態であること。この問題に該当する場合は nginx -t やログでもそれらしきエラー情報が表示されないことがある。
今回は既存の nginx とは別の nginx を podman(docker) で起動して対応する。

podman のインストール、初期設定は別のサイトを参照していただくとして、今回の設定はこちら compose.yaml として作成する。

version: 3.8

services:
  nginx:
    build: .
    image: docker.io/library/nginx:stable
    container_name: nginx
    ports:
      - 853:853
    volumes:
      - type: bind
        source: ./nginx.conf
        target: /etc/nginx/nginx.conf
      - type: bind
        source: ./conf.d
        target: /etc/nginx/conf.d
      - type: bind
        source: ./stream.d
        target: /etc/nginx/stream.d
      - type: bind
        source: /etc/letsencrypt
        target: /etc/letsencrypt
      #  read_only: true
    hostname: nginx

Dockerfile の例 dnsutils は確認用に追加

FROM nginx

RUN apt-get update && \
    apt-get -y install dnsutils

CMD ["nginx", "-g", "daemon off;"]

nginx.conf の例 ログ設定は確認用

user nginx;
worker_processes auto;
# error_log /var/log/nginx/error.log;
error_log /tmp/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

#http {
    #access_log /tmp/access.log;
    #include /etc/nginx/conf.d/*;
#}

stream {
    log_format dns '$remote_addr [$time_local] $protocol';
    access_log /tmp/access.log dns;
    include /etc/nginx/stream.d/*;
}

stream.d/pi-hole.stream として stream の設定ファイルを作成

upstream dns-servers {
    zone dns-servers 64k;
    server    127.0.0.1:53;
}
server {
  listen 853 ssl;

    ssl_certificate     /etc/letsencrypt/live/ドメイン/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ドメイン/privkey.pem;

  ssl_protocols        TLSv1.2 TLSv1.3;
  ssl_ciphers          HIGH:!aNULL:!MD5;

  ssl_handshake_timeout    10s;
  ssl_session_cache        shared:SSL:20m;
  ssl_session_timeout      4h;
  proxy_pass dns-servers;
}

podman-compose up -d で起動確認

注意点として証明書の bind を
/etc/letsencrypt/live/ドメイン/
指定で行うと実体は
/etc/letsencrypt/archive/ドメイン/
の内容の symlink なので
SSL: error:80000002:system library::No such file or directory
のようなエラーが起動時に表示される。/etc/letsencrypt までを bind する。

podman の場合の systemd 連携は別投稿とする予定です。

pi-holeってゲーム内で使われている動画を見るとアイテムゲットみたいな広告はどう?については、デフォ設定ではブロックされるがログを見て3ドメインくらい許可に入れればOKでした。

Geminiで作成