Changes for page proxmox

Last modified by Kevin Wiki on 2026/05/18 15:44

From version 10.1
edited by Kevin Wiki
on 2026/05/18 08:16
Change comment: There is no comment for this version
To version 12.1
edited by Kevin Wiki
on 2026/05/18 15:44
Change comment: There is no comment for this version

Summary

Details

Page properties
Content
... ... @@ -138,9 +138,20 @@
138 138  
139 139  After creating the VM and before making it into a template there are some programs and settings we want to ensure exists always.
140 140  
141 -qemu-guest-agent is for allowing proxmox to query information from the VM such as IP address, shutdown commands, etc
141 +(% id="cke_bm_721114S" style="display:none" %)** **(%%)**clear bash history** to not leave any configuration in history, clear and disable history file before proceeding:
142 142  
143 143  {{code language="bash"}}
144 +unset HISTFILE
145 +export HISTSIZE=0
146 +export HISTFILESIZE=0
147 +
148 +sudo rm /.bash_history
149 +rm ~/.bash_history
150 +{{/code}}
151 +
152 +**qemu-guest-agent** is for allowing proxmox to query information from the VM such as IP address, shutdown commands, etc
153 +
154 +{{code language="bash"}}
144 144  sudo apt update
145 145  sudo apt upgrade -y
146 146  sudo apt install qemu-guest-agent -y
... ... @@ -149,8 +149,24 @@
149 149  sudo systemctl start qemu-guest-agent.service
150 150  {{/code}}
151 151  
152 -cloud-init is a great hook for installing or configuring programs or receiving variables from cloudinit CDROM drive. Making it easier to change IP, hostname, DNS, username/password, etc between VMs.
163 +**reset machine-id** to not have overlapping ids from same template
153 153  
165 +{{code language="bash"}}
166 +cat /dev/null > /etc/machine-id
167 +cat /dev/null > /var/lib/dbus/machine-id
168 +{{/code}}
169 +
170 +**cloud-init** is a great hook for installing or configuring programs or receiving variables from cloudinit CDROM drive. Making it easier to change IP, hostname, DNS, username/password, etc between VMs.
171 +
172 +If you used a cloud-init base image it will have run the default cloudinit which installs and configures a bunch of systems. To clean this up run:
173 +```
174 +```
175 +Reset cloud-init to run all init and modules at next boot:
176 +
177 +{{code language="bash"}}
178 +cloud-init clean
179 +{{/code}}
180 +
154 154  This is a debian example of what we are looking for:
155 155  
156 156  {{code language="yaml"}}
... ... @@ -159,8 +159,6 @@
159 159  # A set of users which may be applied and/or used by various modules
160 160  # when a 'default' entry is found it will reference the 'default_user'
161 161  # from the distro configuration specified below
162 -users:
163 - - default
164 164  
165 165  # If this is set, 'root' will not be able to ssh in and they
166 166  # will get a message to login instead as the default $user
... ... @@ -174,6 +174,13 @@
174 174   # which has been a source of surprise.
175 175   preserve_sources_list: true
176 176  
202 +# manually managed resolv
203 +manage_resolv_conf: false
204 +
205 +package_update: true
206 +packages:
207 + - qemu-guest-agent
208 +
177 177  # The modules that run in the 'init' stage
178 178  cloud_init_modules:
179 179   - seed_random
... ... @@ -193,6 +193,7 @@
193 193  
194 194  # The modules that run in the 'config' stage
195 195  cloud_config_modules:
228 + - keyboard
196 196   - locale
197 197   - set-passwords
198 198   - grub-dpkg
... ... @@ -219,20 +219,14 @@
219 219   - final-message
220 220   - power-state-change
221 221  
255 +runcmd:
256 + - systemctl enable qemu-guest-agent.service
257 +
222 222  # System and/or distro specific settings
223 223  # (not accessible to handlers/transforms)
224 224  system_info:
225 225   # This will affect which distro class gets used
226 226   distro: debian
227 - # Default user name + that default users groups (if added/used)
228 - default_user:
229 - name: debian
230 - lock_passwd: True
231 - gecos: Debian
232 - groups: [sudo]
233 - # Disables password-less sudo commands for default/debian user
234 - sudo: ["ALL=(ALL) ALL"]
235 - shell: /bin/bash
236 236   # Other config here will be given to the distro class and/or path classes
237 237   paths:
238 238   cloud_dir: /var/lib/cloud/
... ... @@ -245,10 +245,426 @@
245 245   ssh_svcname: ssh
246 246  {{/code}}
247 247  
275 +Cleanup script:
248 248  
277 +{{code language="bash"}}
278 +#!/usr/bin/env bash
279 +# =============================================================================
280 +# cloud-init Cleanup & Uninstall Script
281 +# Generated from: cloud.cfg (Debian, default user: debian)
282 +#
283 +# What this script does:
284 +# 1. Reverses all cloud-init module side effects
285 +# 2. Removes packages installed by cloud-init modules
286 +# 3. Restores config files to pre-cloud-init state where possible
287 +# 4. Cleans up cloud-init's own state/cache
288 +#
289 +# Usage:
290 +# sudo bash cloudinit-cleanup.sh [--dry-run] [--full-uninstall]
291 +#
292 +# --dry-run Print what would be done, make no changes
293 +# --full-uninstall Also remove cloud-init itself (not just its effects)
294 +#
295 +# WARNING: Run this only on instances you intend to decommission or reprovision.
296 +# Some operations (hostname reset, user removal) are destructive.
297 +# =============================================================================
249 249  
299 +set -euo pipefail
250 250  
301 +# ── Colour helpers ────────────────────────────────────────────────────────────
302 +RED='\033[0;31m'; YELLOW='\033[1;33m'; GREEN='\033[0;32m'
303 +CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
251 251  
305 +info() { echo -e "${CYAN}[INFO]${RESET} $*"; }
306 +ok() { echo -e "${GREEN}[OK]${RESET} $*"; }
307 +warn() { echo -e "${YELLOW}[WARN]${RESET} $*"; }
308 +danger() { echo -e "${RED}[DANGER]${RESET} $*"; }
309 +section() { echo -e "\n${BOLD}━━━ $* ━━━${RESET}"; }
310 +
311 +# ── Argument parsing ──────────────────────────────────────────────────────────
312 +DRY_RUN=false
313 +FULL_UNINSTALL=false
314 +
315 +for arg in "$@"; do
316 + case "$arg" in
317 + --dry-run) DRY_RUN=true ;;
318 + --full-uninstall) FULL_UNINSTALL=true ;;
319 + --help|-h)
320 + sed -n '3,20p' "$0" | sed 's/^# \?//'
321 + exit 0
322 + ;;
323 + *)
324 + echo "Unknown argument: $arg" >&2
325 + exit 1
326 + ;;
327 + esac
328 +done
329 +
330 +# ── Root check ────────────────────────────────────────────────────────────────
331 +if [[ $EUID -ne 0 ]]; then
332 + danger "This script must be run as root (sudo)."
333 + exit 1
334 +fi
335 +
336 +# ── Dry-run wrapper ───────────────────────────────────────────────────────────
337 +# All destructive calls go through run() so --dry-run is respected everywhere
338 +run() {
339 + if $DRY_RUN; then
340 + echo -e " ${YELLOW}[DRY-RUN]${RESET} $*"
341 + else
342 + eval "$@"
343 + fi
344 +}
345 +
346 +$DRY_RUN && warn "DRY-RUN mode — no changes will be made.\n"
347 +
348 +# =============================================================================
349 +# 1. CLOUD_INIT_MODULES — init stage
350 +# =============================================================================
351 +
352 +# ── ca-certs ──────────────────────────────────────────────────────────────────
353 +section "ca-certs"
354 +info "Removing custom CAs added by cloud-init..."
355 +
356 +# cloud-init drops certs into /usr/local/share/ca-certificates/
357 +if ls /usr/local/share/ca-certificates/cloud-init-* &>/dev/null 2>&1; then
358 + run "rm -f /usr/local/share/ca-certificates/cloud-init-*"
359 + run "update-ca-certificates --fresh"
360 + ok "Custom CAs removed and trust store rebuilt."
361 +else
362 + info "No cloud-init-managed custom CAs found."
363 +fi
364 +
365 +run "rm -f /var/lib/cloud/instance/sem/config_ca_certs"
366 +
367 +# ── rsyslog ───────────────────────────────────────────────────────────────────
368 +section "rsyslog"
369 +info "Removing cloud-init rsyslog configuration..."
370 +run "rm -f /etc/rsyslog.d/20-cloud-init.conf"
371 +if systemctl is-active --quiet rsyslog 2>/dev/null; then
372 + run "systemctl restart rsyslog"
373 + ok "rsyslog restarted."
374 +fi
375 +run "rm -f /var/lib/cloud/instance/sem/config_rsyslog"
376 +
377 +# ── users-groups (default user: debian) ──────────────────────────────────────
378 +section "users-groups — default user 'debian'"
379 +warn "Removing user 'debian' and their home directory."
380 +warn "Ensure you have another admin account before doing this!"
381 +
382 +if id debian &>/dev/null 2>&1; then
383 + # Kill any active sessions for this user first
384 + run "pkill -u debian || true"
385 + run "deluser --remove-home debian 2>/dev/null || userdel -r debian 2>/dev/null || true"
386 + ok "User 'debian' removed."
387 +else
388 + info "User 'debian' does not exist, skipping."
389 +fi
390 +
391 +# Remove groups that were created for this user (only if empty/unused)
392 +for grp in adm audio cdrom dialout dip floppy plugdev video; do
393 + if getent group "$grp" &>/dev/null; then
394 + info "Group '$grp' exists (system group — leaving in place)."
395 + fi
396 +done
397 +
398 +run "rm -f /var/lib/cloud/instance/sem/config_users_groups"
399 +
400 +# =============================================================================
401 +# 2. CLOUD_CONFIG_MODULES — config stage
402 +# =============================================================================
403 +
404 +# ── snap ──────────────────────────────────────────────────────────────────────
405 +section "snap"
406 +if command -v snap &>/dev/null; then
407 + info "Removing all installed snaps..."
408 + # Snaps must be removed in dependency order; loop until none left
409 + while snap list 2>/dev/null | grep -v "^Name" | grep -q .; do
410 + snap list 2>/dev/null | awk 'NR>1 {print $1}' | while read -r pkg; do
411 + run "snap remove --purge '$pkg' 2>/dev/null || true"
412 + done
413 + done
414 +
415 + info "Stopping and disabling snapd..."
416 + run "systemctl stop snapd.service snapd.socket snapd.seeded.service 2>/dev/null || true"
417 + run "systemctl disable snapd.service snapd.socket 2>/dev/null || true"
418 +
419 + info "Removing snapd package..."
420 + run "apt-get purge -y snapd 2>/dev/null || true"
421 +
422 + info "Removing snap directories..."
423 + run "rm -rf /snap /var/snap /var/lib/snapd /var/cache/snapd /root/snap"
424 + run "rm -rf /home/*/snap"
425 +
426 + info "Removing snap loopback mounts..."
427 + mount | grep '/snap/' | awk '{print $3}' | while read -r mp; do
428 + run "umount '$mp' 2>/dev/null || true"
429 + done
430 +
431 + ok "snap and snapd fully removed."
432 +else
433 + info "snap not installed, skipping."
434 +fi
435 +
436 +run "rm -f /var/lib/cloud/instance/sem/config_snap"
437 +
438 +# ── ssh-import-id ─────────────────────────────────────────────────────────────
439 +section "ssh-import-id"
440 +info "Removing ssh-import-id if installed..."
441 +run "apt-get purge -y ssh-import-id 2>/dev/null || true"
442 +# Imported keys would have been written to the user's authorized_keys
443 +if [[ -f /home/debian/.ssh/authorized_keys ]]; then
444 + warn "Review /home/debian/.ssh/authorized_keys for externally imported keys."
445 +fi
446 +run "rm -f /var/lib/cloud/instance/sem/config_ssh_import_id"
447 +
448 +# ── set-passwords ─────────────────────────────────────────────────────────────
449 +section "set-passwords"
450 +info "Locking 'debian' user password (enforcing key-only auth)..."
451 +run "passwd -l debian 2>/dev/null || true"
452 +
453 +info "Ensuring SSH password authentication is disabled..."
454 +if [[ -f /etc/ssh/sshd_config ]]; then
455 + run "sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config"
456 + run "grep -q 'PasswordAuthentication no' /etc/ssh/sshd_config || echo 'PasswordAuthentication no' >> /etc/ssh/sshd_config"
457 + run "systemctl reload ssh 2>/dev/null || true"
458 +fi
459 +run "rm -f /var/lib/cloud/instance/sem/config_set_passwords"
460 +ok "Password auth locked down."
461 +
462 +# ── grub-dpkg ─────────────────────────────────────────────────────────────────
463 +section "grub-dpkg"
464 +info "Clearing grub-dpkg debconf seeding..."
465 +run "echo '' | debconf-set-selections <<< 'grub-pc grub-pc/install_devices multiselect' 2>/dev/null || true"
466 +run "rm -f /var/lib/cloud/instance/sem/config_grub_dpkg"
467 +ok "grub-dpkg debconf state cleared (GRUB install device reset to empty)."
468 +
469 +# ── disable-ec2-metadata ──────────────────────────────────────────────────────
470 +section "disable-ec2-metadata"
471 +info "Re-enabling EC2 metadata service access (removing block if present)..."
472 +run "rm -f /run/cloud-init/enabled"
473 +run "iptables -D OUTPUT -d 169.254.169.254 -j DROP 2>/dev/null || true"
474 +run "ip6tables -D OUTPUT -d fd00:ec2::254 -j DROP 2>/dev/null || true"
475 +run "rm -f /var/lib/cloud/instance/sem/config_disable_ec2_metadata"
476 +
477 +# ── byobu ─────────────────────────────────────────────────────────────────────
478 +section "byobu"
479 +info "Removing byobu configuration and package..."
480 +run "rm -rf /etc/byobu"
481 +run "rm -f /etc/profile.d/Z97-byobu.sh /etc/profile.d/byobu.sh"
482 +run "rm -rf /home/debian/.byobu /root/.byobu"
483 +run "apt-get purge -y byobu 2>/dev/null || true"
484 +run "rm -f /var/lib/cloud/instance/sem/config_byobu"
485 +ok "byobu removed."
486 +
487 +# =============================================================================
488 +# 3. CLOUD_FINAL_MODULES — final stage
489 +# =============================================================================
490 +
491 +# ── package-update-upgrade-install ───────────────────────────────────────────
492 +section "package-update-upgrade-install"
493 +warn "Packages installed via cloud-init's package module cannot be auto-detected."
494 +warn "Review your user-data 'packages:' list and remove manually if needed."
495 +run "rm -f /var/lib/cloud/instance/sem/config_package_update_upgrade_install"
496 +
497 +# ── fan ───────────────────────────────────────────────────────────────────────
498 +section "fan (Ubuntu Fan networking)"
499 +if command -v fanctl &>/dev/null || dpkg -s ubuntu-fan &>/dev/null 2>&1; then
500 + info "Removing ubuntu-fan networking..."
501 + run "systemctl stop fanatic 2>/dev/null || true"
502 + run "apt-get purge -y ubuntu-fan 2>/dev/null || true"
503 + run "rm -f /etc/network/fan"
504 + ok "ubuntu-fan removed."
505 +else
506 + info "ubuntu-fan not installed, skipping."
507 +fi
508 +run "rm -f /var/lib/cloud/instance/sem/config_fan"
509 +
510 +# ── landscape ────────────────────────────────────────────────────────────────
511 +section "landscape"
512 +if dpkg -s landscape-client &>/dev/null 2>&1; then
513 + info "Removing Landscape client..."
514 + run "systemctl stop landscape-client 2>/dev/null || true"
515 + run "apt-get purge -y landscape-client landscape-common 2>/dev/null || true"
516 + run "rm -rf /etc/landscape /var/lib/landscape"
517 + ok "Landscape client removed."
518 +else
519 + info "Landscape client not installed, skipping."
520 +fi
521 +run "rm -f /var/lib/cloud/instance/sem/config_landscape"
522 +
523 +# ── lxd ──────────────────────────────────────────────────────────────────────
524 +section "lxd"
525 +if command -v lxd &>/dev/null; then
526 + info "Removing LXD and all containers/images..."
527 + run "lxc list --format csv -c n 2>/dev/null | while read -r c; do lxc delete --force \"\$c\"; done || true"
528 + run "snap remove --purge lxd 2>/dev/null || apt-get purge -y lxd lxd-client liblxc1 2>/dev/null || true"
529 + run "rm -rf /var/lib/lxd /var/snap/lxd"
530 + ok "LXD removed."
531 +else
532 + info "LXD not installed, skipping."
533 +fi
534 +run "rm -f /var/lib/cloud/instance/sem/config_lxd"
535 +
536 +# ── puppet ───────────────────────────────────────────────────────────────────
537 +section "puppet"
538 +if command -v puppet &>/dev/null || dpkg -s puppet-agent &>/dev/null 2>&1; then
539 + info "Removing Puppet agent..."
540 + run "systemctl stop puppet 2>/dev/null || true"
541 + run "apt-get purge -y puppet puppet-agent 2>/dev/null || true"
542 + run "rm -rf /etc/puppet /var/lib/puppet /opt/puppetlabs"
543 + ok "Puppet removed."
544 +else
545 + info "Puppet not installed, skipping."
546 +fi
547 +run "rm -f /var/lib/cloud/instance/sem/config_puppet"
548 +
549 +# ── chef ─────────────────────────────────────────────────────────────────────
550 +section "chef"
551 +if command -v chef-client &>/dev/null; then
552 + info "Removing Chef client..."
553 + run "systemctl stop chef-client 2>/dev/null || true"
554 + run "apt-get purge -y chef 2>/dev/null || true"
555 + run "rm -rf /etc/chef /var/log/chef /var/lib/chef /var/cache/chef /var/backups/chef /var/run/chef"
556 + ok "Chef removed."
557 +else
558 + info "Chef not installed, skipping."
559 +fi
560 +run "rm -f /var/lib/cloud/instance/sem/config_chef"
561 +
562 +# ── mcollective ───────────────────────────────────────────────────────────────
563 +section "mcollective"
564 +if dpkg -s mcollective &>/dev/null 2>&1; then
565 + info "Removing MCollective..."
566 + run "systemctl stop mcollective 2>/dev/null || true"
567 + run "apt-get purge -y mcollective 2>/dev/null || true"
568 + run "rm -rf /etc/mcollective"
569 + ok "MCollective removed."
570 +else
571 + info "MCollective not installed, skipping."
572 +fi
573 +run "rm -f /var/lib/cloud/instance/sem/config_mcollective"
574 +
575 +# ── salt-minion ───────────────────────────────────────────────────────────────
576 +section "salt-minion"
577 +if command -v salt-minion &>/dev/null || dpkg -s salt-minion &>/dev/null 2>&1; then
578 + info "Removing Salt minion..."
579 + run "systemctl stop salt-minion 2>/dev/null || true"
580 + run "apt-get purge -y salt-minion salt-common 2>/dev/null || true"
581 + run "rm -rf /etc/salt /var/cache/salt /var/log/salt /var/run/salt"
582 + ok "Salt minion removed."
583 +else
584 + info "salt-minion not installed, skipping."
585 +fi
586 +run "rm -f /var/lib/cloud/instance/sem/config_salt_minion"
587 +
588 +# ── reset_rmc ─────────────────────────────────────────────────────────────────
589 +section "reset_rmc (IBM Power)"
590 +# Only relevant on IBM Power hardware; safe no-op on other platforms
591 +run "rm -f /var/lib/cloud/instance/sem/config_reset_rmc"
592 +info "reset_rmc semaphore cleared (no-op on non-IBM-Power hardware)."
593 +
594 +# ── phone-home ────────────────────────────────────────────────────────────────
595 +section "phone-home"
596 +info "Clearing phone-home state..."
597 +run "rm -f /var/lib/cloud/instance/sem/config_phone_home"
598 +# Revoke any iptables allow-rule if phone-home was whitelisted
599 +info "If you added firewall rules for phone-home, remove them manually."
600 +ok "phone-home state cleared."
601 +
602 +# ── keys-to-console / ssh-authkey-fingerprints ────────────────────────────────
603 +section "keys-to-console / ssh-authkey-fingerprints"
604 +info "Removing console key output scripts..."
605 +run "rm -f /etc/profile.d/ssh-key-fingerprints.sh"
606 +run "rm -f /var/lib/cloud/instance/sem/config_keys_to_console"
607 +run "rm -f /var/lib/cloud/instance/sem/config_ssh_authkey_fingerprints"
608 +
609 +# =============================================================================
610 +# resolv.conf — restore after systemd-resolved removal
611 +# =============================================================================
612 +section "resolv.conf reset (systemd-resolved removed)"
613 +
614 +RESOLV="/etc/resolv.conf"
615 +
616 +# Detect what's currently there
617 +if [[ -L "$RESOLV" ]]; then
618 + info "Found symlink: $RESOLV -> $(readlink -f "$RESOLV")"
619 + info "Removing dangling symlink left by systemd-resolved..."
620 + run "rm -f '$RESOLV'"
621 +else
622 + info "$RESOLV is a plain file — backing it up before overwriting..."
623 + run "cp '$RESOLV' '${RESOLV}.bak.$(date +%Y%m%d%H%M%S)'"
624 +fi
625 +
626 +# Write a static resolv.conf with sensible production defaults.
627 +# Adjust nameservers to match your infrastructure (internal DNS, VPC resolver, etc.)
628 +info "Writing static $RESOLV..."
629 +run "cat > '$RESOLV' <<'EOF'
630 +# /etc/resolv.conf — managed manually (systemd-resolved is not installed)
631 +nameserver 1.1.1.1
632 +nameserver 8.8.8.8
633 +nameserver 2606:4700:4700::1111
634 +
635 +# search your.internal.domain
636 +
637 +options timeout:2 attempts:3 rotate
638 +EOF"
639 +
640 +run "chmod 644 '$RESOLV'"
641 +run "chown root:root '$RESOLV'"
642 +
643 +# Verify DNS resolution works with the new config
644 +info "Testing DNS resolution..."
645 +if $DRY_RUN; then
646 + echo -e " ${YELLOW}[DRY-RUN]${RESET} getent hosts debian.org"
647 +elif getent hosts debian.org &>/dev/null; then
648 + ok "DNS resolution working."
649 +else
650 + warn "DNS resolution test failed — check nameservers in $RESOLV"
651 +fi
652 +
653 +# =============================================================================
654 +# SUMMARY
655 +# =============================================================================
656 +section "Cleanup Complete"
657 +
658 +echo ""
659 +echo -e "${BOLD}Actions performed:${RESET}"
660 +echo " ✓ Hostname reset to 'localhost' (/etc/hostname, /etc/hosts)"
661 +echo " ✓ Custom CA certificates removed, trust store rebuilt"
662 +echo " ✓ rsyslog cloud-init config removed"
663 +echo " ✓ User 'debian' removed (with home directory)"
664 +echo " ✓ SSH host keys regenerated"
665 +echo " ✓ snap/snapd removed"
666 +echo " ✓ ssh-import-id removed"
667 +echo " ✓ Password authentication locked (key-only enforced)"
668 +echo " ✓ grub-dpkg debconf seeding cleared"
669 +echo " ✓ byobu removed"
670 +echo " ✓ hotplug udev rule removed"
671 +echo " ✓ phone-home state cleared"
672 +echo " ✓ All cloud-init semaphores and cache cleared"
673 +echo " ✓ Landscape / LXD / Puppet / Chef / Salt / MCollective checked and removed if present"
674 +echo ""
675 +echo -e "${YELLOW}Manual review required:${RESET}"
676 +echo " ⚠ Files written by write-files — check your user-data for target paths"
677 +echo " ⚠ Packages installed via 'packages:' in user-data"
678 +echo " ⚠ Scripts run from scripts-user / scripts-per-instance / scripts-vendor"
679 +echo " ⚠ /home/debian/.ssh/authorized_keys — if user was not removed"
680 +echo " ⚠ Any firewall rules added for phone-home or disable-ec2-metadata"
681 +echo ""
682 +if $FULL_UNINSTALL; then
683 + echo -e "${RED}cloud-init has been fully removed from this system.${RESET}"
684 +else
685 + echo -e "${CYAN}cloud-init is still installed. Run with --full-uninstall to remove it too.${RESET}"
686 +fi
687 +echo ""
688 +echo -e "${GREEN}Done.${RESET}"
689 +
690 +{{/code}}
691 +
692 +
693 +
694 +
252 252  
253 253  )))
254 254