#!/usr/bin/env bash set -euo pipefail shopt -s nullglob if [[ "${EUID}" -ne 0 ]]; then echo "ERROR: This script must be run as root." exit 1 fi echo "========================================" echo " PVE VM Template Cleanup" echo "========================================" echo "[1/9] Cleaning Cloud-Init state..." if command -v cloud-init >/dev/null 2>&1; then clean_args=(--logs) clean_help=$(cloud-init clean --help 2>&1 || true) if grep -q -- "--machine-id" <<<"${clean_help}"; then clean_args+=(--machine-id) fi if grep -q -- "--seed" <<<"${clean_help}"; then clean_args+=(--seed) fi if grep -q -- "--configs" <<<"${clean_help}"; then clean_args+=(--configs ssh_config network) fi cloud-init clean "${clean_args[@]}" else echo "cloud-init is not installed; cleaning machine-id directly." fi # Ensure both systemd and legacy dbus machine IDs are empty. On this host the # dbus path is a symlink to /etc/machine-id, so it is not modified separately. truncate -s 0 /etc/machine-id if [[ -f /var/lib/dbus/machine-id && ! -L /var/lib/dbus/machine-id ]]; then truncate -s 0 /var/lib/dbus/machine-id fi echo "[2/9] Removing SSH host keys and generated network configuration..." rm -f /etc/ssh/ssh_host_* rm -f /etc/network/interfaces.d/50-cloud-init # This file pins the current VM's MAC address and must not be cloned. rm -f /etc/udev/rules.d/70-persistent-net.rules echo "[3/9] Removing clone-specific SSH keys and history..." find /root /home -maxdepth 3 -type f \ \( -name authorized_keys -o -name known_hosts -o \ -name .bash_history -o -name .zsh_history -o \ -name .fish_history -o -name .python_history -o \ -name .mysql_history \) \ -delete 2>/dev/null || true echo "[4/9] Resetting hostname metadata..." printf '%s\n' localhost > /etc/hostname sed -i '/^127\.0\.1\.1[[:space:]]/d' /etc/hosts echo "[5/9] Cleaning DHCP leases..." rm -f /var/lib/dhcp/*.leases 2>/dev/null || true rm -f /var/lib/dhcp/*.lease 2>/dev/null || true rm -f /var/lib/dhclient/*.leases 2>/dev/null || true rm -f /var/lib/dhclient/*.lease 2>/dev/null || true rm -f /run/systemd/netif/leases/* 2>/dev/null || true rm -f /var/lib/NetworkManager/*.lease 2>/dev/null || true rm -f /var/lib/NetworkManager/*lease* 2>/dev/null || true echo "[6/9] Removing systemd random seed and runtime state..." rm -f /var/lib/systemd/random-seed rm -rf /run/cloud-init 2>/dev/null || true echo "[7/9] Cleaning temporary files and logs..." find /tmp /var/tmp -mindepth 1 -maxdepth 1 -exec rm -rf -- {} + 2>/dev/null || true if command -v journalctl >/dev/null 2>&1; then journalctl --rotate 2>/dev/null || true journalctl --vacuum-time=1s 2>/dev/null || true fi find /var/log -type f -name '*.log' -exec truncate -s 0 -- {} + 2>/dev/null || true find /var/log -type f -name '*.gz' -delete 2>/dev/null || true echo "[8/9] Trimming filesystem..." sync if command -v fstrim >/dev/null 2>&1; then fstrim -av || true fi sync echo "[9/9] Final checks..." [[ ! -e /etc/ssh/ssh_host_ed25519_key ]] || { echo "ERROR: SSH host keys still exist." exit 1 } [[ ! -e /etc/udev/rules.d/70-persistent-net.rules ]] || { echo "ERROR: persistent network rule still exists." exit 1 } echo echo "========================================" echo " Cleanup completed" echo "========================================" echo echo "Cleaned:" echo " - Cloud-Init instance state, seed, and generated network config" echo " - machine-id" echo " - SSH host keys and user SSH known/authorized keys" echo " - shell history" echo " - persistent MAC mapping" echo " - hostname metadata" echo " - DHCP leases" echo " - systemd random seed" echo " - temporary files and logs" echo echo "Preserved:" echo " - package installation and package cache" echo " - base network configuration in /etc/network/interfaces" echo echo "DO NOT reboot this VM." echo "Power it off and convert it to a PVE template." echo