Skip to content

Instantly share code, notes, and snippets.

@ts-sz
Last active August 21, 2025 14:37
Show Gist options
  • Save ts-sz/f47fee16e567bc4a992a7bf40affb40b to your computer and use it in GitHub Desktop.
Save ts-sz/f47fee16e567bc4a992a7bf40affb40b to your computer and use it in GitHub Desktop.
Universal Linux System Anonymization Script - Clean and anonymize any Linux system before cloning or imaging

Universal Linux System Anonymization Script

License: MIT Version Linux

Overview

A comprehensive bash script for complete Linux system anonymization and cleanup. This script prepares Linux systems for cloning, imaging, or template creation by removing all identifying information, logs, and temporary data.

Features

🔍 Auto-Detection

  • Distribution Detection: Automatically identifies the Linux distribution
  • Package Manager Detection: Detects and uses the appropriate package manager
  • Init System Detection: Identifies systemd, SysV, or OpenRC

🧹 Complete Cleanup

  • System logs and journals
  • SSH host keys and known_hosts
  • Machine IDs and hardware identifiers
  • Network persistent rules and configurations
  • Package manager caches
  • User histories and caches
  • Temporary files and directories
  • Cloud-init data
  • Docker/Snap cleanup (if present)

🔐 Security Features

  • Removes all user traces
  • Clears shell histories
  • Removes SSH authorized keys
  • Cleans GPG keys
  • Removes saved credentials

🏷️ System Anonymization

  • Generates unique hostname based on MAC address and date
  • Resets all machine identifiers
  • Removes network interface mappings

Supported Distributions

Distribution Version Package Manager Tested
RHEL/CentOS 7, 8, 9 YUM/DNF
Fedora 35+ DNF
Rocky Linux 8, 9 DNF
AlmaLinux 8, 9 DNF
Debian 10, 11, 12 APT
Ubuntu 20.04, 22.04, 24.04 APT
Arch Linux Latest Pacman
openSUSE Leap, Tumbleweed Zypper
Alpine Linux 3.x APK

Installation

Quick Install (One-liner)

wget -qO- https://gist.githubusercontent.com/[username]/[gist-id]/raw/anonymize-system.sh | sudo bash -s -- --auto-yes

Manual Installation

  1. Download the script:
wget https://gist.githubusercontent.com/[username]/[gist-id]/raw/anonymize-system.sh
# or
curl -O https://gist.githubusercontent.com/[username]/[gist-id]/raw/anonymize-system.sh
  1. Make it executable:
chmod +x anonymize-system.sh
  1. Run the script:
sudo ./anonymize-system.sh

Usage

Interactive Mode

sudo ./anonymize-system.sh

The script will prompt for confirmation before proceeding.

Automatic Mode

sudo ./anonymize-system.sh --auto-yes
# or
sudo ./anonymize-system.sh -y

Skips all prompts and runs automatically (useful for automation).

Verbose Mode

sudo ./anonymize-system.sh --verbose
# or combine with auto-yes
sudo ./anonymize-system.sh -y --verbose

Provides detailed output of all operations.

Command Line Options

Option Description
--auto-yes, -y Skip confirmation prompts
--verbose Enable verbose output
--help, -h Display help message

What Gets Cleaned

