AdGuardHome API 实战:4 个运维场景 + 1 套 Python 自动化脚本模板

发布时间:2026/10/27 18:41:07
AdGuardHome API 实战:4 个运维场景 + 1 套 Python 自动化脚本模板

AdGuardHome API 实战:4 个运维场景 + 1 套 Python 自动化脚本模板

【免费下载链接】AdGuardHomeNetwork-wide ads & trackers blocking DNS server项目地址: https://gitcode.com/gh_mirrors/ad/AdGuardHome

每天进 Web 界面手点一次“刷新规则”,故障排查时翻不到想要的查询记录,拦截率到底多少心里没底——这些重复劳动适合交给脚本。AdGuardHome API 走的是 HTTP 协议、Basic Auth 认证,用 Python 写自动化脚本,巡检、取证、监控几行代码就能固化下来。

从架构图能看出关键点:Web 界面本身只是这套 REST API 的客户端之一,Query log、Stats、Filters 等模块全部挂在同一个 API 层上。也就是说,你的脚本和界面走的是完全相同的通道,界面上能做的,API 都能做。

🌐 先用一个 Session 打通 GET /control/status

依赖只有一个。认证信息在整个脚本里只写一次:挂到requests.Session上,后续所有请求自动携带,不再重复传。

# 依赖安装:pip install requests import requests # 主机与认证只写一次,后面所有小节复用这个 session AGH_URL = "http://192.168.0.200:80" session = requests.Session() session.auth = ("admin", "admin") # 换成你的管理员账号 session.headers.update({"Content-Type": "application/json"}) # 首次调用:拿服务器状态 status = session.get(f"{AGH_URL}/control/status", timeout=10).json() print(status["protection_enabled"], status["running"], status["version"])

GET /control/status返回protection_enabled(保护是否开启)、running、dns_addresses、dns_port、version等字段,是任何巡检逻辑的第一站。如果这一行跑通了,剩下的场景只是换路径和参数。

过滤规则自动开关与刷新

目的:规则源更新后不用再进界面点刷新,过滤开关也能被脚本控制。POST /control/filtering/config负责启停(interval是自动刷新间隔,单位小时),POST /control/filtering/add_url追加规则源,POST /control/filtering/refresh立即重新下载合并全部规则。

# 开启 DNS 过滤,并设定 24 小时的自动刷新间隔 session.post(f"{AGH_URL}/control/filtering/config", json={"enabled": True, "interval": 24}) # 追加规则源;url 换成你自己的,指向本地镜像更稳 session.post(f"{AGH_URL}/control/filtering/add_url", json={"url": "http://192.168.0.255/lists/ads.txt", "name": "local-ads", "whitelist": False}) # 立即重新下载并合并全部规则 session.post(f"{AGH_URL}/control/filtering/refresh")

这些规则源维护的就是上图里 Rewrite、Lists 环节的数据,请求在到达上游 DNS 之前就会被拦下。坑在refresh:它是同步调用,规则列表多时耗时远超 10 秒,要单独放宽超时;add_url传重复的url会被拒绝,追加前先查现有列表。

查询日志拉取与清空

目的:排查“某域名为什么没解析出来”时,秒级把证据捞出来,而不是在界面里翻页。GET /control/querylog支持limit控制条数、search按域名或客户端 IP 过滤。

# 取最近 50 条,并按域名过滤 log = session.get(f"{AGH_URL}/control/querylog", params={"limit": 50, "search": "ads.example.com"}).json() for item in log["data"]: print(item["time"], item["client"], item["name"], item["status"], item["reason"])

返回体是{oldest, data},data每条含name(域名)、client(来源 IP)、upstream、status、reason、cached等字段,status能区分是规则拦截、安全浏览替换还是搜索改写。清空只有一个接口,且不可恢复:

session.post(f"{AGH_URL}/control/querylog_clear") # 建议先落盘备份再执行

查询量与拦截量周期监控

目的:让拦截量从“看不见”变成周期采样的数字。GET /control/stats一次拿到全部计数。

stats = session.get(f"{AGH_URL}/control/stats").json() total = stats["num_dns_queries"] blocked = stats["num_blocked_filtering"] print(f"查询 {total},拦截 {blocked},拦截率 {blocked / max(total, 1):.1%}")

返回里还有num_replaced_safebrowsing、num_replaced_safesearch、num_replaced_rewrites等分项。监控别写死循环常驻进程:让定时任务每 5 分钟采样一次、把结果追加到文件,需要归零统计窗口时再调POST /control/stats_reset。

上游 DNS 配置查询与健康测试

目的:上游挂了时先确认是谁的锅。GET /control/dns_info读出当前配置,POST /control/test_upstream_dns实测可达性。

dns = session.get(f"{AGH_URL}/control/dns_info").json() # 测试一组上游是否可用;bootstrap_dns 负责解析上游域名 result = session.post(f"{AGH_URL}/control/test_upstream_dns", json={"bootstrap_dns": ["1.1.1.1"], "upstream_dns": dns["upstream_dns"]}).json() bad = {k: v for k, v in result.items() if v != "OK"} print("异常上游:", bad or "无")

