自己從頭搞整套 Pi-hole 方案 (DNS 阻擋廣告的方案)

如果不用 Pi-hole 這種套件的話,從頭自己搞差不多就是這樣:「Ads blocking with OpenBSD unbound(8)」。

作者除了阻擋的必要功能部份以外,還把 log 導出來丟進 InfluxDB,透過 Grafana 可以看狀態,這類似於 Pi-hole 提供的方案:

Grafana to render the statistics ;
InfluxDB to store the information ;
syslogd(8) and awk(1) to turn DNS queries into statistics ;
collectd(1) and shell script to store unbound statistics and logs ;
unbound(8) and shell script to get and block DNS queries.

對應的 diagram 長這樣 (但為什麼作者要用 comic sans 呢...):

瀏覽器可以用 uBlock Origin 這類方案來做,可以擋的更細緻,而手機 app 一般就只能靠這種方法過濾掉部份的廣告。

如果想要擋更多的話 (像是只擋某個 url,而不是整個 domain),得用自建的 root CA 加上 MITM 的方式攔 HTTPS 連線,這通常都是在手機上面跑 virtual VPN,像是 iOS 上的 Surge 5 或是 Quantumult X

AWS 台北區的網路狀況 (Routing & CDN 的情況)

在「目前 AWS 台北區只能開 *.2xlarge 的機器」這邊把機器開起來了,所以先測一下 AWS 台北區對台灣各家的 ISP 的網路狀況。

先看台灣內的點,看起來都有 peering,用 IP 測可以看到 latency 都很低:

再來試看海外 internet 的部份,美國蠻多點是從東京 AWS 過去,但測了香港的部份 www.three.com.hk,是從 TPIX 換出去,看起來台灣這邊也有一些出口,peering 與 transit 目前沒看到大問題。

但幾乎所有透過 GeoDNS-based 的查詢都會被丟到東京:

走 anycast 的 Cloudflare 就好不少,像是付費版本的 www.plurk.com 就是台北的 PoP,而免費版本的 wiki.gslin.org 也會丟到亞洲的某個點上?(看不出來是不是東京,出現 jtha 這個有點像是日本,但也有可能是泰國的點?)

這應該主要還是因為這段 IP 目前還是被認到東京的 ap-northeast-1 上,得等各家調整才有機會放到台灣的 PoP 上,不然就是要故意用沒有 EDNS Client Subnet 的 DNS resolver 了。

Route 53 提供 100% SLA,而 Route 53 Resolver 提供 99.99% SLA

AWS 宣佈 Route 53 提供 100% 的 SLA,而 Route 53 Resolver 則提供 99.99% 的 SLA:「Amazon Route 53 Resolver Endpoints announces 99.99% Service Level Agreement and updates its Service Level Agreement for Route 53 hosted zones」。

Route 53 的部份指的是 hosted zone,參考「Amazon Route 53 Service Level Agreement」這邊,可以看到是 2022/08/29 更新的條款。

Route 53 Resolver 的部份可以參考「Amazon Route 53 Resolver Endpoints Service Level Agreement」這邊,也是 2022/08/29 更新的條款。

要注意 99.99% 是指 Multi-AZ Resolver Endpoints SLA;如果是 Single-AZ Resolver Endpoints SLA 看起來是設定在 99.5%。

不確定是不是 AWS 的第一個 100% SLA 條款...

用 dig 查瑞士的 top domain 剛好會遇到的 "feature"

Hacker News 上看到「DNS Esoterica - Why you can't dig Switzerland」這篇,裡面提到 dig 的 "feature"。

拿來查 tw 的 NS 會這樣下:

$ dig tw ns

結果會是列出所有的 NS server:

;; ANSWER SECTION:
tw.                     3600    IN      NS      h.dns.tw.
tw.                     3600    IN      NS      a.dns.tw.
tw.                     3600    IN      NS      g.dns.tw.
tw.                     3600    IN      NS      d.dns.tw.
tw.                     3600    IN      NS      anytld.apnic.net.
tw.                     3600    IN      NS      f.dns.tw.
tw.                     3600    IN      NS      b.dns.tw.
tw.                     3600    IN      NS      e.dns.tw.
tw.                     3600    IN      NS      c.dns.tw.
tw.                     3600    IN      NS      ns.twnic.net.

照著作者說的,ukdig uk ns 可以得到類似的結果:

;; ANSWER SECTION:
uk.                     86400   IN      NS      dns1.nic.uk.
uk.                     86400   IN      NS      dns4.nic.uk.
uk.                     86400   IN      NS      nsa.nic.uk.
uk.                     86400   IN      NS      nsb.nic.uk.
uk.                     86400   IN      NS      nsc.nic.uk.
uk.                     86400   IN      NS      nsd.nic.uk.
uk.                     86400   IN      NS      dns3.nic.uk.
uk.                     86400   IN      NS      dns2.nic.uk.

但如果你下 dig ch ns 就會出現錯誤,像是這樣:

; <<>> DiG 9.16.1-Ubuntu <<>> ch ns
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 5019
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;.                              CH      NS

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Fri Jul 15 06:54:24 CST 2022
;; MSG SIZE  rcvd: 28

原因是因為 CH 這個關鍵字是 Chaosnet 的縮寫,而被特殊解讀:

Set the query class. The default class is IN; other classes are HS for Hesiod records or CH for Chaosnet records.

要避開這個解讀需要加上一個 dot (.),採用 FQDN 的方式列出:

dig ch. ns

就會得到正確的結果:

;; ANSWER SECTION:
ch.                     86400   IN      NS      a.nic.ch.
ch.                     86400   IN      NS      b.nic.ch.
ch.                     86400   IN      NS      f.nic.ch.
ch.                     86400   IN      NS      d.nic.ch.
ch.                     86400   IN      NS      e.nic.ch.

另外的方式是 dig -c IN -t NS ch,透過參數的方式讓 dig 不會誤會。

Ptt 信件伺服器 node.ptt.cc 查不到反解的問題

就如同標題所說的,Ptt 對外寄信的伺服器 node.ptt.cc 查不到反解的問題:

$ host node.ptt.cc
node.ptt.cc has address 140.112.172.16
$ host 140.112.172.16
Host 16.172.112.140.in-addr.arpa not found: 2(SERVFAIL)

情況大概是這樣,Ptt 使用的 140.112.172.0/27 這個網段 (尾碼從 0~31) 不是 /24 以上的範圍,而 140.112.172.0/24 是台大計中管轄範圍,所以台大就把 140.112.172.x 這段的反解 PTR record 用 CNAME 的方式指到 x.0-31.172.112.140.in-addr.arpa,像是 140.112.172.16 這樣:

;; ANSWER SECTION:
16.172.112.140.in-addr.arpa. 86400 IN   CNAME   16.0-31.172.112.140.in-addr.arpa.

然後再針對 0-31.172.112.140.in-addr.arpa 設定 NS RR 到 ns0.ptt.ccns1.ptt.cc 兩台 NS server:

;; AUTHORITY SECTION:
0-31.172.112.140.in-addr.arpa. 86400 IN NS      ns1.ptt.cc.
0-31.172.112.140.in-addr.arpa. 86400 IN NS      ns0.ptt.cc.

但是 ns0.ptt.ccns1.ptt.cc 都不見了:

$ host ns0.ptt.cc
Host ns0.ptt.cc not found: 3(NXDOMAIN)
$ host ns1.ptt.cc
Host ns1.ptt.cc not found: 3(NXDOMAIN)

導致反解查不到對應的資料 (會是 SERVFAIL):

;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 58463

猜了一下,看起來 ns0.ptt.cc 還活著,只是 Cloudflare 上面的 DNS record 沒設定過去:

$ dig ns0.ptt.cc @140.112.172.16

;; ANSWER SECTION:
ns0.ptt.cc.             300     IN      A       140.112.172.16

不過 ns1.ptt.cc (140.112.172.10) 看起來就沒服務了,但至少在 Cloudflare 上補個 DNS record 上去應該就會動了 (只是沒有兩台互相備援)。

Curl 的 --socks5 與 --socks5-hostname

SOCKS5 支援用 hostname 連線,而且是透過 SOCKS server 查 IP address,然後查資料的時候翻到「Curl: Re: ��: Why resolve the dns locally when using a socks5 proxy」這篇,發現 Curl--socks5 原來有雷,一般人會想要用的應該是 --socks5-hostname 才對 XDDD

這是 --socks5

Use the specified SOCKS5 proxy - but resolve the host name locally. If the port number is not specified, it is assumed at port 1080.

而這是 --socks5-hostname

Use the specified SOCKS5 proxy (and let the proxy resolve the host name). If the port number is not specified, it is assumed at port 1080.

另外也有 --socks4--socks4a,不過 SOCKS4a 的協定最主要就是加上 hostname 的連線:

SOCKS4a extends the SOCKS4 protocol to allow a client to specify a destination domain name rather than an IP address; this is useful when the client itself cannot resolve the destination host's domain name to an IP address.

然後隔壁棚的 socks5://socks5h:// 也有異曲同工之妙...

Akamai Shared Domains 加入 PSL (Public Suffix List)

Akamai 把自家的 shared domains 申請加入 PSL (Public Suffix List):「Adding Akamai Shared Domains to the Public Suffix List」。

提到 PSL,常被拿來舉例的應該就是 supercookie 了,也就是把 cookie 的有效網域設到 .com 或是 .org 這種 top level domain,這樣就可以跨很多站台追蹤使用者了 (所以被稱為 supercookie),而 PSL 則可以被拿來限制這些網域名稱。

而在 Akamai 的例子來說,edgekey.net 下面的使用者都會共用 cookie,對於安全與隱私的考量其實不太好。這次把這些網域加到 PSL 之後,變成 edgekey.net 這層無法設定 cookie,而 one.edgekey.nettwo.edgekey.net 各自有自己的 cookie namespace,這樣就好一些了...

順帶一提,除了瀏覽器會引入 PSL 來過濾外,使用者端可以靠 Privacy Badge 來過濾掉這類的 cookie,因為 Privacy Badge 會針對這類網域清掉 cookie 再送出 HTTP request。

Akamai 的文章裡面也有提到這件事情:

The PSL contains multi-party domain suffixes and is used by a wide range of client software (for example, web browsers) to implement policy decisions, such as to prevent cookies from being set on public or multi-party domains.

透過 DNS 擋 Smart TV 的廣告

Hacker News Daily 上看到「Smart-TV Blocklist for Pi-hole」這個,給 Pi-hole 吃的 blocklist,可以拿來擋 Smart TV 抓一堆廣告亂搞,另外很多自己刷的 WRT 也都支援這個格式,不一定要自己弄 Pi-hole,像是我家裡的 AdvancedTomato 也有支援:

本來馬上想到的是 AdGuard 提供的 DNS 94.140.14.14,結果掃了一下發現都沒有擋掉,難怪會需要另外自己搞...

另外一個想到的搞法是 NextDNS,但 IPv4 的部份要固定 IP 才有辦法 (像是 HiNet 提供的 PPPoE 固一),免費版的 quota 拿來掛 Smart TV 應該夠用...

Hacker News 上的討論在「Smart-TV blocklist for Pi-Hole (perflyst.github.io)」這邊,這年頭要弄台不連網的電視機愈來愈難了...

Route 53 支援 DNS64,以及 NAT Gateway 支援 NAT64

AWS 宣佈了一套機制,讓 IPv6-only 的機器可以連到 IPv4-only 的服務:「Let Your IPv6-only Workloads Connect to IPv4 Services」。

首先是 DNS64,針對只有 IPv4-only 的 A record 自動加上 AAAA record (如果已經有 AAAA record 的則不變),這邊提到的 64:ff9b::/96 是來自 DNS64 標準內的規範:

The DNS resolver first checks if the record contains an IPv6 address (AAAA record). If it does, the IPv6 address is returned. The IPv6 host can connect to the service using just IPv6. When the record only contains an IPv4 address, the Route 53 resolver synthesizes an IPv6 address by prepending the well-known 64:ff9b::/96 prefix to the IPv4 address.

再來就是 NAT Gateway 可以把 64:ff9b::/96 透過 NAT64 轉到 IPv4 network 上:

You may configure subnet routing to send all packets starting with 64:ff9b::/96 to the NAT gateway. The NAT gateway recognizes the IPv6 address prefix, extracts the IPv4 address from it, and initiates an IPv4 connection to the destination. As usual, the source IPv4 address is the IPv4 address of the NAT gateway itself.

由於有些 protocol 會帶 IP address 資訊,所以不能保證 NAT64 一定會動,但大多數的情況應該是可以解決,至少提供了 IPv6-only server 連到 IPv4-only network 上的方法...

自動偵測 DNS over HTTPS 或是 DNS over TLS 的 Discovery of Designated Resolvers (DDR)

看到 Cloudflare 宣佈支援 Discovery of Designated Resolvers (DDR):「Announcing experimental DDR in 1.1.1.1」。

看了一下這以後會是新的標準 (現在還在 draft),可以自動偵測現在用的 DNS resolver 是否支援 DNS over HTTPS 或是 DNS over TLS,如果有的話就 upgrade 到 DoH 或 DoT:

This document defines Discovery of Designated Resolvers (DDR), a mechanism for DNS clients to use DNS records to discover a resolver's encrypted DNS configuration.

目前的 spec 是去查 _dns.resolver.arpa,下面第一個是支援 DoH 的回應,第二個則是支援 DoT 的回應:

_dns.resolver.arpa  7200  IN SVCB 1 doh.example.net ( alpn=h2 dohpath=/dns-query{?dns} )
_dns.example.net  7200  IN SVCB 1 dot.example.net ( alpn=dot port=8530 )

實際上拿 1.1.1.1 測試,在 Ubuntu 20.04 下用 dig 可以看到一堆亂碼 XDDD

;; ANSWER SECTION:
_dns.resolver.arpa.     300     IN      TYPE64  \# 104 0001036F6E65036F6E65036F6E65036F6E6500000100060268320268 330003000201BB000400080101010101000001000600202606470047 00000000000000000011112606470047000000000000000000100100 0700112F646E732D71756572797B3F6E616D657D
_dns.resolver.arpa.     300     IN      TYPE64  \# 81 0002036F6E65036F6E65036F6E65036F6E65000001000403646F7400 03000203550004000801010101010000010006002026064700470000 00000000000000111126064700470000000000000000001001

改用 Docker 開了 Ubuntu 22.04 (jammy) 的 image 後,裡面的 dig 可以用 SVCB 查詢 (本來的 type64 也還是會動),另外也把內容解出來了:

;; ANSWER SECTION:
_dns.resolver.arpa.     300     IN      SVCB    1 one.one.one.one. alpn="h2,h3" port=443 ipv4hint=1.1.1.1,1.0.0.1 ipv6hint=2606:4700:4700::1111,2606:4700:4700::1001 key7="/dns-query{?name}"
_dns.resolver.arpa.     300     IN      SVCB    2 one.one.one.one. alpn="dot" port=853 ipv4hint=1.1.1.1,1.0.0.1 ipv6hint=2606:4700:4700::1111,2606:4700:4700::1001

這樣就不需要寫死在系統內,但要注意這樣還是會有 attacker 擋住讓你 upgrade 的問題...