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クエリの半数以上がインターネットに出ず、ローカルで即座に解決される。本番サーバーではドメインの多様性が低いため、この数値はさらに高くなる。

