Skip to content

Instantly share code, notes, and snippets.

@AfroThundr3007730
Last active February 27, 2025 22:12
Show Gist options
  • Save AfroThundr3007730/ff5229c5b1f9a018091b14ceac95aa55 to your computer and use it in GitHub Desktop.
Save AfroThundr3007730/ff5229c5b1f9a018091b14ceac95aa55 to your computer and use it in GitHub Desktop.
Cloning preparation script for linux systems.
#!/bin/bash
# Does the equivalent of sysprep for linux boxes to prepare them for cloning.
# SPDX-License-Identifier: GPL-3.0-or-later
# For issues or updated versions of this script, browse to the following URL:
# https://gist.github.com/AfroThundr3007730/ff5229c5b1f9a018091b14ceac95aa55
set -euo pipefail
shopt -s extdebug nullglob
AUTHOR='AfroThundr'
BASENAME="${0##*/}"
MODIFIED='20241217'
VERSION='1.9.1'
sysprep.parse_args() {
(($# > 0)) || utils.die 'No arguments specified, use -h for help.'
while (($# > 0)); do
case $1 in
-v) printf '%s: Version %s, updated %s by %s\n' \
"$BASENAME" "$VERSION" "$MODIFIED" "$AUTHOR" &&
shift && (($# > 0)) || exit 0 ;;
-h)
printf 'Cloning preparation script for linux systems.\n\n'
printf 'Usage: %s [-v ] (-h | -y [-b] [-l <log_file>] [-s])\n' \
"$BASENAME"
printf '\nOptions:\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
;;
-b) FIRSTBOOT=true && break ;;
-l) LOGFILE=$2 && shift 2 ;;
-s) SHUTDOWN=true && shift ;;
-y) CONFIRM=true && shift ;;
*) utils.die 'Invalid argument specified, use -h for help.' ;;
esac
done
[[ ${CONFIRM:-} == true ]] || utils.die 'Confirm with -y to start sysprep.'
}
utils.die() { printf '\e[31m%s\n\e[m' "${1:-}" >&2 && exit 1; }
utils.say() {
LOGFILE=${LOGFILE:=/var/log/sysprep.log}
[[ ${TZOFFSET:-} ]] || utils.get_tz_offset_secs -s
[[ -n $LOGFILE && ! $LOGFILE == no ]] && {
[[ -f $LOGFILE ]] || (umask 027 && : >"$LOGFILE")
printf '%(%FT%TZ)T: %s\n' $((EPOCHSECONDS - TZOFFSET)) "$1" >>"$LOGFILE"
}
printf '%(%FT%TZ)T: %s\n' $((EPOCHSECONDS - TZOFFSET)) "$1"
}
utils.get_tz_offset_secs() {
local off && printf -v off '\n%(%z)T' -1
off=${off: -5:1}$((${off: -4:2} * 3600 + ${off: -2:2} * 60))
[[ $1 == -s ]] || printf '%s\n' "$off" && declare -gi TZOFFSET="$off"
}
sysprep.detect_os() {
[[ -f /etc/os-release ]] && source /etc/os-release
if [[ ${ID:-} =~ (fedora|rhel|centos) ||
${ID_LIKE:-} =~ (fedora|rhel|centos) ]]; then
FEDORA_DERIV=true
elif [[ ${ID:-} =~ (debian|ubuntu|mint) ||
${ID_LIKE:-} =~ (debian|ubuntu|mint) ]]; then
DEBIAN_DERIV=true
else
utils.say 'An unknown base linux distribution was detected.'
utils.say 'This script works with Debian and Fedora based distros.'
exit 1
fi
}
sysprep.apt_purge() {
apt remove -qy --purge "$(
for i in $(find /boot/vmlinuz-* | sort | head -n -1 |
grep -v "$(uname -r)" | cut -d- -f2-); do
echo linux-{image,headers,modules}-"$i"
done
)" &>/dev/null && apt autoremove -qy --purge &>/dev/null
}
sysprep.firstboot() {
utils.say 'Running sysprep first-boot setup script.'
[[ ${DEBIAN_DERIV:-} == true ]] && {
find /etc/ssh/*key &>/dev/null || {
utils.say 'Regenerating SSH host keys...'
dpkg-reconfigure openssh-server
}
}
[[ ${HOSTNAME:-} == CHANGEME ]] && {
utils.say 'Regenerating hostname and rebooting...'
hostnamectl set-hostname \
"LINUX-$(tr -cd '[:upper:][:digit:]' </dev/urandom | head -c 9)"
systemctl reboot
}
[[ -f /var/lib/aide/aide.db.gz ]] && {
utils.say 'Regenerating AIDE database...'
aide --update && mv -f /var/lib/aide/aide.db{.new,}.gz
}
utils.say 'Sysprep firtst-boot setup complete, disabling service.'
systemctl -q disable sysprep-firstboot
exit 0
}
sysprep.clean_packages() {
utils.say 'Removing old kernels.'
if [[ ${FEDORA_DERIV:-} == true ]]; then
if command -v dnf &>/dev/null; then
dnf remove -qy "$(dnf repoquery --installonly --latest-limit=-1)"
else
command -v package-cleanup &>/dev/null ||
yum install -qy yum-utils &>/dev/null
package-cleanup -qy --oldkernels --count=1 &>/dev/null
fi
elif [[ ${DEBIAN_DERIV:-} == true ]]; then
command -v purge-old-kernels &>/dev/null ||
apt install -qy byobu &>/dev/null
purge-old-kernels -qy --keep 1 &>/dev/null || sysprep.apt_purge
fi
utils.say 'Clearing package cache.'
if [[ ${FEDORA_DERIV:-} == true ]]; then
yum clean all -q &>/dev/null
rm -rf /var/cache/yum/*
elif [[ ${DEBIAN_DERIV:-} == true ]]; then
apt clean &>/dev/null
rm -rf /var/cache/apt/archives/*
fi
}
sysprep.clean_logs() {
utils.say 'Clearing old logs.'
logrotate -f /etc/logrotate.conf &>/dev/null || :
find /var/log -type f -regextype posix-extended -regex \
".*/*(-[0-9]{8}|.[0-9]|.gz)$" -delete
command -V setenforce &>/dev/null && setenforce 0
journalctl -q --sync --rotate --vacuum-files 1
rm -f /var/log/dmesg.old /var/log/anaconda/*
utils.say 'Clearing audit logs.'
: >/var/log/audit/audit.log
: >/var/log/wtmp
: >/var/log/lastlog
: >/var/log/grubby
}
sysprep.clean_network() {
utils.say 'Clearing udev persistent rules.'
rm -f /etc/udev/rules.d/70*
utils.say 'Removing MACs/UUIDs from network configs.'
[[ ${FEDORA_DERIV:-} == true ]] && {
for file in /etc/sysconfig/network-scripts/ifcfg-*; do
sed -ri '/^(HWADDR|UUID)=/d' "$file"
done
}
for file in /etc/NetworkManager/system-connections/*; do
sed -ri '/^(mac-address|uuid)=/d' "$file"
done
}
sysprep.clean_files() {
utils.say 'Cleaning out temp directories.'
rm -rf /tmp/* /var/tmp/* /var/cache/*
utils.say 'Cleaning up root home directory.'
: >"${HISTFILE:-/dev/null}" && unset HISTFILE
rm -f /root/anaconda-ks.cfg /root/.ssh/* /root/.gnupg/*
}
sysprep.generalize() {
utils.say 'Removing SSH host keys.'
rm -f /etc/ssh/*key*
utils.say 'Clearing machine-id'
: >/etc/machine-id
utils.say 'Removing random-seed'
rm -f /var/lib/systemd/random-seed
[[ -f /opt/McAfee/agent/bin/maconfig ]] && {
utils.say 'Resetting McAfee Agent'
systemctl stop mcafee.ma
command -V setenforce &>/dev/null && setenforce 0
/opt/McAfee/agent/bin/maconfig -enforce -noguid
}
utils.say 'Resetting hostname.'
hostnamectl set-hostname 'CHANGEME'
}
sysprep.setup_firstboot() {
utils.say 'Enabling sysprep firstboot service.'
local _unit=/etc/systemd/system/sysprep-firstboot.service
[[ -f $_unit ]] || cat <<'EOF' >$_unit
[Unit]
Description=Sysprep first-boot setup tasks
[Service]
Type=simple
ExecStart=/usr/local/sbin/sysprep -y -b
[Install]
WantedBy=multi-user.target
EOF
systemctl -q enable sysprep-firstboot
}
sysprep.run() {
sysprep.parse_args "$@"
sysprep.detect_os
utils.say 'Beginning sysprep.'
[[ ${FIRSTBOOT:-} == true ]] && sysprep.firstboot
utils.say 'Stopping logging and auditing daemons.'
systemctl -q stop rsyslog.service
service auditd stop &>/dev/null
sysprep.clean_packages
sysprep.clean_logs
sysprep.clean_network
sysprep.clean_files
sysprep.generalize
sysprep.setup_firstboot
utils.say 'End of sysprep.'
[[ ${SHUTDOWN:-} == true ]] &&
utils.say 'Shutting down the system.' && systemctl poweroff
}
# Only execute if not being sourced
[[ ${BASH_SOURCE[0]} == "$0" ]] || return 0 && sysprep.run "$@"
@AfroThundr3007730
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment