自建DNS与广告过滤-adguardhome与pihole
步骤/目录:
1.背景介绍
2.adguardhome
(1)安装
(2)配置
3.pihole
(1)安装
(2)配置
附录:
(1)macvlan
(2)macvlan下的网段
(3)macvlan的其它操作
(4)pihole配置详解
本文首发于个人博客https://lisper517.top/index.php/archives/476/
,转载请注明出处。
本文的目的是介绍自建DNS、过滤广告的两款软件。
本文写作日期为2024年1月29日,在Ubuntu 22.04LTS系统演示。
1.背景介绍
在用电脑、手机平板浏览网页时,经常能看到网页上各种广告。这些广告最常见的形式是一些图片或视频,而图片和视频资源一般是通过网址加载的。如果在路由器中屏蔽这些广告网址,就能实现去广告。
路由器默认设置下,会自动选择本地运营商的DNS,而运营商有时候会劫持DNS,情节轻时在返回的内容中加上广告,严重时则返回错误的内容。在这种情况下也有必要自建DNS。
2.adguardhome
adguardhome最初应该是国人做的一款去广告DNS。
(1)安装
根据其 docker网址 的引导,在Ubuntu上如下操作:
mkdir -p /docker/adguardhome/data/adguardhome
mkdir -p /docker/adguardhome/conf/adguardhome
cd /docker/adguardhome
vim docker-compose.yml
写入如下内容:
version: '3.8'
services:
adguardhome:
image: 'adguard/adguardhome'
container_name: adguardhome
ports:
- '53:53' #DNS端口
- '3000:3000' #网页UI,初始配置端口
- '8500:80' #网页UI,后续配置端口
volumes:
- ./data/adguardhome:/opt/adguardhome/work
- ./conf/adguardhome:/opt/adguardhome/conf
restart: unless-stopped
以上设置仅将adguardhome作为DNS服务器使用,其实它还可用用来做DHCP服务器(动态分配内网ip)、HTTPS/DNS-over-HTTPS服务器(DNS的过程用HTTPS加密)、DNS-over-TLS服务器(用TLS加密DNS)、DNS-over-QUIC服务器(QUIC加密DNS)、DNSCrypt服务器(不太清楚,应该也是加密),详见原文,需要注意的是HTTPS、TLS、QUIC好像都需要公网ip+域名+HTTPS证书(所以需要在云服务器部署adguardhome),而DNSCrypt虽不需要证书、却需要客户端(电脑,手机平板等使用adguardhome进行DNS的设备)配置软件。
另外需要注意,3000端口是adguardhome初始配置的端口,后续WebUI则会在80端口,已被占用的话可以改到其它端口(此处改到8500端口)。
最后打开端口,运行即可(其实docker会自动打开,笔者用ufw是为了方便查看):
ufw allow 53 comment "DNS-adguardhome"
ufw allow 3000 comment "WebUI-adguardhome-initial"
ufw allow 8500 comment "WebUI-adguardhome"
docker-compose config
docker-compose up
如果提示53端口被占用,可以把53改到其它端口,前提是你的路由器支持DNS服务器使用非53端口;或者使用笔者更推荐的macvlan,见附录。
(2)配置
访问 本地ip:3000
端口,设置登录用户名密码,其它一路默认,完成初始化配置;然后到8500端口进行进一步配置。在adguardhome配置前,或者配置完成后,到你的路由器管理页面,将ipv4的DNS改到adguardhome的ipv4地址。
以下配置参考了 这篇文章 。
a.DNS服务器
在 设置 - DNS设置 - 上游 DNS 服务器
中,填入cloudflare和google的DNS:
1.1.1.1
1.0.0.1
2606:4700:4700::1111
2606:4700:4700::1001
8.8.8.8
8.8.4.4
2001:4860:4860::8888
2001:4860:4860::8844
后备 DNS 服务器
使用adguardhome提供的DNS:
94.140.14.14
94.140.15.15
2a10:50c0::ad1:ff
2a10:50c0::ad2:ff
笔者实验时用不了ipv6的DNS,这是因为docker没有给adguardhome容器分配ipv6地址,自然也用不了ipv6。开启的方法见 参考文章一 (讲解较乱)或者 参考文章二 (使用macvlan,见附录)。
b.DNS 黑名单
在 过滤器 - DNS 黑名单
里,添加DNS过滤规则。有人整理出来某些广告域名,添加到黑名单后adguard就会拦截这些请求。可以添加自带的,也可以自己找一些黑名单,比如 配置参考文章 中提供的:
halflife
https://raw.githubusercontent.com/o0HalfLife0o/list/master/ad.txt
anti-AD
https://anti-ad.net/easylist.txt
neoHosts
https://cdn.jsdelivr.net/gh/neoFelhz/neohosts@gh-pages/full/hosts.txt
大圣净化 - 针对国内视频网站
https://raw.githubusercontent.com/jdlingyu/ad-wars/master/hosts
adgk手机去广告规则
https://raw.githubusercontent.com/banbendalao/ADgk/master/ADgk.txt
广告终结者
http://sub.adtchrome.com/adt-chinalist-easylist.txt
Adbyby
https://raw.githubusercontent.com/adbyby/xwhyc-rules/master/lazy.txt
EasyList China+EasyList
https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt
EasyPrivacy
https://easylist-downloads.adblockplus.org/easyprivacy.txt
不用太多,只添加一两个即可,太多了容易误杀,也拖慢DNS速度。
c.DNS 白名单
为了防止错杀,还可以添加白名单,白名单的优先级高于黑名单。比如google analytics就容易被错杀。
anti-ad白名单
https://raw.githubusercontent.com/privacy-protection-tools/dead-horse/master/anti-ad-white-list.txt
filter_whitelist
https://raw.githubusercontent.com/hl2guide/Filterlist-for-AdGuard/master/filter_whitelist.txt
LWJ's white list
https://raw.githubusercontent.com/liwenjie119/adg-rules/master/white.txt
DNS允许白名单
https://raw.githubusercontent.com/ChengJi-e/AFDNS/master/QD.txt
白名单也不需要太多。
如果正常的网页加载不出来,到adguardhome的后台查看是否误杀,手动加白名单。
d.DNS 重写
如果想通过域名访问一些局域网ip,可以在这里配置。一些路由器如AX9000也可以在路由器里配置(小米路由器需要在同名app配置,找到hosts设置)。
e.自定义过滤规则
一些网站的广告需要其它设置才能去除,比如油管的视频广告(笔者未验证):
/googleads.$~script,domain=~googleads.github.io
/pagead/lvz?
||google.com/pagead/
||static.doubleclick.net^$domain=youtube.com
||youtube.com/get_midroll_
||safebrowsing.googleapis-cn.com
||doubleclick.net
自定义规则需要有一定的网络基础才能玩的转,至少要会用chrome的开发者模式,或者也可以搜索别人的规则试试。
3.pihole
pihole最初是给树莓派做的广告拦截,但是也能用于其它电脑。pihole没有中文界面,需要的可自行找汉化版,但是笔者还是更推荐英文原版。
(1)安装
根据 docker-pihole 的介绍,在 Ubuntu 22.04 LTS 上操作(同样使用macvlan的方法)。
创建docker-net(之前创建过的话这里就不用建了):
docker network create -d macvlan \
--subnet=192.168.31.0/24 \
--gateway=192.168.31.1 \
--ipv6 \
--subnet=fe80:7eb5:2afd::/64 \
--gateway=fe80:7eb5:2afd::1 \
-o parent=eth0 \
macvlan_net
然后是熟悉的步骤:
mkdir -p /docker/pihole/etc-pihole
mkdir -p /docker/pihole/etc-dnsmasq.d
cd /docker/pihole
vim docker-compose.yml
写入如下内容:
version: "3"
services:
pihole:
container_name: pihole
image: pihole/pihole:latest
environment:
TZ: 'Asia/Shanghai'
WEBPASSWORD: '123456' #网页密码
volumes:
- './etc-pihole:/etc/pihole'
- './etc-dnsmasq.d:/etc/dnsmasq.d'
restart: unless-stopped
networks:
macvlan_net:
ipv4_address: 192.168.31.31
ipv6_address: fe80:7eb5:2afd::31
networks:
macvlan_net:
external: true
运行:
docker-compose config
docker-compose up
然后到 [fe80:7eb5:2afd::31]/admin
或者 192.168.31.31/admin
登录,密码为yml中设置的密码。
后面如果把pihole作为DNS,则也能从 http://pi.hole/admin
登录。如果忘记或者想更改登录密码,可以 docker exec -it pihole pihole -a -p
(或者进入宿主机后 pihole -a -p
)。
(2)配置
pihole没有中文界面,配置起来稍微难一点。pihole配置中的高级选项会在附录中解释。另外,目前的pihole管理白名单不是很方便,大概是21年某次更新之前的网页UI还有黑白名单的设计,现在则是用groups代替了。
在 Adlists
里,可以添加别人的屏蔽列表。添加完后, docker exec -it pihole pihole -g
来更新列表;或者到 Tools - Update Gravity
里更新。黑名单在这里添加,白名单见后;
在 Settings - DNS
里,把Google、OpenDNS、Cloudflare的IPv4、IPv6的DNS服务器都勾选上。旁边也可以自己填;
下方有一个 Rate-limiting
,默认应该是某个客户端在60s内请求1000次DNS时就封禁该客户端。玩多线程爬虫的话会有影响;或者用路由器作为pihole的客户端也容易被封。都设置为0可关闭该功能。
其它配置见附录。
附录:
(1)macvlan
macvlan是一种网络虚拟化技术,可以用同一张物理网卡获得不同的局域网ip。在docker中可以调用macvlan,将容器作为一个单独设备、分配局域网ip,使得局域网内的机器(除了宿主机)几乎都能通过局域网ip来访问容器。比如将adguardhome绑定到 192.168.1.30
,那么 192.168.1.30:80
、 192.168.1.30:3000
就分别变成了adguardhome的配置页面、初次配置页面。由于还要使用ipv6访问adguardhome,也需要给它一个v6地址。
回到adguardhome容器启动前,如下设置(参考之前提到的 参考文章二 和之前未提到的 参考文章三 ,后者讲的比较详细):
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
--ipv6 \
--subnet=fe80:7eb5:2afd::/64 \
--gateway=fe80:7eb5:2afd::1 \
-o parent=eth0 \
macvlan_net
-d macvlan
即 --driver macvlan
,指定docker-net的驱动。最后的 macvlan_net
是docker-net名;
ipv4网关为 192.168.1.1
,网段为 192.168.1.0/24
。由于笔者使用小米路由器,网关和网段都要改到 192.168.31
下;
ipv6网关为 fe80:7eb5:2afd::1
, 网段为 fe80:7eb5:2afd::/64
。这个 fe80:7eb5:2afd
局域网v6地址可以按 fe80:xxxx:xxxx
的格式随便写;
指定物理网卡为 eth0
对应的网卡。如果网卡不存在,在linux上用 ifconfig
查看物理网卡,局域网ip对应的那张,类似 enp2s0f1
、 en0
、 ovs_eth0
这种。
另外需要注意,macvlan是独立于docker的,docker需要调用macvlan进行网络虚拟化,所以你需要先安装macvlan包。一般linux发行版是自带的,少数情况则需要手动下载,比如用 树莓派4B + Ubuntu21.10系统 时需要 apt install linux-modules-extra-raspi + reboot
(见 reddit提问 );或者有些openwrt系统下,需要 opkg update && opkg install kmod-macvlan
。
创建完docker-net后,将yml文件更改为:
version: "3"
services:
adguardhome:
image: adguard/adguardhome
container_name: adguardhome
volumes:
- ./data/adguardhome:/opt/adguardhome/work
- ./conf/adguardhome:/opt/adguardhome/conf
restart: unless-stopped
networks:
macvlan_net:
ipv4_address: 192.168.1.30 #用小米路由器则改成192.168.31.30
ipv6_address: fe80:7eb5:2afd::30
networks:
macvlan_net:
external: true
这里没有端口映射的设置。通过以上步骤,已经给容器组中的adguardhome容器分配了v4、v6地址,访问 192.168.1.30
或者 [fe80:7eb5:2afd::30]
的3000、80端口都能设置adguardhome,也可以正常访问ipv6的DNS。设置完adguardhome后,在路由器管理页把v4和v6的DNS分别改到 192.168.1.30
和 [fe80:7eb5:2afd::30]
即可。
必须尝试通过 [fe80:7eb5:2afd::30]:3000
或者 [fe80:7eb5:2afd::30]:80
访问adguardhome。如果不行,可以看下面的方法。
(2)macvlan下的网段
无法从路由器或者局域网的其它机器内访问 [fe80:7eb5:2afd::30]
,可能是因为网段不对。在windows的cmd中 ipconfig
看 默认网关
,或者linux下 ip -6 route show
最后一行 default via ...
确认一下ipv6的网关。如果网关以其它开头,比如不是 fe80
而是 f123
,那么就把上文教程中所有的 fe80
换成 f123
即可。当然,一般网关都是 fe80
开头的,因为 fe80
默认是内网v6,类似于 192.168
在v4中的地位。
如果不能通过以上方法确认v6网关,你也可以进入容器:
docker exec -it adguardhome sh
ifconfig
其部分输出内容为:
eth0 Link encap:Ethernet HWaddr XX:XX:XX:XX:XX:XX
inet addr:192.168.1.30 Bcast:192.168.1.255 Mask:255.255.255.0
inet6 addr: fe80::xx:xxxx:xxxx:xxxx/64 Scope:Link
inet6 addr: fe80:7eb5:2afd::30/64 Scope:Global
inet6 addr: 240e:xxx:xxx:xx:xx:xxxx:xxxx:xxxx/64 Scope:Global
...
这里出现了3个v6地址,它们的顺序可能调换。
第一个 fe80
开头的是随机分配的局域网v6,即使无法通过 fe80:7eb5:2afd::30
访问adguardhome,也一定能通过这个v6访问;
第二个 fe80:7eb5:2afd::30
是yml中设置的,是静态公网v6(实际上是内网);
第三个 240e
开头的,是真正的公网v6地址,但是ping不到(应该是出于安全考虑)。如果你的头两个v6地址开头不同,比如说:
inet6 addr: f123::xx:xxxx:xxxx:xxxx/64 Scope:Link
inet6 addr: fe80:7eb5:2afd::30/64 Scope:Global
同样把所有的 fe80
换成 f123
即可。
在几乎所有其它的docker-macvlan教程中,创建的网段都非 fe80
开头,比如 fd0d:7eb5:2afd
。此时容器虽然能访问外部的v6地址,但v6网段不同,导致局域网的其它机器只能访问到容器的v4地址,却访问不到容器的v6地址。
(3)macvlan的其它操作
使用macvlan的容器,无法直接 docker restart
,会提示ip地址已被占用。你可以重启docker service docker restart
或者重启宿主机,以释放容器的ipv4、ipv6地址。
192.168.1.0/24
下虽然有256个ip地址,但也有可能用完。好在macvlan还能对物理网卡进一步分层,见 参考文章四 。分层后需要注意不同macvlan网络之间的通信问题。
最后,在参考文章四作者的下一篇文章中提到了overlay,也可以达到给容器以局域网ip的效果,见 参考文章五 。
(4)pihole配置详解
这里都是笔者个人的理解,部分参考了 pihole的官方文档 。由于文档都是英文的,笔者没时间细看,难免可能有疏漏错误,请参考原文。
a.Groups与Clients
从主页面左侧边栏的 Groups
可进入Groups management(屏蔽规则的分组)。根据 官方文档 ,组可用于管理域名黑名单、白名单、客户端。但是注意目前版本pihole的白名单域名管理起来不是很方便(网页中只能一个个显示,没有白名单列表)。
可以对屏蔽规则分组,也可以对客户端分组(侧边栏的 Groups
下方的 Clients
)。比如 电脑1 需要访问所有网站, 电脑2 则需要屏蔽掉不健康的网站,就可以对此类网站拉一个组,在 电脑2(客户端2) 的屏蔽规则中单独加上该组,守护少年儿童的心理健康。
每个新的屏蔽规则和客户端都会加到各自的0号组(default)中。
要对客户端分组,首先需要有不同的客户端,这就要求不能在路由器里统一把DNS设置到pihole,但是给每台电子设备分别改DNS又太麻烦了。笔者建议成人用一个路由器,少年儿童用另一个路由器,这样简单一些。
创建组名时如果有空格,则需要用英文双引号将组名括起来。
组的操作:
对某个客户端取消所有屏蔽:
在 Clients - Group assignment 里,把该客户端适用的所有组移除。
对屏蔽规则(黑名单)分组:
在 Groups 里创建屏蔽规则组后,在 Adlists - Group assignment 里,对屏蔽规则分组。新增的所有规则都会加到0组(default),可以手动调整。
添加单独的域名(黑名单或白名单)到组:
在 Domains 里新增一个域名(或者正则)规则,添加到屏蔽规则的组里。目前的pihole不能添加白名单的列表,只能在这里一个个添加,因此笔者建议这里只添加白名单域名。
另外,pihole中白名单的优先级也高于黑名单( 官方文档 )。
对客户端和屏蔽规则都分组后,就能很方便地管理。比如刚才的例子,有域名黑名单A,域名黑名单B(成人能访问,儿童不能访问),域名白名单C,成人客户端1,儿童客户端2,那么进行以下操作:
在 Groups 里,添加 adult_rule 和 kid_rule 两个屏蔽规则组;
在 Clients 里,添加两个客户端,分别为成人和儿童使用的客户端,分别使用 adult_rule 和 kid_rule 两个屏蔽规则组(去掉default组);
在 Domains 里,一个个添加白名单C中的域名,注意选择 Add to Whitelist,并把所有白名单域名的 Group assignment 中勾选两个屏蔽规则组(default组随便);
在 Adlist 里,通过URL,添加黑名单A、B,将A添加到 adult_rule 和 kid_rule ,B则只添加到 kid_rule (default组随便);
b.Domains与Adlists
在 Domains
里可以手动单独添加域名到白名单或黑名单,还能用正则表达式。正式用RE之前最好用pihole检查一下,见 官方文档 (需要进入容器操作)。
在 Adlists
里可以用别人总结好的屏蔽列表(anti-ad之类的),用法同adguardhome。添加完屏蔽表后, docker exec -it pihole pihole -g
来更新列表;或者到 Tools - Update Gravity
里更新。
c.Disable Blocking
可以临时或者永久取消所有的屏蔽,用于测试。
d.Local DNS
自定义解析,一般用于把某个域名绑定到局域网ip上。
e.名词解释
笔者对于 Settings
里的一些高级功能进行解释,可能会有错误的地方。
ECS (Extended Client Subnet)
:应该是DNS的弹性负载。
Content Delivery Networks (CDNs)
:基于ip提供地理信息。
Never forward non-FQDN A and AAAA queries
:如果不勾选的话,有时候Local DNS可能往上游服务器查找;如果开启下面的 Conditional Forwarding
又不勾选本功能,有时会导致DNS死循环。Never forward reverse lookups for private IP ranges
:本地ip如果没有对应的本地域名,不会向上游服务器查找。
这两个 Never forward
选项也许能增加隐私性,但勾选时最好同时把pihole设为DHCP服务器。
Use DNSSEC
:即Domain Name System Security Extensions,验证DNS解析是否正确。如果验证失败或者上游服务器不支持DNSSEC,可能会出问题。另外该选项会显著增加log文件大小。
Rate-limiting
:默认情况下,一个客户端在60s内发出1000次DNS请求后就封禁该客户端。玩多线程爬虫可能有影响,把两个值都改成0可以关闭此功能。
Conditional forwarding
:如果不把pihole作为DHCP服务器,则pihole无法决定局域网内的设备名(注意这个不同于Local DNS),开启这个功能后,填入局域网段、DHCP服务器(一般是路由器)、选填本地域名,pihole会向你的路由器查询局域网内的设备名。选填的本地域名,意思是对该域名的DNS不会离开本地,并且这个本地域名要和路由器的设置一致。开启 Conditional forwarding
同时关闭 Never forward non-FQDN A and AAAA queries
,那么即使是本地域名,pihole也会交给上游的路由器处理。(所以要想用pihole解析本地域名,要么就都不开,要么就把 Conditional forwarding
和 Never forward non-FQDN A and AAAA queries
都打开)