Created
February 27, 2025 21:36
-
-
Save kfiresmith/695f1f1377938d7cd1c711ff8fc06ece to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Does the equivalent of sysprep for linux boxes to prepare them for cloning. | |
# Based on https://lonesysadmin.net/2013/03/26/preparing-linux-template-vms/ | |
AUTHOR='kfiresmith' | |
BASENAME="${0##*/}" | |
MODIFIED='20231020' | |
VERSION='0.1.1' | |
parse_args() { | |
[[ -n $1 ]] || { | |
printf 'No arguments specified, use -h for help.\n' | |
exit 0 | |
} | |
while [[ -n $1 ]]; do | |
if [[ $1 == -v ]]; then | |
printf '%s: Version %s, updated %s by %s\n' \ | |
"$BASENAME" "$VERSION" "$MODIFIED" "$AUTHOR" | |
shift | |
[[ -n $1 ]] || exit 0 | |
elif [[ $1 == -h ]]; then | |
printf 'Cloning preparation script for linux systems.\n\n' | |
printf 'Usage: %s [-v ] (-h | -y [-b] [-l <log_file>] [-s])\n\n' \ | |
"$BASENAME" | |
printf 'Options:\n' | |
printf ' -h Display this help text.\n' | |
printf ' -b Used for firstboot (internal).\n' | |
printf ' -l Specify log file location.\n' | |
printf ' -s Shutdown on completion.\n' | |
printf ' -v Emit version header.\n' | |
printf ' -y Confirm sysprep.\n' | |
exit 0 | |
elif [[ $1 == -b ]]; then | |
FIRSTBOOT=true | |
break | |
elif [[ $1 == -l ]]; then | |
shift | |
LOGFILE=$1 | |
shift | |
elif [[ $1 == -s ]]; then | |
SHUTDOWN=true | |
shift | |
elif [[ $1 == -y ]]; then | |
CONFIRM=true | |
shift | |
else | |
printf 'Invalid argument specified, use -h for help.\n' | |
exit 0 | |
fi | |
done | |
} | |
say() { | |
LOGFILE=${LOGFILE:=/var/log/sysprep.log} | |
if [[ -n $LOGFILE && ! $LOGFILE == no ]]; then | |
[[ -f $LOGFILE ]] || UMASK=027 /usr/bin/touch "$LOGFILE" | |
printf '%s: %s\n' "$(date -u +%FT%TZ)" "$@" | tee -a "$LOGFILE" | |
else | |
printf '%s: %s\n' "$(date -u +%FT%TZ)" "$@" | |
fi | |
} | |
apt_purge() { | |
vers=$(/bin/ls -tr /boot/vmlinuz-* | /usr/bin/head -n -1 | | |
/bin/grep -v "$(uname -r)" | /usr/bin/cut -d- -f2-) | |
for i in $vers; do | |
debs+="$(echo linux-{image,headers,modules}-$i) " | |
done | |
/usr/bin/apt remove -qy --purge $debs &> /dev/null && | |
/usr/bin/apt autoremove -qy --purge &> /dev/null | |
} | |
firstboot() { | |
say 'Running sysprep first-boot setup script.' | |
/usr/bin/find /etc/ssh/*key &>/dev/null || { | |
say 'Regenerating SSH host keys...' | |
/usr/sbin/dpkg-reconfigure openssh-server | |
} | |
[[ $(hostname) == CHANGEME ]] && { | |
say 'Regenerating hostname and rebooting...' | |
/bin/hostnamectl set-hostname changeme.local | |
/bin/systemctl reboot | |
} | |
[[ -f /var/lib/aide/aide.db.gz ]] && { | |
say 'Regenerating AIDE database...' | |
/sbin/aide --update | |
/bin/mv -f /var/lib/aide/aide.db{.new,}.gz | |
} | |
say 'Sysprep firtst-boot setup complete, disabling service.' | |
/bin/systemctl disable sysprep-firstboot | |
exit 0 | |
} | |
clean_packages() { | |
say 'Removing old kernels.' | |
if ! command -v purge-old-kernels &> /dev/null; then | |
/usr/bin/apt install -qy byobu &> /dev/null | |
fi | |
/usr/bin/purge-old-kernels -qy --keep 1 &> /dev/null || apt_purge | |
say 'Clearing package cache.' | |
/usr/bin/apt clean &> /dev/null | |
/bin/rm -rf /var/cache/apt/archives/* | |
return 0 | |
} | |
clean_logs() { | |
say 'Clearing old logs.' | |
/usr/sbin/logrotate -f /etc/logrotate.conf | |
/usr/bin/find /var/log -type f -regextype posix-extended -regex \ | |
".*/*(-[0-9]{8}|.[0-9]|.gz)$" -delete | |
/bin/rm -rf /var/log/journal && /bin/mkdir /var/log/journal | |
/bin/rm -f /var/log/dmesg.old | |
/bin/rm -f /var/log/anaconda/* | |
say 'Clearing audit logs.' | |
: > /var/log/audit/audit.log | |
: > /var/log/wtmp | |
: > /var/log/lastlog | |
: > /var/log/grubby | |
return 0 | |
} | |
clean_network() { | |
say 'Clearing udev persistent rules.' | |
/bin/rm -f /etc/udev/rules.d/70* | |
say 'Wipe netplan config' | |
/bin/rm -f /etc/netplan/*.yaml | |
/bin/cp 02-no-network.yaml.example 02-no-network.yaml | |
/usr/sbin/netplan apply | |
return 0 | |
} | |
clean_files() { | |
say 'Cleaning out temp directories.' | |
/bin/rm -rf /tmp/* | |
/bin/rm -rf /var/tmp/* | |
/bin/rm -rf /var/cache/* | |
say 'Cleaning up root home directory.' | |
unset HISTFILE | |
/bin/rm -f /root/.bash_history | |
/bin/rm -f /root/anaconda-ks.cfg | |
/bin/rm -rf /root/.ssh/ | |
/bin/rm -rf /root/.gnupg/ | |
return 0 | |
} | |
generalize() { | |
say 'Removing SSH host keys.' | |
/bin/rm -f /etc/ssh/*key* | |
say 'Clearing machine-id' | |
: > /etc/machine-id | |
say 'Removing random-seed' | |
/bin/rm -f /var/lib/systemd/random-seed | |
say 'Resetting hostname.' | |
/bin/hostnamectl set-hostname 'CHANGEME' | |
return 0 | |
} | |
setup_firstboot() { | |
say 'Enabling sysprep firstboot service.' | |
FBSERVICE=/etc/systemd/system/sysprep-firstboot.service | |
[[ -f $FBSERVICE ]] || /bin/cat <<'EOF' > $FBSERVICE | |
[Unit] | |
Description=Sysprep first-boot setup tasks | |
[Service] | |
Type=simple | |
ExecStart=/usr/local/sbin/sysprep -y -b | |
[Install] | |
WantedBy=multi-user.target | |
EOF | |
/bin/systemctl enable sysprep-firstboot | |
return 0 | |
} | |
sysprep() { | |
parse_args "$@" | |
[[ $CONFIRM == true ]] || { | |
say 'Confirm with -y to start sysprep.' | |
exit 0 | |
} | |
say 'Beginning sysprep.' | |
[[ $FIRSTBOOT == true ]] && firstboot | |
say 'Stopping logging and auditing daemons.' | |
/bin/systemctl stop rsyslog.service | |
/usr/sbin/service auditd stop | |
clean_packages | |
clean_logs | |
clean_network | |
clean_files | |
generalize | |
setup_firstboot | |
say 'End of sysprep.' | |
[[ $SHUTDOWN == true ]] && { | |
say 'Shutting down the system.' | |
/bin/systemctl poweroff | |
} | |
exit 0 | |
} | |
# Only execute if not being sourced | |
[[ ${BASH_SOURCE[0]} == "$0" ]] && sysprep "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment