From da1bc20ea33d3b6fb63ca68ee63f3109338f5390 Mon Sep 17 00:00:00 2001 From: allan Date: Wed, 11 Feb 2026 09:15:51 +0100 Subject: [PATCH] latest commit --- LICENSE | 21 ++++ README.md | 169 +++++++++++++++++++++++++++++ auto-update-ubuntu | 264 +++++++++++++++++++++++++++++++++++++++++++++ last-tested | 4 + 4 files changed, 458 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100755 auto-update-ubuntu create mode 100644 last-tested diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3eaaa81 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Allan Christensen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..294f9b3 --- /dev/null +++ b/README.md @@ -0,0 +1,169 @@ +# Auto Update for Ubuntu 24.04 Server +[![OS](https://img.shields.io/badge/ubuntu-24.04-E95420)](#) +[![Shell](https://img.shields.io/badge/shell-bash-121011)](#) +[![Feature](https://img.shields.io/badge/feature-controlled_updates-0078D7)](#) +[![Cron](https://img.shields.io/badge/scheduler-cron-lightgrey)](#) +[![License](https://img.shields.io/badge/License-MIT-green)](./LICENSE) + +Install unattended security and system updates on Ubuntu 24.04 server. + +This is not a demo and not a quick experiment. +This is a production-ready policy tool designed to enforce predictable update behaviour. + +--- + +## Why this installer exists +Ubuntu includes unattended-upgrades, but configuration is often inconsistent, unclear, or left in default states that do not match operational policy. + +This script provides a repeatable way to enforce a defined update policy, ensuring systems are patched regularly and reboot behaviour is predictable. + +--- + +## What this installer does +✔ Configures unattended-upgrades using a clean, deterministic policy +✔ Allows switching between four update modes +✔ Ensures updates are enforced on a fixed weekly schedule +✔ Automatically rebuilds the unattended-upgrades configuration safely +✔ Leaves Ubuntu’s timers untouched +✔ Safe to re-run and switch modes at any time +✔ Logs policy changes for auditing and troubleshooting +✔ Uses vendor defaults as a base to preserve compatibility + +--- + +## What this installer does NOT do +It won’t stop you from running the script without reading the documentation like there’s no tomorrow. +Skip the README, and whatever happens next is your headache, not a bug report. + +--- + +## 1. Download the installer + +``` +git clone https://git.x-files.dk/server/auto-update-ubuntu.git +``` + +``` +cd auto-update-ubuntu +``` + +--- + +## 2. Run the installer + +``` +sudo ./auto-update +``` + +Example: + +``` +sudo ./auto-update 2 +``` + +--- + +## Available modes + +The modes control two things: +- Which updates are installed +- Whether the system reboots automatically if required + +The script is built with re-runs in mind. +If you need to switch modes, simply run the script again with another mode. +The current policy will be replaced automatically. + +**Mode 1** +Security + updates (full system updates, not security-only) +Automatic reboot +Reboot happens even if users are logged in + +**Mode 2** +Security + updates (full system updates, not security-only) +No automatic reboot +Users are notified on next login if a reboot is required + +**Mode 3** +Security updates only +Automatic reboot +Reboot happens even if users are logged in + +**Mode 4** +Security updates only +No automatic reboot +Users are notified on next login if a reboot is required + +--- + +## How it works + +The script rebuilds the unattended-upgrades configuration from Ubuntu’s vendor template each time a mode is applied. +This ensures a clean and predictable configuration and avoids problems caused by manually edited or partially modified files. + +If you manually edit `/etc/apt/apt.conf.d/50unattended-upgrades`, those changes will be overwritten, but only when you switch modes. + +A cron job (`/etc/cron.d/auto-update`) is created to enforce updates every Friday at 03:00. +This ensures that systems are updated regularly even if users postpone updates during the week. + +Ubuntu’s systemd timers are left untouched. +If both timers and the cron job run, nothing breaks — updates may simply be checked more than once. + +--- + +## Cron behaviour + +The cron job is created the first time a mode is applied and is scheduled to run every Friday at 03:00. + +The cron file is created as: + +``` +/etc/cron.d/auto-update +``` + +You are free to change the schedule to any time you prefer, or even run it daily if required. +The script does not modify or reset the cron job after it has been created. + +Switching modes does not alter the existing cron schedule. + +--- + +## Common questions + +**Q:** Why am I still seeing available updates? Is the script not working?** +**A:** These notifications are generated by Ubuntu’s default update timers, which this script does not disable. +You may still see update notifications during the week, even though the cron enforcement job runs on Friday. + +If you log in on a Tuesday and updates are available, you are encouraged to install them manually. +This will not conflict with the script. + +You may also see updates appear shortly after the scheduled run. +This simply means new updates were released after the last scheduled update cycle. + +--- + +## Logging + +Policy changes are logged to: + +``` +/var/log/auto-update.log +``` + +This log records when a mode was applied and which user executed the script. + +Package installation and upgrade details are logged by unattended-upgrades in: + +``` +/var/log/unattended-upgrades/ +``` + +--- + +### More Information + +More guides and documentation can be found on [wiki.x-files.dk](https://wiki.x-files.dk) + +--- + +### License +Licensed under the [MIT License](./LICENSE). diff --git a/auto-update-ubuntu b/auto-update-ubuntu new file mode 100755 index 0000000..dcfb01b --- /dev/null +++ b/auto-update-ubuntu @@ -0,0 +1,264 @@ +#!/usr/bin/env bash + +# Author : Allan Christensen +# First Created : 12-03-2021 (DD-MM-YYYY) +# Description : Manage unattended-upgrades policy on Ubuntu 24.04 +# License : MIT License + +# +# Are we root +# +if [[ $(id -u) -ne 0 ]]; then echo "" && echo "Must be root or use sudo" && echo "" ; exit ; fi + +# +# Variables +# +config="/etc/apt/apt.conf.d/50unattended-upgrades" +template="/usr/share/unattended-upgrades/50unattended-upgrades" +cronfile="/etc/cron.d/auto-update" +logfile="/var/log/auto-update.log" + +# +# Log timestamp format +# Default: DD-MM-YYYY HH:MM +# Change to "+%Y-%m-%d %H:%M" for ISO format if preferred +# +log_date_format="+%d-%m-%Y %H:%M" + +# +# Create safe temporary file +# +tmpfile="$(mktemp /tmp/auto-update.XXXXXX)" +trap 'rm -f "$tmpfile"' EXIT + +# +# Function: Ensure config exists +# +chkcfg () { + if [[ ! -f "$1" ]]; then + printf "Config missing: %s → copying from %s\n" "$1" "$2" + cp -Rp "$2" "$1" + fi +} + +# +# Function: Detect current mode +# +detect_mode() { + + managed="false" + updates_enabled="false" + reboot_enabled="false" + reboot_users_enabled="false" + current_mode="unknown" + + if grep -q "Managed by auto-update" "$config"; then + managed="true" + else + return + fi + + grep -q '${distro_codename}-updates' "$config" && updates_enabled="true" + grep -q 'Automatic-Reboot "true"' "$config" && reboot_enabled="true" + grep -q 'Automatic-Reboot-WithUsers "true"' "$config" && reboot_users_enabled="true" + + if [[ "$updates_enabled" == "true" && "$reboot_enabled" == "true" ]]; then + current_mode=1 + elif [[ "$updates_enabled" == "true" && "$reboot_enabled" == "false" ]]; then + current_mode=2 + elif [[ "$updates_enabled" == "false" && "$reboot_enabled" == "true" ]]; then + current_mode=3 + else + current_mode=4 + fi +} + +# +# Function: Show status and usage +# +show_status() { + + detect_mode + + if [[ "$managed" != "true" ]]; then + printf "\nCurrent policy: Not yet managed by auto-update\n" + printf "System default unattended-upgrades configuration detected.\n" + else + printf "\nCurrent policy: Mode %s\n" "$current_mode" + + if [[ "$updates_enabled" == "true" ]]; then + printf "Updates: Security + updates\n" + else + printf "Updates: Security only\n" + fi + + if [[ "$reboot_enabled" == "true" ]]; then + printf "Automatic reboot: Yes\n" + else + printf "Automatic reboot: No\n" + fi + + if [[ "$reboot_users_enabled" == "true" ]]; then + printf "Reboot even if users are logged in: Yes\n" + else + printf "Reboot even if users are logged in: No\n" + fi + fi + + printf "Cron enforcement: Friday at 03:00\n" + + printf "\nUsage:\n" + printf " auto-update \n\n" + + printf "Modes:\n" + printf " 1 Security + updates, automatic reboot\n" + printf " 2 Security + updates, no reboot\n" + printf " 3 Security only, automatic reboot\n" + printf " 4 Security only, no reboot\n\n" +} + +# +# Function: Apply mode +# +apply_mode() { + + local mode="$1" + + cp "$template" "$tmpfile" || { + echo "Failed to copy template. Aborting." + exit 1 + } + + sed -i '/Unattended-Upgrade::Allowed-Origins {/,/};/d' "$tmpfile" + sed -i '/Unattended-Upgrade::Automatic-Reboot /d' "$tmpfile" + sed -i '/Unattended-Upgrade::Automatic-Reboot-WithUsers /d' "$tmpfile" + + case "$mode" in + 1) + origins_block=' "${distro_id}:${distro_codename}-security"; + "${distro_id}:${distro_codename}-updates";' + reboot_value="true" + reboot_users_value="true" + ;; + 2) + origins_block=' "${distro_id}:${distro_codename}-security"; + "${distro_id}:${distro_codename}-updates";' + reboot_value="false" + reboot_users_value="false" + ;; + 3) + origins_block=' "${distro_id}:${distro_codename}-security";' + reboot_value="true" + reboot_users_value="true" + ;; + 4) + origins_block=' "${distro_id}:${distro_codename}-security";' + reboot_value="false" + reboot_users_value="false" + ;; + *) + printf "\nInvalid mode.\n\n" + exit 1 + ;; + esac + + cat <> "$tmpfile" + +// +// Managed by auto-update +// + +Unattended-Upgrade::Allowed-Origins { +$origins_block +}; + +Unattended-Upgrade::Automatic-Reboot "$reboot_value"; +Unattended-Upgrade::Automatic-Reboot-WithUsers "$reboot_users_value"; + +EOF + + mv "$tmpfile" "$config" +} + +# +# Function: Ensure cron job exists +# +ensure_cron() { + + if [[ ! -f "$cronfile" ]]; then + + printf "Creating cron enforcement job...\n" + + cat < "$cronfile" +# Runs auto-update policy enforcement every Friday at 03:00 +0 3 * * 5 root /usr/bin/apt-get update -qq && /usr/bin/unattended-upgrade -v >/dev/null 2>&1 +EOF + + chmod 644 "$cronfile" + chown root:root "$cronfile" + + else + printf "Cron job already exists (%s), leaving it unchanged.\n" "$cronfile" + fi +} + +# +# Install required packages +# +apt-get update -qq +apt-get install -y unattended-upgrades update-notifier-common >/dev/null + +# +# Ensure configuration exists +# +chkcfg "$config" "$template" + +# +# Argument handling +# +if [[ -z "$1" ]]; then + show_status + exit 0 +fi + +mode="$1" + +if [[ ! "$mode" =~ ^[1-4]$ ]]; then + printf "\nInvalid mode. Please select 1-4.\n" + show_status + exit 1 +fi + +# +# Apply selected mode +# +printf "\nApplying Mode %s...\n" "$mode" + +apply_mode "$mode" +ensure_cron + +# +# Log action +# +actor="${SUDO_USER:-root}" +printf "%s Mode %s applied by %s\n" "$(date "$log_date_format")" "$mode" "$actor" >> "$logfile" + +# +# Restart services +# +systemctl restart unattended-upgrades.service >/dev/null +systemctl restart cron.service >/dev/null + +# +# Show resulting status +# +show_status + +# +# All done +# +printf "\nAll Done...\n" + +# +# End of script +# diff --git a/last-tested b/last-tested new file mode 100644 index 0000000..9e6a568 --- /dev/null +++ b/last-tested @@ -0,0 +1,4 @@ +------------------------------------ +Last tested: 10-02-2026 (DD-MM-YYYY) +Environment: Ubuntu Server 24.04 LTS +------------------------------------