返回体里每个上游对应一个状态,"OK"表示可达,其余是具体错误文本(比如i/o timeout)。写入侧同理:POST /control/dns_config提交改后的配置,模式与上面的读-测-写一致,不再重复。

🧰 把脚本做稳:重试、超时与状态码集中封装

前面为了省事没写异常处理。上生产前,把重试、超时、401/404/5xx 集中封装进一个工具函数,调用处保持干净,而不是散落满屏 try/except:

import time def call(session, method, path, retries=3, **kwargs): """统一出口:超时、重试、状态码判断都在这里""" kwargs.setdefault("timeout", 15) for attempt in range(retries): try: resp = session.request(method, f"{AGH_URL}{path}", **kwargs) except requests.exceptions.RequestException as e: # 连接失败/超时:退避重试,最后一次直接抛出 if attempt < retries - 1: time.sleep(2 * (attempt + 1)) continue raise e if resp.status_code == 401: # 凭证失效,重试无意义 raise PermissionError("认证失败:检查 Basic Auth 账号密码") if resp.status_code == 404: # 路径拼错或 API 版本不匹配 raise ValueError(f"接口不存在:{path}") if resp.status_code >= 500 and attempt < retries - 1: time.sleep(2 * (attempt + 1)) # 5xx:退避重试 continue resp.raise_for_status() return resp.json()

调用处只剩一行:call(session, "GET", "/control/status")。401 和 404 必须区分对待:前者是凭证问题,后者多半是路径或版本不对,都别盲目重试。接口全集以官方定义 openapi/openapi.yaml 为准,想扩展别的操作直接查它。调度交给 cron(Windows 用任务计划程序),脚本本身保持幂等、无状态:

*/5 * * * * /usr/bin/python3 /opt/scripts/agh_monitor.py >> /var/log/agh_monitor.log 2>&1

完整脚本:复制即可运行

把上面所有场景合成单main的可运行版本,凭证走环境变量,不落代码库:

#!/usr/bin/env python3 """AdGuardHome 日常运维:状态巡检、规则刷新、日志取证、统计采样""" import json import os import time import requests AGH_URL = os.environ.get("AGH_URL", "http://192.168.0.200:80") AUTH = (os.environ.get("AGH_USER", "admin"), os.environ.get("AGH_PASS", "")) def build_session(): """认证只在这里出现一次""" s = requests.Session() s.auth = AUTH s.headers.update({"Content-Type": "application/json"}) return s def call(session, method, path, retries=3, **kwargs): """统一出口:超时、重试、状态码判断""" kwargs.setdefault("timeout", 15) for attempt in range(retries): try: resp = session.request(method, f"{AGH_URL}{path}", **kwargs) except requests.exceptions.RequestException as e: if attempt < retries - 1: time.sleep(2 * (attempt + 1)) continue raise e if resp.status_code == 401: raise PermissionError("认证失败:检查 Basic Auth 凭证") if resp.status_code == 404: raise ValueError(f"接口不存在:{path}") if resp.status_code >= 500 and attempt < retries - 1: time.sleep(2 * (attempt + 1)) continue resp.raise_for_status() return resp.json() def main(): session = build_session() # 1) 状态巡检 print(json.dumps(call(session, "GET", "/control/status"), ensure_ascii=False)) # 2) 过滤规则:确保开启 + 触发刷新(规则多时放宽超时) call(session, "POST", "/control/filtering/config", json={"enabled": True, "interval": 24}) call(session, "POST", "/control/filtering/refresh", timeout=120) # 3) 日志取证:最近 200 条落盘,不直接清空 log = call(session, "GET", "/control/querylog", params={"limit": 200}) with open("/tmp/agh_querylog.json", "w", encoding="utf-8") as f: json.dump(log["data"], f, ensure_ascii=False, indent=2) # 4) 统计采样 print(json.dumps(call(session, "GET", "/control/stats"), ensure_ascii=False)) if __name__ == "__main__": main()

✅ 上线前自查清单

  • 凭证经环境变量或配置下发,没有硬编码在脚本和仓库里
  • AGH_URL从脚本所在主机可达(管理端口常只绑定内网)
  • querylog_clear、stats_reset不可逆,执行前已有落盘备份
  • cron 行带日志重定向,脚本幂等、可重复执行
  • refresh的超时按实际规则源数量调整(120 秒起步)

两个延伸方向:把第 4 步的统计输出接给 Prometheus 或 Grafana,拦截率就有历史曲线了;照着 openapi/openapi.yaml 把 DNS 重写、客户端管理等接口纳入同一个call(),脚本骨架不用动。

【免费下载链接】AdGuardHomeNetwork-wide ads & trackers blocking DNS server项目地址: https://gitcode.com/gh_mirrors/ad/AdGuardHome

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考