diff --git a/auto-updates-TESTING.sh b/auto-updates-TESTING.sh deleted file mode 100644 index 193a4d3..0000000 --- a/auto-updates-TESTING.sh +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/env bash -# -# setup-auto-updates.sh -# -# Universal unattended-upgrades setup for Debian & Ubuntu. -# - Uses Origins-Pattern (not legacy Allowed-Origins) for forward-compatibility -# - Works with sources.list and deb822 .sources -# - Enables security + updates pockets; optional Ubuntu ESM if present -# - Validates with a dry-run; resilient to apt/dpkg locks -# -# Notes: -# * Backports/proposed are intentionally not enabled. -# * On non-systemd hosts, APT::Periodic drives execution via cron. - -set -Eeuo pipefail -trap 'echo "[ERROR] Line $LINENO failed" >&2' ERR - -REBOOT_TIME="${REBOOT_TIME:-04:00}" - -OS="" -CODENAME="" -declare -a POLICY_PAIRS=() # "Origin:Archive" from apt-cache -declare -a ORIGIN_PATTERNS=() # "origin=...,archive=..." - -require_root() { - if [[ $EUID -ne 0 ]]; then - echo "[ERROR] Run as root (sudo)." >&2 - exit 1 - fi -} - -detect_os() { - [[ -f /etc/os-release ]] || { echo "[ERROR] /etc/os-release not found." >&2; exit 1; } - # shellcheck disable=SC1091 - . /etc/os-release - case "${ID,,}" in - debian) OS="debian" ;; - ubuntu) OS="ubuntu" ;; - *) [[ "${ID_LIKE:-}" =~ debian ]] && OS="debian" || { echo "[ERROR] Unsupported OS: ${ID}."; exit 1; } ;; - esac - - CODENAME="${VERSION_CODENAME:-${UBUNTU_CODENAME:-}}" - if [[ -z "$CODENAME" ]] && command -v lsb_release >/dev/null 2>&1; then - CODENAME="$(lsb_release -cs 2>/dev/null || true)" - fi - if [[ -z "$CODENAME" ]]; then - CODENAME="$(apt-cache policy 2>/dev/null | sed -n 's/.*n=\([^,]*\).*/\1/p' | grep -E '^[a-z]+$' | head -n1 || true)" - fi - echo "[INFO] Detected OS: $OS codename: ${CODENAME:-unknown}" -} - -wait_for_apt() { - echo "[INFO] Checking for apt/dpkg locks…" - local locks=(/var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/lib/apt/lists/lock) - for i in {1..90}; do - if ! fuser "${locks[@]}" >/dev/null 2>&1; then - echo "[INFO] No locks detected." - return 0 - fi - (( i % 10 == 0 )) && echo "[INFO] apt/dpkg busy…waiting $(($i*2))s" - sleep 2 - done - echo "[WARN] apt/dpkg still busy after ~180s; continuing anyway." - return 1 -} - -apt_refresh_and_install() { - wait_for_apt || true - echo "[INFO] Updating APT cache…" - apt-get update -qq || true - - wait_for_apt || true - echo "[INFO] Installing unattended-upgrades…" - DEBIAN_FRONTEND=noninteractive apt-get install -y unattended-upgrades >/dev/null || true - - # Ensure periodic is enabled - echo 'unattended-upgrades unattended-upgrades/enable_auto_updates boolean true' | debconf-set-selections || true - DEBIAN_FRONTEND=noninteractive dpkg-reconfigure -f noninteractive unattended-upgrades >/dev/null || true -} - -extract_policy_pairs() { - # Extract unique Origin:Archive pairs from apt-cache policy lines - mapfile -t POLICY_PAIRS < <( - apt-cache policy 2>/dev/null \ - | sed -n 's/.*o=\([^,]*\),a=\([^,]*\).*/\1:\2/p' \ - | awk 'NF' | sort -u - ) -} - -build_origin_patterns() { - ORIGIN_PATTERNS=() - - have_pair() { printf '%s\n' "${POLICY_PAIRS[@]}" | grep -Fxq -- "$1"; } - add_pat() { ORIGIN_PATTERNS+=("origin=$1,archive=$2"); } - - if [[ "$OS" == "debian" ]]; then - # Consider common Debian suites (codename/stable/testing variants) - local want=( "$CODENAME" "$CODENAME-updates" "$CODENAME-security" - stable stable-updates stable-security - testing testing-security ) - for a in "${want[@]}"; do - have_pair "Debian:$a" && add_pat "Debian" "$a" - done - # Fallback if apt-cache didn’t show anything yet - if [[ ${#ORIGIN_PATTERNS[@]} -eq 0 ]]; then - ORIGIN_PATTERNS+=("origin=Debian,archive=$CODENAME" - "origin=Debian,archive=${CODENAME}-updates" - "origin=Debian,archive=${CODENAME}-security") - fi - else # ubuntu - # Standard pockets - for a in "$CODENAME" "${CODENAME}-updates" "${CODENAME}-security"; do - have_pair "Ubuntu:$a" && add_pat "Ubuntu" "$a" - done - # Optional ESM, only if attached and present - have_pair "UbuntuESM:${CODENAME}-infra-security" && add_pat "UbuntuESM" "${CODENAME}-infra-security" - have_pair "UbuntuESMApps:${CODENAME}-apps-security" && add_pat "UbuntuESMApps" "${CODENAME}-apps-security" - - if [[ ${#ORIGIN_PATTERNS[@]} -eq 0 ]]; then - ORIGIN_PATTERNS+=("origin=Ubuntu,archive=$CODENAME" - "origin=Ubuntu,archive=${CODENAME}-updates" - "origin=Ubuntu,archive=${CODENAME}-security") - fi - fi - - echo "[INFO] Origins-Pattern to be written:" - printf ' %s\n' "${ORIGIN_PATTERNS[@]}" -} - -write_50unattended() { - local patterns="" - for p in "${ORIGIN_PATTERNS[@]}"; do - patterns+=" \"$p\";\n" - done - - cat > /etc/apt/apt.conf.d/50unattended-upgrades < /etc/apt/apt.conf.d/20auto-upgrades <<'EOF' -APT::Periodic::Update-Package-Lists "1"; -APT::Periodic::Download-Upgradeable-Packages "1"; -APT::Periodic::Unattended-Upgrade "1"; -APT::Periodic::AutocleanInterval "7"; -EOF -} - -enable_timers_if_systemd() { - if command -v systemctl >/dev/null 2>&1; then - systemctl enable --now apt-daily.timer apt-daily-upgrade.timer 2>/dev/null || true - fi -} - -validate_with_dryrun() { - wait_for_apt || true - echo "[INFO] Validating unattended-upgrades with a dry run…" - local log="/tmp/unattended-upgrades-dryrun.$$" - if ! timeout 180 unattended-upgrades --dry-run --debug >"$log" 2>&1; then - echo "[WARN] Dry run timed out or failed; see $log" - return 1 - fi - grep -E "Allowed origins are" "$log" | head -n1 || true - if grep -qiE "Unable to parse|ValueError|AttributeError|ImportError" "$log"; then - echo "[ERROR] Parsing error detected; see $log" - return 1 - fi -} - -show_status() { - echo - echo "[INFO] Config files:" - ls -la /etc/apt/apt.conf.d/20auto-upgrades /etc/apt/apt.conf.d/50unattended-upgrades 2>/dev/null || true - echo - if command -v systemctl >/dev/null 2>&1; then - echo "[INFO] Timers:" - systemctl list-timers --all | grep -E 'apt-(daily|daily-upgrade)\.timer' || true - else - echo "[INFO] systemd not present; relying on APT::Periodic via cron." - fi -} - -main() { - echo "[INFO] Unattended-upgrades configurator (Debian/Ubuntu)" - require_root - detect_os - apt_refresh_and_install - extract_policy_pairs - build_origin_patterns - write_50unattended - write_20auto - enable_timers_if_systemd - - set +e - validate_with_dryrun - vr=$? - set -e - if (( vr != 0 )); then - echo "[WARN] Validation failed or timed out. Config is written; normal timers/periodic will still run. Inspect the log above if needed." - fi - - show_status - echo - echo "[OK] Unattended updates configured. Regular + security updates will apply automatically; reboot at ${REBOOT_TIME} if needed." -} - -main "$@"