Linux上でsystemd-resolvedを使ったDNS-over-TLSの設定:ドメイン名前解決のセキュリティ強化

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Ubuntu 22.04でsystemd-resolvedを本番環境で6ヶ月運用して、ようやくなぜこのサービスがsystemdに組み込まれているのかが腑に落ちた。最初はずっと放置していた——デフォルトのまま、DNSが解決できればそれでいい、という感じで。でもDNS-over-TLSを有効にしてから、状況が一変した:ISPによるDNSクエリへの介入を心配する必要がなくなり、コネクション再利用とローカルキャッシュのおかげで名前解決が速くなり、さらにDNSSECでドメインの正当性まで検証できるようになった。

クイックスタート:5分で設定完了

ターミナルを開いて、以下の手順を順番に実行する——追加パッケージのインストールは不要:

1. systemd-resolvedが起動しているか確認

systemctl status systemd-resolved
# まだ起動していない場合:
sudo systemctl enable --now systemd-resolved

2. 旧設定のバックアップとresolved.confの編集

sudo cp /etc/systemd/resolved.conf /etc/systemd/resolved.conf.bak
sudo nano /etc/systemd/resolved.conf

以下の内容を[Resolve]セクションに追加:

[Resolve]
# DoTをサポートするDNSサーバー — 形式: IP#hostname でTLS証明書を検証
DNS=1.1.1.1#cloudflare-dns.com 1.0.0.1#cloudflare-dns.com
FallbackDNS=8.8.8.8#dns.google 8.8.4.4#dns.google

# DNS-over-TLSを有効化
DNSOverTLS=yes

# DNSSECバリデーションを有効化
DNSSEC=yes

# DNSキャッシュ(デフォルトで有効 — 明示的に設定)
Cache=yes

3. 再起動と確認

sudo systemctl restart systemd-resolved

# ステータスを確認 — DNS-over-TLSとDNSSECの行を探す
resolvectl status

DNS-over-TLS setting: yesとDNSSEC setting: yesが表示されれば基本設定は完了。

4. /etc/resolv.confがスタブリゾルバーを正しく指しているか確認

# 現在のresolv.confを確認
cat /etc/resolv.conf

# nameserver 127.0.0.53 という行が必要
# なければシンボリックリンクを作成:
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf

動作確認:

resolvectl query itfromzero.com
# または:
dig @127.0.0.53 itfromzero.com

詳しく理解する:なぜ従来のDNSはセキュリティ上の問題なのか?

従来のDNSはUDPポート53を使って平文でクエリを送信する。つまり、自分のマシンとDNSサーバーの間にいる者は誰でも、どのドメインを調べているかを読み取れる——ISP、カフェの公共ルーター、ネットワーク経路上のあらゆる機器が対象だ。

DNS-over-TLS(DoT)はすべてのDNSクエリをTLS接続でラップする。ちょうどHTTPSがHTTPをラップするのと同じ仕組みだ。クエリはTCPポート853を通じて送信され、エンドツーエンドで暗号化される。ISPには1.1.1.1:853への接続が見えるだけで、クエリの内容は見えない。

stubbyやdnscrypt-proxyをインストールする代わりにsystemd-resolvedを選ぶ理由は?systemdを使うすべてのディストリビューション(Ubuntu、Fedora、Debian、Arch…)に最初から組み込まれており、追加パッケージが不要で、NetworkManagerとの連携も良好で、キャッシュも最初から処理してくれるからだ。

DNS#hostname形式の意味

IP#hostnameという記法はDNSサーバーのTLS証明書を検証するために使う:

# Cloudflare DoT
DNS=1.1.1.1#cloudflare-dns.com

# Google DoT
DNS=8.8.8.8#dns.google

# Quad9(マルウェアドメインも追加でブロック)
DNS=9.9.9.9#dns.quad9.net

#hostnameの部分はSNI(Server Name Indication)——resolvedはこのホスト名を使ってサーバーのTLS証明書を検証する。この部分がなくてもDoTは暗号化されるが、証明書の検証ができないため、セキュリティが一段階落ちる。

応用:DNSSEC、スマートフォールバック、Per-link DNS

DNSSEC — DNSレスポンスの改ざんを検証

