updating dns override
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 4s

This commit is contained in:
2026-05-16 11:16:59 -03:00
parent c800ec1888
commit 4976227ce8

View File

@@ -3,7 +3,8 @@
set -euo pipefail
readonly SCRIPT_NAME="dns-override.sh"
readonly SCRIPT_VERSION="1.0.0"
readonly SCRIPT_VERSION="1.0.2"
readonly IMMUTABLE_PROMPT_TIMEOUT_SEC=20
readonly NC='\033[0m'
readonly RED='\033[1;31m'
@@ -22,6 +23,8 @@ readonly EMOJI_NET='🌐'
readonly EMOJI_LOCK='🔒'
SUDO_BIN=""
IS_REMOTE_SSH=0
IMMUTABLE_LOCK_REQUESTED=0
declare -a DNS_SERVERS=()
declare -a DETECTED_MANAGERS=()
declare -a ACTIONS_ATTEMPTED=()
@@ -44,6 +47,27 @@ command_exists() {
command -v "$1" >/dev/null 2>&1
}
binary_or_path_exists() {
local bin_name="$1"
shift
if command_exists "$bin_name"; then
return 0
fi
local path_candidate
for path_candidate in "$@"; do
[[ -x "$path_candidate" ]] && return 0
done
return 1
}
systemd_unit_exists() {
local unit_name="$1"
command_exists systemctl || return 1
systemctl list-unit-files "$unit_name" >/dev/null 2>&1
}
trim() {
local value="$1"
value="${value#"${value%%[![:space:]]*}"}"
@@ -98,6 +122,15 @@ read_tty_input() {
printf '%s' "$(trim "$value")"
}
read_tty_input_timeout() {
local prompt="$1"
local timeout_sec="$2"
local value
printf "%b" "$prompt" >/dev/tty
IFS= read -r -t "$timeout_sec" value </dev/tty || return $?
printf '%s' "$(trim "$value")"
}
run_root() {
if [[ -n "$SUDO_BIN" ]]; then
"$SUDO_BIN" "$@"
@@ -144,6 +177,49 @@ ensure_root_context() {
SUDO_BIN="sudo"
}
detect_runtime_context() {
if [[ -n "${SSH_CONNECTION:-}" || -n "${SSH_TTY:-}" ]]; then
IS_REMOTE_SSH=1
log_warning "SSH session detected. Using safer apply behavior to reduce connection drops."
fi
}
has_systemd_resolved() {
binary_or_path_exists "resolvectl" "/usr/bin/resolvectl" "/bin/resolvectl" && return 0
systemd_unit_exists "systemd-resolved.service" && return 0
binary_or_path_exists "systemd-resolved" "/lib/systemd/systemd-resolved" "/usr/lib/systemd/systemd-resolved" && return 0
return 1
}
has_networkmanager() {
binary_or_path_exists "nmcli" "/usr/bin/nmcli" "/bin/nmcli" && return 0
binary_or_path_exists "NetworkManager" "/usr/sbin/NetworkManager" "/sbin/NetworkManager" && return 0
systemd_unit_exists "NetworkManager.service" && return 0
return 1
}
has_resolvconf_openresolv() {
binary_or_path_exists "resolvconf" "/sbin/resolvconf" "/usr/sbin/resolvconf" && return 0
return 1
}
has_dhclient() {
binary_or_path_exists "dhclient" "/sbin/dhclient" "/usr/sbin/dhclient" && return 0
return 1
}
has_systemd_networkd() {
systemd_unit_exists "systemd-networkd.service" && return 0
binary_or_path_exists "systemd-networkd" "/lib/systemd/systemd-networkd" "/usr/lib/systemd/systemd-networkd" && return 0
return 1
}
has_ifupdown() {
binary_or_path_exists "ifup" "/sbin/ifup" "/usr/sbin/ifup" && return 0
[[ -f /etc/network/interfaces || -d /etc/network/interfaces.d ]] && return 0
return 1
}
add_manager() {
local manager="$1"
if ! contains_value "$manager" "${DETECTED_MANAGERS[@]}"; then
@@ -154,31 +230,27 @@ add_manager() {
detect_managers() {
DETECTED_MANAGERS=()
if command_exists systemctl && systemctl is-active --quiet systemd-resolved 2>/dev/null; then
add_manager "systemd-resolved"
elif [[ -L /etc/resolv.conf ]] && readlink /etc/resolv.conf 2>/dev/null | grep -Eq 'systemd/resolve'; then
if has_systemd_resolved; then
add_manager "systemd-resolved"
fi
if command_exists nmcli || [[ -d /etc/NetworkManager ]]; then
if has_networkmanager; then
add_manager "NetworkManager"
fi
if command_exists resolvconf || [[ -d /etc/resolvconf ]] || [[ -f /etc/resolvconf.conf ]]; then
if has_resolvconf_openresolv; then
add_manager "resolvconf/openresolv"
fi
if command_exists dhclient || [[ -f /etc/dhcp/dhclient.conf ]]; then
if has_dhclient; then
add_manager "dhclient"
fi
if command_exists systemctl && systemctl is-active --quiet systemd-networkd 2>/dev/null; then
add_manager "systemd-networkd"
elif [[ -d /etc/systemd/network ]]; then
if has_systemd_networkd; then
add_manager "systemd-networkd"
fi
if [[ -f /etc/network/interfaces ]] || [[ -d /etc/network/interfaces.d ]]; then
if has_ifupdown; then
add_manager "ifupdown"
fi
}
@@ -336,7 +408,7 @@ run_action() {
rc=$?
if [[ "$rc" -eq 2 ]]; then
ACTIONS_SKIPPED+=("$action")
log_info "$action not applicable on this system"
log_info "$action skipped: target manager is not present on this host"
return 0
fi
@@ -374,7 +446,8 @@ apply_static_resolv_conf() {
}
apply_systemd_resolved() {
if ! command_exists systemctl && ! command_exists resolvectl && [[ ! -d /etc/systemd ]]; then
if ! has_systemd_resolved; then
log_info "Skipping systemd-resolved override: systemd-resolved is not present."
return 2
fi
@@ -409,7 +482,8 @@ apply_systemd_resolved() {
}
apply_networkmanager() {
if ! command_exists nmcli && [[ ! -d /etc/NetworkManager ]]; then
if ! has_networkmanager; then
log_info "Skipping NetworkManager override: NetworkManager is not present."
return 2
fi
@@ -430,21 +504,37 @@ apply_networkmanager() {
run_root nmcli connection modify "$conn" ipv6.ignore-auto-dns yes >/dev/null 2>&1 || true
run_root nmcli connection modify "$conn" ipv4.dns "$dns_csv" >/dev/null 2>&1 || true
run_root nmcli connection modify "$conn" ipv6.dns "$dns_csv" >/dev/null 2>&1 || true
done < <(nmcli -g NAME connection show --active 2>/dev/null || true)
run_root nmcli connection reload >/dev/null 2>&1 || true
if [[ "$IS_REMOTE_SSH" -eq 1 ]]; then
log_warning "Skipping NetworkManager reconnect/restart operations in SSH session."
local dev
while IFS= read -r dev; do
[[ -n "$dev" && "$dev" != "lo" ]] || continue
run_root nmcli device reapply "$dev" >/dev/null 2>&1 || true
done < <(nmcli -t -f DEVICE,STATE device status 2>/dev/null | awk -F: '$2=="connected" || $2=="connecting" {print $1}')
else
while IFS= read -r conn; do
[[ -n "$conn" ]] || continue
run_root nmcli connection up "$conn" >/dev/null 2>&1 || true
done < <(nmcli -g NAME connection show --active 2>/dev/null || true)
fi
run_root nmcli general reload >/dev/null 2>&1 || true
fi
if command_exists systemctl; then
run_root systemctl reload NetworkManager >/dev/null 2>&1 || run_root systemctl restart NetworkManager >/dev/null 2>&1 || true
run_root systemctl reload NetworkManager >/dev/null 2>&1 || true
fi
return 0
}
apply_resolvconf_openresolv() {
if ! command_exists resolvconf && [[ ! -d /etc/resolvconf ]] && [[ ! -f /etc/resolvconf.conf ]]; then
if ! has_resolvconf_openresolv; then
log_info "Skipping resolvconf/openresolv override: resolvconf/openresolv is not present."
return 2
fi
@@ -464,7 +554,8 @@ apply_resolvconf_openresolv() {
}
apply_dhclient() {
if ! command_exists dhclient && [[ ! -f /etc/dhcp/dhclient.conf ]] && [[ ! -d /etc/dhcp ]]; then
if ! has_dhclient; then
log_info "Skipping dhclient override: dhclient is not present."
return 2
fi
@@ -485,7 +576,8 @@ apply_dhclient() {
}
apply_systemd_networkd() {
if ! command_exists systemctl && [[ ! -d /etc/systemd/network ]]; then
if ! has_systemd_networkd; then
log_info "Skipping systemd-networkd override: systemd-networkd is not present."
return 2
fi
@@ -515,7 +607,18 @@ apply_systemd_networkd() {
if command_exists systemctl; then
run_root systemctl daemon-reload >/dev/null 2>&1 || true
if command_exists networkctl; then
run_root networkctl reload >/dev/null 2>&1 || true
if [[ "$IS_REMOTE_SSH" -eq 0 ]]; then
run_root networkctl reconfigure --all >/dev/null 2>&1 || true
else
log_warning "Skipping networkctl reconfigure --all in SSH session."
fi
elif [[ "$IS_REMOTE_SSH" -eq 0 ]]; then
run_root systemctl restart systemd-networkd >/dev/null 2>&1 || run_root systemctl try-restart systemd-networkd >/dev/null 2>&1 || true
else
log_warning "Skipping systemd-networkd restart in SSH session."
fi
fi
return 0
@@ -567,7 +670,8 @@ patch_ifupdown_file() {
}
apply_ifupdown() {
if [[ ! -f /etc/network/interfaces ]] && [[ ! -d /etc/network/interfaces.d ]]; then
if ! has_ifupdown; then
log_info "Skipping ifupdown override: ifupdown is not present."
return 2
fi
@@ -611,20 +715,39 @@ prompt_immutable_lock() {
}
local answer
answer="$(read_tty_input "${YELLOW}${EMOJI_LOCK} Make /etc/resolv.conf immutable with chattr +i? [y/N]: ${NC}")" || return 1
if ! answer="$(read_tty_input_timeout "${YELLOW}${EMOJI_LOCK} Make /etc/resolv.conf immutable with chattr +i? [y/N] (auto No in ${IMMUTABLE_PROMPT_TIMEOUT_SEC}s): ${NC}" "$IMMUTABLE_PROMPT_TIMEOUT_SEC")"; then
local rc=$?
if [[ "$rc" -gt 128 ]]; then
log_warning "No response received for immutable lock prompt; defaulting to No."
IMMUTABLE_LOCK_REQUESTED=0
return 0
fi
return 1
fi
case "$answer" in
y|Y|yes|YES)
IMMUTABLE_LOCK_REQUESTED=1
log_info "Immutable lock requested. It will be applied after DNS updates."
;;
*)
IMMUTABLE_LOCK_REQUESTED=0
log_info "Immutable lock skipped."
;;
esac
}
apply_immutable_lock_if_requested() {
if [[ "$IMMUTABLE_LOCK_REQUESTED" -ne 1 ]]; then
return 0
fi
if run_root chattr +i /etc/resolv.conf; then
log_success "Immutable lock applied to /etc/resolv.conf."
log_warning "To edit DNS later, run: sudo chattr -i /etc/resolv.conf"
else
log_warning "Failed to apply immutable lock."
fi
;;
*)
log_info "Immutable lock skipped."
;;
esac
}
print_summary() {
@@ -669,7 +792,9 @@ main() {
require_tty
ensure_root_context "$@"
detect_runtime_context
prompt_dns_servers
prompt_immutable_lock || log_warning "Immutable lock prompt failed; defaulting to unlocked."
run_connectivity_checks
detect_managers
@@ -681,7 +806,7 @@ main() {
run_action "Apply ifupdown override" apply_ifupdown
run_action "Write /etc/resolv.conf directly" apply_static_resolv_conf
prompt_immutable_lock || log_warning "Immutable lock prompt failed; continuing."
run_action "Apply immutable /etc/resolv.conf lock (if requested)" apply_immutable_lock_if_requested
print_summary
echo