Dnsmasq 服务搭建完全教程 / 第 08 章:访问控制与过滤
第 08 章:访问控制与过滤
8.1 访问控制基础
8.1.1 接口级访问控制
# /etc/dnsmasq.d/50-acl.conf
# 只在指定接口提供服务
bind-interfaces
interface=eth1
# 排除特定接口
except-interface=eth0
# 监听特定 IP(自动限制访问)
listen-address=127.0.0.1,192.168.1.1
# 限制 DHCP 只在特定接口
interface=eth1
no-dhcp-interface=eth0
8.1.2 端口控制
# 自定义 DNS 端口(隐藏默认端口)
port=5353
# 禁止 DNS(只提供 DHCP)
port=0
# 允许所有端口的查询(用于端口转发)
# Dnsmasq 不直接支持,需要 iptables 配合
8.1.3 客户端认证(基于 MAC)
# 只为已知 MAC 地址提供 DHCP 服务
# /etc/dnsmasq.d/51-mac-auth.conf
# 静态绑定列表即白名单
dhcp-host=aa:bb:cc:dd:ee:01,192.168.1.10,device1
dhcp-host=aa:bb:cc:dd:ee:02,192.168.1.11,device2
# 只分配静态绑定的地址(拒绝未知设备)
dhcp-range=192.168.1.10,192.168.1.50,static
dhcp-authoritative
注意:MAC 地址可以被伪造,这仅是基本防护,不能替代真正的网络认证(如 802.1X)。
8.2 域名过滤
8.2.1 黑名单模式
# /etc/dnsmasq.d/52-domain-filter.conf
# 方法 1:address 指令屏蔽(返回 0.0.0.0)
address=/ads.example.com/0.0.0.0
address=/tracker.example.com/0.0.0.0
address=/malware.example.com/0.0.0.0
# 方法 2:返回 NXDOMAIN(域名不存在)
address=/ads.example.com/#
# 方法 3:使用外部黑名单文件
# 每行格式:address=/域名/0.0.0.0
conf-file=/etc/dnsmasq.blacklist
8.2.2 白名单模式
# 只允许解析特定域名,其他全部拒绝
# 这种模式较为极端,仅适用于高度安全场景
# 指定允许的域名
server=/allowed-domain.com/223.5.5.5
server=/another-allowed.com/223.5.5.5
# 拒绝所有其他域名(返回 NXDOMAIN)
address=/#/0.0.0.0
# 或者将未列出的域名指向特定 IP
address=/#/192.168.1.100
8.2.3 按域名分类过滤
# 社交媒体过滤
address=/facebook.com/0.0.0.0
address=/twitter.com/0.0.0.0
address=/instagram.com/0.0.0.0
address=/tiktok.com/0.0.0.0
# 广告域名过滤
address=/doubleclick.net/0.0.0.0
address=/googlesyndication.com/0.0.0.0
8.2.4 域名通配符过滤
# 屏蔽整个域名及其所有子域名
# address=/example.com/0.0.0.0 会屏蔽:
# - example.com
# - www.example.com
# - any.subdomain.example.com
# 屏蔽特定子域名
address=/ads.example.com/0.0.0.0
# 不影响 example.com 或 www.example.com
8.3 广告屏蔽
8.3.1 手动维护广告屏蔽列表
# /etc/dnsmasq.d/53-adblock.conf
# 创建屏蔽列表文件
# /etc/dnsmasq.adblock
# address=/ads.googlevideo.com/0.0.0.0
# address=/pagead2.googlesyndication.com/0.0.0.0
# address=/ads.facebook.com/0.0.0.0
# address=/analytics.tiktok.com/0.0.0.0
# 引用列表
conf-file=/etc/dnsmasq.adblock
8.3.2 自动下载并更新屏蔽列表
#!/bin/bash
# /usr/local/bin/update-adblock.sh
BLOCKLIST_DIR="/etc/dnsmasq.blocklists"
OUTPUT="/etc/dnsmasq.adblock"
TEMP="/tmp/adblock-temp.txt"
mkdir -p "$BLOCKLIST_DIR"
# 清空旧列表
> "$TEMP"
# 下载多个广告列表
URLS=(
"https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"
"https://adaway.org/hosts.txt"
"https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&mimetype=plaintext"
"https://raw.githubusercontent.com/notracking/hosts-blocklists/master/hostnames.txt"
)
for url in "${URLS[@]}"; do
echo "Downloading: $url"
curl -sL "$url" >> "$TEMP" 2>/dev/null
done
# 转换为 Dnsmasq 格式
awk '
/^0\.0\.0\.0[[:space:]]/ {
gsub(/\r/, "");
split($0, a, /[[:space:]]+/);
if (a[2] !~ /^(localhost|localhost\.localdomain|127\.0\.0\.1|::1|0\.0\.0\.0)$/) {
print "address=/" a[2] "/0.0.0.0"
}
}
' "$TEMP" | sort -u > "$OUTPUT"
# 重新加载 Dnsmasq
sudo systemctl reload dnsmasq
echo "Updated $(wc -l < "$OUTPUT") blocked domains"
# 设置自动更新(每天凌晨 3 点)
echo "0 3 * * * root /usr/local/bin/update-adblock.sh" | sudo tee /etc/cron.d/adblock-update
8.3.3 使用 AdGuard Home 风格的列表
# AdGuard 格式的域名列表
# 转换脚本
#!/bin/bash
# convert-adguard.sh
INPUT="$1"
OUTPUT="${INPUT%.txt}.dnsmasq"
awk '
/^\|\|[a-z]/ && !/\^/ && !/\*/ {
gsub(/\|\|/, "");
gsub(/\^.*$/, "");
if ($0 !~ /^#/) {
print "address=/" $0 "/0.0.0.0"
}
}
' "$INPUT" > "$OUTPUT"
echo "Converted $(wc -l < "$OUTPUT") domains"
8.3.4 白名单(豁免列表)
# 某些域名即使在屏蔽列表中也需要放行
# /etc/dnsmasq.d/54-whitelist.conf
# 强制覆盖屏蔽规则(address 指令的后加载优先)
# 将白名单放在单独文件中,确保在屏蔽列表之后加载
# /etc/dnsmasq.whitelist
# server=/example.com/223.5.5.5
# server=/ads.example.com/223.5.5.5
conf-file=/etc/dnsmasq.whitelist
注意:在 Dnsmasq 中,后加载的
address指令会覆盖先加载的。因此白名单文件应放在屏蔽列表文件之后加载。
# 配置加载顺序
# /etc/dnsmasq.d/50-filter.conf
conf-file=/etc/dnsmasq.adblock # 屏蔽列表
conf-file=/etc/dnsmasq.whitelist # 白名单(后加载,优先级高)
8.4 基于时间的访问控制
8.4.1 定时开关 DNS 过滤
#!/bin/bash
# /usr/local/bin/time-filter.sh
FILTER_CONF="/etc/dnsmasq.timed-filter"
case "$1" in
enable)
# 启用过滤
cp /etc/dnsmasq.adblock "$FILTER_CONF"
sudo systemctl reload dnsmasq
echo "Filter enabled"
;;
disable)
# 禁用过滤
> "$FILTER_CONF"
sudo systemctl reload dnsmasq
echo "Filter disabled"
;;
esac
# 在 /etc/dnsmasq.d/ 中引用
# /etc/dnsmasq.d/55-timed-filter.conf
conf-file=/etc/dnsmasq.timed-filter
# Cron 定时任务
# 工作日 22:00 到次日 06:00 启用社交媒体过滤
# 0 22 * * 1-5 /usr/local/bin/time-filter.sh enable
# 0 6 * * 1-5 /usr/local/bin/time-filter.sh disable
# 周末全天启用
# 0 0 * * 6 /usr/local/bin/time-filter.sh enable
# 0 22 * * 0 /usr/local/bin/time-filter.sh disable
8.4.2 业务场景:儿童上网时间管理
# 完整的儿童上网时间管理方案
# 1. 创建不同过滤级别的配置
# /etc/dnsmasq.filter-kids-strict (严格过滤)
# /etc/dnsmasq.filter-kids-normal (普通过滤)
# /etc/dnsmasq.filter-kids-off (关闭过滤)
# 2. 定时切换
# 学习时间(周一-周五 16:00-18:00):严格过滤
# 0 16 * * 1-5 cp /etc/dnsmasq.filter-kids-strict /etc/dnsmasq.active-filter && sudo systemctl reload dnsmasq
# 自由时间(周一-周五 18:00-21:00):普通过滤
# 0 18 * * 1-5 cp /etc/dnsmasq.filter-kids-normal /etc/dnsmasq.active-filter && sudo systemctl reload dnsmasq
# 睡觉时间(21:00-次日 07:00):断网(通过 DHCP 或 iptables)
8.5 DHCP 访问控制
8.5.1 限制 DHCP 客户端
# /etc/dnsmasq.d/56-dhcp-acl.conf
# 只为已知设备分配地址
dhcp-range=192.168.1.10,192.168.1.50,static
dhcp-authoritative
# 按 MAC 前缀过滤(厂商白名单)
dhcp-vendorclass=set:known,MagicVerse
dhcp-ignore=tag:!known
# 拒绝特定 MAC 地址
# Dnsmasq 不直接支持 MAC 黑名单,但可以通过 iptables 实现
# iptables -A INPUT -m mac --mac-source AA:BB:CC:DD:EE:FF -j DROP
8.5.2 基于标签的访问控制
# 为不同类型的设备分配不同的策略
# 设备类型标记
dhcp-vendorclass=set:mobile,android
dhcp-vendorclass=set:mobile,iPhone
dhcp-vendorclass=set:pc,MSFT
dhcp-vendorclass=set:printer,Hewlett-Packard
# 移动设备短租约
dhcp-range=tag:mobile,set:mobile-net,192.168.1.100,192.168.1.150,2h
dhcp-option=tag:mobile-net,option:dns-server,192.168.1.1
# PC 长租约
dhcp-range=tag:pc,set:pc-net,192.168.1.200,192.168.1.250,24h
dhcp-option=tag:pc-net,option:dns-server,192.168.1.1
# 打印机静态地址
dhcp-range=tag:printer,192.168.1.20,static
8.6 高级过滤技术
8.6.1 正则表达式过滤(配合脚本)
Dnsmasq 本身不支持正则表达式,但可以通过脚本生成域名列表:
#!/bin/bash
# /usr/local/bin/generate-blocklist.sh
# 生成通配符屏蔽列表
OUTPUT="/etc/dnsmasq.dynamic-blocklist"
> "$OUTPUT"
# 屏蔽所有包含特定关键词的域名
KEYWORDS="ads|tracking|analytics|telemetry|beacon|pixel|stat|metrics"
# 从已知列表中筛选
grep -iE "$KEYWORDS" /etc/dnsmasq.adblock >> "$OUTPUT" 2>/dev/null
# 补充特定模式
cat >> "$OUTPUT" << 'EOF'
address=/ads.*\.com/0.0.0.0
address=/tracking.*\.com/0.0.0.0
address=/pixel.*\.com/0.0.0.0
EOF
sudo systemctl reload dnsmasq
8.6.2 使用 ipset/nftset 配合 iptables
# /etc/dnsmasq.d/57-ipset.conf
# 将特定域名解析结果加入 ipset
# 当 Dnsmasq 编译时启用了 ipset 支持
# 格式:ipset=/<域名>/<ipset名称>
ipset=/ads.example.com/adblock
ipset=/tracker.example.com/adblock
# 创建 ipset
sudo ipset create adblock hash:ip
# 使用 iptables 阻止 ipset 中的 IP
sudo iptables -A OUTPUT -m set --match-set adblock dst -j DROP
8.6.3 nftables 集成(Dnsmasq 2.86+)
# /etc/dnsmasq.d/58-nftset.conf
# 将解析结果加入 nftables 集合
# 格式:nftset=/<域名>/<family>/<table>/<set>
nftset=/ads.example.com/inet#dnsmasq#adblock
nftset=/tracker.example.com/inet#dnsmasq#adblock
# 创建 nftables 集合
sudo nft add set inet dnsmasq adblock { type ipv4_addr \; }
sudo nft add rule inet dnsmasq output ip daddr @adblock drop
8.7 日志与审计
8.7.1 查询日志
# /etc/dnsmasq.d/59-logging.conf
# 记录所有 DNS 查询
log-queries
# 记录 DHCP 事件
log-dhcp
# 日志文件
log-facility=/var/log/dnsmasq/dnsmasq.log
# 日志轮转
# /etc/logrotate.d/dnsmasq
# /var/log/dnsmasq/*.log {
# daily
# rotate 30
# compress
# delaycompress
# missingok
# notifempty
# create 0640 dnsmasq adm
# postrotate
# sudo systemctl reload dnsmasq
# endscript
# }
8.7.2 分析查询日志
# 统计查询最多的域名
sudo awk '/query\[A\]/ {print $6}' /var/log/dnsmasq/dnsmasq.log | \
sort | uniq -c | sort -rn | head -20
# 统计查询来源 IP
sudo awk '/query\[/ {print $NF}' /var/log/dnsmasq/dnsmasq.log | \
sort | uniq -c | sort -rn | head -20
# 查找被屏蔽的查询
sudo grep "/0.0.0.0" /var/log/dnsmasq/dnsmasq.log | tail -20
# 实时监控被拦截的广告域名
sudo tail -f /var/log/dnsmasq/dnsmasq.log | grep "0.0.0.0"
8.8 完整访问控制配置示例
# /etc/dnsmasq.d/50-acl-full.conf
# === 接口控制 ===
bind-interfaces
listen-address=127.0.0.1,192.168.1.1
except-interface=eth0
# === DHCP 访问控制 ===
dhcp-range=192.168.1.10,192.168.1.50,static
dhcp-authoritative
# 已知设备白名单
dhcp-host=aa:bb:cc:dd:ee:01,192.168.1.10,device1
dhcp-host=aa:bb:cc:dd:ee:02,192.168.1.11,device2
# === 域名过滤 ===
# 广告屏蔽列表
conf-file=/etc/dnsmasq.adblock
# 白名单(覆盖屏蔽规则)
conf-file=/etc/dnsmasq.whitelist
# 本地域名放行
local=/home.lan/
address=/home.lan/192.168.1.1
# === 安全防护 ===
# 防 DNS 重绑定
stop-dns-rebind
rebind-localhost-ok
# 防 Bogon
bogus-priv
# 禁止 DNSSEC 不安全的响应(可选)
# dnssec-check-unsigned
# === 日志 ===
log-queries
log-dhcp
log-facility=/var/log/dnsmasq/dnsmasq.log
8.9 小结
| 功能 | 实现方式 | 适用场景 |
|---|---|---|
| 接口控制 | interface= / bind-interfaces | 基础网络隔离 |
| MAC 过滤 | dhcp-host + static | 设备准入控制 |
| 域名黑名单 | address=/域名/0.0.0.0 | 广告屏蔽、恶意域名 |
| 域名白名单 | conf-file= 加载顺序 | 豁免特定域名 |
| 时间控制 | cron + reload | 家长控制 |
| ipset/nftset | ipset= / nftset= | 防火墙联动 |