課題:暗号化されていないDNSと静かに漏洩するテレメトリ
ローカルネットワーク上のデバイスがWebサービスに接続する際、常にUDPポート53経由の暗号化されていないDNSクエリから始まります。スマートテレビは診断テレメトリを送信し、IoT家電は未知のエンドポイントに定期通信を行い、OSは数秒ごとにバックグラウンドでテレメトリクエリを発行しています。標準のDNSには暗号化機能がないため、インターネットサービスプロバイダ(ISP)や通信を盗聴できる第三者は、家庭内でアクセスしたすべてのドメインをログとして記録できてしまいます。
一般的な広告ブロッカーはブラウザのランタイム内でのみ機能するため、スマートフォンのアプリやIoT機器、バックグラウンドのデーモンプロセスは完全に無防備な状態のままになります。悪意のあるドメインがマルウェアやC2(コマンド&コントロール)ペイロードを配信している場合でも、通常のエンドポイントは何の障壁もなくそれらのIPアドレスを名前解決してしまいます。
根本原因:平文での名前解決とシンクホールの不在
従来のDNSプロトコル仕様は何十年も前に策定されたものであり、プライバシーの保護は考慮されていませんでした。平文のクエリは中間者攻撃(MitM)による改ざん、DNSスプーフィング、メタデータの収集に対して脆弱です。また、上位リゾルバのリダイレクト機能により、NXDOMAIN応答時にプロバイダ独自のランディングページが挿入されることもあります。
トラッキングと監視の双方に対処するには、連携して動作する2つのレイヤーが必要です。セキュリティブロックリストと照合してドメイン検索を検査するローカルの「DNSシンクホール」と、外部への上流クエリを第三者の覗き見から保護する暗号化トランスポートラッパー「DNS-over-HTTPS(DoH)」です。
クイックスタート(5分)
Dockerを利用すれば、Pi-holeとCloudflaredをわずか5分で立ち上げることができます。以前、深夜に自宅サーバーがSSHブルートフォース攻撃を受けた経験から、初期セットアップ時からセキュリティを最優先することを強く推奨します。ホストのファイアウォール設定で、ポート53へのアクセスを信頼できる内部サブネットのみに制限するようにしてください。
以下の設定をLinuxホスト上に docker-compose.yml として保存します:
version: "3.8"
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: proxy-dns --port 5053 --address 0.0.0.0 --upstream https://1.1.1.1/dns-query --upstream https://1.0.0.1/dns-query
networks:
pihole_net:
ipv4_address: 172.20.0.2
pihole:
image: pihole/pihole:latest
container_name: pihole
restart: unless-stopped
ports:
- "53:53/tcp"
- "53:53/udp"
- "8080:80/tcp"
environment:
TZ: "UTC"
WEBPASSWORD: "ChangeThisSecurePassword123!"
PIHOLE_DNS_: "172.20.0.2#5053"
DNSSEC: "true"
volumes:
- ./etc-pihole:/etc/pihole
- ./etc-dnsmasq.d:/etc/dnsmasq.d
networks:
pihole_net:
ipv4_address: 172.20.0.3
depends_on:
- cloudflared
networks:
pihole_net:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/24
スタックを起動し、コンテナが正常に動作しているか確認します:
docker compose up -d
docker compose ps
ローカルターミナルからシンクホール経由でDNS名前解決をテストします:
dig @127.0.0.1 -p 53 itfromzero.com
クエリによって解決されたIPアドレスが返されます。この通信はCloudflaredを経由して暗号化されたHTTPSでルーティングされています。composeファイルで設定したパスワードを使用して、http://<your-server-ip>:8080/admin からPi-holeの管理ダッシュボードにアクセスしてください。
徹底解説:アーキテクチャとクエリのライフサイクル
ネットワークパケットが通過する正確なルートを理解しておくことは、レイテンシ問題の調査やポリシーによるブロックの診断に役立ちます。
1. クライアントからの受信とフィルタリング
内部クライアント(ワークステーション、スマートフォン、スマートサーモスタットなど)が tracker.adnetwork.com の名前解決を開始すると、ポート53経由でPi-holeのIPアドレスに標準のUDP/TCPクエリが送信されます。Pi-holeはメモリ常駐型のFTLエンジン(dnsmasq をベースに構築されたFaster Than Light DNS)を参照します。
対象ドメインがブロックリストに存在するか、正規表現パターンに一致する場合、Pi-holeは即座にリクエストを遮断し、0.0.0.0(または NXDOMAIN)を返します。この通信はローカル境界の外に出ることがないため、トラッキングテレメトリを遮断し、悪意のあるペイロードのダウンロードを未然に防ぎます。
2. Cloudflaredへの転送
ドメインが正当なもの(例:itfromzero.com)である場合、Pi-holeはローカルキャッシュを確認します。キャッシュミスが発生すると、Cloudflaredがリッスンしている上位の 172.20.0.2#5053 へクエリを転送します。
3. DoHカプセル化とTLSハンドシェイク
Cloudflaredは標準のRFC 1035ワイヤーフォーマットクエリを受け取り、HTTP/2またはHTTP/3ペイロード(RFC 8484)にカプセル化して、標準のTCPポート443経由でCloudflareのエニーキャストエンドポイント(https://1.1.1.1/dns-query)に対するアウトバウンドTLS 1.3トンネルを確立します。上流のネットワーク監視システムやISPから見ると、このパケットは通常のHTTPS Webトラフィックと区別がつかないため、ディープパケットインスペクション(DPI)やメタデータのログ記録は無効化されます。
ベアメタルへのネイティブインストール(Dockerを使わない代替手段)
軽量なRaspberry PiやDebianミニマルサーバー上でコンテナのオーバーヘッドなしに運用したい場合は、Cloudflaredをネイティブにインストールします:
sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt-get update && sudo apt-get install -y cloudflared
/etc/default/cloudflared に専用のサービス設定ファイルを作成します:
CLOUDFLARED_OPTS="--port 5053 --address 127.0.0.1 --upstream https://1.1.1.1/dns-query --upstream https://1.0.0.1/dns-query"
デーモンを管理するためにsystemdを設定します:
sudo useradd -s /usr/sbin/nologin -r -M cloudflared
sudo tee /etc/systemd/system/cloudflared.service <<EOF
[Unit]
Description=Cloudflared DNS-over-HTTPSプロキシ
After=syslog.target network-online.target
[Service]
Type=simple
User=cloudflared
EnvironmentFile=/etc/default/cloudflared
ExecStart=/usr/local/bin/cloudflared proxy-dns $CLOUDFLARED_OPTS
Restart=on-failure
RestartSec=10
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now cloudflared
ポート5053でローカルプロキシを直接テストします:
dig @127.0.0.1 -p 5053 cloudflare.com +short
高度な設定と活用法
フォールバックおよび冗長なアップストリームの設定
単一のDoHプロバイダに依存すると、単一障害点(SPOF)となるリスクがあります。Cloudflaredは複数のアップストリームエンドポイントを同時にサポートしており、ヘルスチェックを利用して自動的に負荷分散を行います:
cloudflared proxy-dns \
--port 5053 \
--address 127.0.0.1 \
--upstream https://1.1.1.1/dns-query \
--upstream https://1.0.0.1/dns-query \
--upstream https://dns.quad9.net/dns-query
Quad9を追加することで、マルウェアに関連するインフラに対するセカンダリ検証が行われ、名前解決の段階で多層防御を実現できます。
厳格なDNSSEC検証
Pi-holeの管理ダッシュボードで Settings > DNS > Use DNSSEC を有効にするか、コンテナの環境変数に DNSSEC=true を設定します。これにより、Pi-holeは署名付きゾーンから返されたDNSレコードの暗号化署名を検証するようになります。レコードが経路途中で改ざんまたは偽造されている場合、Pi-holeは名前解決を破棄して BOGUS 応答としてフラグを立て、キャッシュポイズニング攻撃を防止します。
ローカルドメインレコードと条件付き転送
外部へのシンクホール機能を維持しつつ、内部ホスト(nas.home.local や proxmox.lan など)の名前解決を行うには、/etc/pihole/custom.list にカスタムレコードを直接追加します:
192.168.1.10 nas.home.local
192.168.1.20 hypervisor.home.local
または、Settings > DNS > Conditional Forwarding(条件付き転送)に移動し、ローカルサブネットの逆引きクエリをメインルーターまたはDHCPサーバーに向けるよう設定します。
運用の実践的なTips
IoT機器によるDNSハードコード回避の防止
多くのスマートデバイス(Chromecast、監視カメラ、スマートテレビなど)は、DHCPによって割り当てられたDNSサーバーを無視し、8.8.8.8 や 1.1.1.1 などのパブリックリゾルバをハードコードしています。これらのデバイスによるシンクホールの回避を防ぐには、ルーター(pfSense、OPNsense、OpenWrtなど)にファイアウォールルールを追加し、不正なポート53のトラフィックを迎撃(インターセプト)またはブロックします:
# すべてのアウトバウンドポート53トラフィックをPi-holeへリダイレクトするiptablesルールの例
iptables -t nat -A PREROUTING -i eth1 -p udp ! -d 192.168.1.5 --dport 53 -j DNAT --to-destination 192.168.1.5:53
iptables -t nat -A PREROUTING -i eth1 -p tcp ! -d 192.168.1.5 --dport 53 -j DNAT --to-destination 192.168.1.5:53
192.168.1.5 をご自身のPi-holeサーバーの静的IPアドレスに置き換えてください。
高パフォーマンス化に向けたキャッシュサイズのチューニング
デフォルトでPi-holeは10,000件のエントリをキャッシュします。稼働中のコンテナが多い環境や、多数のマイクロサービスが大量のクエリを生成する環境では、キャッシュサイズを調整してCPU使用率を抑え、冗長なアップストリームへのリクエストを回避します。/etc/dnsmasq.d/02-custom-cache.conf にカスタム設定ファイルを作成します:
cache-size=25000
min-cache-ttl=300
新しい制限値を適用するためにDNSリゾルバサービスを再起動します:
pihole restartdns
ブロックリストの自動更新とバックアップ
Pi-holeはcronを介してgravityリスト(広告ブロックリスト)を毎週自動更新しますが、カスタムのブラックリストやホワイトリストを維持するには定期的なバックアップが不可欠です。Pi-holeのTeleporter機能を使い、毎晩実行されるcronタスクで設定を自動的にエクスポートします:
pihole -a -t /var/backups/pihole/pihole-backup-$(date +\%F).tar.gz
このバックアップコマンドを外部バックアップスクリプトと連携させることで、ホストストレージに障害が発生した場合でも迅速にリカバリできるようになります。

