Compare commits
No commits in common. "e5f6f4c37192ec9dd427532d15f25e7c69bcd983" and "dec3055b630e8a67edd4d899feb2d67bf710efc3" have entirely different histories.
e5f6f4c371
...
dec3055b63
@ -11,8 +11,6 @@ appUpdateSpecifics()
|
|||||||
if [[ $CFG_REQUIREMENT_DNS_UPDATER == "true" ]]; then
|
if [[ $CFG_REQUIREMENT_DNS_UPDATER == "true" ]]; then
|
||||||
updateDNS $app_name install;
|
updateDNS $app_name install;
|
||||||
fi
|
fi
|
||||||
# Split-horizon local DNS: app subdomains resolve to the box on the LAN.
|
|
||||||
declare -F setupLocalDnsRewrites >/dev/null 2>&1 && setupLocalDnsRewrites
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $app_name == "libreportal" ]]; then
|
if [[ $app_name == "libreportal" ]]; then
|
||||||
|
|||||||
@ -9,6 +9,4 @@ appAdguardApplyDnsUpdater()
|
|||||||
fi
|
fi
|
||||||
updateDNS "adguard" "manual"
|
updateDNS "adguard" "manual"
|
||||||
isSuccessful "/etc/resolv.conf updated to use AdGuard as the host DNS resolver."
|
isSuccessful "/etc/resolv.conf updated to use AdGuard as the host DNS resolver."
|
||||||
# Split-horizon: make app subdomains resolve to the box on the LAN.
|
|
||||||
declare -F setupLocalDnsRewrites >/dev/null 2>&1 && setupLocalDnsRewrites
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,4 @@ appPiholeApplyDnsUpdater()
|
|||||||
fi
|
fi
|
||||||
updateDNS "pihole" "manual"
|
updateDNS "pihole" "manual"
|
||||||
isSuccessful "/etc/resolv.conf updated to use Pi-hole as the host DNS resolver."
|
isSuccessful "/etc/resolv.conf updated to use Pi-hole as the host DNS resolver."
|
||||||
# Split-horizon: make app subdomains resolve to the box on the LAN.
|
|
||||||
declare -F setupLocalDnsRewrites >/dev/null 2>&1 && setupLocalDnsRewrites
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,10 +11,11 @@ setupDNSIP()
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# STUB: meant to resolve the reachable IP of the given resolver
|
# Build variable names based on app_name
|
||||||
# (adguard/pihole) into $dns_ip_setup for updateDNS. Not implemented yet,
|
dns_host_name_var="CFG_${app_name^^}_HOST_NAME"
|
||||||
# so updateDNS falls back to CFG_DNS_SERVER_* upstreams. (Previously read
|
|
||||||
# CFG_<APP>_HOST_NAME here, which is unused — removed during the per-port
|
# Access the variables using variable indirection
|
||||||
# subdomain refactor so HOST_NAME has no remaining DNS dependency.)
|
dns_host_name="${!dns_host_name_var}"
|
||||||
:
|
|
||||||
|
# UNFINISHED
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,140 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Split-horizon ("local") DNS.
|
|
||||||
#
|
|
||||||
# Points every configured domain at the LibrePortal server's LAN IP inside the
|
|
||||||
# self-hosted resolver, so app subdomains resolve to the box on the local
|
|
||||||
# network and hit Traefik directly — with valid Let's Encrypt certs — instead
|
|
||||||
# of bouncing out to the public IP (which most home routers can't hairpin).
|
|
||||||
#
|
|
||||||
# Domain-level, single-host model: every domain -> the one server IP. Traefik
|
|
||||||
# sorts out which app by Host header. Adding apps/domains later is covered
|
|
||||||
# automatically (AdGuard wildcard) or on the next run (Pi-hole host list).
|
|
||||||
#
|
|
||||||
# SAFE BY CONSTRUCTION: every action is idempotent, guarded by an "installed"
|
|
||||||
# check, and cannot corrupt the resolver if a detail is off —
|
|
||||||
# * AdGuard goes through its REST API; a bad URL/cred just fails harmlessly.
|
|
||||||
# * Pi-hole writes ONLY to the supported, mounted /etc/pihole/custom.list,
|
|
||||||
# inside a clearly-marked managed block (never touches /etc/dnsmasq.d).
|
|
||||||
# NOTE: the AdGuard API call and the Pi-hole reload need a live smoke-test —
|
|
||||||
# they can't be exercised without the running containers.
|
|
||||||
|
|
||||||
# The server's LAN IP (the source address used to reach the network).
|
|
||||||
localDnsServerIp() {
|
|
||||||
local ip
|
|
||||||
ip=$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{for (i=1;i<=NF;i++) if ($i=="src") {print $(i+1); exit}}')
|
|
||||||
[[ -z "$ip" ]] && ip="${public_ip_v4:-}" # last-resort fallback
|
|
||||||
printf '%s' "$ip"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Configured (non-empty) domains, one per line.
|
|
||||||
localDnsDomains() {
|
|
||||||
local i d
|
|
||||||
for i in 1 2 3 4 5 6 7 8 9; do
|
|
||||||
d="CFG_DOMAIN_${i}"; d="${!d:-}"
|
|
||||||
[[ -n "$d" ]] && printf '%s\n' "$d"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Every app host (subdomain.domain) across all apps' Traefik-managed ports —
|
|
||||||
# for resolvers without wildcard support (Pi-hole custom.list). Mirrors the
|
|
||||||
# per-port host rule: subdomain ""/@/root -> apex, else <subdomain>.<domain>.
|
|
||||||
localDnsAppHosts() {
|
|
||||||
local cfg app up d_idx dom portv sub host
|
|
||||||
local -a parts
|
|
||||||
for cfg in "${containers_dir}"*/*.config; do
|
|
||||||
[[ -f "$cfg" ]] || continue
|
|
||||||
app=$(basename "$cfg" .config); up=${app^^}
|
|
||||||
d_idx=$(grep -oE "CFG_${up}_DOMAIN=[0-9]+" "$cfg" | head -1 | cut -d= -f2)
|
|
||||||
[[ -z "$d_idx" ]] && continue
|
|
||||||
dom="CFG_DOMAIN_${d_idx}"; dom="${!dom:-}"
|
|
||||||
[[ -z "$dom" ]] && continue
|
|
||||||
while IFS= read -r portv; do
|
|
||||||
IFS='|' read -ra parts <<< "$portv"
|
|
||||||
[[ "${parts[6]:-}" == "true" ]] || continue # Traefik-managed only
|
|
||||||
sub="${parts[10]:-}"
|
|
||||||
if [[ "$sub" == "@" || "$sub" == "root" ]]; then
|
|
||||||
host="$dom" # apex (explicit only)
|
|
||||||
elif [[ -n "$sub" ]]; then
|
|
||||||
host="${sub}.${dom}"
|
|
||||||
else
|
|
||||||
host="${app}.${dom}" # no subdomain set -> app-name default
|
|
||||||
fi
|
|
||||||
printf '%s\n' "$host"
|
|
||||||
done < <(grep -oE "CFG_${up}_PORT_[0-9]+=\"[^\"]*\"" "$cfg" | sed -E 's/^[^"]*"//; s/"$//')
|
|
||||||
done | sort -u
|
|
||||||
}
|
|
||||||
|
|
||||||
# AdGuard Home: wildcard rewrite per domain (covers every subdomain) + apex,
|
|
||||||
# via the REST API. Idempotent (delete-then-add). Admin port discovered at
|
|
||||||
# runtime via `docker port`, creds from the saved CFG_ADGUARD_* values.
|
|
||||||
localDnsApplyAdguard() {
|
|
||||||
local ip="$1"; shift
|
|
||||||
local user="${CFG_ADGUARD_ADMIN_USER:-admin}"
|
|
||||||
local pass="${CFG_ADGUARD_ADMIN_PASSWORD:-${CFG_ADGUARD_PASSWORD:-}}"
|
|
||||||
local hostport base d entry payload
|
|
||||||
hostport=$(dockerCommandRun "docker port adguard-service 3000/tcp" 2>/dev/null | head -1 | sed 's/.*://')
|
|
||||||
if [[ -z "$hostport" ]]; then
|
|
||||||
isNotice "AdGuard admin port not found (running?). Skipping AdGuard rewrites."
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
base="http://127.0.0.1:${hostport}"
|
|
||||||
for d in "$@"; do
|
|
||||||
for entry in "*.${d}" "${d}"; do
|
|
||||||
payload="{\"domain\":\"${entry}\",\"answer\":\"${ip}\"}"
|
|
||||||
curl -fsS -u "${user}:${pass}" -H 'Content-Type: application/json' \
|
|
||||||
-X POST "${base}/control/rewrite/delete" -d "$payload" >/dev/null 2>&1
|
|
||||||
if curl -fsS -u "${user}:${pass}" -H 'Content-Type: application/json' \
|
|
||||||
-X POST "${base}/control/rewrite/add" -d "$payload" >/dev/null 2>&1; then
|
|
||||||
isSuccessful "AdGuard rewrite: ${entry} -> ${ip}"
|
|
||||||
else
|
|
||||||
isNotice "AdGuard rewrite for ${entry} not applied (check API URL/creds) — safe to retry."
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Pi-hole: per-host A records in the mounted, supported custom.list, inside a
|
|
||||||
# managed block (no wildcards on Pi-hole; no touching /etc/dnsmasq.d). Reloads
|
|
||||||
# via `pihole restartdns`, falling back to a container restart.
|
|
||||||
localDnsApplyPihole() {
|
|
||||||
local ip="$1"
|
|
||||||
local list="${containers_dir}pihole/pihole-dnsmasq-unbound/custom.list" # mounts to /etc/pihole
|
|
||||||
local b="# >>> libreportal-local >>>" e="# <<< libreportal-local <<<"
|
|
||||||
local hosts tmp h n
|
|
||||||
hosts=$(localDnsAppHosts)
|
|
||||||
if [[ -z "$hosts" ]]; then isNotice "No app hosts for Pi-hole — skipping."; return 0; fi
|
|
||||||
tmp=$(sudo mktemp)
|
|
||||||
if [[ -f "$list" ]]; then # preserve anything outside our block
|
|
||||||
sudo awk -v b="$b" -v e="$e" '$0==b{skip=1} !skip{print} $0==e{skip=0}' "$list" | sudo tee "$tmp" >/dev/null
|
|
||||||
fi
|
|
||||||
{
|
|
||||||
echo "$b"
|
|
||||||
while IFS= read -r h; do [[ -n "$h" ]] && echo "${ip} ${h}"; done <<< "$hosts"
|
|
||||||
echo "$e"
|
|
||||||
} | sudo tee -a "$tmp" >/dev/null
|
|
||||||
sudo cp "$tmp" "$list"; sudo rm -f "$tmp"
|
|
||||||
n=$(printf '%s\n' "$hosts" | grep -c .)
|
|
||||||
dockerCommandRun "docker exec pihole-service pihole restartdns" >/dev/null 2>&1 || dockerComposeRestart pihole
|
|
||||||
isSuccessful "Pi-hole custom.list updated: ${n} hosts -> ${ip}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Orchestrator — call after a domain change or app install.
|
|
||||||
setupLocalDnsRewrites() {
|
|
||||||
isHeader "Local (split-horizon) DNS"
|
|
||||||
local ip domains
|
|
||||||
ip=$(localDnsServerIp)
|
|
||||||
[[ -z "$ip" ]] && { isNotice "Could not determine the server LAN IP — skipping local DNS."; return 0; }
|
|
||||||
domains=$(localDnsDomains)
|
|
||||||
[[ -z "$domains" ]] && { isNotice "No domains configured (CFG_DOMAIN_*) — skipping local DNS."; return 0; }
|
|
||||||
isNotice "Server IP ${ip} · domains: $(printf '%s ' $domains)"
|
|
||||||
|
|
||||||
local did=0
|
|
||||||
if [[ "$(dockerCheckAppInstalled "adguard" "docker")" == "installed" ]]; then
|
|
||||||
localDnsApplyAdguard "$ip" $domains; did=1
|
|
||||||
fi
|
|
||||||
if [[ "$(dockerCheckAppInstalled "pihole" "docker")" == "installed" ]]; then
|
|
||||||
localDnsApplyPihole "$ip"; did=1
|
|
||||||
fi
|
|
||||||
[[ "$did" -eq 0 ]] && isNotice "Neither AdGuard nor Pi-hole installed — local DNS skipped."
|
|
||||||
}
|
|
||||||
@ -38,12 +38,10 @@ tagsProcessorPortSubdomains()
|
|||||||
|
|
||||||
if [[ "$p_traefik" == "true" ]]; then
|
if [[ "$p_traefik" == "true" ]]; then
|
||||||
local host
|
local host
|
||||||
if [[ "$p_sub" == "@" || "$p_sub" == "root" ]]; then
|
if [[ -z "$p_sub" || "$p_sub" == "@" || "$p_sub" == "root" ]]; then
|
||||||
host="${domain_full}" # apex / root of the domain (explicit only)
|
host="${domain_full}" # apex / root of the domain
|
||||||
elif [[ -n "$p_sub" ]]; then
|
|
||||||
host="${p_sub}.${domain_full}" # subdomain (single or multi-level)
|
|
||||||
else
|
else
|
||||||
host="${app_name}.${domain_full}" # no subdomain set -> app-name default
|
host="${p_sub}.${domain_full}" # subdomain (single or multi-level)
|
||||||
fi
|
fi
|
||||||
tagsManagerUpdateUniversalTag "$file" "DOMAINSUBNAME_TAG_${idx}" "$host"
|
tagsManagerUpdateUniversalTag "$file" "DOMAINSUBNAME_TAG_${idx}" "$host"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -16,7 +16,6 @@ network_scripts=(
|
|||||||
"network/display/show_traefik_services.sh"
|
"network/display/show_traefik_services.sh"
|
||||||
"network/dns/setup_dns_ip.sh"
|
"network/dns/setup_dns_ip.sh"
|
||||||
"network/dns/setup_dns.sh"
|
"network/dns/setup_dns.sh"
|
||||||
"network/dns/setup_local_dns.sh"
|
|
||||||
"network/firewall/firewall_initial_setup.sh"
|
"network/firewall/firewall_initial_setup.sh"
|
||||||
"network/firewall/rules/firewall_clear_rules.sh"
|
"network/firewall/rules/firewall_clear_rules.sh"
|
||||||
"network/firewall/rules/firewall_rebuild_from_db.sh"
|
"network/firewall/rules/firewall_rebuild_from_db.sh"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user