Skip to content

Instantly share code, notes, and snippets.

@kfiresmith
Created February 27, 2025 21:36
Show Gist options
  • Save kfiresmith/695f1f1377938d7cd1c711ff8fc06ece to your computer and use it in GitHub Desktop.
Save kfiresmith/695f1f1377938d7cd1c711ff8fc06ece to your computer and use it in GitHub Desktop.

Revisions

  1. kfiresmith created this gist Feb 27, 2025.
    220 changes: 220 additions & 0 deletions linux-sysprep.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,220 @@
    #!/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 "$@"