- New runInstallOp helper for manager install-dir/template ops (rooted: sudo; rootless: run as the current manager user, which owns the tree). - adguard.sh, traefik.sh: container-config sed -> runFileOp. - crowdsec.sh: host crowdsec systemctl/apt-get -> runSystem. - dashy_update_conf.sh: conf-file mkdir/chown/md5sum/tee -> runFileOp/ runFileWrite; docker ps/restart -> dockerCommandRun. Deferred (cross-owner copy / temp-file across /tmp<->/docker, need rootless env to bridge correctly): owncloud_setup_config.sh, adguard_auth.sh. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: librelad <librelad@digitalangels.vip>
277 lines
12 KiB
Bash
277 lines
12 KiB
Bash
#!/bin/bash
|
|
|
|
# Category : Networking
|
|
# Description : AdGuard - DNS based Ad Blocking (c/u/s/r/i):
|
|
|
|
installAdguard()
|
|
{
|
|
local config_variables="$1"
|
|
|
|
if [[ "$adguard" == *[cCtTuUsSrRiI]* ]]; then
|
|
dockerConfigSetupToContainer silent adguard;
|
|
local app_name=$CFG_ADGUARD_APP_NAME
|
|
initializeAppVariables $app_name;
|
|
fi
|
|
|
|
if [[ "$adguard" == *[cC]* ]]; then
|
|
editAppConfig $app_name;
|
|
fi
|
|
|
|
if [[ "$adguard" == *[uU]* ]]; then
|
|
dockerUninstallApp $app_name;
|
|
fi
|
|
|
|
if [[ "$adguard" == *[sS]* ]]; then
|
|
dockerComposeDown $app_name;
|
|
fi
|
|
|
|
if [[ "$adguard" == *[rR]* ]]; then
|
|
dockerComposeRestart $app_name;
|
|
fi
|
|
|
|
if [[ "$adguard" == *[iI]* ]]; then
|
|
isHeader "Install $app_name"
|
|
|
|
((menu_number++))
|
|
echo ""
|
|
echo "---- $menu_number. Setting up install folder and config file for $app_name."
|
|
echo ""
|
|
|
|
dockerConfigSetupToContainer "loud" "$app_name" "install" "$config_variables";
|
|
isSuccessful "Install folders and Config files have been setup for $app_name."
|
|
|
|
((menu_number++))
|
|
echo ""
|
|
echo "---- $menu_number. Setting up the $app_name docker-compose.yml file."
|
|
echo ""
|
|
|
|
dockerComposeSetupFile $app_name;
|
|
|
|
monitoringToggleAppConfig "$app_name" "docker-compose.yml";
|
|
|
|
((menu_number++))
|
|
echo ""
|
|
echo "---- $menu_number. Updating file permissions before starting."
|
|
echo ""
|
|
|
|
fixPermissionsBeforeStart $app_name;
|
|
|
|
((menu_number++))
|
|
echo ""
|
|
echo "---- $menu_number. Running the docker-compose.yml to install and start $app_name"
|
|
echo ""
|
|
|
|
dockerComposeUpdateAndStartApp $app_name install;
|
|
|
|
((menu_number++))
|
|
echo ""
|
|
echo "---- $menu_number. Completing AdGuardHome initial setup automatically"
|
|
echo ""
|
|
|
|
# The legacy `$usedport1` variable is no longer populated by the
|
|
# current install pipeline; the resolved host port is stored in the
|
|
# PORTS_TAG_1 docker-compose tag (format `external:internal`). Pull
|
|
# it from there so the curl + URL printout actually point somewhere.
|
|
local adguard_compose_file="$containers_dir$app_name/docker-compose.yml"
|
|
local adguard_port_pair
|
|
adguard_port_pair=$(tagsManagerGetTagContent "$adguard_compose_file" "PORTS_TAG_1")
|
|
local adguard_admin_port="${adguard_port_pair%%:*}"
|
|
|
|
if [[ -n "$public_ip_v4" && -n "$adguard_admin_port" ]]; then
|
|
echo " External : http://$public_ip_v4:$adguard_admin_port/"
|
|
fi
|
|
if [[ -n "$host_setup" ]]; then
|
|
echo " Hostname : http://$host_setup/"
|
|
fi
|
|
echo ""
|
|
|
|
# AdGuardHome ships a setup wizard that normally needs five clicks in a
|
|
# browser before the daemon writes its config file. Same wizard is
|
|
# exposed as an HTTP API (POST /control/install/configure), so we
|
|
# drive it from here and skip the manual interaction. We pre-poll the
|
|
# admin endpoint until the container is up, then send the form, then
|
|
# let the existing post-install sed edits run against the freshly
|
|
# written AdGuardHome.yaml.
|
|
local adguard_setup_url="http://127.0.0.1:${adguard_admin_port}"
|
|
local adguard_attempts=0
|
|
local adguard_max_attempts=60
|
|
while ((adguard_attempts < adguard_max_attempts)); do
|
|
if curl -fsS -o /dev/null --max-time 2 "${adguard_setup_url}/control/status" 2>/dev/null \
|
|
|| curl -fsS -o /dev/null --max-time 2 "${adguard_setup_url}/control/install/get_addresses" 2>/dev/null; then
|
|
break
|
|
fi
|
|
sleep 2
|
|
((adguard_attempts++))
|
|
done
|
|
|
|
if ((adguard_attempts >= adguard_max_attempts)); then
|
|
isError "AdGuardHome admin endpoint did not respond on $adguard_setup_url within $((adguard_max_attempts * 2))s — open the URL and complete setup manually, then re-run the installer to apply the post-setup tweaks."
|
|
else
|
|
local adguard_user="${CFG_ADGUARD_USER:-admin}"
|
|
local adguard_pass="${CFG_ADGUARD_PASSWORD:-}"
|
|
if [[ -z "$adguard_pass" ]]; then
|
|
adguard_pass=$(generateRandomPassword)
|
|
updateConfigOption "CFG_ADGUARD_PASSWORD" "$adguard_pass" >/dev/null 2>&1 || true
|
|
isNotice "Generated a random AdGuardHome admin password and saved it to CFG_ADGUARD_PASSWORD."
|
|
fi
|
|
|
|
# Internal container ports are fixed (3000 admin, 53 DNS); host
|
|
# mapping is what `usedport1` etc. handle.
|
|
local adguard_payload
|
|
adguard_payload=$(cat <<JSON
|
|
{
|
|
"web": { "ip": "0.0.0.0", "port": 3000, "autofix": false },
|
|
"dns": { "ip": "0.0.0.0", "port": 53, "autofix": false },
|
|
"username": "${adguard_user}",
|
|
"password": "${adguard_pass}"
|
|
}
|
|
JSON
|
|
)
|
|
if curl -fsS -X POST \
|
|
-H 'Content-Type: application/json' \
|
|
--data "$adguard_payload" \
|
|
--max-time 15 \
|
|
"${adguard_setup_url}/control/install/configure" >/dev/null 2>&1; then
|
|
isSuccessful "AdGuardHome admin setup completed automatically (user: $adguard_user)."
|
|
else
|
|
# 422/403 here typically means setup was already done on a
|
|
# previous install; the post-setup tweaks below are still
|
|
# safe to run against the existing yaml.
|
|
isNotice "AdGuardHome /control/install/configure rejected the request — assuming it's already configured. If this is a fresh install, complete setup manually at $adguard_setup_url."
|
|
fi
|
|
# Small breather so AdGuardHome finishes flushing AdGuardHome.yaml
|
|
# to disk before the sed edits below touch it.
|
|
#sleep 3
|
|
fi
|
|
|
|
#result=$(sudo sed -i "s/address: 0.0.0.0:80/address: 0.0.0.0:${usedport2}/g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
|
|
#checkSuccess "Changing port 80 to $usedport2 for Admin Panel"
|
|
|
|
#result=$(sudo sed -i "s/port: 53/port: ${usedport3}/g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
|
|
#checkSuccess "Changing port 53 to $usedport3 for DNS Port"
|
|
|
|
#result=$(sudo sed -i "s/port_https: 443/port_https: ${usedport4}/g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
|
|
#checkSuccess "Changing port 443 to $usedport4 for DNS Port"
|
|
|
|
#result=$(sudo sed -i "s/port_dns_over_tls: 853/port_dns_over_tls: ${usedport5}/g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
|
|
#checkSuccess "Changing port 853 to $usedport5 for port_dns_over_tls"
|
|
|
|
#result=$(sudo sed -i "s/port_dns_over_quic: 853/port_dns_over_quic: ${usedport5}/g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
|
|
#checkSuccess "Changing port 853 to $usedport5 for port_dns_over_quic"
|
|
|
|
# NOTE: We deliberately do *not* force `tls.enabled: true` here.
|
|
# That section configures encrypted DNS (DoT/DoH/DoQ) and AdGuardHome
|
|
# crash-loops on startup with `[fatal] creating dns server: parsing
|
|
# tls key pair: tls: failed to find any PEM data in certificate input`
|
|
# if `enabled: true` is set without a real certificate pair pointed
|
|
# at by `certificate_path` / `private_key_path`. The admin user can
|
|
# opt into encrypted DNS from Settings → Encryption once they've
|
|
# provided a cert.
|
|
|
|
if [[ $public == "true" ]]; then
|
|
result=$(runFileOp sed -i "s|allow_unencrypted_doh: false|allow_unencrypted_doh: true|g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
|
|
checkSuccess "Setting allow_unencrypted_doh to false for Traefik"
|
|
fi
|
|
|
|
result=$(runFileOp sed -i "s|anonymize_client_ip: false: false|anonymize_client_ip: true|g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
|
|
checkSuccess "Setting anonymize_client_ip to true for privacy reasons"
|
|
|
|
# Force the admin web bind back to 0.0.0.0:3000 inside the container.
|
|
# The docker-compose mapping is `<host_port>:3000`, so the container
|
|
# MUST listen on 3000 internally for the host port to reach it. After
|
|
# the install API call AdGuardHome sometimes ends up bound to 0.0.0.0:80
|
|
# (its build-time default) instead of the port we sent — which is
|
|
# exactly what causes "unable to connect" on the host port.
|
|
local adguard_yaml="$containers_dir$app_name/conf/AdGuardHome.yaml"
|
|
if [[ -f "$adguard_yaml" ]]; then
|
|
# New schema (v0.107+): single `address: 0.0.0.0:NN` line under `http:`.
|
|
runFileOp sed -i 's|^\(\s*address:\s*\)0\.0\.0\.0:[0-9]\+|\10.0.0.0:3000|' "$adguard_yaml"
|
|
# Old schema fallback: separate `bind_host:` / `bind_port:` keys.
|
|
runFileOp sed -i 's|^\(\s*bind_host:\s*\).*|\10.0.0.0|' "$adguard_yaml"
|
|
runFileOp sed -i 's|^\(\s*bind_port:\s*\)[0-9]\+|\13000|' "$adguard_yaml"
|
|
checkSuccess "Pinned AdGuardHome admin bind to 0.0.0.0:3000 (matches the compose port mapping)."
|
|
fi
|
|
|
|
dockerComposeRestart "$app_name";
|
|
|
|
# Health-check after the restart so the user finds out *here* if
|
|
# AdGuardHome didn't come back up cleanly, rather than later when
|
|
# they try to open the URL and just see "unable to connect".
|
|
#
|
|
# Drop `-f` and accept any HTTP status code: now that the admin
|
|
# account is configured, `/control/status` returns 401 to an
|
|
# unauthenticated request — which is fine, it means the server is
|
|
# up and answering. We only care whether the connection succeeded
|
|
# at all, not what the response body says. `-w '%{http_code}'`
|
|
# gives us a 3-digit code on success and an empty string on a
|
|
# connection failure / timeout.
|
|
local adguard_health_attempts=0
|
|
while ((adguard_health_attempts < 20)); do
|
|
local adguard_health_code
|
|
adguard_health_code=$(curl -sS -o /dev/null --max-time 2 \
|
|
-w '%{http_code}' "${adguard_setup_url}/control/status" 2>/dev/null)
|
|
if [[ "$adguard_health_code" =~ ^[1-5][0-9][0-9]$ ]]; then
|
|
isSuccessful "AdGuardHome admin UI is reachable on $adguard_setup_url (HTTP $adguard_health_code)"
|
|
break
|
|
fi
|
|
sleep 1
|
|
((adguard_health_attempts++))
|
|
done
|
|
if ((adguard_health_attempts >= 20)); then
|
|
isError "AdGuardHome admin UI did not respond after restart on $adguard_setup_url. Check the container logs (\`docker logs adguard-service\`) and the conf/AdGuardHome.yaml bind address."
|
|
fi
|
|
|
|
((menu_number++))
|
|
echo ""
|
|
echo "---- $menu_number. Running Application specific updates (if required)"
|
|
echo ""
|
|
|
|
appUpdateSpecifics $app_name;
|
|
|
|
((menu_number++))
|
|
echo ""
|
|
echo "---- $menu_number. Running Headscale setup (if required)"
|
|
echo ""
|
|
|
|
setupHeadscale $app_name;
|
|
|
|
((menu_number++))
|
|
echo ""
|
|
echo "---- $menu_number. Adding $app_name to the Apps Database table."
|
|
echo ""
|
|
|
|
databaseInstallApp $app_name;
|
|
|
|
((menu_number++))
|
|
echo ""
|
|
echo "---- $menu_number. Updating the WebUI config file."
|
|
echo ""
|
|
|
|
webuiContainerSetup $app_name install;
|
|
|
|
((menu_number++))
|
|
echo ""
|
|
echo "---- $menu_number. Refreshing monitoring integration."
|
|
echo ""
|
|
|
|
monitoringRefreshAll;
|
|
|
|
((menu_number++))
|
|
echo ""
|
|
echo "---- $menu_number. You can find $app_name files at $containers_dir$app_name"
|
|
echo ""
|
|
echo " You can now navigate to your $app_name service using any of the options below : "
|
|
echo ""
|
|
|
|
# Same final-summary call shape as wireguard / vaultwarden. Pass the
|
|
# admin user/password we just configured so the user sees the
|
|
# credentials exactly once, at the end of the install.
|
|
menuShowFinalMessages "$app_name" "${CFG_ADGUARD_USER:-admin}" "$CFG_ADGUARD_PASSWORD";
|
|
|
|
menu_number=0
|
|
#sleep 3s
|
|
cd
|
|
fi
|
|
adguard=n
|
|
}
|