mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 15:03:01 +02:00
- Add enforce-resolved.sh: validates ReadEtcHosts=yes, prevents DNSOverTLS bypass, removes drop-in overrides, locks drop-in dir - Add resolved-guard.path/service: watches /etc/systemd/resolved.conf and its drop-in directory for tampering - Update pacman hooks to unlock/relock nsswitch.conf and resolved.conf alongside /etc/hosts during package transactions - Extend setup_hosts_guard.sh with --skip-resolved option, resolved canonical snapshot, drop-in directory locking, and enforcement - Add resolved.conf checks to check_and_enable_services.sh: validates ReadEtcHosts, DNSOverTLS, drop-in overrides, immutable attribute, and resolved-guard.path status with auto-fix capability Fixed on live system: ReadEtcHosts was set to 'no' and nsswitch.conf was missing 'files' in the hosts line, completely bypassing /etc/hosts.
148 lines
4.8 KiB
Bash
Executable File
148 lines
4.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# Guard script to enforce canonical /etc/systemd/resolved.conf
|
|
# Ensures ReadEtcHosts=yes and prevents DNS-over-TLS bypass of /etc/hosts
|
|
# Installed to /usr/local/sbin/enforce-resolved.sh by setup_hosts_guard.sh
|
|
|
|
set -euo pipefail
|
|
|
|
CANONICAL_SOURCE="/usr/local/share/locked-resolved.conf"
|
|
TARGET="/etc/systemd/resolved.conf"
|
|
DROPIN_DIR="/etc/systemd/resolved.conf.d"
|
|
LOG_FILE="/var/log/resolved-guard.log"
|
|
|
|
log() {
|
|
printf '%s - %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" | tee -a "$LOG_FILE" >&2
|
|
}
|
|
|
|
# Validate that resolved.conf has correct settings to honour /etc/hosts
|
|
# Critical settings:
|
|
# ReadEtcHosts=yes — must be present and set to yes
|
|
# DNSOverTLS=no — must NOT be opportunistic/yes (bypasses local hosts)
|
|
validate_resolved() {
|
|
local file="$1"
|
|
|
|
# ReadEtcHosts must be explicitly yes (not commented, not "no")
|
|
local read_hosts
|
|
read_hosts=$(grep -E '^\s*ReadEtcHosts\s*=' "$file" 2>/dev/null | tail -1 |
|
|
sed 's/.*=\s*//' | tr -d '[:space:]')
|
|
if [[ "$read_hosts" != "yes" ]]; then
|
|
log "INVALID: ReadEtcHosts='$read_hosts' (expected 'yes') in $file"
|
|
return 1
|
|
fi
|
|
|
|
# DNSOverTLS must not be set to yes or opportunistic
|
|
local dot
|
|
dot=$(grep -E '^\s*DNSOverTLS\s*=' "$file" 2>/dev/null | tail -1 |
|
|
sed 's/.*=\s*//' | tr -d '[:space:]')
|
|
if [[ -n "$dot" && "$dot" != "no" ]]; then
|
|
log "INVALID: DNSOverTLS='$dot' (must be 'no' or commented out) in $file"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Remove any drop-in overrides that could bypass protections
|
|
enforce_no_dropins() {
|
|
if [[ -d "$DROPIN_DIR" ]]; then
|
|
local count
|
|
count=$(find "$DROPIN_DIR" -name '*.conf' -type f 2>/dev/null | wc -l)
|
|
if [[ "$count" -gt 0 ]]; then
|
|
log "TAMPERING: Found $count drop-in config(s) in $DROPIN_DIR — removing"
|
|
find "$DROPIN_DIR" -name '*.conf' -type f -delete
|
|
log "Removed all drop-in overrides"
|
|
fi
|
|
# Lock the directory itself to prevent new drop-ins
|
|
chattr +i "$DROPIN_DIR" 2>/dev/null || log "Failed to lock $DROPIN_DIR"
|
|
else
|
|
# Create and lock the directory to prevent creation with overrides
|
|
mkdir -p "$DROPIN_DIR"
|
|
chattr +i "$DROPIN_DIR" 2>/dev/null || log "Failed to lock $DROPIN_DIR"
|
|
log "Created and locked empty $DROPIN_DIR"
|
|
fi
|
|
}
|
|
|
|
# Main enforcement logic
|
|
log "Starting resolved.conf enforcement"
|
|
|
|
# 1. Handle drop-in overrides first
|
|
enforce_no_dropins
|
|
|
|
# 2. Check current resolved.conf
|
|
if [[ ! -f "$TARGET" ]]; then
|
|
log "CRITICAL: $TARGET does not exist"
|
|
if [[ -f "$CANONICAL_SOURCE" ]]; then
|
|
chattr -i "$TARGET" 2>/dev/null || true
|
|
cp "$CANONICAL_SOURCE" "$TARGET"
|
|
chmod 644 "$TARGET"
|
|
chattr +i "$TARGET" 2>/dev/null || log "Failed to set immutable on $TARGET"
|
|
log "Restored $TARGET from canonical copy"
|
|
else
|
|
log "ERROR: No canonical source at $CANONICAL_SOURCE — cannot restore"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
if ! validate_resolved "$TARGET"; then
|
|
log "TAMPERING DETECTED in $TARGET"
|
|
|
|
if [[ -f "$CANONICAL_SOURCE" ]]; then
|
|
chattr -i "$TARGET" 2>/dev/null || true
|
|
cp "$CANONICAL_SOURCE" "$TARGET"
|
|
chmod 644 "$TARGET"
|
|
chattr +i "$TARGET" 2>/dev/null || log "Failed to set immutable on $TARGET"
|
|
log "Restored $TARGET from canonical copy"
|
|
else
|
|
log "No canonical source — applying emergency fix"
|
|
chattr -i "$TARGET" 2>/dev/null || true
|
|
|
|
# Fix ReadEtcHosts
|
|
if grep -qE '^\s*ReadEtcHosts\s*=' "$TARGET"; then
|
|
sed -i -E 's/^\s*ReadEtcHosts\s*=.*/ReadEtcHosts=yes/' "$TARGET"
|
|
elif grep -q '^\[Resolve\]' "$TARGET"; then
|
|
sed -i '/^\[Resolve\]/a ReadEtcHosts=yes' "$TARGET"
|
|
else
|
|
printf '\n[Resolve]\nReadEtcHosts=yes\n' >>"$TARGET"
|
|
fi
|
|
|
|
# Fix DNSOverTLS
|
|
if grep -qE '^\s*DNSOverTLS\s*=' "$TARGET"; then
|
|
sed -i -E 's/^\s*DNSOverTLS\s*=.*/#DNSOverTLS=no/' "$TARGET"
|
|
fi
|
|
|
|
chattr +i "$TARGET" 2>/dev/null || true
|
|
log "Emergency fix applied"
|
|
fi
|
|
|
|
# Restart resolved to pick up changes
|
|
systemctl restart systemd-resolved 2>/dev/null || log "Failed to restart systemd-resolved"
|
|
exit 0
|
|
fi
|
|
|
|
# 3. If canonical exists, check for any drift
|
|
if [[ -f "$CANONICAL_SOURCE" ]]; then
|
|
if ! cmp -s "$CANONICAL_SOURCE" "$TARGET"; then
|
|
log "Drift detected in $TARGET — restoring canonical"
|
|
chattr -i "$TARGET" 2>/dev/null || true
|
|
cp "$CANONICAL_SOURCE" "$TARGET"
|
|
chmod 644 "$TARGET"
|
|
chattr +i "$TARGET" 2>/dev/null || log "Failed to set immutable"
|
|
log "Restored $TARGET from canonical copy"
|
|
systemctl restart systemd-resolved 2>/dev/null || log "Failed to restart systemd-resolved"
|
|
else
|
|
log "No drift detected in $TARGET"
|
|
fi
|
|
else
|
|
log "Creating initial canonical snapshot"
|
|
mkdir -p "$(dirname "$CANONICAL_SOURCE")"
|
|
cp "$TARGET" "$CANONICAL_SOURCE"
|
|
chmod 644 "$CANONICAL_SOURCE"
|
|
chattr +i "$CANONICAL_SOURCE" 2>/dev/null || log "Failed to protect canonical copy"
|
|
fi
|
|
|
|
# 4. Ensure immutable attribute is set on live file
|
|
chattr -i "$TARGET" 2>/dev/null || true
|
|
chattr +i "$TARGET" 2>/dev/null || log "Failed to set immutable on $TARGET"
|
|
|
|
log "resolved.conf enforcement complete"
|