update 2026-09-24 16:56:26

This commit is contained in:
action committed 2026-09-24 16:56:26 +08:00
1 parent b31a5c3878
commit e4cee331e9
6885 files changed
+3240523

No files matched your search

@@ -0,0 +1,189 @@
# 模板用户名为 admin 密码为 admin
# The username is admin and the password is admin.
# 保存并应用就可以直接用的配置模板文件
# a template config can be use with a apply
http:
pprof:
port: 6060
enabled: false
address: 0.0.0.0:3000
session_ttl: 720h
users:
- name: admin
password: $2y$10$iGkTW98QtGEZn4I/GC0J4eQhfuLQ53v7L4DDVauEJyWylJrXWwWeO
auth_attempts: 5
block_auth_min: 15
http_proxy: ""
language: ""
theme: auto
dns:
bind_hosts:
- 0.0.0.0
port: 3053
anonymize_client_ip: false
ratelimit: 20
ratelimit_subnet_len_ipv4: 24
ratelimit_subnet_len_ipv6: 56
ratelimit_whitelist: []
refuse_any: true
upstream_dns:
- https://dns.cloudflare.com/dns-query
- '#'
- '# 对 ts.net 相关域名启用 DNS 分流, 交由 Tailscale 解析, 如不使用 Tailscale 可以删除.'
- '# Enable DNS conditional forwarding for ts.net via Tailscale; remove if Tailscale is not used.'
- '[/ts.net/]100.100.100.100'
upstream_dns_file: ""
bootstrap_dns:
- 1.1.1.1
- 1.0.0.1
fallback_dns:
- quic://dns.adguard-dns.com
upstream_mode: parallel
fastest_timeout: 1s
allowed_clients: []
disallowed_clients: []
blocked_hosts:
- version.bind
- id.server
- hostname.bind
trusted_proxies:
- 127.0.0.0/8
- ::1/128
cache_enabled: true
cache_size: 4194304
cache_ttl_min: 0
cache_ttl_max: 0
cache_optimistic: false
cache_optimistic_answer_ttl: 30s
cache_optimistic_max_age: 12h
bogus_nxdomain: []
aaaa_disabled: false
enable_dnssec: false
edns_client_subnet:
custom_ip: ""
enabled: false
use_custom: false
max_goroutines: 300
handle_ddr: true
ipset: []
ipset_file: ""
bootstrap_prefer_ipv6: false
upstream_timeout: 10s
private_networks: []
use_private_ptr_resolvers: true
local_ptr_upstreams: []
use_dns64: false
dns64_prefixes: []
serve_http3: false
use_http3_upstreams: false
serve_plain_dns: true
hostsfile_enabled: true
pending_requests:
enabled: true
tls:
enabled: false
server_name: ""
force_https: false
port_https: 443
port_dns_over_tls: 853
port_dns_over_quic: 853
port_dnscrypt: 0
dnscrypt_config_file: ""
allow_unencrypted_doh: false
certificate_chain: ""
private_key: ""
certificate_path: ""
private_key_path: ""
strict_sni_check: false
querylog:
dir_path: ""
ignored: []
interval: 2160h
size_memory: 1000
enabled: true
file_enabled: true
statistics:
dir_path: ""
ignored: []
interval: 24h
enabled: true
filters:
- enabled: true
url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt
name: AdGuard DNS filter
id: 1
whitelist_filters: []
user_rules: []
dhcp:
enabled: false
interface_name: ""
local_domain_name: lan
dhcpv4:
gateway_ip: ""
subnet_mask: ""
range_start: ""
range_end: ""
lease_duration: 86400
icmp_timeout_msec: 1000
options: []
dhcpv6:
range_start: ""
lease_duration: 86400
ra_slaac_only: false
ra_allow_slaac: false
filtering:
blocking_ipv4: ""
blocking_ipv6: ""
blocked_services:
schedule:
time_zone: UTC
ids: []
protection_disabled_until: null
safe_search:
enabled: false
bing: true
duckduckgo: true
ecosia: true
google: true
pixabay: true
yandex: true
youtube: true
blocking_mode: default
parental_block_host: family-block.dns.adguard.com
safebrowsing_block_host: standard-block.dns.adguard.com
rewrites: []
safe_fs_patterns:
- /tmp/AdGuardHome/userfilters/*
safebrowsing_cache_size: 1048576
safesearch_cache_size: 1048576
parental_cache_size: 1048576
cache_time: 30
filters_update_interval: 24
blocked_response_ttl: 10
filtering_enabled: true
rewrites_enabled: true
parental_enabled: false
safebrowsing_enabled: false
protection_enabled: true
clients:
runtime_sources:
whois: true
arp: true
rdns: true
dhcp: true
hosts: true
persistent: []
log:
enabled: true
file: ""
max_backups: 0
max_size: 100
max_age: 3
compress: false
local_time: false
verbose: false
os:
group: ""
user: ""
rlimit_nofile: 0
schema_version: 32
@@ -0,0 +1,10 @@
table inet adguardhome {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
__AGH_WAN_RETURN_LINE__
fib daddr type local udp dport 53 redirect to :__AGH_PORT__
fib daddr type local tcp dport 53 redirect to :__AGH_PORT__
}
}
@@ -0,0 +1,20 @@
#!/bin/sh
PATH="/usr/sbin:/usr/bin:/sbin:/bin"
logread -e AdGuardHome > /tmp/AdGuardHometmp.log
logread -e AdGuardHome -f >> /tmp/AdGuardHometmp.log &
pid=$!
echo "1" > /var/run/AdGuardHomesyslog
while true
do
sleep 12
watchdog=$(cat /var/run/AdGuardHomesyslog 2>/dev/null)
if [ "${watchdog}x" = "0x" ]; then
kill "$pid"
rm /tmp/AdGuardHometmp.log
rm /var/run/AdGuardHomesyslog
exit 0
else
echo "0" > /var/run/AdGuardHomesyslog
fi
done
@@ -0,0 +1,176 @@
#!/bin/sh
PATH="/usr/sbin:/usr/bin:/sbin:/bin"
update_mode=$1
binpath=$(uci get AdGuardHome.AdGuardHome.binpath 2>/dev/null)
if [ -z "$binpath" ]; then
uci set AdGuardHome.AdGuardHome.binpath="/tmp/AdGuardHome/AdGuardHome"
binpath="/tmp/AdGuardHome/AdGuardHome"
fi
[ ! -d "${binpath%/*}" ] && mkdir -p "${binpath%/*}"
enabled=$(uci get AdGuardHome.AdGuardHome.enabled 2>/dev/null)
core_api_url="https://api.github.com/repos/AdguardTeam/AdGuardHome/releases/latest"
Check_Task(){
running_tasks=$(pgrep -f "update_core.sh" 2>/dev/null | grep -v "^$$\$" | wc -l)
running_tasks=${running_tasks:-0}
case $1 in
force)
printf "%s\n" "执行: 强制更新核心"
if [ "$running_tasks" -gt 0 ]; then
printf "%s\n" "清除 ${running_tasks} 个进程 ..."
pkill -9 -f "update_core.sh" 2>/dev/null
sleep 1
fi
;;
*)
if [ "$running_tasks" -gt 1 ]; then
printf "%s\n" "已经有更新任务正在运行, 请等待其执行结束或点击强制更新!"
EXIT 2
fi
;;
esac
}
Check_Updates(){
downloader_cmd="uclient-fetch"
downloader_opts="--no-check-certificate -T 5 -q -O"
downloader_quiet_opts="-q -O -"
printf "%s\n" "开始检查更新, 请耐心等待 ..."
Cloud_Version="$(${downloader_cmd} ${downloader_quiet_opts} "$core_api_url" 2>/dev/null | sed -n 's/.*"tag_name":[ ]*"\(v[^"]*\)".*/\1/p' | head -n 1)"
[ -z "$Cloud_Version" ] && printf "\n检查更新失败, 请检查网络或稍后重试!\n" && EXIT 1
if [ -f "$binpath" ]; then
Current_Version="$($binpath --version 2>/dev/null | sed -n 's/.*\(v[0-9][0-9.]*\).*/\1/p' | head -n 1)"
else
Current_Version="未知"
fi
[ -z "$Current_Version" ] && Current_Version="未知"
printf "\n执行文件路径: %s\n\n正在检查更新, 请耐心等待 ...\n" "${binpath%/*}"
printf "\n当前 AdGuardHome 版本: %s\n云端 AdGuardHome 版本: %s\n" "$Current_Version" "$Cloud_Version"
if [ "$Cloud_Version" != "$Current_Version" ] || [ "$1" = force ]; then
Update_Core
else
printf "\n已是最新版本, 无需更新!\n"
EXIT 0
fi
EXIT 0
}
Update_Core(){
rm -rf /tmp/AdGuardHome_Update > /dev/null 2>&1
mkdir -p "/tmp/AdGuardHome_Update"
GET_Arch
link="https://github.com/AdguardTeam/AdGuardHome/releases/download/${Cloud_Version}/AdGuardHome_linux_${Arch}.tar.gz"
printf "%s\n" "下载链接:${link}"
printf "%s\n" "文件名称:${link##*/}"
printf "\n开始下载 AdGuardHome 核心文件 ...\n\n"
${downloader_cmd} ${downloader_opts} "/tmp/AdGuardHome_Update/${link##*/}" "$link" > /dev/null 2>&1
if [ $? -ne 0 ]; then
printf "\nAdGuardHome 核心下载失败 ...\n"
rm -rf /tmp/AdGuardHome_Update
EXIT 1
fi
if [ "${link##*.}" = "gz" ]; then
printf "\n正在解压 AdGuardHome ...\n"
tar -zxf "/tmp/AdGuardHome_Update/${link##*/}" -C "/tmp/AdGuardHome_Update/"
if [ ! -e /tmp/AdGuardHome_Update/AdGuardHome ]
then
printf "%s\n" "AdGuardHome 核心解压失败!"
rm -rf "/tmp/AdGuardHome_Update" > /dev/null 2>&1
EXIT 1
fi
downloadbin="/tmp/AdGuardHome_Update/AdGuardHome/AdGuardHome"
else
downloadbin="/tmp/AdGuardHome_Update/${link##*/}"
fi
chmod +x "$downloadbin"
size_bytes=$(wc -c < "$downloadbin" 2>/dev/null || echo 0)
printf "\nAdGuardHome 核心体积: %s\n" "$(awk -v bytes="$size_bytes" 'BEGIN{printf "%.2fMB", bytes/1000000}')"
/etc/init.d/AdGuardHome stop > /dev/null 2>&1
printf "\n移动 AdGuardHome 核心文件到 %s ...\n" "${binpath%/*}"
mv -f "$downloadbin" "$binpath" > /dev/null 2>&1
if [ ! -s "$binpath" ] && [ $? -ne 0 ]; then
printf "AdGuardHome 核心移动失败!\n可能是设备空间不足导致, 请尝试更改 [执行文件路径] 为 /tmp/AdGuardHome\n"
EXIT 1
fi
rm -rf /tmp/AdGuardHome_Update
chmod +x "$binpath"
if [ "$enabled" = 1 ]; then
printf "\n正在重启 AdGuardHome 服务...\n"
/etc/init.d/AdGuardHome restart > /dev/null 2>&1
fi
printf "\nAdGuardHome 核心更新成功!\n"
}
GET_Arch() {
if command -v apk > /dev/null 2>&1; then
PM="apk"
elif command -v opkg > /dev/null 2>&1; then
PM="opkg"
else
printf "%s\n" "未找到包管理器 (apk/opkg)!" && EXIT 1
fi
case "${PM}" in
apk)
Archt="$(apk info -a kernel 2>/dev/null | grep Architecture | awk -F ":[ ]*" '{print $2}')"
[ -z "$Archt" ] && Archt="$(uname -m)"
;;
opkg)
Archt="$(opkg info kernel | grep Architecture | awk -F "[ _]" '{print($2)}')"
;;
esac
case "${Archt}" in
i386)
Arch=i386
;;
i686)
Arch=i386
;;
x86|x86_64)
Arch=amd64
;;
mipsel)
Arch=mipsle_softfloat
;;
mips)
Arch=mips_softfloat
;;
mips64el)
Arch=mips64le_softfloat
;;
mips64)
Arch=mips64_softfloat
;;
arm)
Arch=arm
;;
armeb)
Arch=armeb
;;
aarch64)
Arch=arm64
;;
*)
printf "\nAdGuardHome 暂不支持当前的设备架构: [%s]!\n" "$Archt"
EXIT 1
esac
printf "\n当前设备架构: %s\n" "$Arch"
}
EXIT(){
rm -rf /var/run/update_core $LOCKU 2>/dev/null
[ "$1" -ne 0 ] && touch /var/run/update_core_error
exit $1
}
main(){
Check_Task "$update_mode"
Check_Updates "$update_mode"
}
trap "EXIT 1" SIGTERM SIGINT
touch /var/run/update_core
rm -rf /var/run/update_core_error 2>/dev/null
main
@@ -0,0 +1,13 @@
#!/bin/sh
PATH="/usr/sbin:/usr/bin:/sbin:/bin"
configpath=$(uci get AdGuardHome.AdGuardHome.configpath)
while :
do
sleep 10
if [ -f "$configpath" ]; then
/etc/init.d/AdGuardHome do_redirect 1
break
fi
done
exit 0
@@ -0,0 +1,45 @@
{
"admin/services/AdGuardHomeJS": {
"title": "AdGuard Home JS",
"order": 12,
"action": {
"type": "firstchild"
},
"depends": {
"acl": [ "luci-app-adguardhome" ]
}
},
"admin/services/AdGuardHomeJS/base": {
"title": "Base Setting",
"order": 1,
"action": {
"type": "view",
"path": "adguardhome/base"
},
"depends": {
"acl": [ "luci-app-adguardhome" ]
}
},
"admin/services/AdGuardHomeJS/log": {
"title": "Log",
"order": 3,
"action": {
"type": "view",
"path": "adguardhome/log"
},
"depends": {
"acl": [ "luci-app-adguardhome" ]
}
},
"admin/services/AdGuardHomeJS/manual": {
"title": "Manual Config",
"order": 2,
"action": {
"type": "view",
"path": "adguardhome/manual"
},
"depends": {
"acl": [ "luci-app-adguardhome" ]
}
}
}
@@ -0,0 +1,21 @@
{
"luci-app-adguardhome": {
"description": "Grant UCI access for luci-app-adguardhome",
"read": {
"file": {
"/var/run/AdGredir": [ "read" ]
},
"ubus": {
"service": [ "list" ],
"luci.adguardhome": [ "getCoreInfo", "getCoreUpdate", "getInterfaces", "getRuntimeLog", "getManualConfig" ]
},
"uci": [ "AdGuardHome" ]
},
"write": {
"ubus": {
"luci.adguardhome": [ "startCoreUpdate", "changeWebPassword", "clearRuntimeLog", "setManualConfig" ]
},
"uci": [ "AdGuardHome" ]
}
}
}
@@ -0,0 +1,198 @@
#!/usr/bin/ucode
'use strict';
import { stat, popen, readfile, writefile, unlink, lsdir } from 'fs';
import { cursor } from 'uci';
function shellQuote(value) {
return "'" + replace(value, /'/g, "'\\''") + "'";
}
return {
'luci.adguardhome': {
getManualConfig: {
call: function() {
const conf = cursor();
const configpath = conf.get('AdGuardHome', 'AdGuardHome', 'configpath') ||
'/etc/AdGuardHome.yaml';
conf.unload('AdGuardHome');
const template = readfile('/usr/share/AdGuardHome/AdGuardHome_template.yaml') || '';
const current = readfile(configpath);
if (current != null)
return { content: current, template: template, source: 'current' };
if (stat(configpath) != null)
return { content: '', template: template, source: 'error' };
return {
content: template,
template: template,
source: template ? 'template' : 'missing'
};
}
},
setManualConfig: {
args: { content: '' },
call: function(request) {
const content = request.args.content;
if (content == null)
return { saved: false, reason: 'invalid_content' };
const conf = cursor();
const configpath = conf.get('AdGuardHome', 'AdGuardHome', 'configpath') ||
'/etc/AdGuardHome.yaml';
const binpath = conf.get('AdGuardHome', 'AdGuardHome', 'binpath') ||
'/usr/bin/AdGuardHome/AdGuardHome';
conf.unload('AdGuardHome');
const temporaryConfig = '/tmp/AdGuardHometmpconfig.yaml';
const validationLog = '/tmp/AdGuardHometest.log';
if (writefile(temporaryConfig, replace(content, /\r\n/g, '\n')) == null)
return { saved: false, reason: 'write_failed' };
if (stat(binpath)?.type == 'file') {
const valid = system(['/bin/sh', '-c', '"$1" -c "$2" --check-config > "$3" 2>&1',
'sh', binpath, temporaryConfig, validationLog], 30000) == 0;
if (!valid)
return { saved: false, reason: 'validation_failed', log: readfile(validationLog) || '' };
}
if (writefile(configpath, readfile(temporaryConfig) || '') == null)
return { saved: false, reason: 'save_failed' };
unlink(temporaryConfig);
if (system(['/etc/init.d/AdGuardHome', 'reload'], 30000) != 0)
return { saved: false, reason: 'reload_failed' };
return { saved: true };
}
},
getInterfaces: {
call: function() {
return { interfaces: lsdir('/sys/class/net') || [] };
}
},
getCoreInfo: {
call: function() {
const conf = cursor();
const binpath = conf.get('AdGuardHome', 'AdGuardHome', 'binpath') ||
'/usr/bin/AdGuardHome/AdGuardHome';
const configpath = conf.get('AdGuardHome', 'AdGuardHome', 'configpath') ||
'/etc/AdGuardHome.yaml';
const result = {
core_exists: stat(binpath)?.type == 'file',
config_exists: stat(configpath)?.type == 'file',
version: null
};
if (result.core_exists) {
// Pass the configured path as a positional argument; capture both output streams.
const proc = popen('/bin/sh -c \'"$1" --version 2>&1\' sh ' + shellQuote(binpath), 'r');
if (proc) {
const output = proc.read('all') || '';
proc.close();
result.version = match(output, /version ([^, \t\r\n]+)/)?.[1] || null;
}
}
conf.unload('AdGuardHome');
return result;
}
},
getCoreUpdate: {
call: function() {
const running = stat('/var/run/update_core') != null;
const failed = stat('/var/run/update_core_error') != null;
const log = readfile('/tmp/AdGuardHome_update.log', 131072) || '';
return {
state: running ? 'running' : failed ? 'failure' : log ? 'finished' : 'idle',
log: log
};
}
},
startCoreUpdate: {
args: { force: false },
call: function(request) {
if (stat('/var/run/update_core') != null)
return { started: false, reason: 'busy' };
unlink('/var/run/update_core_error');
unlink('/tmp/AdGuardHome_update.log');
const command = request.args.force
? '/bin/sh /usr/share/AdGuardHome/update_core.sh force >/tmp/AdGuardHome_update.log 2>&1 </dev/null &'
: '/bin/sh /usr/share/AdGuardHome/update_core.sh >/tmp/AdGuardHome_update.log 2>&1 </dev/null &';
return { started: system(['/bin/sh', '-c', command], 3000) == 0 };
}
},
changeWebPassword: {
args: { hash: '' },
call: function(request) {
const hash = request.args.hash;
if (!match(hash, /^\$2[aby]\$(0[4-9]|[12][0-9]|3[01])\$[.\/A-Za-z0-9]{53}$/))
return { changed: false, reason: 'invalid_hash' };
const conf = cursor();
const configpath = conf.get('AdGuardHome', 'AdGuardHome', 'configpath') ||
'/etc/AdGuardHome.yaml';
if (stat(configpath)?.type != 'file')
return { changed: false, reason: 'no_config' };
if (!conf.set('AdGuardHome', 'AdGuardHome', 'hashpass', hash) ||
!conf.commit('AdGuardHome'))
return { changed: false, reason: 'save_failed' };
conf.unload('AdGuardHome');
if (system(['/etc/init.d/AdGuardHome', 'reload'], 30000) != 0)
return { changed: false, reason: 'reload_failed' };
if (index(readfile(configpath) || '', hash) < 0)
return { changed: false, reason: 'verify_failed' };
return { changed: true };
}
},
getRuntimeLog: {
args: { offset: 0 },
call: function(request) {
const conf = cursor();
const logfile = conf.get('AdGuardHome', 'AdGuardHome', 'logfile') || '';
conf.unload('AdGuardHome');
if (!logfile)
return { enabled: false, localtime: false, content: '', offset: 0 };
let source = logfile;
if (logfile == 'syslog') {
source = '/tmp/AdGuardHometmp.log';
writefile('/var/run/AdGuardHomesyslog', '1');
if (stat(source)?.type != 'file')
system(['/bin/sh', '-c', '/bin/sh /usr/share/AdGuardHome/getsyslog.sh >/dev/null 2>&1 &'], 3000);
}
const log = readfile(source) || '';
const size = length(log);
let offset = +request.args.offset || 0;
if (offset < 0 || offset > size)
offset = 0;
const content = substr(log, offset, 2097152);
return {
enabled: true,
localtime: logfile != 'syslog',
content: content,
offset: offset + length(content)
};
}
},
clearRuntimeLog: {
call: function() {
const conf = cursor();
const logfile = conf.get('AdGuardHome', 'AdGuardHome', 'logfile') || '';
conf.unload('AdGuardHome');
if (!logfile)
return { cleared: false, reason: 'no_logfile' };
const source = logfile == 'syslog' ? '/tmp/AdGuardHometmp.log' : logfile;
return { cleared: writefile(source, '') != null };
}
}
}
};