DNSSECはDoTとは別の問題を解決する:DNSレスポンスが偽装されていないことを保証する(DNSスプーフィング/キャッシュポイズニング対策)。DNSSECを有効にすると、resolvedはDNSレコードの電子署名を検証する——誰かが偽のレコードを注入しようとすると、間違ったアドレスを返す代わりにクエリが失敗する。

# DNSSECが動作しているか確認
resolvectl query --type=DNSKEY google.com

# 特定のドメインがDNSSECをサポートしているかテスト
resolvectl query --type=DS cloudflare.com

注意:独自DNSを持つVPNを使っている場合、DNSSEC=yesは競合を起こすことがある。その場合はDNSSEC=allow-downgradeに設定する——サーバーがサポートしていれば検証し、サポートしていなければ自動でスキップする。

フォールバックDNSの正しい設定

自分が管理するRAM 4GBのUbuntu 22.04本番サーバーで、これによって処理時間が大幅に削減されることを実感した——特にプライマリDNSでレイテンシスパイクが発生したときに。フォールバックにはQuad9を使っている。マルウェアドメインを自動でブロックしてくれるため、無料でセキュリティレイヤーを一つ追加できる:

[Resolve]
# プライマリ: Cloudflare DoT
DNS=1.1.1.1#cloudflare-dns.com 1.0.0.1#cloudflare-dns.com

# フォールバック: Quad9 DoT — マルウェアドメインを追加でブロック
FallbackDNS=9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net

DNSOverTLS=yes
DNSSEC=yes
Cache=yes
DNSStubListener=yes
ReadEtcHosts=yes

Per-link DNS — インターフェースごとに別々のDNSを使う

複数のインターフェース(VPN、LAN、WiFi)があって、それぞれでDNSルーティングを変えたい場合:

# 各インターフェースに割り当てられているDNSを確認
resolvectl

# eth0インターフェースに個別DNSを割り当て(一時的、再起動後に消える)
sudo resolvectl dns eth0 1.1.1.1

# 検索ドメインを割り当て — ~プレフィックスはルーティングドメインを意味する
# *.company.internalへのクエリはこのインターフェースを経由する
sudo resolvectl domain eth0 ~company.internal

ドメインの前の~はルーティングドメインを示す:company.internalドメインへのクエリはこのインターフェースを経由し、他のドメインは引き続きグローバルDNSを使う。企業VPNに接続しながらも、インターネットトラフィックは高速なDNSを使いたい場合にとても便利だ。

実践Tips:デバッグとモニタリング

DNSが解決できない場合のチェックリスト

# リアルタイムでログを確認
journalctl -u systemd-resolved -f

# キャッシュが古い疑いがある場合はフラッシュ
sudo resolvectl flush-caches

# キャッシュ統計を確認 — 良好なキャッシュヒット率は40%以上
resolvectl statistics

# デバッグ用に詳細な出力でクエリ
resolvectl query --legend=yes itfromzero.com

DoTが実際にトラフィックを暗号化しているか確認

# アクティブなTCP:853接続を確認
ss -tnp | grep :853

# ポート853を通過するトラフィックを確認するためにキャプチャ
# (暗号化されているのでコンテンツは見えない — それが良い証拠)
sudo tcpdump -i any port 853 -n -c 20

/etc/resolv.confとの競合を解消する

これが最もよく遭遇する問題だ。NetworkManagerやDHCPクライアントが/etc/resolv.confを上書きして、DNSがresolvedを完全にバイパスすることがある。

# resolv.confがシンボリックリンクか実ファイルかを確認
ls -la /etc/resolv.conf

# 実ファイルの場合 — バックアップしてから正しいシンボリックリンクを作成:
sudo mv /etc/resolv.conf /etc/resolv.conf.bak
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf

# NetworkManagerを使用している場合 — 上書きしないように設定:
sudo mkdir -p /etc/NetworkManager/conf.d
sudo tee /etc/NetworkManager/conf.d/dns.conf <<EOF
[main]
dns=systemd-resolved
EOF
sudo systemctl restart NetworkManager

設定をすべて適用した後は、resolvectl statisticsで定期的にモニタリングしている。開発マシンではキャッシュヒット率が通常60%を超える——DNSクエリの半数以上がインターネットに出ず、ローカルで即座に解決される。本番サーバーではドメインの多様性が低いため、この数値はさらに高くなる。

Share: