| 1 | #!/usr/bin/env bash |
| 2 | |
| 3 | set -euo pipefail |
| 4 | shopt -s nullglob |
| 5 | |
| 6 | if [[ "${EUID}" -ne 0 ]]; then |
| 7 | echo "ERROR: This script must be run as root." |
| 8 | exit 1 |
| 9 | fi |
| 10 | |
| 11 | echo "========================================" |
| 12 | echo " PVE VM Template Cleanup" |
| 13 | echo "========================================" |
| 14 | |
| 15 | echo "[1/9] Cleaning Cloud-Init state..." |
| 16 | |
| 17 | if command -v cloud-init >/dev/null 2>&1; then |
| 18 | clean_args=(--logs) |
| 19 | clean_help=$(cloud-init clean --help 2>&1 || true) |
| 20 | |
| 21 | if grep -q -- "--machine-id" <<<"${clean_help}"; then |
| 22 | clean_args+=(--machine-id) |
| 23 | fi |
| 24 | if grep -q -- "--seed" <<<"${clean_help}"; then |
| 25 | clean_args+=(--seed) |
| 26 | fi |
| 27 | if grep -q -- "--configs" <<<"${clean_help}"; then |
| 28 | clean_args+=(--configs ssh_config network) |
| 29 | fi |
| 30 | |
| 31 | cloud-init clean "${clean_args[@]}" |
| 32 | else |
| 33 | echo "cloud-init is not installed; cleaning machine-id directly." |
| 34 | fi |
| 35 | |
| 36 | # Ensure both systemd and legacy dbus machine IDs are empty. On this host the |
| 37 | # dbus path is a symlink to /etc/machine-id, so it is not modified separately. |
| 38 | truncate -s 0 /etc/machine-id |
| 39 | if [[ -f /var/lib/dbus/machine-id && ! -L /var/lib/dbus/machine-id ]]; then |
| 40 | truncate -s 0 /var/lib/dbus/machine-id |
| 41 | fi |
| 42 | |
| 43 | echo "[2/9] Removing SSH host keys and generated network configuration..." |
| 44 | |
| 45 | rm -f /etc/ssh/ssh_host_* |
| 46 | rm -f /etc/network/interfaces.d/50-cloud-init |
| 47 | |
| 48 | # This file pins the current VM's MAC address and must not be cloned. |
| 49 | rm -f /etc/udev/rules.d/70-persistent-net.rules |
| 50 | |
| 51 | echo "[3/9] Removing clone-specific SSH keys and history..." |
| 52 | |
| 53 | find /root /home -maxdepth 3 -type f \ |
| 54 | \( -name authorized_keys -o -name known_hosts -o \ |
| 55 | -name .bash_history -o -name .zsh_history -o \ |
| 56 | -name .fish_history -o -name .python_history -o \ |
| 57 | -name .mysql_history \) \ |
| 58 | -delete 2>/dev/null || true |
| 59 | |
| 60 | echo "[4/9] Resetting hostname metadata..." |
| 61 | |
| 62 | printf '%s\n' localhost > /etc/hostname |
| 63 | sed -i '/^127\.0\.1\.1[[:space:]]/d' /etc/hosts |
| 64 | |
| 65 | echo "[5/9] Cleaning DHCP leases..." |
| 66 | |
| 67 | rm -f /var/lib/dhcp/*.leases 2>/dev/null || true |
| 68 | rm -f /var/lib/dhcp/*.lease 2>/dev/null || true |
| 69 | rm -f /var/lib/dhclient/*.leases 2>/dev/null || true |
| 70 | rm -f /var/lib/dhclient/*.lease 2>/dev/null || true |
| 71 | rm -f /run/systemd/netif/leases/* 2>/dev/null || true |
| 72 | rm -f /var/lib/NetworkManager/*.lease 2>/dev/null || true |
| 73 | rm -f /var/lib/NetworkManager/*lease* 2>/dev/null || true |
| 74 | |
| 75 | echo "[6/9] Removing systemd random seed and runtime state..." |
| 76 | |
| 77 | rm -f /var/lib/systemd/random-seed |
| 78 | rm -rf /run/cloud-init 2>/dev/null || true |
| 79 | |
| 80 | echo "[7/9] Cleaning temporary files and logs..." |
| 81 | |
| 82 | find /tmp /var/tmp -mindepth 1 -maxdepth 1 -exec rm -rf -- {} + 2>/dev/null || true |
| 83 | |
| 84 | if command -v journalctl >/dev/null 2>&1; then |
| 85 | journalctl --rotate 2>/dev/null || true |
| 86 | journalctl --vacuum-time=1s 2>/dev/null || true |
| 87 | fi |
| 88 | |
| 89 | find /var/log -type f -name '*.log' -exec truncate -s 0 -- {} + 2>/dev/null || true |
| 90 | find /var/log -type f -name '*.gz' -delete 2>/dev/null || true |
| 91 | |
| 92 | echo "[8/9] Trimming filesystem..." |
| 93 | |
| 94 | sync |
| 95 | if command -v fstrim >/dev/null 2>&1; then |
| 96 | fstrim -av || true |
| 97 | fi |
| 98 | sync |
| 99 | |
| 100 | echo "[9/9] Final checks..." |
| 101 | |
| 102 | [[ ! -e /etc/ssh/ssh_host_ed25519_key ]] || { |
| 103 | echo "ERROR: SSH host keys still exist." |
| 104 | exit 1 |
| 105 | } |
| 106 | [[ ! -e /etc/udev/rules.d/70-persistent-net.rules ]] || { |
| 107 | echo "ERROR: persistent network rule still exists." |
| 108 | exit 1 |
| 109 | } |
| 110 | |
| 111 | echo |
| 112 | echo "========================================" |
| 113 | echo " Cleanup completed" |
| 114 | echo "========================================" |
| 115 | echo |
| 116 | echo "Cleaned:" |
| 117 | echo " - Cloud-Init instance state, seed, and generated network config" |
| 118 | echo " - machine-id" |
| 119 | echo " - SSH host keys and user SSH known/authorized keys" |
| 120 | echo " - shell history" |
| 121 | echo " - persistent MAC mapping" |
| 122 | echo " - hostname metadata" |
| 123 | echo " - DHCP leases" |
| 124 | echo " - systemd random seed" |
| 125 | echo " - temporary files and logs" |
| 126 | echo |
| 127 | echo "Preserved:" |
| 128 | echo " - package installation and package cache" |
| 129 | echo " - base network configuration in /etc/network/interfaces" |
| 130 | echo |
| 131 | echo "DO NOT reboot this VM." |
| 132 | echo "Power it off and convert it to a PVE template." |
| 133 | echo |
simbafs / Debian VM Template Clean Scriopt
Last active 1 week ago
Revision 9e2e2c70cc92e5baa0d39e67edbbc24794b8291d