System Logs

  • /var/log/* - All system logs
  • Systemd journals
  • Audit logs
  • Application logs (Apache, Nginx, etc.)
  • Boot logs
  • Cron logs

Network Configuration

  • Persistent network rules
  • NetworkManager connections
  • Netplan configurations (Ubuntu)
  • Interface configurations

User Data

  • Shell histories (bash, zsh, fish)
  • SSH known_hosts and authorized_keys
  • Cache directories
  • GPG keys
  • Browser data
  • Temporary files

System Identifiers

  • /etc/machine-id
  • /var/lib/dbus/machine-id
  • /var/lib/systemd/random-seed
  • SSH host keys

Package Manager

  • Package caches
  • Package databases
  • Download caches
  • Repository metadata

Use Cases

1. Virtual Machine Templates

Perfect for creating clean VM templates in:

  • VMware vSphere
  • Proxmox
  • VirtualBox
  • KVM/QEMU
  • Hyper-V

2. Cloud Images

Prepare cloud-ready images for:

  • AWS AMIs
  • Azure Images
  • Google Cloud Images
  • OpenStack Images
  • DigitalOcean Snapshots

3. Container Base Images

Create clean base images for:

  • Docker
  • Podman
  • LXC/LXD

4. System Cloning

Prepare systems for:

  • Clonezilla
  • dd imaging
  • Acronis
  • Ghost

Integration Examples

Ansible Playbook

---
- name: Anonymize Linux System
  hosts: target_systems
  become: yes
  tasks:
    - name: Download anonymization script
      get_url:
        url: https://gist.githubusercontent.com/[username]/[gist-id]/raw/anonymize-system.sh
        dest: /tmp/anonymize-system.sh
        mode: '0755'

    - name: Run anonymization
      command: /tmp/anonymize-system.sh --auto-yes
      
    - name: Shutdown system
      command: shutdown -h now

Packer Template

{
  "provisioners": [
    {
      "type": "shell",
      "inline": [
        "wget -qO /tmp/anonymize.sh https://gist.githubusercontent.com/[username]/[gist-id]/raw/anonymize-system.sh",
        "chmod +x /tmp/anonymize.sh",
        "sudo /tmp/anonymize.sh --auto-yes"
      ]
    }
  ]
}

CI/CD Pipeline (GitLab CI)

anonymize_image:
  stage: cleanup
  script:
    - wget -qO- https://gist.githubusercontent.com/[username]/[gist-id]/raw/anonymize-system.sh | sudo bash -s -- --auto-yes
  tags:
    - shell

Safety Features

  1. Root Check: Ensures script runs with proper privileges
  2. Error Handling: Continues operation even if individual commands fail
  3. Safe Deletion: Uses careful file removal with error checking
  4. Attribute Handling: Removes immutable attributes before deletion
  5. Confirmation Prompt: Requires user confirmation in interactive mode

Post-Anonymization

What Gets Regenerated

After anonymization, the following will be automatically regenerated on first boot:

  • Machine ID: New unique system identifier
  • SSH Host Keys: New cryptographic keys for SSH
  • Random Seed: New entropy seed for system
  • Network Rules: New persistent network mappings

Verification Steps

  1. Check hostname:
hostname
  1. Verify machine ID is empty:
cat /etc/machine-id
  1. Confirm SSH keys are removed:
ls -la /etc/ssh/ssh_host_*
  1. Check log directories:
du -sh /var/log/

Troubleshooting

Common Issues

Issue: Permission denied errors Solution: Ensure running with sudo or as root

Issue: Package manager detection fails Solution: Script will continue with generic cleanup

Issue: Hostname not changing Solution: Check if hostnamectl is available or /etc/hostname is writable

Debug Mode

For troubleshooting, run with verbose output:

sudo bash -x ./anonymize-system.sh --verbose

Security Considerations

⚠️ WARNING: This script performs irreversible operations!

  • All logs will be permanently deleted
  • SSH keys cannot be recovered
  • User histories will be lost
  • Cached credentials will be removed

Always ensure you have proper backups before running this script on production systems.

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create your feature branch
  3. Test on multiple distributions
  4. Submit a pull request

Best Practices

  1. Always test in a non-production environment first
  2. Create backups before running on important systems
  3. Shutdown immediately after running for imaging
  4. Document the anonymization in your deployment logs
  5. Verify the cleanup was successful before creating images

License

MIT License - See LICENSE file for details

Author

System Administrator Tools

Changelog

Version 2.0

  • Added automatic distribution detection
  • Added package manager auto-detection
  • Added init system detection
  • Improved error handling
  • Added verbose mode
  • Extended distribution support
  • Added Docker/Snap cleanup
  • Improved network configuration reset

Version 1.0

  • Initial release
  • Basic system cleanup
  • Manual distribution selection

Support

For issues, questions, or contributions, please open an issue on the GitHub Gist page.

Disclaimer

This script is provided "as is" without warranty of any kind. Always test in a safe environment before using on production systems. The authors are not responsible for any data loss or system damage resulting from the use of this script.


Remember: After running this script, immediately shutdown the system and create your image/snapshot. Do not boot the system again before imaging to maintain the clean state.

#!/bin/bash
# Universal Linux System Anonymization Script
# Version: 2.0
# Supports: RHEL, CentOS, Fedora, Rocky, AlmaLinux, Debian, Ubuntu, Arch, openSUSE, Alpine
# License: MIT
# Author: System Administrator
# Purpose: Complete system anonymization before cloning/imaging
set -euo pipefail
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# System detection variables
DISTRO="Unknown"
PKG_MANAGER="Unknown"
INIT_SYSTEM="Unknown"
# Check for auto-yes parameter
AUTO_YES=false
if [ "${1:-}" = "--auto-yes" ] || [ "${1:-}" = "-y" ]; then
AUTO_YES=true
fi
DELETE_AUTHORIZED_KEYS=false
if [ "${1:-}" = "--delete-authorized-keys" ] || [ "${2:-}" = "--delete-authorized-keys" ]; then
DELETE_AUTHORIZED_KEYS=true
fi
# Verbose mode
VERBOSE=false
if [ "${1:-}" = "--verbose" ] || [ "${2:-}" = "--verbose" ]; then
VERBOSE=true
fi
# Function to print colored output
print_status() {
echo -e "${GREEN}[+]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[!]${NC} $1"
}
print_error() {
echo -e "${RED}[!]${NC} $1"
}
print_info() {
echo -e "${BLUE}[i]${NC} $1"
}
print_verbose() {
if [ "$VERBOSE" = true ]; then
echo -e "${BLUE}[v]${NC} $1"
fi
}
# Function to detect Linux distribution
detect_distro() {
if [ -f /etc/os-release ]; then
. /etc/os-release
DISTRO=$ID
print_verbose "Detected distribution: $DISTRO"
elif [ -f /etc/redhat-release ]; then
DISTRO="rhel"
elif [ -f /etc/debian_version ]; then
DISTRO="debian"
elif [ -f /etc/arch-release ]; then
DISTRO="arch"
elif [ -f /etc/alpine-release ]; then
DISTRO="alpine"
fi
# Normalize distribution names
case $DISTRO in
ubuntu|debian|raspbian|linuxmint|pop)
DISTRO="debian"
;;
rhel|centos|fedora|rocky|almalinux|ol|scientific)
DISTRO="rhel"
;;
opensuse*|sles)
DISTRO="suse"
;;
arch|manjaro|endeavouros)
DISTRO="arch"
;;
esac
}
# Function to detect package manager
detect_package_manager() {
if command -v dnf &> /dev/null; then
PKG_MANAGER="dnf"
elif command -v yum &> /dev/null; then
PKG_MANAGER="yum"
elif command -v apt-get &> /dev/null; then
PKG_MANAGER="apt"
elif command -v pacman &> /dev/null; then
PKG_MANAGER="pacman"
elif command -v zypper &> /dev/null; then
PKG_MANAGER="zypper"
elif command -v apk &> /dev/null; then
PKG_MANAGER="apk"
fi
print_verbose "Detected package manager: $PKG_MANAGER"
}
# Function to detect init system
detect_init_system() {
if command -v systemctl &> /dev/null && systemctl 2>/dev/null | grep -q '\-\.mount'; then
INIT_SYSTEM="systemd"
elif command -v service &> /dev/null; then
INIT_SYSTEM="sysvinit"
elif command -v rc-service &> /dev/null; then
INIT_SYSTEM="openrc"
fi
print_verbose "Detected init system: $INIT_SYSTEM"
}
# Function to clean package manager cache
clean_package_cache() {
print_status "Cleaning package manager caches..."
case $PKG_MANAGER in
dnf)
dnf clean all -y &>/dev/null || true
rm -rf /var/cache/dnf/* 2>/dev/null || true
;;
yum)
yum clean all -y &>/dev/null || true
rm -rf /var/cache/yum/* 2>/dev/null || true
;;
apt)
apt-get clean -y &>/dev/null || true
apt-get autoclean -y &>/dev/null || true
rm -rf /var/cache/apt/archives/* 2>/dev/null || true
rm -rf /var/lib/apt/lists/* 2>/dev/null || true
;;
pacman)
pacman -Scc --noconfirm &>/dev/null || true
rm -rf /var/cache/pacman/pkg/* 2>/dev/null || true
;;
zypper)
zypper clean --all &>/dev/null || true
rm -rf /var/cache/zypp/* 2>/dev/null || true
;;
apk)
apk cache clean &>/dev/null || true
rm -rf /var/cache/apk/* 2>/dev/null || true
;;
esac
}
# Function to clean logs based on init system
clean_logs() {
print_status "Cleaning system logs..."
# Clean systemd journals if present
if [ "$INIT_SYSTEM" = "systemd" ] && command -v journalctl &> /dev/null; then
print_verbose "Cleaning systemd journals..."
journalctl --rotate 2>/dev/null || true
journalctl --vacuum-time=1s 2>/dev/null || true
rm -rf /var/log/journal/* 2>/dev/null || true
fi
# Clean traditional log files
print_verbose "Cleaning traditional log files..."
find /var/log -type f \( -name "*.log" -o -name "*.log.*" -o -name "*.gz" -o -name "*.xz" -o -name "*.bz2" \) 2>/dev/null | while read -r logfile; do
chattr -i "$logfile" 2>/dev/null || true
truncate -s 0 "$logfile" 2>/dev/null || rm -f "$logfile" 2>/dev/null || true
done
# Clean specific log directories
for dir in /var/log/audit /var/log/sssd /var/log/samba /var/log/nginx /var/log/apache2 /var/log/httpd; do
if [ -d "$dir" ]; then
rm -rf "${dir:?}"/* 2>/dev/null || true
fi
done
# Clean wtmp and btmp
for file in /var/log/wtmp /var/log/btmp /var/log/lastlog; do
[ -f "$file" ] && truncate -s 0 "$file"
done
}
# Function to clean distribution-specific files
clean_distro_specific() {
print_status "Cleaning distribution-specific files..."
case $DISTRO in
debian)
# Debian/Ubuntu specific
rm -f /var/lib/apt/lists/lock 2>/dev/null || true
rm -f /var/cache/debconf/*.old 2>/dev/null || true
rm -rf /var/lib/dhcp/* 2>/dev/null || true
;;
rhel)
# RHEL/CentOS/Fedora specific
rm -rf /var/cache/yum/* 2>/dev/null || true
rm -rf /var/lib/yum/history/* 2>/dev/null || true
rm -rf /var/lib/dnf/history/* 2>/dev/null || true
;;
arch)
# Arch specific
rm -rf /var/lib/pacman/sync/* 2>/dev/null || true
;;
suse)
# openSUSE specific
rm -rf /var/lib/zypp/history/* 2>/dev/null || true
;;
alpine)
# Alpine specific
rm -rf /var/cache/apk/* 2>/dev/null || true
;;
esac
}
# Function to reset network configuration
reset_network() {
print_status "Resetting network configuration..."
# Remove persistent network rules
rm -f /etc/udev/rules.d/70-persistent-net.rules 2>/dev/null || true
find /etc/udev/rules.d -name "*persistent-net*" -o -name "*net.rules" 2>/dev/null -delete || true
# Clean NetworkManager connections
if [ -d /etc/NetworkManager/system-connections ]; then
rm -f /etc/NetworkManager/system-connections/* 2>/dev/null || true
fi
# Clean netplan configs (Ubuntu)
if [ -d /etc/netplan ] && [ "$DISTRO" = "debian" ]; then
find /etc/netplan -name "*.yaml" -o -name "*.yml" | grep -v "00-installer-config" | xargs rm -f 2>/dev/null || true
fi
# Clean network interfaces (traditional)
if [ -f /etc/network/interfaces ]; then
cat > /etc/network/interfaces << EOF
# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback
EOF
fi
}
# Check if running as root
if [ "$EUID" -ne 0 ]; then
print_error "This script must be run as root"
echo "Usage: sudo $0 [--auto-yes] [--verbose]"
exit 1
fi
# Detect system configuration
print_info "Detecting system configuration..."
detect_distro
detect_package_manager
detect_init_system
# Banner
echo ""
echo "=========================================="
echo " UNIVERSAL SYSTEM ANONYMIZATION SCRIPT"
echo "=========================================="
echo ""
print_info "System Detection Results:"
echo " Distribution: $DISTRO"
echo " Package Manager: $PKG_MANAGER"
echo " Init System: $INIT_SYSTEM"
echo ""
print_warning "This script will:"
echo " - Clean all system logs and journals"
echo " - Remove SSH host keys"
echo " - Reset machine IDs"
echo " - Clean network persistent rules"
echo " - Clear all temporary files"
echo " - Remove shell histories"
echo " - Clean package manager caches"
echo " - Generate new unique hostname"
echo " - Remove all user traces"
echo ""
# Confirmation prompt
if [ "$AUTO_YES" = false ]; then
read -p "Do you want to proceed with system anonymization? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_warning "Operation cancelled by user"
exit 0
fi
else
print_status "Auto-yes mode enabled, proceeding..."
fi
echo ""
print_status "Starting system anonymization..."
# 1. Clean logs
clean_logs
# 2. Remove SSH host keys
print_status "Removing SSH host keys..."
rm -f /etc/ssh/ssh_host_* 2>/dev/null || true
rm -f /etc/ssh/*_key /etc/ssh/*_key.pub 2>/dev/null || true
# 3. Reset machine IDs
print_status "Resetting machine IDs..."
truncate -s 0 /etc/machine-id 2>/dev/null || true
if [ -f /var/lib/dbus/machine-id ]; then
truncate -s 0 /var/lib/dbus/machine-id 2>/dev/null || true
fi
rm -f /var/lib/systemd/random-seed 2>/dev/null || true
rm -f /var/lib/dbus/machine-id 2>/dev/null || true
# 4. Reset network configuration
reset_network
# 5. Clean temporary directories
print_status "Cleaning temporary directories..."
rm -rf /tmp/* /tmp/.* 2>/dev/null || true
rm -rf /var/tmp/* /var/tmp/.* 2>/dev/null || true
rm -rf /dev/shm/* 2>/dev/null || true
# 6. Clear shell histories
print_status "Clearing shell histories..."
for hist_file in /root/.bash_history /root/.zsh_history /root/.sh_history /root/.fish_history; do
[ -f "$hist_file" ] && truncate -s 0 "$hist_file"
done
find /home -type f \( -name ".bash_history" -o -name ".zsh_history" -o -name ".sh_history" -o -name ".fish_history" \) 2>/dev/null | while read -r hist; do
truncate -s 0 "$hist"
done
unset HISTFILE
history -c 2>/dev/null || true
# 7. Clean package manager caches
clean_package_cache
# 8. Clean user data
print_status "Cleaning user data..."
for user_home in /home/* /root; do
if [ -d "$user_home" ]; then
# Clear caches
rm -rf "${user_home}/.cache/"* 2>/dev/null || true
rm -rf "${user_home}/.local/share/Trash/"* 2>/dev/null || true
# Clear SSH data
rm -f "${user_home}/.ssh/known_hosts" 2>/dev/null || true
rm -f "${user_home}/.ssh/known_hosts.old" 2>/dev/null || true
# Delete SSH authorized_keys
if [ "$DELETE_AUTHORIZED_KEYS" = true ]; then
rm -f "${user_home}/.ssh/authorized_keys" 2>/dev/null || true
fi
# Clear other sensitive files
rm -f "${user_home}/.lesshst" 2>/dev/null || true
rm -f "${user_home}/.wget-hsts" 2>/dev/null || true
rm -f "${user_home}/.viminfo" 2>/dev/null || true
rm -f "${user_home}/.nano_history" 2>/dev/null || true
rm -rf "${user_home}/.gnupg" 2>/dev/null || true
rm -rf "${user_home}/.config/pulse" 2>/dev/null || true
fi
done
# 9. Generate new hostname
print_status "Generating new unique hostname..."
INTERFACE=$(ip -o link show 2>/dev/null | grep -v "lo" | awk '{print $2}' | sed 's/://' | head -n 1)
if [ -n "$INTERFACE" ] && [ -f "/sys/class/net/$INTERFACE/address" ]; then
MAC_SUFFIX=$(cat "/sys/class/net/$INTERFACE/address" 2>/dev/null | tail -c 9 | tr -d ':' | tr '[:lower:]' '[:upper:]')
else
MAC_SUFFIX=$(openssl rand -hex 3 2>/dev/null | tr '[:lower:]' '[:upper:]') || MAC_SUFFIX=$(date +%s | tail -c 7)
fi
DATE_SUFFIX=$(date +"%m%d")
NEW_HOSTNAME="linux-${MAC_SUFFIX}-${DATE_SUFFIX}"
if command -v hostnamectl &> /dev/null; then
hostnamectl set-hostname "$NEW_HOSTNAME" 2>/dev/null || echo "$NEW_HOSTNAME" > /etc/hostname
else
echo "$NEW_HOSTNAME" > /etc/hostname
fi
# Update /etc/hosts
sed -i '/127.0.1.1/d' /etc/hosts 2>/dev/null || true
echo -e "127.0.1.1\t$NEW_HOSTNAME" >> /etc/hosts
# 10. Distribution-specific cleanup
clean_distro_specific
# 11. Additional cleanup
print_status "Performing final cleanup..."
# Clear mail
rm -f /var/mail/* 2>/dev/null || true
rm -f /var/spool/mail/* 2>/dev/null || true
# Clear cron
for cron_dir in /var/spool/cron /var/spool/cron/crontabs; do
if [ -d "$cron_dir" ]; then
find "$cron_dir" -type f -exec truncate -s 0 {} \; 2>/dev/null || true
fi
done
# Clear systemd coredumps
if [ -d /var/lib/systemd/coredump ]; then
rm -rf /var/lib/systemd/coredump/* 2>/dev/null || true
fi
# Clear crash reports
rm -rf /var/crash/* 2>/dev/null || true
# Cloud-init cleanup
if [ -d /var/lib/cloud ]; then
rm -rf /var/lib/cloud/instances/* 2>/dev/null || true
rm -rf /var/lib/cloud/data/* 2>/dev/null || true
fi
# Docker cleanup (if present)
if command -v docker &> /dev/null; then
docker system prune -af --volumes 2>/dev/null || true
fi
# Snap cleanup (if present)
if command -v snap &> /dev/null; then
snap list --all | awk '/disabled/{print $1, $3}' | while read snapname revision; do
snap remove "$snapname" --revision="$revision" 2>/dev/null || true
done
fi
# Final sync
sync
# Summary
echo ""
echo "=========================================="
echo -e "${GREEN} ANONYMIZATION COMPLETE${NC}"
echo "=========================================="
echo ""
print_status "System successfully anonymized!"
echo ""
echo " ✓ Distribution: $DISTRO"
echo " ✓ All logs cleaned"
echo " ✓ SSH keys removed"
echo " ✓ Machine IDs reset"
echo " ✓ Network rules cleaned"
echo " ✓ Package cache cleared"
echo " ✓ User data anonymized"
echo " ✓ New hostname: $NEW_HOSTNAME"
echo ""
print_warning "Important notes:"
echo " • Machine ID will regenerate on next boot"
echo " • SSH keys will regenerate on SSH service start"
echo " • System is ready for cloning/imaging"
echo ""
print_info "Recommended next steps:"
echo " 1. Shutdown the system: shutdown -h now"
echo " 2. Create your image/snapshot"
echo " 3. Deploy to new systems"
echo ""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment