Unboundでお手軽DNSシンクホール
背景 見出しにジャンプ
以前からルータのDNSフォワーダにDNSシンクホールをさせていた。
しかし近年、ChromiumやAppleデバイスでブロック漏れが発生する理由に、対応していないクエリタイプ(SVCB/HTTPS)があることや、FQDN単位でのルールしか書けないためにサブドメイン対応が冗長だったこと、CNAME Cloakingを阻止できないことからDNSソフトウェアの利用は視野に入れていた。
YAMAHA RTXと異なり、NEC IXルータではクエリタイプ毎にフォワード先を切り替えられないため、検討が加速した。BINDやknot、PowerDNSがある中、初学者にも書きやすそうな縛りのない構文が魅力的と感じてUnboundを選んだ。
結果的にはソースコードの変更を要したが、満足に機能している。
更新履歴 見出しにジャンプ
日時 | 修正・追加内容 |
---|---|
2024-08-15 |
|
2024-08-26 |
|
2024-09-19 |
|
2024-09-30 |
|
ソースコードの変更 見出しにジャンプ
git clone https://github.com/NLnetLabs/unbound.git -b release-1.21.0rc1 -o upstream
- CNAME Scrubbing動作を止める
- if(!scrub_message(pkt, prs, &iq->qinfo_out, iq->dp->name,
- qstate->env->scratch, qstate->env, qstate, ie)) {
- /* if 0x20 enabled, start fallback, but we have no message */
- if(event == module_event_capsfail && !iq->caps_fallback) {
- iq->caps_fallback = 1;
- iq->caps_server = 0;
- iq->caps_reply = NULL;
- iq->caps_response = NULL;
- iq->caps_minimisation_state = DONOT_MINIMISE_STATE;
- iq->state = QUERYTARGETS_STATE;
- iq->num_current_queries--;
- verbose(VERB_DETAIL, "Capsforid: scrub failed, starting fallback with no response");
- }
- iq->scrub_failures++;
- goto handle_it;
- }
cd unbound
git remote add PRblockaaaa https://github.com/strlcat/unbound.git
git cherry-pick 026af6b
ビルド 見出しにジャンプ
依存関係を解消
Installation — Unbound 1.19.3 documentation
DNSアプリの機能比較 on Rocky8編 - 20230623-kosaka.pdf
sudo dnf install git tar gcc make openssl openssl-devel expat-devel systemd-devel flex bison
sudo dnf install libevent-devel # outgoing-port-permitが1024を超えているので必要
sudo dnf install clang # gccの代わりにclangでコンパイルしたいなら
systemdあり, 1024ポート制限超え--with-libevent
有効でビルド
【学習メモ】unboundをインストールして最適化と言われる内容を適用する #unbound - Qiita
build with --enable-fully-static
cannot specify username
· Issue #886 · NLnetLabs/unbound
[Unbound-users] unbound performance tuning
Performance Tuning — Unbound 1.19.3 documentation
cd unbound
./configure --help
# --enable-subnet: response-ip-dataを壊す?
# --disable-explicit-port-randomisation: 遅い?
./configure --enable-systemd --enable-static --enable-fully-static --with-libevent #CFLAGS="-O3" CC=clang CXX=clang++
make -j4
インストール 見出しにジャンプ
sudo make install
cd contrib
sudo cp unbound.service unbound.socket /usr/lib/systemd/system/
CentOS8でunboundをソースからコンパイルして使う #dns - Qiita
サービスの起動 見出しにジャンプ
unbound.confを触らないでunboundを立ち上げても、DNSSECのトラストアンカーを更新するunbound-anchor.service
が実行されていないため、おそらく起動しない。
今回のユースケースでは、DNSSECを無効化で対応した。
sudo systemctl daemon-reload
sudo systemctl enable unbound # OS再起動時に自動起動
sudo systemctl start unbound
sudo systemctl status unbound
OS再起動時にunboundが起動しない 見出しにジャンプ
error: can't bind socket: Cannot assign requested address for...
これで壊れて
- Fix #773: When used with systemd-networkd, unbound does not start · NLnetLabs/unbound@5c041c0 · GitHub
これで直った
- For #773: In contrib/unbound.service.in set unbound to start after · NLnetLabs/unbound@d43760a · GitHub
感謝人人
StartLimitBurst
を超過しないために、RestartSec
を伸ばす回避策もあったようだ。
私的サーバー構築日誌:LAN内DNSサーバー Unbound #dns - Qiita
Systemd Restart=always is not honored - Unix & Linux Stack Exchange
SELinux 見出しにジャンプ
SELinuxが有効な環境で、Unboundには下記のようなエラーが発生すると思う。
journalctl -xeu unbound.service
# unbound.service: Failed to set up special execution directory in /var/lib: Permission denied
# unbound.service: Failed at step STATE_DIRECTORY spawning /usr/local/sbin/unbound: Permission denied
実際、UnboundにおいてもSELinuxを寛容モードに変更するとエラーは無くなった。
getenforce
# Enforcing
# permissiveモード
sudo grubby --update-kernel ALL --args enforcing=0
getenforce
# Permissive
Changing SELinux States and Modes :: Fedora Docs
SELinuxの設定ファイル/etc/selinux/config
や公式ドキュメントでは、無効化selinux=0
と再度有効化するコマンドが平然と紹介されているが、後者は二度とOSが立ち上がらなくなる点に留意したい。
「SELinuxのせいで動かない」撲滅ガイド #CentOS - Qiita
ソフトウェアは本来のLinuxの動作に基づいてエラーを処理するため、誤った動作をするSELinux環境は、エラーメッセージがアテにならない。監査ログ/var/log/audit/audit.log
と併せて読む必要がある。
Fedora ServerにプリインストールされているCockpitは、unboundをSELinuxから除外するソリューションを提案した。これによりUnboundにSELinuxが強制されなくなるため、SELinuxを強制することができた。
sudo ausearch -c '(unbound)' --raw | audit2allow -M my-unbound
sudo semodule -X 300 -i my-unbound.pp
# Enforcingに戻す
sudo grubby --update-kernel ALL --remove-args enforcing
アンインストール 見出しにジャンプ
sudo systemctl stop unbound
sudo make uninstall
sudo rm -r /usr/local/etc/unbound
firewalld 見出しにジャンプ
端末がUnboundへクエリする、内向きのトラフィックを許可する。外向きは通常許可されている。
# Do53 53/tcp 53/udp
sudo firewall-cmd --add-service=dns --permanent
# DoT 853/tcp
sudo firewall-cmd --add-service=dns-over-tls --permanent
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
上記はWAN境界で外部からのアクセスを破棄できている前提のFW設定だ。それが難しいなら、access-controlは気休めにしかならないため、少なくともLinuxのFWで落としたい。
unbound.conf 見出しにジャンプ
NLnet Labs Documentation - Unbound - unbound.conf.5
パッケージマネージャから導入すると/etc/unbound/unbound.conf
、自ビルドしたunboundは/usr/local/etc/unbound/unbound.conf
をデフォルトで使う。
この記事では後者に寄せて書いている。
FreeBSD に於ける/etc と /usr/local/etc
configの更新はunbound-control reload
で行えるが、一部systemctl restart unbound
を要する。
unbound-checkconf
で設定に誤りがないか事前に確認できる。
unboundconf/update.sh at main · nyanshiba/unboundconf
DNSSECの無効化 見出しにジャンプ
キャッシュDNSソフトウェアとして、unbound.confのデフォルトでDNSSEC検証が有効になっている。
しかしDNSフォワーダとしてUnboundを利用するユースケースでは、例えばISPのキャッシュDNSがDNSSEC検証に対応していないし、local-zoneやRPZで応答を改ざんすると機能しないので、無効化した。
server:
# DNSSEC OKじゃない
- # disable-edns-do: no
+ disable-edns-do: yes
# validatorモジュールの無効化
- module-config: "ipsecmod validator iterator"
+ module-config: "iterator"
# トラストアンカーの削除
- auto-trust-anchor-file: "/usr/local/etc/unbound/root.key"
- auto-trust-anchor-file: "/var/lib/unbound/root.key"
- trusted-keys-file: /etc/unbound/keys.d/*.key
Unbound - DNSSECを無効にする方法 | 日本Unboundユーザー会 2. 3.
NLnet Labs Documentation - Unbound - Howto Turn Off DNSSEC
Listenインターフェースの設定 見出しにジャンプ
https://unbound.docs.nlnetlabs.nl/en/latest/manpages/unbound.conf.html#unbound-conf-interface
server:
interface: 10.0.3.5@53
port: 53
# do-udp: yes
# do-tcp: yes
incoming-num-tcp: 1024 # libeventなしの場合、*-num-tcpの合計<=125
- ここで指定したインターフェースのIPアドレスをDHCP、IPv6アドレスをDHCPv6やRA RDNSSオプションで通知することで、端末はUnboundで名前解決するようになる。フレッツ東のHGWは、DHCP機能で任意のDNSサーバを通知できないらしい。
暗号化DNSはDDRやDNRで自動設定する。 - IPアドレスのほか、
eth0
などのインターフェース名にも対応している。RHEL系では0.0.0.0
::0
は使えない。 - ブラウザエンジンのシェアの殆どを占めるChromiumは、UDPフラグメントに関係なく、ソースポートやtransaction IDのエントロピーが低いとTCPにフォールバックする。
Huge amount of "ERR_NAME_NOT_RESOLVED" error [40891645] - Chromium
標準的なブラウザ(Firefox)にこのような実装はないが、接続性のためネットワーク側での対応は必要だ。
Unboundはincoming-num-tcpのデフォルト値が非常に小さいので注意
summerday2023-chrome - 20230623-yamaguchi.pdf
日本DNSオペレーターズグループ | DNS Summer Day 2023
https://github.com/NLnetLabs/unbound/issues/366#issuecomment-844165241
そもそもプライベートネットワークでListenするDNSサーバ宛にTCPを強制する意味はほぼないし、NAT動作によっては内側ポートをトリガーにするだけでは不十分だ
EDNS buffer size 見出しにジャンプ
DNSのペイロードサイズは512 bytesまでだったが、EDNS拡張(頭痛が痛い?)により4096 bytesまで許可された。IPv4は理論値1472 bytes、IPv6は1452 bytesを超えたらフラグメントするしかないが、TCPフォールバックのオーバーヘッドを嫌ってかRFC 6891はMTUを考慮しない最大値を推している。
しかし実際のネットワークでは、UDPのフラグメントは再構成のオーバーヘッドはおろか、接続性やセキュリティやの問題があると分かっている。
IPv6 Fragmentation Failure Rate: 56%
3-slides-119-iepg-sessa-is-the-dns-ready-for-ipv6-geoff-huston-00.pdf
server:
# edns-buffer-size: 1232
UnboundのデフォルトはDNS flag dayで提案された1232 bytesで、IPv6の最小MTU 1280 bytesからUDPヘッダ 48 bytesを除いたデータグラムのサイズを示している。DNSSECを使っていない状態では、1232 bytesを超える応答は殆どなく、パフォーマンスへの影響はない。
EDNSを考慮していない機器との互換性をとって512 bytesとするのもよいだろう。1220 bytes以下のBufsizeでは、DNSSECを無効化が必須だ。
draft-ietf-dnsop-avoid-fragmentation-16
dig の適切な使い方 (10) モダンなサーバ (EDNS 対応) のポイント
IPv6でNGNのキャッシュDNSにフォワードするだけなら、大きなBufsizeを設定してもフラグメントは起こらないはずだ。
DNS 2XL | blabs
dns-troubleshoot.pptx - dns-troubleshoot-1.pdf
QUICもUDPのフラグメントを回避するために、小さいデータグラムを使用している。この方面からPMTUDが発展すると期待したい。
QUIC Analysis - A UDP-Based Multiplexed and Secure Transport
id.server 見出しにジャンプ
クエリクラスがINではなくCHなことに留意する。
dig id.server. ch txt +noall +answer
dig hostname.bind. ch txt +noall +answer
# id.server. 0 CH TXT "fedora"
dig version.server. ch txt +noall +answer
dig version.bind. ch txt +noall +answer
# version.server. 0 CH TXT "unbound 1.20.0"
dig trustanchor.unbound. ch txt +noall +answer
ISPのキャッシュDNSではREFUSEDされたので、模倣した。
server:
hide-identity: yes
hide-version: yes
hide-trustanchor: yes
forward-addr 見出しにジャンプ
unboundは大前提としてフルリゾルバのためのソフトウェアだが、local-zoneやRPZを利用するだけのユースケースにも対応している。
forward-addrを設定すると、CPEが搭載しているようなDNSフォワーダ/プロキシのように、特定のキャッシュDNSにFQDNを問い合わせる。
IPv4でDNSを使える環境は限られる 見出しにジャンプ
IPv4/IPv6接続判定ツールでv6プラス判定が出たり、MAP-E展開にこなれていないISPをお使いなら、8.8.8.8
や1.1.1.1
を使うべきでない。NAT Behaviorを制御できるなら止めないが、多くはそうでないだろう。
フレッツのIPv6 IPoEおよびIPv4 over IPv6では、キャッシュDNSはIPv6のみ提供され、IPv4は提供されない。そもそもIPv4/IPv6デュアルスタックの中核となるDNSをIPv4延命技術に依存していたら、それはIPv6への移行でもなんでもなく障害点が増えただけのIPv4&IPv6キメラスタックだ。該当する環境は、IPv6で問い合わせよう。
些細なことを言えば、Do53のクエリ毎にNAPTキャッシュ生成&ショートパケットをencapslationしながらブラウジングしたいかという話でもある。IPv4 PPPoEにパケットを流すのに比べて、IPv4 over IPv6では気を遣ってしまう。
Public DNSを使うべきか 見出しにジャンプ
フォワード先の候補には、ISPが提供するキャッシュDNSや、GoogleやCloudflareなどが提供するPublic DNSが挙げられる。
パフォーマンスの観点でいえば、ISPのDNSが適切に運用されていないとき、後者はときによい。
「速い」という言い方はミスリードだけど、ISPやルータのリゾルバDNSが諸般の事情で腐ってタイムアウト連発とか時折あるので、public dns 全否定も違うかなぁと思ったり。
— Shirouzu Hiroaki(白水啓章) (@shirouzu) July 29, 2024
(ISPですらDNSにDDOS食らって一時的に google DNS誘導することあったり) https://t.co/9gYwdHvAiL
ISPのDNSキャッシュサーバはTTLを越えてキャッシュを保持するか?
ただしプライバシー面でいえば、日本では通信の秘密が保障されているし、セキュリティの観点では攻撃面は同一ASからthe Internetに広がる点には留意したい。特にDNS over 53(Do53)(Cleartext)でクエリすれば、AS外のインターネット区間で中間者攻撃される恐れがあるため、Public DNS利用時はDNS over TLS(DoT)やDNS over HTTPS(DoH)が望ましい。
8.8.8.8に対するBGPハイジャックの話:Geekなぺーじ
DoT、DoHはUDPを使わずDNS ampの踏み台に利用される恐れがないため、オープンリゾルバとしてIIJ以外のユーザにも開放
4. フォーカス・リサーチ(3)IIJとDNSの30年 | Internet Infrastructure Review(IIR)Vol.59 | IIJの技術 | インターネットイニシアティブ(IIJ)
ISPのDNSで機能するDo53に比べ、Public DNSで利用できるDoTやDoHは常にオーバーヘッドが課されるが、TCPやHTTP/2のフロー制御が効いてくるシーンもある。
残念ながらUnboundで利用する場合、端末はDo53でクエリを繰り出すので、ブラウザでDoHを利用するときのようなパフォーマンス上の恩恵はない。
getaddrinfoはブロッキングコールだからスレッドプール経由でマルチスレッドからの一定並列度での呼び出しになるけど、DoHならHTTP/2で確立済の単一接続にドバッと多数のクエリを丸ごと流せるみたいな https://t.co/UviFhHTpev
— Kazuho Oku (@kazuho) July 30, 2024
[1907.08089] Comparing the Effects of DNS, DoT, and DoH on Web Performance
server:
edns-tcp-keepalive: yes
forward-zone:
name: '.' # 全てのゾーンをフォワードする
forward-addr: 2606:4700:4700::1111@853 # Cloudflare DNS via DoT
forward-addr: 2001:4860:4860::8888@443 # Google Public DNS via DoH
forward-tls-upstream: yes
UnboundにもTCPコネクションの再利用tcp-reuse-timeout: 60000
は一応あるが、DoTは(num-threads: 1
でも)コネクションを度々切断していた。DoHは都度コネクションを張っていたのでオーバーヘッドになりそうだ。
Unbound 1.13.1 .. 1.16.1 memory leak (complete RAM exhaustion) due to high volume of TCP FD's · Issue #724 · NLnetLabs/unbound
[bug] DoT - TCP reset on egress · Issue #137 · NLnetLabs/unbound DoTサーバが切断してくる場合もあるとか
ISP内サーバの利用 見出しにジャンプ
フレッツ東のIPv6 IPoEを利用するISPは、CPEや端末に対してDHCPv6のオプションでキャッシュDNSサーバのIPv6アドレスを通知する。このとき、多くのISPがNTT東日本に割り当てられた共通のアドレス2404:1a8:7f01:a::3
2404:1a8:7f01:b::3
を利用する。
NTT ComのIPv6 PPPoEでは、同ASのアドレスがIPCPv6で通知される。4ヘクステット目が4は東, 104は西になっており、BGP.Toolsによると送信元が東京, 大阪になっている。東京/大阪それぞれに9つのIPv6アドレス、IPv4は星の数ほどあった。
OCN モバイル ONE - Wi-Fiルーター:マルウェア不正通信ブロックサービスの適用を解除したい | 個人向けOCNお客さまサポート
twitterのDNS Attackが、OCNのDNSキャッシュサーバーに残っていたので記録
驚くべきことに、東京のサーバにフォワードしたときも、送信元にかなりの率で大阪が含まれていた。(フレッツ東のNTT Comにおいて)送信元が一貫してフォッサマグナを跨がなかったのは下記だけだった。DHCPv6やIPCPv6で自動設定される内容と若干異なっていた。
forward-zone:
name: "."
forward-addr: 2001:380:0:4::1 # nv6-ef701.ocn.ad.jp.
forward-addr: 2001:380:0:4::2 # nv6-ef702.ocn.ad.jp.
forward-addr: 2404:1a8:7f01:a::3
フレッツ東共通のa/bも同ASからクエリされており、bは送信元に大阪が含まれていた。IPv6 Hop Limitも1小さかった。各ISP内のキャッシュDNSを指すAnycastになっているのだろう。
このISPは東西ひとつづつを渡しているのかな。東西冗長してあるのは良いことだけど、平時は近くのものを使いたいような... https://t.co/qIf3wzi0fU
— Tomoyuki Sahara (@tomosann) July 30, 2024
Biglobeさんは全国に同じアドレスを配っているようだ。pingは5ms未満なので二つとも関東近郊にあるように見える。TTLが違うから、たぶんDCも分けてある。これで関東以外にもあってanycastしているのなら最善ではなかろうか。すばらしい。
— Tomoyuki Sahara (@tomosann) July 30, 2024
NGN網内のサービス情報サイト(東)利用には、DHCPv6で通知されたサーチリストにあるflets-east.jp.
ゾーンを、少なくともNGNのキャッシュDNSにフォワードする必要がある。
forward-zone:
name: "flets-east.jp."
forward-addr: 2404:1a8:7f01:a::3
forward-addr: 2404:1a8:7f01:b::3
forward-zone:
name: "iptvf.jp."
forward-addr: 2404:1a8:7f01:a::3
forward-addr: 2404:1a8:7f01:b::3
- ひかりTV(フレッツ・テレビではない)の利用には、同サーチリストにある
iptvf.jp.
も必要そうだ。
ひかりTV導入記 〜次世代のTVインフラを試してみる〜 神保道夫@NISOC - VNEによるが、標準プロビジョニング方式の場合、プロビジョニングサーバ
4over6.info.
の名前解決は網内DNSに向ける必要がある。
v6mig-prov/spec.md at master · v6pc/v6mig-prov - IPv4のサービス情報サイトに接続するなら、IPCP情報を見て
v4flets-east.jp.
を向ければよい。
送信元アドレスやEDNS Client Subnetによって応答を変えるタイプのGSLBでは、キャッシュDNSによって接続先がレイテンシの小さいISP内キャッシュからピアリング、トランジットへと変わり、体験が悪化する場合がある。
国内トラフィックエンジニアリングの現状 | PPT
例えば、一部のISPで http://test.edge.apple/debug/ にアクセスすると、Apple Edge Cache*.ec.edge.apple
が使われれているのではないかと思う。
RIPE Atlas - Measurement Detail
同じドメイン名test.edge.apple.
の応答がDNSによって変わっているので、ISPのDNSから別のものに変更することで体験が悪化する例といえる。ISPの力関係によって、同じサービスでも接続状況が異なるため、常に 「○○DNSがオススメ」はすべて詐欺だ。
顧客は無知を貫けばよいが、ISP視点で見ればトランジットのコスト増を招くため思わしくない。こちらの記事はそんなISP側の視点で書かれたものではないかと思う。
DNSを変更するとネットワークは速くなるか | IIJ Engineers Blog
Anyone having significant issues with some of Apple's services? ISP related? | MyBroadband Forum ECSも効くらしいので、Public DNSで利用できる可能性は高いかも?
IIRC you're replacing the whole OS inage on every update now. Also, their edge c... | Hacker News 逆にApple Edge Cacheが遅いからDNSをISPのものから変えている、という話もありそう。
このようなISP内キャッシュの利用に全てををフォワードname: '.'
する必要はないが、サービスを逐一除外するほどのメリットがPublic DNSになければ、ISPのDNSを利用すればよい。
高速なDNSの自動選択 見出しにジャンプ
fast-server-permil: 0
のとき、クエリ毎に複数のforward-server:
が等コストでラウンドロビンされる。
Unboundにクエリを集約することでDNS RRLに引っかかりやすくなるのではないか、といった懸念を軽減できるかもしれない。
900/1000クエリに、3つのRTTが低いサーバを選択する例
https://unbound.docs.nlnetlabs.nl/en/latest/manpages/unbound.conf.html#unbound-conf-fast-server-permil
server:
fast-server-permil: 900
fast-server-num: 3
# infra-host-ttl: 900
infra-cache-min-rtt: 100
infra-cache-max-rtt: 5000
infra-keep-probing: yes
- クエリが失敗したとき、タイムアウトの度にinfra-cache-min-rttからinfra-cache-max-rttの間で増加する再送タイマの後に再試行される。
forward-addr先の再帰を待機するにはいささか過敏すぎるのでこれを100msecに伸ばし、NAPTやSPIのタイマー値を超える応答は待っても意味がないので、5000msecに制限した。
https://unbound.docs.nlnetlabs.nl/en/latest/manpages/unbound.conf.html#unbound-conf-infra-cache-min-rtt - infra-cache-max-rttに到達または連続してタイムアウトしたforward-addrは、少なくともinfra-host-ttlの間使われない。短くするか、
infra-keep-probing: yes
で早められる。 - infra-cacheの内容は
dump_infra
で確認できる。恐らくpingがRTT(Round Trip Time)を指し、rtt(roundtrip-timeout)(RTTではない)とrto(roundtrip backoff?)が再送タイマを指している。
下記では、低速なDNSサーバは往々にして再送タイマが増加していることが分かる。この後、最も遅い2001:380:0:4::2
はinfra-cacheから削除された。
unbound-control dump_infra
# 2001:380:0:4::2 . ttl 176 ping 79 var 96 rtt 463 rto 463 tA 0 tAAAA 0 tother 0 ednsknown 1 edns 0 delay 0 lame dnssec 0 rec 0 A 0 other 0
# 2404:1a8:7f01:a::3 . ttl 176 ping 16 var 4 rtt 100 rto 100 tA 0 tAAAA 0 tother 0 ednsknown 1 edns 0 delay 0 lame dnssec 0 rec 0 A 0 other 0
# 2001:380:0:4::1 . ttl 176 ping 44 var 49 rtt 240 rto 240 tA 0 tAAAA 1 tother 0 ednsknown 1 edns 0 delay 0 lame dnssec 0 rec 0 A 0 other 0
# 2001:380:0:4::3 . ttl 272 ping 20 var 8 rtt 100 rto 100 tA 0 tAAAA 0 tother 0 ednsknown 1 edns 0 delay 0 lame dnssec 0 rec 0 A 0 other 0
- infra-keep-probingは有効化。全てのforward-addrのrtoがinfra-cache-max-rttに達したときにSERVFAILになるため。
Documenation clarification on RTT band · Issue #567 · NLnetLabs/unbound
NLnet Labs Documentation - Unbound - Unbound Timeout Information
CNAME Scrubbing 見出しにジャンプ
dig logincdn.msauth.net aaaa +noall +answer logincdn.msauth.net. 139 IN CNAME lgincdnmsftuswe2.azureedge.net. lgincdnmsftuswe2.azureedge.net. 373 IN CNAME lgincdnmsftuswe2.afd.azureedge.net. lgincdnmsftuswe2.afd.azureedge.net. 71 IN CNAME firstparty-azurefd-prod.trafficmanager.net. firstparty-azurefd-prod.trafficmanager.net. 30 IN CNAME shed.dual-low.s-part-0018.t-0009.t-msedge.net. shed.dual-low.s-part-0018.t-0009.t-msedge.net. 30 IN CNAME s-part-0018.t-0009.t-msedge.net. s-part-0018.t-0009.t-msedge.net. 30 IN AAAA 2620:1ec:bdf::46
logincdn.msauth.net
はCNAMEチェーンが長いことで有名なFQDNの一つだ。
Resolving records through more than 8 CNAME fails due to hardcoded MAX_RESTART_COUNT · Issue #438 · NLnetLabs/unbound
フルリゾルバは、仮にlogincdn.msauth.net. IN CNAME
の応答にIN AAAA 2620:1ec:bdf::46
が付随しても信頼しない。信頼できるt-msedge.net.
の応答を聞くまでCNAMEチェーンを一つずつ再クエリする。このように信頼のないCNAMEチェーンを再検索することを、Scrubbing(擦る)という。
Strange DNS Samples 4. CNAME に付随する A を信じてはいけない
Clients should assume that other recordsmay have come from the server's cache. Where authoritative answersare required, the client should query again, using the canonical nameassociated with the alias.
RFC 2181: Clarifications to the DNS Specification
Unboundのforward-addr:
はフルリゾルバと共通のコードベースなので、同じくCNAMEの解決に数往復掛かってしまう。パフォーマンスへの影響は大きい。
Consider disabling CNAME scrubbing for forwarded queries · Issue #132 · NLnetLabs/unbound
こちらのIssueでは、Do53では依然として利点があるとして話が進んでいるが、通信の秘密のない国や、Public DNSに限った話に聞こえる。殆どのスタブリゾルバはキャッシュDNSを信頼しているので、Scrubを止めても追加のリスクはない。
完全には止められていないが、ソースコードの変更により2往復で済むようになった。上記の例だとs-part-0018.t-0009.t-msedge.net.
だけがScrubされるが、RPZを評価できるし、Appleのスタブリゾルバも同じ動作なので先読みされる形になる。
access-control-tag 見出しにジャンプ
dig @dns.home.arpa example.com +noall +comments
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 30804
;; flags: qr rd ad; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; EDE: 18 (Prohibited)
access-control:
で許可されていない送信元は、アプリケーション層で拒否される。L7は保護にならないのでACLはネットワーク側で行うべきだ。
server:
access-control: 10.0.0.0/21 allow
local-zone-tag:
やrpz-tag:
で、一部の端末にDNSシンクホールを適用したいときに使うタグを、予め定義する。
server:
define-tag: "admin dualstack guest windows"
# access-control: 10.0.0.0/21 allow access-control-tag:にあればallowされる
access-control-tag: 10.0.0.0/21 "dualstack"
access-control-tag: 10.0.0.0/24 "dualstack admin"
access-control-tag: 10.0.0.2/32 "dualstack admin windows"
access-control-tag: 10.0.0.12/32 "admin"
access-control-tag: 10.0.5.57/32 "dualstack windows"
access-control-tag: 10.0.7.0/24 "dualstack guest"
この例では、小さいIP範囲10.0.0.2/32
が優先されるが、大きいIP範囲10.0.0.0/21
のタグを継承しないので、dualstack
から冗長に書く必要がある。
逆にデュアルスタックでない端末10.0.0.12/32
には書かなければ適用されない。
セキュリティ 見出しにジャンプ
価値の高い広告を提供するアドネットワークは、時にMalvertisingを仲介する悪意あるコンテンツになり下がることがある。紹介料で稼ぐメディアやその利用者、最終的には適切な広告出稿を行う広告主に不利益をもたらす。
このような管理下にないドメインへの対処に一般的なのはuBlock Originや拡張機能版AdGuardだ。復号後のパス名までルールに書けるのは強みである一方、拡張機能を全面的に信頼する必要があることや、複数の端末に自動的に展開できない欠点がある。
米CISAによると、Malvertising防止には拡張機能ではなく自組織のDNSでの提供が推奨されている。
Capacity Enhancement Guide: Securing Web Browsers and Defending Against Malvertising for Federal Agencies - Capacity_Enhancement_Guide-Securing_Web_Browsers_and_Defending_Against_Malvertising_for_Federal_Agencies.pdf
DNS型のセキュリティは、DNSの応答を改ざんし、ホストを取得できなくすることで、ネットワークレベルで防御する。
商用ではCisco UmbrellaやFortiGateが挙げられる。個人がセルフホストするものではpi-holeやAdGuard Homeがあるが、以前にも指摘したように、Appleデバイスでブロック漏れが発生していないか、パケットレベルで確認すべきだ。
local-zone 見出しにジャンプ
local-data
を用いると、権威のないゾーンのRRsetを編集できる。
応答を改ざんするので、当然DNSSECの信頼は失われる。DNSSECを無効にしないと動かない。
https://unbound.docs.nlnetlabs.nl/en/latest/manpages/unbound.conf.html#unbound-conf-local-zone
Ad DNSブロッキングおよび、kawango DNSブロッキングの実装をしてみたら、超快適だった とも ちゃ日記(Tomo cha) - 元大学生のOL日記-
local-zone type | ログレベル | local-data | local-dataと異なるタイプ | 子ゾーン | response-ip | 備考 |
---|---|---|---|---|---|---|
deny | 応答 | (DROP) | 端末はタイムアウトを待つので、何かしら応答すべき | |||
inform_deny | INFO | |||||
refuse | 応答 | REFUSED | 非対応 | response-ipでも使えるが、悟られたくない感じはある | ||
always_refuse | 常にREFUSED | REFUSED | ||||
static | 応答 | NODATA, local-data IN SOA |
NXDOMAIN, local-data IN SOA |
非対応 | ゾーン頂点にSOAレコードを置くと、全てのクエリタイプで否定応答が任意のTTL端末にキャッシュされる。 | |
transparent | 応答 | NODATA | 名前解決 | 非対応 | local-dataを書けばtransparentゾーンが作成されるため、AS112ゾーンを除きlocal-zoneは書かなくてよい。 | |
inform | INFO | 名前解決 | ||||
always_transparent | 常に名前解決 | 名前解決 | ||||
typetransparent | 応答 | 非対応 |
ゾーンでなくクエリタイプ単位で評価する性質から、AAAAはlocal-data、Aは名前解決といった混在が可能。 |
|||
redirect | 応答 (CNAMEを解決) |
NODATA | local-data | response-ip-data | ほぼワイルドカードドメイン。唯一、local-dataに書かれたCNAMEを解決する。非常に遅いので、TTL付きの否定応答だけ欲しいならstaticを使う。 | |
inform_redirect | INFO | 非対応 | ||||
nodefault | 応答 | 名前解決 | 非対応 |
逆引きクエリを抑制するためにハードコーディングされたAS112ゾーンのデフォルト応答を無効化するだけ。local-dataを応答するために使うならstaticが確実。 |
||
block_a | IN AはNODATA 他は名前解決 |
非対応 |
Happy Eyeballsさせず、IPv6接続のみ試行して欲しいときに使う。 |
|||
block_aaaa | IN AAAAはNODATA 他は名前解決 |
非対応 |
正式な機能ではないので、ソースコードの変更が必要。 |
|||
always_nodata | NODATA | 非対応 | プライベートリレーへのアクセスをブロックするときの、推奨された応答形式。
iCloudプライベートリレーに向けたネットワークやWebサーバの準備 - iCloud - Apple Developer |
|||
always_nxdomain | NXDOMAIN | |||||
always_null | 応答 | IN A 0.0.0.0 IN AAAA :: IN HTTPS等はNODATA |
非対応 | 否定応答ではないが、A/AAAAに限りブロック応答が端末で3600秒キャッシュされる。
このため、レガシーなスタブリゾルバでは100%、ChromiumやAppleデバイスでは2/3のクエリを一行でキャッシュさせられる。 |
||
noview | 応答 | 非対応 | view機能から除外するゾーン |
安全なTLDだけ通す 見出しにジャンプ
maliciousなドメインは日々変化する。TLDによってはAbuse対応が悪かったり、安価に取得できるためだ。
Networks, Countries, Registrars & TLD charts | Reputation Statistics | Spamhaus
Unboundでは.
com.
example.com.
などのゾーン単位で操作できるので、いっそのことTLDごとブロックしてしまえばよい。
荒い網なので期待はできないが、例えば一体何のためかUser-Agentによるリダイレクト機能を持つ短縮URLサービスなどは一行で落とせる。
このようにルートゾーン.
以下をリダイレクトすると、transparentしたTLD以外に否定応答を返せる。
syslogを取りたいのでinform_redirect
にしているだけで、SOAを返すだけならstatic
がよい。
server:
local-zone: "com." transparent
local-zone: "." inform_redirect
local-data: ". 60 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2024080800 1800 900 604800 86400"
逆に、TLD毎にブラックリスト式で記述できる。AS112ゾーン以外は名前解決される。
server:
local-zone: "com." static
local-data: "com. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1723351845 1800 900 604800 86400"
SOAレコードを置くと、SOAのTTLとMINIMUMフィールドまで(つまり短い方)ネガティブキャッシュが効くと期待できる。
否定応答がわからなくなる話 (11) RFC 2308: 5. 否定回答のキャッシング
これにより端末は高頻度でクエリしなくなるため、バッテリー寿命に優しいDNSができる。
また、SOAの内容は実際のものを参考にした。できる限り自然なRCODE・RRsetに徹して、往生際の悪いソフトウェアにUnboundがブロックしているのではなく正常な応答と見せかけたいからだ。
dig . +noall +authority | sed -E 's/\t+/ /g'
# . 86400 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2024080800 1800 900 604800 86400
dig com. +noall +authority | sed -E 's/\t+/ /g'
# com. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1723351845 1800 900 604800 86400
com
でもunbound-checkconfに怒られないが、末尾にルートゾーンを示す.
を付けることを推奨する。
IANAのRoot Zone Databaseなどを利用して、gTLDとccTLDを取得しlocal-zoneのベースをつくるとよいだろう。
NTP 見出しにジャンプ
Linux環境は大抵時刻合わせにpool.ntp.org.
を利用しているが、某通信機器メーカーをdisる傍ら、福岡大のNTPに問い合わせていたら目も当てられない。
中には疑わしいNTPサーバが存在するのも事実で、攻撃先を収集するために利用される可能性があるのではないかと個人的に思っている。
DHCPv6のオプションで降ってきた、フレッツ東のNGN網内NTPサーバに向けた。
ブロックルールを書くことだけが、DNSシンクホールの役割ではないのだ。
server:
local-zone: "www.pool.ntp.org." transparent
local-zone: "pool.ntp.org." redirect
local-data: "pool.ntp.org. 10800 IN AAAA 2404:1a8:1102::a"
local-data: "pool.ntp.org. 10800 IN AAAA 2404:1a8:1102::b"
local-data: "pool.ntp.org. 10800 IN AAAA 2404:1a8:1102::c"
ついでにWindows, Apple, Androidも対策。
server:
local-data: "ntp.nict.jp. 10800 IN AAAA 2404:1a8:1102::c"
local-data: "ntp-b2.nict.go.jp. 10800 IN AAAA 2404:1a8:1102::c"
local-data: "time.windows.com. 10800 IN AAAA 2404:1a8:1102::c"
local-data: "time-ios.apple.com. 10800 IN A 10.0.0.254"
local-data: "time-ios.apple.com. 10800 IN AAAA 2404:1a8:1102::c"
local-data: "time-macos.apple.com. 10800 IN A 10.0.0.254"
local-data: "time-macos.apple.com. 10800 IN AAAA 2404:1a8:1102::c"
local-data: "time.apple.com. 10800 IN A 10.0.0.254"
local-data: "time.apple.com. 10800 IN AAAA 2404:1a8:1102::c"
local-data: "time1.apple.com. 10800 IN AAAA 2404:1a8:1102::a"
local-data: "time2.apple.com. 10800 IN AAAA 2404:1a8:1102::b"
local-data: "time3.apple.com. 10800 IN AAAA 2404:1a8:1102::c"
local-data: "time4.apple.com. 10800 IN AAAA 2404:1a8:1102::a"
local-data: "time5.apple.com. 10800 IN AAAA 2404:1a8:1102::b"
local-data: "time6.apple.com. 10800 IN AAAA 2404:1a8:1102::c"
local-data: "time7.apple.com. 10800 IN AAAA 2404:1a8:1102::a"
local-data: "time.euro.apple.com. 10800 IN AAAA 2404:1a8:1102::c"
local-data: "time.google.com. 10800 IN AAAA 2404:1a8:1102::c"
local-data: "time1.google.com. 10800 IN AAAA 2404:1a8:1102::a"
local-data: "time2.google.com. 10800 IN AAAA 2404:1a8:1102::b"
local-data: "time3.google.com. 10800 IN AAAA 2404:1a8:1102::c"
local-data: "time4.google.com. 10800 IN AAAA 2404:1a8:1102::a"
local-data: "time.facebook.com. 10800 IN AAAA 2404:1a8:1102::c"
local-data: "time1.facebook.com. 10800 IN AAAA 2404:1a8:1102::a"
local-data: "time2.facebook.com. 10800 IN AAAA 2404:1a8:1102::b"
local-data: "time3.facebook.com. 10800 IN AAAA 2404:1a8:1102::c"
local-data: "time4.facebook.com. 10800 IN AAAA 2404:1a8:1102::a"
local-data: "time.cloudflare.com. 10800 IN AAAA 2404:1a8:1102::c"
NTT GINのNTPサーバも見つけたが、異種NTPを混在させても無視されるので使う機会はないだろう。
server:
local-zone: "time.gin.ntt.net." redirect
local-data: "time.gin.ntt.net. 60 IN CNAME time1.gin.ntt.net."
local-zone: "time2.gin.ntt.net." redirect
local-data: "time2.gin.ntt.net. 60 IN CNAME time1.gin.ntt.net."
dns-blocklists 見出しにジャンプ
私はページ読み込みに違和感を感じたときにFirefoxのDevToolsを開いて悪性ドメインを見つけている。とはいえ、今となっては真っ当な商売ヅラしているECサイト、L0(米議会)からL7を推測して語られることの多い陽キャ御用達SNSなどは経験上ルールが追いつかない。
hagezi/dns-blocklistsはDNSソフトウェア向けに現在rpzのみ提供されている。RPZはCNAME Cloaking対策でもなければ無駄な処理なので、dnsmasq用ルールからいくつか選んで、unbound.conf形式に書き替えて使っている。
- local=/aan.amazon.co.jp/
+ local-zone: "aan.amazon.co.jp." always_nodata
...
include:
を書いた行に別ファイルの内容が反映される。応答内容を書き換える機能なので、外部のものを使うときは中身を確認しよう。
server:
# chroot: "/usr/local/etc/unbound"
# username: "unbound"
- # directory: "/usr/local/etc/unbound"
+ directory: "/usr/local/etc/unbound"
...
+ include: *.txt
rpz 見出しにジャンプ
local-zoneと違いHTTPを介して外部からルールセットをインポートできるほか、Scrub時も評価されるためCNAME Cloakingにも対応できる。
Response Policy Zones — Unbound 1.19.3 documentation
Response Policy Zones in Unbound. We are incredibly happy to introduce… | by Ralph Dolmans | The NLnet Labs Blog | Medium
Public DNS 見出しにジャンプ
せっかくセキュリティ目的でunboundを導入しても、Public DNSへ端末がブレークアウトするのを許してしまえば無用の長物だ。
例えば、AndroidはGoogle Public DNSにDoTを試行するし、一部のモバイルアプリではDo53やDoHを利用して、端末のスタブリゾルバを回避する動きがある。
Do53やDoTは5tuple Firewallで853/tcpをブロックすれば事足りるが、名の知れたPublic DNSはIPアドレスで証明書を取得しているため、それだけはL3 FWでブロックせざるを得ない。
openssl s_client -connect 8.8.8.8:443 | openssl x509 -noout -text | grep DNS:
# DNS:dns.google, DNS:dns.google.com, DNS:*.dns.google.com, DNS:8888.google, DNS:dns64.dns.google, IP Address:8.8.8.8, IP Address:8.8.4.4, IP Address:2001:4860:4860:0:0:0:0:8888, IP Address:2001:4860:4860:0:0:0:0:8844, IP Address:2001:4860:4860:0:0:0:0:6464, IP Address:2001:4860:4860:0:0:0:0:64
幸いなことに、殆どのDoHは証明書の検証を行うときにDNS名が必要なため、DNSでブロックできる。
Blocking DNS-over-HTTPS (DoH) - General - Pi-hole Userspace
Block DNS over HTTPS (DoH), using pfsense - Block DOH with pfsense.pdf
Using Unbound response policy zones - Unbound response policy zones.pdf
rpz:
name: rpz.block.doh
url: https://raw.githubusercontent.com/jpgpi250/piholemanual/master/DOH.rpz
rpz-action-override: nodata
rpz-log: yes
rpz-log-name: rpz.block.doh
外部のルールを直接利用するときは、安全のためrpz-action-override:
を使用すべきだ。
RethinkDNSは有用なサービスのため、ホワイトリストにするとよい。
server:
local-data: "rethinkdns.com. 300 IN AAAA 2606:4700:3030::6815:533e"
local-data: "rethinkdns.com. 300 IN AAAA 2606:4700:3030::ac43:d6f6"
use-application-dns.net 見出しにジャンプ
悪意のある通信はL3 FWで防ぐしかないが、善意の通信はUnboundで歓迎する。
DoHを利用しないように指示するCanary domainの仕組み - ASnoKaze blog
server:
local-zone: "use-application-dns.net." inform_redirect
iCloud Private Relay 見出しにジャンプ
iCloud Private Relay経由で名前解決されると、当然Unboundの庇護下に入らない。
server:
local-zone: "mask.icloud.com." static
local-data: "mask.icloud.com. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1723185585 1800 900 604800 86400"
local-zone: "mask-api.icloud.com." static
local-data: "mask-api.icloud.com. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1723185585 1800 900 604800 86400"
local-zone: "mask-h2.icloud.com." static
local-data: "mask-h2.icloud.com. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1723185585 1800 900 604800 86400"
AppleはNODATAstatic
かNXDOMAINalways_nxdomain
でのブロックを想定している。TTLを指定できるstatic
を使用した。
cf. ネットワークのトラフィック監査への対応 iCloudプライベートリレーに向けたネットワークやWebサーバの準備 - iCloud - Apple Developer
ドメイン名が似ているmask-canary.icloud.com
を、NODATAや0.0.0.0でブロックしてしまうと、既知のトラッカーがありそうなサイト(Google検索やニュースサイト)で5秒程度のタイムアウトが発生した。
恐らく調査目的でセキュリティ上の問題はないが、防ぎたければWi-Fiの設定で「IPアドレスのトラッキングを制限」をオフにするしかない。
"Limit IP Address Tracking" setting on iOS circumvents ad-blocking - Help / Community Help - Pi-hole Userspace
CNAME Cloaking 見出しにジャンプ
Unboundを導入する理由の一つでもあったCNAME Cloakingとはどのような技術か。
何のことはない、CDN利用にCNAMEを向けることで「same-siteであるもののネットワークは異なる」のと同義で、これをMalvertisingやプライバシー侵害判定を受けたドメイン名を隠すのに利用しているだけだ。
「Same-site」と「same-origin」 | Articles | web.dev
下記の例では、通常のスタブリゾルバをバックエンドにするDNSシンクホールにはtargetlr.adobe.com.
にしか見えず、これをルールに書くしかない。一方、フルリゾルバ、正確にはCNAME ScrubbingするDNSフォワーダでもよいが、data.adobedc.net.
であることをRPZで判定できる。
dig targetlr.adobe.com +nostats
; <<>> DiG 9.18.26 <<>> targetlr.adobe.com +nostats
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21745
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 2
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;targetlr.adobe.com. IN A
;; ANSWER SECTION:
targetlr.adobe.com. 8154 IN CNAME adobeinternalmobiles.tt.omtrdc.net.
adobeinternalmobiles.tt.omtrdc.net. 60 IN CNAME adobetarget.data.adobedc.net.
;; ADDITIONAL SECTION:
rpz.block.cloaking. 300 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2024081300 1800 900 604800 86400
例外的に、AppleのスタブリゾルバはScrubbing動作し、ITPのサードパーティCookie制限に利用される。誰でもAppプライバシーレポートを見ればその様子を確認できる。
Tracking Prevention in WebKit | WebKit
このITPの強化はさらなるイタチごっこを引き起こした。CNAMEがドメイン名を隠せないとなれば、今度はNSをサービス側がCNAME flatteningを行える権威DNSに委任することで、直接Aレコードを設定するサービスが現れた。これに対しITPではファーストパーティとのIP範囲の一致を見るようになった。CDN利用のリスクを直視すれば自然ともいえるが、美しい回避策ではないと思う。こちらもrpz-ipで対応できる。
Cap cookie lifetimes to 7 days for responses from third party IP addresses by whsieh · Pull Request #5347 · WebKit/WebKit
現在進行形で続くこれらのイタチごっこは、セキュリティの観点から見れば、maliciousなサードパーティリソースをどのように防ぐかという話が、そのファーストパーティ自身がmaliciousなのでブロックするのかというグラデーションを移動しただけに過ぎない。自らが提供するサービスを危険にさらす技術に投資するでなく、脆弱でないか考えることが長期的な利益につながるだろう。
ログインはともかくとしても、ECサイトの広告ライブラリが汚染されてスキミングの被害が出ています:https://t.co/S7cv3dKa24
— Yuki2718 (@Yuki27183) August 2, 2024
rpz:
name: rpz.block.cloaking
zonefile: /usr/local/etc/unbound/rpz.block.cloaking.zone
rpz-log: yes
rpz-log-name: rpz.block.cloaking
$ORIGIN rpz.block.cloaking.
$TTL 300
rpz.block.cloaking. IN SOA a.root-servers.net. nstld.verisign-grs.com. (
2024081300
1800
900
604800
86400
)
; https://github.com/AdguardTeam/cname-trackers/blob/master/data/combined_original_trackers.txt
*.data.adobedc.net CNAME *.
; gtm
32.21.32.239.216.rpz-ip CNAME *.
32.21.34.239.216.rpz-ip CNAME *.
32.21.36.239.216.rpz-ip CNAME *.
32.21.38.239.216.rpz-ip CNAME *.
128.15.zz.32.4802.4860.2001.rpz-ip CNAME *.
128.15.zz.34.4802.4860.2001.rpz-ip CNAME *.
128.15.zz.36.4802.4860.2001.rpz-ip CNAME *.
128.15.zz.38.4802.4860.2001.rpz-ip CNAME *.
ghs.googlehosted.com CNAME *.
directory:
後であれば相対パスでもよいが、unbound-checkconfがエラーになるので絶対パスを書いている- RPZはゾーンファイルを拡張した構文になっている。
Zonefile example — NSD 4.3.9 documentation - CNAME Cloakingが流行りだした当初から継続してメンテナンスされているAdGuardのルールを採用した。
cname-trackers/data/combined_original_trackers.txt at master · AdguardTeam/cname-trackers · GitHub
前述の通りUnboundはScrubするため、用意された冗長なRPZは必要なく、簡素なオリジナルルールをRPZに書き換えればよい。*.data.adobedc.net
は省略形で、正式には*.data.adobedc.net.rpz.block.cloaking.
のため、末尾に.
を付けるとunbound-checkconfで構文エラーになる。
rpz-ip
を用いると、A/AAAA応答を引っ掛けられる。CIDR表記のresponse-ip
の方が書きやすいので、他のDNSソフトウェアでの使用を想定したときにrpz-ip
を用いるとよい。
DNSの逆引きと似たり寄ったりで、CIDR表記のネットマスク128
を先頭に、ヘクステット毎に先に接尾辞、最後に接頭辞が来るように並び替えられ、ゼロの連続::
はzz
で表現する。ghs.googlehosted.com
はGoogle Tag Managerに使われており、CNAME名(頭痛が痛い?)とIPアドレスが公開されている。様々なサービスが利用しているため、除外が必要かもしれない。
CNAME record values - Google Workspace Admin Help
server:
# rpz.block.cloaking ghs.googlehosted.com
local-data: "nordot.app. 300 IN AAAA 2001:4860:4802:32::15"
local-data: "bugs.chromium.org. 300 IN AAAA 2001:4860:4802:34::15"
local-data: "dwango.co.jp. 300 IN AAAA 2001:4860:4802:36::15"
local-data: "material.io. 300 IN AAAA 2001:4860:4802:38::15"
local-data: "blog.google. 300 IN AAAA 2001:4860:4802:32::15"
abuse 見出しにジャンプ
RPZは複数のDNSソフトウェアで使える共通規格のため、各社からブロックリストが提供されている。
https://dnsrpz.info/
DNSセキュリティを語る上で避けられないあのSpamhausが無償で提供する、最低限のDo Not Route or Peer (DROP)とService FeedsをUnboundのIssueにあった設定例を参考に設定した。
rpz:
name: drop.ip.dtq
zonefile: /usr/local/etc/unbound/drop.ip.dtq.zone
master: 34.194.195.25 # US.spamhaus.zone
rpz-action-override: nodata
rpz-log: yes
rpz-log-name: 'drop.ip.dtq.spamhaus'
rpz:
name: coinblocker.srv
zonefile: /usr/local/etc/unbound/coinblocker.srv.zone
master: 34.194.195.25
rpz-action-override: nodata
rpz-log: yes
rpz-log-name: 'coinblocker.srv.spamhaus'
...
name:
はSpamhausにあるゾーン名と一致させ、ログの接頭辞だけ変えている。RPZゾーン(頭痛が痛い?)は$ORIGIN
が異なると使えなくなるためだ。- Free Use用のホストはIPv6が機能していなかった。Unboundのフォールバックが遅いので、IPアドレスを設定した。IP TTLはUSの方が1大きかった。
response-ip 見出しにジャンプ
https://unbound.docs.nlnetlabs.nl/en/latest/manpages/unbound.conf.html#unbound-conf-response-ip
DNS Rebinding 見出しにジャンプ
DNSリバインディング攻撃は、ブラウザの保護機構をバイパスするために温故知新された、古くからある攻撃手法だ。
要はWebと権威DNSで連携し、maliciousなWebにアクセスがあれば、すかさず権威DNS側でRRsetを対象の内部ホストに書き換えてしまおうというまさに今、浸透を待っているあなたに耳よりの技術だ。
ドメイン名だけを見てアクセス許可を行う傾向にあるブラウザは、ドメイン名がいつの間にか内部ホストを指していても素通ししてしまう。
Private Network AccessのIP範囲をベースに、DNSリバインディング攻撃が刺さりそうなアドレス範囲をCIDR表記で表現した。
State of DNS Rebinding in 2023 | NCC Group Research Blog | Making the world safer and more secure
server:
cache-min-ttl: 60
# "this network" ("This host on this network" 0.0.0.0/32 for non-Windows OS)
private-address: 0.0.0.0/8
# IPv4 Loopback
private-address: 127.0.0.0/8
local-zone: "localhost." nodefault
# Private-Use
private-address: 10.0.0.0/8
private-address: 172.16.0.0/12
private-address: 192.168.0.0/16
# Shared Address Space (e.g. Tailscale)
private-address: 100.64.0.0/10
# IPv4 Link-Local
private-address: 169.254.0.0/16
# IPv4-mapped Address ::ffff:0:0/96, IPv6 Unspecified Address ::/128, IPv6 Loopback ::1/128
private-address: ::/3
# Unique-Local fc00::/7, Link-Local Unicast fe80::/10
private-address: 8000::/1
private-domain: "dot.home.arpa."
cache-min-ttl
はブラウザのDNS Pinningを模倣している。
DNSリバインディングにどう立ち向かうべきか 徳丸浩のウェブセキュリティ講座- ルール数を減らすためIPv6は大雑把に括っているが、BCP 38に使われるBogonプレフィックスと同義なので支障はない。
Bogon Prefixes – BGP Filter Guide – Guidance on BGP Filtering
IANA IPv4 Special-Purpose Address Registry
IANA IPv6 Special-Purpose Address Registry
Internet Protocol Version 6 Address Space - macOSやLinuxでは0.0.0.0がブロックされないらしいので、追加した。
0.0.0.0 Day: Exploiting Localhost APIs From the Browser | Oligo Security localhost.
はデフォルト応答を無効化し、ルート.
にリダイレクトした。絶対的な変更をするなら、static
やalways_nodata
が望ましい。- Shared Address Spaceを使うTailscaleも気になり、公式に警告されていたのでこれも追加した。
DNS Problems with internal services and DNS rebinding protection · Tailscale Docs - Unboundに設定された内部ドメイン以外のプライベートアドレスを弾くことを意味するので、
private-domain
を用いて除外する。
Local hostnames are not resolved by unbound · Issue #900 · NLnetLabs/unbound Protection Bypasses · nccgroup/singularity Wiki
これにより、管理下にないドメイン名を介して内部ホストへ不正にアクセスされなくなる。
インターネット到達性のあるアドレス範囲にACLがされていない(DMZ, 旧帝大, IPv6 GUA)ならこの限りではない。
自ビルド版では動かなかったので、response-ipで書き直した。
server:
response-ip: 0.0.0.0/8 redirect
response-ip: 127.0.0.0/8 redirect
response-ip: 10.0.0.0/8 redirect
response-ip: 172.16.0.0/12 redirect
response-ip: 192.168.0.0/16 redirect
response-ip: 100.64.0.0/10 redirect
response-ip: 169.254.0.0/16 redirect
response-ip: ::/3 redirect
response-ip: 8000::/1 redirect
Google Public DNSを自宅に設置する 見出しにジャンプ
Public DNSによる脱獄は対策したが、それだけでは面白みがない。直接IPアドレスを叩いてくるアプリケーションなどは逆NATでもするほかないが、ご丁寧にUnboundにクエリして頂けたのなら、ぜひとも他のクエリも歓迎したい。
server:
# dns.google.
response-ip: 8.8.4.0/24 redirect
response-ip-data: 8.8.4.0/24 "60 IN A 10.0.3.5"
response-ip: 8.8.8.0/24 redirect
response-ip-data: 8.8.8.0/24 "60 IN A 10.0.3.5"
response-ip: 2001:4860:4840::/42 always_nxdomain
# one.one.one.one.
response-ip: 1.1.1.0/24 redirect
response-ip-data: 1.1.1.0/24 "60 IN A 10.0.3.5"
response-ip: 1.0.0.0/24 redirect
response-ip-data: 1.0.0.0/24 "60 IN A 10.0.3.5"
response-ip: 2606:4700:4700::/40 always_nxdomain
アンチアンチアドブロッカー 見出しにジャンプ
unboundに施したセキュリティが干渉し、安全なコンテンツまで表示できなくなってしまうWebサイトがある。
jkrejcha/AdmiraList: A text-based list of 1000+ Admiral domains and IP addresses
server:
module-config: "respip iterator"
# Admiral
response-ip: 104.18.24.111/32 always_nxdomain
response-ip: 104.18.25.111/32 always_nxdomain
response-ip: 2606:4700::6812:186f/128 always_nxdomain
response-ip: 2606:4700::6812:196f/128 always_nxdomain
local-zone: "getadmiral.com." static
local-data: "getadmiral.com. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1723184575 1800 900 604800 86400"
local-zone: "cdnral.com." static
local-data: "cdnral.com. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1723184575 1800 900 604800 86400"
local-zone: "scarceshock.com." static
local-data: "scarceshock.com. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1723184575 1800 900 604800 86400"
local-zone: "succeedscene.com." static
local-data: "succeedscene.com. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1723184575 1800 900 604800 86400"
...
- これまで推測が難しいFQDNとCDNを利用してブロッカーの回避を試みていたが、最近はCloudflareを挟んでいるため簡単にルールを記述できる。
- respipではHTTPS RRのipv4hint/ipv6hintがすり抜けてしまうため、代わりに
inform_redirect
を用いて監視するのもよい。発見したFQDNはlocal-zone設定する。
HTTPS RRで遊んでいたら、AliasModeを解釈できるApple macOS/iOS 17+で機能する、DNSを使ったアンチアンチアンチアンチアドブロッカーが実現してしまった。
RFC 9460: Service Binding and Parameter Specification via the DNS (SVCB and HTTPS Resource Records)
実用性はともかく面白いので共有しておく。
server:
# www.cbsnews.com
local-data: 'scarceshock.com. 300 IN HTTPS 0 invalid.'
local-data: 'succeedscene.com. 300 IN HTTPS 0 invalid.'
# www.youtube.com
local-zone: 'jnn-pa.googleapis.com.' typetransparent
local-zone-tag: 'jnn-pa.googleapis.com.' admin
local-data: 'jnn-pa.googleapis.com. 300 IN HTTPS 0 invalid.'
これにより、通常ブラウザレベルでないと機能しないFirefoxのShimのような動きになる。
iOS 17、AliasModeの向き先が否定応答のとき、タイムアウトしないらしい。 pic.twitter.com/kaneszdQvR
— しばにゃん (@shibanyan_1) January 4, 2024
ルールを自分で書くメリットとして、ブロック漏れや過剰なブロックに対応しやすい利点がある。
メジャーなフィルターリストはブロックしていませんが、jnn-pa.googleapis\.comをブロックするとYoutubeのライブをしばらく再生したのちエラーが出るようになりました。 pic.twitter.com/tArmaqZM59
— Yuki2718 (@Yuki27183) August 8, 2024
home.arpa. 見出しにジャンプ
内部ドメインには好き勝手にそれらしい名前のTLDが使われていて、.localがmDNSと被ったり、.homeが後にTLDに追加されるなど混沌としている。また、これらの内部ドメインの問い合わせが外に漏れると、ルートサーバの過負荷に繋がったり、セキュリティリスクが伴う。
ICANNのSAC113 SSAC Advisory on Private-Use TLDsは、パブリックに登録されたドメイン名のサブドメインを使うのが最善としている。例えばexample.com.
を持っているならserver.example.com.
とか。
内部構成を公開したくないなどのユースケースもあるので、他の選択肢にICANNのinternal.
, IANAのhome.arpa.
が挙げられている。
RFC 8375 - Special-Use Domain 'home.arpa.'
一点付け加えておくと,議論の元になったアドバイザリ (SAC113) では,プライベート用途ドメイン名が予約された場合でも,すでに委任されたドメイン名のサブドメイン (つまり自社ドメイン名からサブドメインを出す) を使うのが最善とされています→
— alt (@jj1lfc) August 5, 2024
server:
local-zone: "router.home.arpa." static
local-zone-tag: "router.home.arpa." admin
local-data: "router.home.arpa. 10800 IN A 10.0.0.254"
local-data: "server.home.arpa. 10800 IN A 10.0.2.8"
local-data: "dns.home.arpa. 10800 IN A 10.0.3.5"
local-data: "printer.home.arpa. 10800 IN A 10.0.4.62"
NGN IPv6のキャッシュDNS(東)をlocal-dataに設定する例。元の応答を調べるときにエイリアスがあると捗るよ。
server:
local-zone: "flets." static
local-data: "ntp.flets. 3600 IN AAAA 2404:1a8:1102::a"
local-data: "ntp.flets. 3600 IN AAAA 2404:1a8:1102::b"
local-data: "ntp.flets. 3600 IN AAAA 2404:1a8:1102::c"
local-data: "dns.flets. 3600 IN AAAA 2404:1a8:7f01:a::3"
local-data: "dns.osa.flets. 3600 IN AAAA 2404:1a8:7f01:a::3"
local-zone: "ocn." static
local-data: "dns.ocn. 3600 IN AAAA 2001:380:0:4::1"
local-data: "dns.ocn. 3600 IN AAAA 2001:380:0:4::2"
local-data: "dns.osa.ocn. 3600 IN AAAA 2001:380:0:104::1"
local-data: "dns.osa.ocn. 3600 IN AAAA 2001:380:0:104::2"
dig @dns.flets. example.com
Discord 見出しにジャンプ
DiscordはCloudflare CDNを利用している。
```sh
dig discord.com ns
dig @sima.ns.cloudflare.com. discord.com a +norec +noall +answer
# discord.com. 300 IN A 162.159.138.232
# discord.com. 300 IN A 162.159.128.233
# discord.com. 300 IN A 162.159.135.232
# discord.com. 300 IN A 162.159.137.232
# discord.com. 300 IN A 162.159.136.232
whois -h whois.cymru.com -v 162.159.128.233
Warning: RIPE flags used with a traditional server.
AS | IP | BGP Prefix | CC | Registry | Allocated | AS Name
13335 | 162.159.128.233 | 162.159.128.0/19 | US | arin | 2013-05-23 | CLOUDFLARENET, US
Cloudflareは積極的にIPv6やHTTP/3などのモダンな技術に対応してきたが、Discordが使用するドメインにはAAAAレコードが設定されていないため、実際にはIPv4で接続する。
dig @sima.ns.cloudflare.com. discord.com aaaa +norec +noall +answer +comments
# ;; Got answer:
# ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48529
# ;; flags: qr aa; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
# ;; OPT PSEUDOSECTION:
# ; EDNS: version: 0, flags:; udp: 1232
dig @sima.ns.cloudflare.com. discord.com type65 +norec +noall +answer
# discord.com. 300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=162.159.128.233,162.159.135.232,162.159.136.232,162.159.137.232,162.159.138.232
他のホストも調べるが、AAAAレコードはほぼ設定されていない。discord.com.
, discordapp.com.
, discordapp.net.
, discord.gg.
, discord.media.
ゾーン毎にそれぞれ5つのIPアドレスが存在するようだ。
※RFC 9460 SvcParamが網羅的に存在するためHTTPSレコードをクエリするが、通常はA/AAAAをクエリすると留意してほしい。
discord.com. 300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=162.159.128.233,162.159.135.232,162.159.136.232,162.159.137.232,162.159.138.232
ptb.discord.com. 300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=162.159.128.233,162.159.135.232,162.159.136.232,162.159.137.232,162.159.138.232
streamkit.discord.com. 300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=162.159.128.233,162.159.135.232,162.159.136.232,162.159.137.232,162.159.138.232
updates.discord.com. 300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=162.159.128.233,162.159.135.232,162.159.136.232,162.159.137.232,162.159.138.232
support.discord.com. 300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=162.159.128.233,162.159.135.232,162.159.136.232,162.159.137.232,162.159.138.232
cdn.discordapp.com. 300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=162.159.129.233,162.159.130.233,162.159.133.233,162.159.134.233,162.159.135.233
discordapp.com. 300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=162.159.129.233,162.159.130.233,162.159.133.233,162.159.134.233,162.159.135.233
streamkit.discordapp.com. 300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=162.159.129.233,162.159.130.233,162.159.133.233,162.159.134.233,162.159.135.233
media.discordapp.net. 300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=162.159.128.232,162.159.129.232,162.159.130.232,162.159.133.232,162.159.134.232
images-ext-1.discordapp.net. 300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=162.159.128.232,162.159.129.232,162.159.130.232,162.159.133.232,162.159.134.232
images-ext-2.discordapp.net. 300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=162.159.128.232,162.159.129.232,162.159.130.232,162.159.133.232,162.159.134.232
gateway.discord.gg. 300 IN HTTPS 1 . alpn="h2" ipv4hint=162.159.130.234,162.159.133.234,162.159.134.234,162.159.135.234,162.159.136.234
latency.discord.media. 300 IN HTTPS 1 . alpn="h2" ipv4hint=162.159.128.235,162.159.129.235,162.159.130.235,162.159.137.234,162.159.138.234 ipv6hint=2606:4700:7::a29f:80eb,2606:4700:7::a29f:81eb,2606:4700:7::a29f:82eb,2606:4700:7::a29f:89ea,2606:4700:7::a29f:8aea
このように、CDNが対応している方式が使われていない事例があり、サービス提供側と利用者側に温度差がある。
実際、Discordのサポートフォーラムには完全なIPv6サポートを求める投稿があるが、日本においてIPv4接続性のない環境は考えにくいし、個人的にはIPv6推進派ですらない。IPv6化のメリットはせいぜいフレッツで高速だとか、大量のHTTP/3トラフィックが発生する状況くらいなものだ。
同Cloudflareを利用するnyanshiba.comはというと、ipv6hintやAAAAが設定されている。
dig nyanshiba.com https +noall +answer
# nyanshiba.com. 300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=104.21.0.152,172.67.151.30 ipv6hint=2606:4700:3031::6815:98,2606:4700:3032::ac43:971e
CloudflareのIPv6アドレス割り当てには法則があり、基本的に2606:4700::/96
の接頭辞に、IPv4アドレスのdecimalをhexに変換した接尾辞を持つ。Anycast方式のため、IPアドレスは世界共通。
DiscordはAAAAがないものの、この法則に従って変換すると機能するサービスの一つだ。
Discord works on IPv6 if you force it : r/ipv6
そこで、他のドメイン名にも対応するために、CLoudflareを利用するサービスのHTTPSレコードのipv4hintを渡すとIPv6アドレスに変換するスクリプトを作成した。
unboundconf/calc-cfv6addr.ps1 at main · nyanshiba/unboundconf
./calc-cfv6addr.ps1 -ip 162.159.128.233,162.159.135.232,162.159.136.232,162.159.137.232,162.159.138.232
# 2606:4700::a29f:80e9
# 2606:4700::a29f:87e8
# 2606:4700::a29f:88e8
# 2606:4700::a29f:89e8
# 2606:4700::a29f:8ae8
unboundに設定すると下記の通り。同じIPアドレスを使う他のドメインにも同様に設定する。新規チャットの購読gateway.discord.gg.
や、ここに上がっていない通話のICEサーバはIPv6非対応だ。
IPv4でも接続できるように、typetransparent
でIN Aを透過するとよい。
server:
local-zone: 'discord.com.' typetransparent
local-data: 'discord.com. 300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=162.159.128.233,162.159.135.232,162.159.136.232,162.159.137.232,162.159.138.232 ipv6hint=2606:4700::a29f:80e9,2606:4700::a29f:87e8,2606:4700::a29f:88e8,2606:4700::a29f:89e8,2606:4700::a29f:8ae8'
local-data: 'discord.com. 300 IN AAAA 2606:4700::a29f:80e9'
local-data: 'discord.com. 300 IN AAAA 2606:4700::a29f:87e8'
local-data: 'discord.com. 300 IN AAAA 2606:4700::a29f:88e8'
local-data: 'discord.com. 300 IN AAAA 2606:4700::a29f:89e8'
local-data: 'discord.com. 300 IN AAAA 2606:4700::a29f:8ae8'
...
local-data: 'cdn.discordapp.com. 300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=162.159.129.233,162.159.130.233,162.159.133.233,162.159.134.233,162.159.135.233 ipv6hint=2606:4700::a29f:81e9,2606:4700::a29f:82e9,2606:4700::a29f:85e9,2606:4700::a29f:86e9,2606:4700::a29f:87e9'
...
local-data: 'media.discordapp.net. 1800 IN HTTPS 1 . alpn="h3,h2" ipv4hint=162.159.128.232,162.159.129.232,162.159.130.232,162.159.133.232,162.159.134.232 ipv6hint=2606:4700::a29f:80e8,2606:4700::a29f:81e8,2606:4700::a29f:82e8,2606:4700::a29f:85e8,2606:4700::a29f:86e8'
...
QoSに引っ掛けやすいように、Prefixを変えてもよい。
local-zone: 'media.discordapp.net' typetransparent
local-data: 'media.discordapp.net. 1800 IN HTTPS 1 . alpn="h3,h2" ipv4hint=162.159.128.232,162.159.129.232,162.159.130.232,162.159.133.232,162.159.134.232 ipv6hint=2606:4700::a29f:80e8,2606:4700::a29f:81e8,2606:4700::a29f:82e8,2606:4700::a29f:85e8,2606:4700::a29f:86e8'
local-data: 'media.discordapp.net. 1800 IN AAAA 2606:4700:3032::a29f:80e8'
local-data: 'media.discordapp.net. 1800 IN AAAA 2606:4700:3032::a29f:81e8'
local-data: 'media.discordapp.net. 1800 IN AAAA 2606:4700:3032::a29f:82e8'
local-data: 'media.discordapp.net. 1800 IN AAAA 2606:4700:3032::a29f:85e8'
local-data: 'media.discordapp.net. 1800 IN AAAA 2606:4700:3032::a29f:86e8'
CloudflareとFastlyを利用するStack Overflowによる、IPv6対応が難しい理由。ちょっと勿体ない。
Stack Overflow not reachable via IPv6 - Meta Stack Overflow
server:
local-zone: 'meta.stackoverflow.com.' typetransparent
local-data: 'meta.stackoverflow.com. 300 IN HTTPS 1 . alpn="h2" ipv4hint=104.18.32.7,172.64.155.249 ipv6hint=2606:4700::6812:2007,2606:4700::ac40:9bf9'
local-data: 'meta.stackoverflow.com. 300 IN AAAA 2606:4700::6812:2007'
local-data: 'meta.stackoverflow.com. 300 IN AAAA 2606:4700::ac40:9bf9'
local-zone: 'i.sstatic.net.' typetransparent
local-data: 'i.sstatic.net. 300 IN HTTPS 1 . alpn="h2" ipv4hint=104.18.41.33,172.64.146.223 ipv6hint=2606:4700::6812:2921,2606:4700::ac40:92df'
local-data: 'i.sstatic.net. 300 IN AAAA 2606:4700::6812:2921'
local-data: 'i.sstatic.net. 300 IN AAAA 2606:4700::ac40:92df'
このようにIPv6化の余地があるサービスは大抵Why No IPv6?(強い思想に注意)に載っている
Twitter 見出しにジャンプ
2024年9月現在、Twitter(自称𝕏)は多くの通信をIPv6で行い、Unboundで殆どの通信をIPv6化できる。
Cloudflare 見出しにジャンプ
イーロン・マスク氏がTwitterを買収されてから月日が経ち、少なくともt.co
, twitter.com
/x.com
, api.twitter.com
/api.x.com
に関してはCloudflareにBYOIPしているようだ。
curl -4 -i https://t.co/
# server: cloudflare tsa_m
Discordと同様にIPv6アドレスを計算すると、IPv6化できる。
server:
local-data: 'twitter.com. 1800 IN AAAA 2606:4700::68f4:2a01'
local-data: 'twitter.com. 1800 IN AAAA 2606:4700::68f4:2a41'
local-data: 'twitter.com. 1800 IN AAAA 2606:4700::68f4:2a81'
local-data: 'twitter.com. 1800 IN AAAA 2606:4700::68f4:2ac1'
local-data: 'x.com. 1800 IN AAAA 2606:4700::68f4:2a01'
local-data: 'x.com. 1800 IN AAAA 2606:4700::68f4:2a41'
local-data: 'x.com. 1800 IN AAAA 2606:4700::68f4:2a81'
local-data: 'x.com. 1800 IN AAAA 2606:4700::68f4:2ac1'
HTTP/3は非対応。
コミュニティノートに非対応。
server:
# local-zone: 't.co.' static
# local-data: 't.co. 300 IN HTTPS 1 . alpn="h2" ipv4hint=104.244.42.5,104.244.42.69,104.244.42.133,104.244.42.197 ipv6hint=2606:4700::68f4:2a05,2606:4700::68f4:2a45,2606:4700::68f4:2a85,2606:4700::68f4:2ac5'
# local-data: 't.co. 300 IN A 104.244.42.5'
# local-data: 't.co. 300 IN A 104.244.42.69'
# local-data: 't.co. 300 IN A 104.244.42.133'
# local-data: 't.co. 300 IN A 104.244.42.197'
# local-data: 't.co. 300 IN AAAA 2606:4700::68f4:2a05'
# local-data: 't.co. 300 IN AAAA 2606:4700::68f4:2a45'
# local-data: 't.co. 300 IN AAAA 2606:4700::68f4:2a85'
# local-data: 't.co. 300 IN AAAA 2606:4700::68f4:2ac5'
local-zone: 't.co.' typetransparent
local-data: 't.co. 300 IN HTTPS 1 . alpn="h2" ipv4hint=162.159.140.229,172.66.0.227 ipv6hint=2606:4700::ac42:e3,2606:4700::ac42:e3'
local-data: 't.co. 300 IN AAAA 2606:4700::ac42:e3'
local-data: 't.co. 300 IN AAAA 2606:4700::ac42:e3'
# local-data: 't.co. 300 IN A 162.159.140.229'
# local-data: 't.co. 300 IN A 172.66.0.227'
t.co
は外部ドメインへのバウンストラッキングに使われる。Cloudflareに移行することで、チャレンジが頻発するようになってしまった。
CloudflareのWAFがIPアドレス単位のrate-limitをかけているか知らないが、CGNATなどのIPv4延命技術下では、同じIPアドレスから異なるユーザが大量にアクセスが予想されるため、避けたい。これにはIPv6化が有効だ。
t.co
が以前使っていたAS13414のIPと、現行のCloudflareのIPから、合わせて計6つIPv6アドレスが使えるので、安定したものを選ぶとよい。
server:
# 500 https://api.x.com/1.1/onboarding/task.json on desktop
local-zone: 'api.x.com.' typetransparent
local-zone-tag: "api.x.com." admin
local-data: 'api.x.com. 60 IN AAAA 2606:4700::68f4:2a02'
local-data: 'api.x.com. 60 IN AAAA 2606:4700::68f4:2a42'
local-data: 'api.x.com. 60 IN AAAA 2606:4700::68f4:2a82'
local-data: 'api.x.com. 60 IN AAAA 2606:4700::68f4:2ac2'
Web版のTwitterでは、サインイン時https://api.x.com/1.1/onboarding/task.json
が500を返すため、IPv4で接続しなければならない。上記は使えないと思ってよいだろう。
server:
# 500 https://api.twitter.com/1.1/onboarding/task.json on ios
local-zone: 'api.twitter.com.' typetransparent
local-zone-tag: 'api.twitter.com.' dualstack
local-data: 'api.twitter.com. 300 IN HTTPS 1 . alpn="h2" ipv4hint=104.244.42.2,104.244.42.66,104.244.42.130,104.244.42.194 ipv6hint=2606:4700::68f4:2a02,2606:4700::68f4:2a42,2606:4700::68f4:2a82,2606:4700::68f4:2ac2'
local-data: 'api.twitter.com. 300 IN AAAA 2606:4700::68f4:2a02'
local-data: 'api.twitter.com. 300 IN AAAA 2606:4700::68f4:2a42'
local-data: 'api.twitter.com. 300 IN AAAA 2606:4700::68f4:2a82'
local-data: 'api.twitter.com. 300 IN AAAA 2606:4700::68f4:2ac2'
# after 300sec, 200 https://tpop-api.twitter.com/1.1/onboarding/task.json
Twitter for iPhone(com.atebits.Tweetie2)では、未だにapi.twitter.com
が使われている。
一見Web版と瓜二つに見えるが、実際のフローは全く異なる。表の通り、typetransparent
ではAppleのスタブリゾルバがIN CNAME tpop-api.twitter.com.
をScrubするためだ。
dig api.twitter.com a +noall +answer
# api.twitter.com. 565 IN CNAME tpop-api.twitter.com.
# tpop-api.twitter.com. 450 IN A 104.244.42.2
このためAppleデバイス向けにはapi.twitter.com. IN A
かtpop-api.twitter.com. IN AAAA
を設定しないと、tpop-api.twitter.com. 450 IN A
- api.twitter.com. 300 IN A
の150秒間はIPv4で接続してしまう。
逆に言えば、DNSのレイヤでは通常api.twitter.com
以下のパスまで扱えないところ、https://api.twitter.com/1.1/onboarding/task.json
とhttps://tpop-api.twitter.com/1.1/onboarding/task.json
で判別できるため、IPv6化しても設定変更なく使用できる訳だ。api.twitter.com.
のTTLでIPv6の頻度を変えられるのも分かるだろう。キャッシュDNSで減算されたTTL次第で割合が変動し、その最大値は権威次第と留意しよう。
CloudFront 見出しにジャンプ
Twitterに𝕏というあだ名が付く以前は、twitter.com.
はAWS CloudFront特有のヘッダX-Amz-Cf-Pop:
を返していた。CloudFrontは、共有IPアドレスへのトラフィックをSNIを使って区別している。
CloudFront で HTTPS リクエストを処理する方法を選択する - Amazon CloudFront
[WEB-197] Minecraft authentication server not reachable over IPv6 - Jira
つまりAAAAが設定されていなくても、例えば以下のようにAAAAを含むCloudFrontドメイン名に向けることで、IPv6化できていた。現在は利用できない。
server:
local-zone: "twitter.com." redirect
local-zone-tag: "twitter.com" "admin"
local-data: "twitter.com. 60 IN CNAME dr49lng3n1n2s.cloudfront.net." # aws.amazon.com.
あれ?もしかして、Twitterって完全デュアルスタックいける?https://t.co/FBZxzaXmK9 pic.twitter.com/0ee83Dny68
— しばにゃん (@shibanyan_1) December 21, 2023
CloudFrontは基本的にIPv6やHTTP/3に対応しているため、よく使っているサービスも実は対応しているかもしれない。
server:
local-zone: 'www.amazon.co.jp.' typetransparent
local-data: 'www.amazon.co.jp 1800 IN HTTPS 1 . alpn="h3,h2"'
local-zone: 'm.media-amazon.com.' typetransparent
local-data: 'm.media-amazon.com 1800 IN HTTPS 1 . alpn="h3,h2"'
local-zone: 'images-fe.ssl-images-amazon.com.' typetransparent
local-data: 'images-fe.ssl-images-amazon.com 1800 IN HTTPS 1 . alpn="h3,h2"'
HTTP/3対応版のcurlで確認できる。
New – HTTP/3 Support for Amazon CloudFront | AWS News Blog
curl/docs/HTTP3.md at master · curl/curl
curl --http3-only --connect-timeout 1 --head -i "https://www.amazon.co.jp/"
# HTTP/3 405
TCP MSS 見出しにジャンプ
Twitterの画像サーバpbs.twimg.com
はEdgecastpbs-ec.twimg.com
, Fastlypbs-ft.twimg.com
, 電気自動車メーカーのCEOに買収されるまではAkamaipbs-ak.twimg.com
が、時期不明でGoogle Cloudpbs-gc.twimg.com
が稼働していた模様。
動画のvideo.twimg.com
や、Web版の動作に関わるabs.twimg.com
も命名規則は同じで、いずれもIPv6に対応している。
重箱の隅をつつくと、CDNによってTCP MSSに大きく開きがある。EdgecastもGoogleもCloudflarevideo-cf.twimg.com
もそうだが、QUICに対応すると同時に、TCPのMSSもまるでEDNSバッファサイズのように小さくしてしまう実装が多い。
日本のフレッツ系回線ではIPv6のデータグラムが逆転して大きいという状態なのに、ここにきてIPv6だけデータグラムが小さいCDNがあるとなっては書き換えるしかない。
server:
local-zone: "pbs.twimg.com." redirect
# pbs-ec.twimg.com IPv6 MSS: 1220, IPv4: 1420
# pbs-ft.twimg.com IPv6 MSS: 1394, IPv4: 1414
local-data: "pbs.twimg.com. 3600 IN CNAME dualstack.twimg.twitter.map.fastly.net."
local-zone: "abs.twimg.com." redirect
local-data: "abs.twimg.com. 3600 IN CNAME dualstack.twimg.twitter.map.fastly.net."
local-zone: "video.twimg.com." redirect
# video-cf.twimg.com IPv6 MSS: 1360
local-data: "video.twimg.com. 3600 IN CNAME dualstack.video.twitter.map.fastly.net."
純粋にTCP MSSの長さだけ考えるならAAAAをブロックするのも手。block_aaaa
は存在しないが、transparent
で表現できなくもない。
# local-zone: "pbs.twimg.com." transparent 暗黙
local-data: "pbs.twimg.com. 3600 IN A ..." # 静的にIPアドレスを設定
Fastlyでは、ドメイン名にdualstack.
を付けるだけでIPv6に対応できる場合がある。また、2a04:4e42::/32
をインクリメントして、IPv6対応ホストを見つけるプロジェクトもある。
画像やスクリプト関係は接尾辞159
、動画は158
だが、実はtwimg.com.
は2a04:4e42::157
から2a04:4e42::160
まで許可されている。3へクステット目が0000
ならAnycastで、日本では15
,1a
,36
,8c
辺りが使えるのではないかと思う。
openssl s_client -connect [2a04:4e42::157]:443 | openssl x509 -noout -text | grep DNS:
openssl s_client -connect [2a04:4e42::160]:443 | openssl x509 -noout -text | grep DNS:
# DNS:*.twimg.com, DNS:twimg.com, DNS:platform.twitter.com, DNS:cdn.syndication.twimg.com
IPv6Data/fastly at master · miyurusankalpa/IPv6Data · GitHub
HTTP/3化 見出しにジャンプ
Edgecastpbs-ec.twimg.com.
はHTTP/3に対応しているが、Alt-SvcにないためHTTP/3にアップグレードされることはない。
HTTPSの接続情報を通知する "HTTPS DNSレコード" の提案仕様 (2021/07更新) - ASnoKaze blog
HTTPS RRを用意すれば、AppleデバイスでHTTP/3接続される。
TN3102: HTTP/3 in your app | Apple Developer Documentation
server:
local-zone: 'pbs.twimg.com.' typetransparent
local-data: "pbs.twimg.com. 60 IN AAAA 2606:2800:248:1707:10d3:19d0:1ba2:1a23"
local-data: 'pbs.twimg.com. 60 IN HTTPS 1 . alpn="h3" ipv6hint=2606:2800:248:1707:10d3:19d0:1ba2:1a23'
Appleデバイスでは、Alt-Svcがあれば従う(HTTP/2にダウングレードする)が、無ければHTTPS RRを見ているような気がする。
RFC 9460: Service Binding and Parameter Specification via the DNS (SVCB and HTTPS Resource Records)
アプリの表示崩れを修正 見出しにジャンプ
最近𝕏(通称Twitter)利用者から「興味がない」が押せない広告や、画面いっぱいにユーザ名も何もない謎の余白が広がるといった不具合が寄せられているが、当環境では再現しなかった。
iOSユーザーなら
— ハイオメガシシャモ (@SHISHAMO_SEA) July 21, 2024
"https://t.co/wJX2ke8Wcm"
このURLブロックしたらtwitterのブロックできない系の広告全部消えるんだよね https://t.co/hFQuZZhSsS pic.twitter.com/a9ONvLQVmk
TwitterにはTwitter広告があるのでまさかと思ったが、確かにアプリからdoubleclick.net.
のクエリがなされていた。
unbound-control set_option log-queries: yes
[1722315240] unbound[4955:3] info: 10.0.0.13 firebaselogging-pa.googleapis.com. HTTPS IN
[1722315240] unbound[4955:3] info: 10.0.0.13 googleads.g.doubleclick.net. A IN
[1722315240] unbound[4955:3] info: 10.0.0.13 googleads.g.doubleclick.net. AAAA IN
[1722315240] unbound[4955:3] info: 10.0.0.13 googleads.g.doubleclick.net. HTTPS IN
[1722315240] unbound[4955:3] info: 10.0.0.13 dualstack.video.twitter.map.fastly.net. HTTPS IN
unbound-control set_option log-queries: no
doubleclick.net.
はGoogle広告に使われているドメインで、これはMalvertisingを含む場合があるため既にセキュリティ対策が施されていた。
server:
local-zone: "doubleclick.net." static
local-data: "doubleclick.net. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1 1800 900 604800 86400"
Twitter(現𝕏)では"とりあえず動くからリリース"した様子が伺えた。
動きを減らす 見出しにジャンプ
Twitter Appには、画面遷移のアニメーションやライブエンゲージメントを減らすアクセシビリティ設定「動きを減らす」がある。
アニメーションはUIの軽快さに一役買っている一方、ライブエンゲージメントは常にprobe.twitter.com
へTCPコネクションを張り続けて、RTやふぁぼをリアルタイムに取得するため、バッテリーへの影響が大きい。発熱の問題を報告するユーザも少なくない。
そこで私はアニメーションはそのままに、ライブエンゲージメントを止めるルールを書いた。
server:
module-config: "respip iterator"
# probe.twitter.com
response-ip: 104.244.42.3/32 always_nxdomain
response-ip: 104.244.42.67/32 always_nxdomain
response-ip: 104.244.42.131/32 always_nxdomain
response-ip: 104.244.42.195/32 always_nxdomain
local-zone: "api-stream.twitter.com." static
local-zone: "api-stream.x.com." static
local-zone: "probe.twitter.com." static
local-zone: "probe.x.com." static
local-zone: 'albtls.t.co.' static
local-zone: 'cftls.t.co.' static
local-zone: "dc-api.twitter.com." static
local-zone: "api-0-4-0.twitter.com." static
local-zone: "api-0-4-2.twitter.com." static
local-zone: "api-0-4-3.twitter.com." static
# local-zone: "api-0-4-4.twitter.com."
local-zone: "api-0-4-5.twitter.com." static
local-zone: "api-0-4-6.twitter.com." static
local-zone: "api-0-4-7.twitter.com." static
local-zone: "api-0-4-8.twitter.com." static
local-zone: "api-0-5-0.twitter.com." static
local-zone: "api-1-0-0.twitter.com." static
# local-zone: "api-2-0-0.twitter.com."
# local-zone: "api-3-0-0.twitter.com."
# local-zone: "api-20-0-0.twitter.com."
# local-zone: "api-21-0-0.twitter.com."
# local-zone: "api-23-0-0.twitter.com."
# local-zone: "api-24-0-0.twitter.com."
# local-zone: "api-26-0-0.twitter.com."
# local-zone: "api-28-0-0.twitter.com."
# local-zone: "api-30-0-0.twitter.com."
local-zone: "api-31-0-0.twitter.com." static
# local-zone: "api-32-0-0.twitter.com."
local-zone: "api-33-0-0.twitter.com." static
# local-zone: "api-34-0-0.twitter.com."
# local-zone: "api-36-0-0.twitter.com."
local-zone: "api-37-0-0.twitter.com." static
# local-zone: "api-38-0-0.twitter.com."
# local-zone: "api-39-0-0.twitter.com."
# local-zone: "api-40-0-0.twitter.com."
# local-zone: "api-41-0-0.twitter.com."
# local-zone: "api-42-0-0.twitter.com."
local-zone: "api-43-0-0.twitter.com." static
local-zone: "api-44-0-0.twitter.com." static
# local-zone: "api-45-0-0.twitter.com."
local-zone: "api-46-0-0.twitter.com." static
local-data: "publish.twitter.com. 1800 IN A 104.244.42.3"
local-data: "publish.twitter.com. 1800 IN A 104.244.42.67"
local-data: "publish.twitter.com. 1800 IN A 104.244.42.131"
local-data: "publish.twitter.com. 1800 IN A 104.244.42.195"
Apple Push Notification service 見出しにジャンプ
Apple, Google含めプッシュ通知と呼ばれるものは大概、息の長いTCPコネクションで行われる。
この際、Keep-Alive間隔の長さゆえCPEやAFTRのNAPTキャッシュからセッション情報が消される場合がある。こうなると最悪、端末がKeep-Alive間隔待ってから新たにTCPコネクションを張りなおすまでプッシュ通知は届かない。逆にRFC通りに動作するとAterm病と汚名を付けられるくらいには難のあるところだ。
ともあれNATしないIPv6を通せば何ら問題ない。
Appleは17.0.0.0/8
つまり世界のIPv4アドレスプールの1/256を占める米国企業だ。そのためIPv4でも満足に事業を展開できるはず。
しかしながら、2016年という早い時期からアプリストアの審査条件にDNS64/NAT64対応を加えている。
IPv6のみのネットワークのサポート - サポート - Apple Developer
AppleがIPv6を推進する理由はひとえに、顧客側の接続性にあると言えよう。
(思想が強いだけかもしれないが)
Appleのプッシュ通知サービス(APNs)には5223/tcpが使われており、管理者向けにIPv6アドレスの範囲も公開されている。
エンタープライズネットワークで Apple 製品を使う - Apple サポート (日本)
Apple 製のデバイスで Apple プッシュ通知が届かない場合 - Apple サポート (日本)
ところが、iPhoneをtcp port 5223
でパケットキャプチャしても17.0.0.0/8
の通信しか流れていなかった。どうやら2022年の一時期を除き、IPv4シングルスタックで提供されてるようだ(iOS 18現在)。
syslog_20220325_223125:
45732: 2022/03/20 16:45:57: [INSPECT] LAN2[out][63900] TCP 2001:db8:b0ba:10ee::53e1.52859 > 2620:149:a44:30c::9.5223 (2022/03/20 16:41:57)
45734: 2022/03/20 16:45:59: [INSPECT] LAN2[out][63900] TCP 2001:db8:b0ba:10ee::53e1.52858 > 2620:149:a44:30c::6.5223 (2022/03/20 16:41:57)
62272: 2022/03/23 03:07:40: [INSPECT] LAN2[out][63900] TCP 2001:db8:b0ba:10ee::9395.52401 > 2a01:b740:a42:50c::9.5223 (2022/03/23 03:03:41)
62648: 2022/03/23 08:48:05: [INSPECT] LAN2[out][63900] TCP 2001:db8:b0ba:10ee::9395.52400 > 2a01:b740:a42:50c::b.5223 (2022/03/23 03:03:40)
syslog_20220402_163206:
21582: 2022/03/27 02:32:29: [INSPECT] LAN2[out][63900] TCP 2001:db8:b0ba:10ee::7436.59855 > 2620:149:a44:1100::6.5223 (2022/03/27 02:10:43)
syslog_20220605_135143:
28059: 2022/05/22 01:06:29: [INSPECT] LAN2[out][63900] TCP 2001:db8:b0ba:10ee::36f4.62427 > 2403:300:a42:c0a::7.5223 (2022/05/21 17:09:51)
syslog_20220924_122548:
71022: 2022/09/17 16:32:44: [INSPECT] LAN2[out][63900] TCP 2001:db8:b0ba:10ee::af64.49708 > 2620:149:a44:a0c::8.5223 (2022/09/17 16:24:31)
syslog_20221118_010457:
80047: 2022/11/17 15:14:45: [INSPECT] LAN2[out][63900] TCP 2001:db8:b0ba:10ee::ccab.49916 > 2620:149:a44:80c::c.5223 (2022/11/17 08:49:52)
dig 0-courier.push.apple.com aaaa +noall +answer
0-courier.push.apple.com. 7716 IN CNAME 0.courier-push-apple.com.akadns.net.
0.courier-push-apple.com.akadns.net. 28 IN CNAME apac-asia-courier-4.push-apple.com.akadns.net.
私にそんな趣味はないが、プッシュ通知が遅れて一コメを取れなかったら泣いてしまう人もいるだろう。
プッシュ通知は再送を保証されていない[要出典]ので、単に新着動画を見逃すこともあるだろうし、例えば推しのVTuberの枠を開いたころには既に打ち上っていて軌道に乗っていたら、再突入試験でもない限りげんなりするだろう。
アクセスログにあるホストへTCP接続すると、courier2.push.apple.com
がAAAAレコードを持つと分かった。
openssl s_client -connect [2403:300:a42:20c::6]:5223 | openssl x509 -noout -text | grep DNS:
# DNS:courier.push.apple.com, DNS:courier2.push.apple.com, DNS:windows.courier.push.apple.com
dig courier2.push.apple.com aaaa +noall +answer
# courier2.push.apple.com. 28800 IN CNAME 1.courier2-push-apple.com.akadns.net.
# 1.courier2-push-apple.com.akadns.net. 60 IN CNAME apac-asia-courier-vs.push-apple.com.akadns.net.
# apac-asia-courier-vs.push-apple.com.akadns.net. 60 IN AAAA 2403:300:a42:c0a::5
# ...
これをUnboundに設定すると下記のようになる。
server:
local-zone: "init-p01st.push.apple.com." transparent
local-zone: "homekit.push.apple.com." transparent
local-zone: "init.push.apple.com." transparent
local-zone: "sandbox.push.apple.com." transparent # TestFlight init., n-courier., courier.
local-zone: "push.apple.com." inform_redirect
local-zone-tag: "push.apple.com" dualstack
local-data: "push.apple.com. 60 IN AAAA 2403:300:a42:902::6"
local-data: "push.apple.com. 60 IN AAAA 2403:300:a42:902::7"
local-data: "push.apple.com. 60 IN AAAA 2403:300:a42:902::8"
local-data: "push.apple.com. 60 IN AAAA 2403:300:a42:902::9"
local-data: "push.apple.com. 60 IN AAAA 2403:300:a42:902::a"
...
local-data: "push.apple.com. 60 IN AAAA 2403:300:a42:902::13"
0-courier.push.apple.com
から50-courier.push.apple.com
までを一括設定するために、push.apple.com.
ゾーンをリダイレクトする。
これにinit.push.apple.com.
等が巻き込まれないためにtransparent
で除外する。
これにより*push.apple.comはlocal-dataの内容だけを応答する。後述するが、IPv4接続性が必要でもtypetransparent
は適合しない。- APNsへのIPv4接続性が必要な場合、このlocal-zoneの応答をIPv6接続性のあるaccess-control-tagに限定する。
typetransparent
やCNAMEにredirect
するとIPv4を優先してしまうので、AAAAをハードコーディングした。 CNAMEがある場合、それ以外のレコードは存在してはならないため、空のAを追加することはできない。 RPZとblock_a
を組み合わせる方法もあるが、- いずれにせよ
apac-asia-courier-vs.push-apple.com.akadns.net.
にCNAMEを向けるのは、APACのCDNをランダムに応答するためかえって遅延が大きくなる。
IPinfo.ioでのregion確認、SecurityTrailsで別リージョンの発見行った。nmapの計測では東西を跨がない方が遅延が小さかった。
# 2403:300:a42:900::/56 Tokyo
nmap -6 -Pn -p 5223 2403:300:a42:900::6 # apac-china-courier-vs.
nmap -6 -Pn -p 5223 2403:300:a42:900::8 # apac-china-courier-vs.
nmap -6 -Pn -p 5223 2403:300:a42:901::6
nmap -6 -Pn -p 5223 2403:300:a42:901::13
nmap -6 -Pn -p 5223 2403:300:a42:902::6
nmap -6 -Pn -p 5223 2403:300:a42:902::13
nmap -6 -Pn -p 5223 2403:300:a42:903::6
nmap -6 -Pn -p 5223 2403:300:a42:903::15
# 2403:300:a42:200::/56 Osaka
nmap -6 -Pn -p 5223 2403:300:a42:20c::6
nmap -6 -Pn -p 5223 2403:300:a42:20c::7
# 2403:300:a42:b00::/56 apac-taiwan-courier-vs.
nmap -6 -Pn -p 5223 2403:300:a42:b0c::6
# 2403:300:a42:c00::/56 Singapore
nmap -6 -Pn -p 5223 2403:300:a42:c0a::6
APNsホストはpingに応答しないが、当然TCPはリッスンしているのでRTTを計測できる。nmapならWindows 10のWSL1でも動く。
apac-asia-courier-vs.
に含まれる最も遅延が小さいapac-china-courier-vs.
は、やんごとなき理由か5223/tcpでAPNsに繋がらなかったため、除外した。- 調査の副産物で、
apac-china-courier-vs.
への5223/tcpが失敗且つAがNODATAだと443/tcpにフォールバックするが、このように空のlocal-data(ANSWER: 1)で応答すると5223/tcpを試行し続けた。
未知の要因で発生する443/tcpへのフォールバックを回避するのに使えるが、5223/tcpが壊れ気味なのでおすすめしない。
local-data: "push.apple.com. 60 IN A"
任意の回線をスピードテストする 見出しにジャンプ
Webのスピードテストは、IPv4/IPv6どちらをテストしたか不明瞭だったり、選べないことが多い。
そんなときはDNSで振り分けるのが簡単だ。Speedtest by OoklaはIPv6計測専用にしたいし、Wi-FiミレルはIPv4計測用に使いたかった。
server:
local-zone: "prod.hosts.ooklaserver.net." block_a
local-zone: "wifimireru.inonius.net" typetransparent
local-data: "wifimireru.inonius.net 3600 IN AAAA"
Minecraft 見出しにジャンプ
Minecraft Launcherの高速化 見出しにジャンプ
IPv4を優先したりするので。突然ランチャーアップデートが来ても、一秒でも早くゲームに参加したい。
server:
local-zone: "redstone-launcher.mojang.com." block_a
local-zone: "api.mojang.com." block_a
local-zone: "pc.realms.minecraft.net." block_a
local-zone: "textures.minecraft.net." block_a
Minecraftサーバの管理者にメッセージを送る 見出しにジャンプ
Minecraftサーバへの接続は、SRV, A, AAAAの順で試行される。接続に使われたSRVレコードの内容はサーバ側に伝わる。
Protocol - wiki.vg
例えば、下記のように設定をするとmc.example.net.
への接続にmake-peace-with.cat.
を利用でき、猫と和解するよう相手に要求できる。管理者がコネクション確立時のログを読んでいれば、の話だが。
server:
local-zone: "cat." static
local-data: "_minecraft._tcp.make-peace-with.cat. 60 IN SRV 0 0 25565 mc.example.net."
DoT downstream 見出しにジャンプ
Unbound <--> キャッシュDNS間のupstreamにDo53を使っているにも拘らず、端末 <--> Unbound間のdownstreamにDoTを使う馬鹿みたいな設定を行った。
Do53はキャッシュポイズニングの軽減にポートランダマイゼーションを行うが、フルトラストネットワークでは無駄なオーバーヘッドでしかないからだ。
DoTはApple macOS 11+/iOS 14+、Android 9+、Windows 11と主にOSの対応が主なのに対し、DoHはブラウザでの対応が中心だから、自動設定に重きを置くならDoT、パフォーマンスに重きを置くならDoHになるだろう。
実際のところ、例えばApple iOSが繰り出すDo53クエリは、リゾルバへのDoSではないかと思うほど高速だ。主に非同期なURLSessionを使っているためと思われる。それでも、Appleデバイスは通常のHappy Eyeballs比で1.5 - 3倍のクエリを発するため、暗号化のオーバーヘッドを超えるかも分からんな、程度のモチベーションだ。
server:
interface: 10.0.3.5@853
port: 853
tcp-idle-timeout: 120000
edns-tcp-keepalive: yes
# edns-tcp-keepalive-timeout: 120000
# unbound-control-setup で生成した自己署名証明書
tls-service-key: "/usr/local/etc/unbound/unbound_server.key"
tls-service-pem: "/usr/local/etc/unbound/unbound_server.pem"
tls-port: 853
tcp-idle-timeout
はdownstreamにも有効らしく、頻繫にコネクションを張りなおしていては意味がないので2分(2MSL)に設定した。edns-tcp-keepalive: yes
時はedns-tcp-keepalive-timeout
を優先するとか。
Reduce number of TLS connections to forwarded (DoT) when using "forward-tls-upstream" · Issue #47 · NLnetLabs/unbound
# WSL2
dig @10.0.3.5 www.google.com aaaa +tls
DDR 見出しにジャンプ
Do53をListenするリゾルバを端末に自動設定するなら、多くのOSが対応しているDHCPやDHCPv6, RAオプションが使用できる。
ただし、DoTやDoHはIPアドレスだけでなくホスト名や接続方式、ポート番号、DoHなら更にパスの情報を用いて接続するため、端末が_dns.resolver.arpa. IN SVCB
をクエリすることで機能するDDRと、DHCP/DHCPv6オプションで通知するDNRを使う。
RFC 9462 - Discovery of Designated Resolvers
RFC 9463 - DHCP and Router Advertisement Options for the Discovery of Network-designated Resolvers (DNR)
Apple macOS 13+/iOS 16+はDDRやMDMによるDoT/DoHの自動設定に対応している。後述の構成プロファイルで暗号化リゾルバを手動設定できるが、Do53はできない。
AppとサーバのDNSセキュリティの改善 - WWDC22 - ビデオ - Apple Developer
Android 9以降はOpportunistic Discovery(Do53リゾルバのアドレスにDoTを試行)&日和見暗号化する。
DNS over HTTPS/TLS (DoH/DoT)の設定方法 | IIJ Engineers Blog
RubyKaigi 2023でのセキュアなDNSリゾルバの運用 (DNSOPS.JP BoF IW2023) - 20231121-hanazuki.pdf
Windows 11はDDRはBuild 22489で正式対応、DNRはBuild 25982以降で対応する。
Introducing DNR support for Windows Insiders
Unboundが管理するのはDDRの方だ。
server:
local-zone: '_dns.resolver.arpa.' static
local-zone-tag: '_dns.resolver.arpa.' windows
local-data: '_dns.resolver.arpa. 60 IN SVCB 1 dot.example.com. alpn="dot" port=853 ipv4hint=10.0.3.5'
# DNS rebinding対策から除外
local-zone: 'dot.example.com' static
local-data: 'dot.example.com 300 IN A 10.0.3.5'
# certbot
tls-service-key: "/etc/letsencrypt/live/dot.example.com/privkey.pem"
tls-service-pem: "/etc/letsencrypt/live/dot.example.com/fullchain.pem"
- プライベートアドレス範囲の応答はDNS rebinding対策で潰しているため、通常の名前解決は行わずにlocal-zoneで静的に設定した。
dig +tls
は自己署名証明書でも通ったが、後述の構成プロファイルの挙動からAppleがVerified Discoveryのみ対応しているとみて、正規の証明書を設定した。
certbot certonly --manual --preferred-challenges dns -d dot.example.com -m [email protected]
openssl s_client -connect 10.0.3.5:853 | openssl x509 -noout -text | grep -A1 'Subject Alternative Name'
# X509v3 Subject Alternative Name:
# DNS:dot.example.com
とりあえずcertbotでSSL証明書を発行する #Let’sEncrypt - Qiita
- Appleデバイス(iOS 17.6.1時点)は、DDRを試行するものの下記エラーが出てDoT接続が持続しなかった。自己署名かは関係なかった。
[1723521202] unbound[6426:1] error: ssl handshake failed crypto error:0A000416:SSL routines::ssl/tls alert certificate unknown
SSL handshake failed · Issue #561 · NLnetLabs/unbound
- Appleデバイスは、構成プロファイルでのDoT/DoH設定に対応しており、DDRと違って機能した。
DNSSettings | Apple Developer Documentation
下記の例では、OnDemandRules
によって既知のWi-Fi接続時にプライベートなDoTサーバへ接続する設定になっている。これにより同じくOnDemandRules
で自動接続するVPNプロファイルと協調して動作する。
ユニークなPayloadUUID
を指定する。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>DNSSettings</key>
<dict>
<key>DNSProtocol</key>
<string>TLS</string>
<key>ServerAddresses</key>
<array>
<string>10.0.3.5</string>
</array>
<key>ServerName</key>
<string>dot.example.com</string>
</dict>
<key>OnDemandRules</key>
<array>
<dict>
<key>InterfaceTypeMatch</key>
<string>WiFi</string>
<key>SSIDMatch</key>
<array>
<string>ssid1</string>
<string>ssid2</string>
</array>
<key>Action</key>
<string>Connect</string>
</dict>
<dict>
<key>Action</key>
<string>Disconnect</string>
</dict>
</array>
<key>PayloadDescription</key>
<string>com.example.dotpayload</string>
<key>PayloadDisplayName</key>
<string>com.example.dotpayload</string>
<key>PayloadIdentifier</key>
<string>com.example.dotpayload</string>
<key>PayloadType</key>
<string>com.apple.dnsSettings.managed</string>
<key>PayloadUUID</key>
<string>6d16eda2-3a25-4db1-8f51-5c7ea915a7f1</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</array>
<key>PayloadDisplayName</key>
<string>Unbound DoT</string>
<key>PayloadIdentifier</key>
<string>com.example.dot</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadUUID</key>
<string>703ddb36-2a72-46f3-b566-2bd1970e4b5b</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</plist>