Pass data to an instance to configure it or run scripts after it starts.
Find a file
2025-10-08 23:36:39 +00:00
ripcord Add ripcord/README.md 2025-07-06 07:17:35 +00:00
synterloper linux fixes 2025-07-07 19:48:20 +00:00
LICENSE Initial commit 2025-07-06 06:44:59 +00:00
README.md detect init in step 7 for ripcord 2025-10-08 23:36:39 +00:00
user-data.sh detect init for step 7 ripcord 2025-10-08 23:35:46 +00:00

Unified First-Boot Server Setup Script

A complete, self-contained provisioning script for automated server deployment that combines both Ripcord and SYNterloper with system hardening tasks (user creation, SSH setup, sudoers) in one idempotent user-data script.


🔧 Purpose

This script automates the following tasks on first boot:

  • Creates a non-root user with SSH access and sudo privileges
  • Installs Ansible-ready Python packages
  • Deploys Ripcord (emergency disk space reserve)
  • Deploys SYNterloper (rolling SSH connection logger)
  • Validates each step with .flag files
  • Leaves all components ready for manual management

➡️ For tool-specific details, see individual READMEs:


📦 Repository Structure

.
├── user-data.sh     # This script
├── ripcord/
│   ├── ripcord.sh
│   └── README.md
├── synterloper/
│   ├── synterloper.sh
│   └── README.md
└── README.md               # You are here

🚀 Installation

Paste this script into your cloud provider's User Data field during instance creation:

#!/bin/bash
set -e  # Exit on any error

# Define user and flag locations
USRNAME=fastsoul
FLAG_DIR=/var/lib/cloud/scripts/user
mkdir -p "$FLAG_DIR"

# --- Step 1: Create user ---
if [ ! -f "$FLAG_DIR/.user_created.flag" ]; then
    echo "Creating user: $USRNAME"
    if ! id "$USRNAME" &>/dev/null; then
        useradd "$USRNAME" -s /bin/bash -d /home/"$USRNAME" -m || { echo "User creation failed"; exit 1; }
        chown -R "$USRNAME":"$USRNAME" /home/"$USRNAME"
    else
        echo "User $USRNAME already exists. Skipping creation."
    fi
    touch "$FLAG_DIR/.user_created.flag"
fi

# --- Step 2: Create SSH directory and authorized_keys file ---
if [ ! -f "$FLAG_DIR/.ssh_setup.flag" ]; then
    echo "Setting up SSH for $USRNAME"
    mkdir -p "/home/$USRNAME/.ssh"
    touch "/home/$USRNAME/.ssh/authorized_keys"
    chown -R "$USRNAME":"$USRNAME" "/home/$USRNAME/.ssh"
    chmod 700 "/home/$USRNAME/.ssh"
    chmod 600 "/home/$USRNAME/.ssh/authorized_keys"
    touch "$FLAG_DIR/.ssh_setup.flag"
fi

# --- Step 3: Add SSH keys to authorized_keys ---
if [ ! -f "$FLAG_DIR/.ssh_keys_added.flag" ]; then
    echo "Adding SSH keys to authorized_keys"
    cat << EOF >> /home/$USRNAME/.ssh/authorized_keys
cert-authority ssh-rsa AAAAB3NzaXXXX== user@example.com

ssh-rsa AAAAB3NzaYYYYYYYYYYYYYYYYYYYYYYYYYYYYQ==

ssh-rsa AAAAB3NzaXXXXXXXXXXXXXXXXXXXXXXXXXXXXQ==
EOF

    if [ $? -eq 0 ]; then
        chown -R "$USRNAME":"$USRNAME" "/home/$USRNAME/.ssh/authorized_keys"
        touch "$FLAG_DIR/.ssh_keys_added.flag"
    else
        echo "Failed to write SSH keys to authorized_keys"
        exit 1
    fi
fi

# --- Step 4: Set NOPASSWD sudoers ---
if [ ! -f "$FLAG_DIR/.sudoers_set.flag" ]; then
    echo "Setting up NOPASSWD sudo for $USRNAME"
    echo "$USRNAME ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/"$USRNAME" || { echo "Failed to write sudoers file"; exit 1; }
    touch "$FLAG_DIR/.sudoers_set.flag"
fi

# --- Step 5: Install python3-apt for Ansible support ---
if [ ! -f "$FLAG_DIR/.python_installed.flag" ]; then
    echo "Updating APT and installing python3-apt"
    apt update -y || { echo "APT update failed"; exit 1; }
    apt install -y python3-apt || { echo "Failed to install python3-apt"; exit 1; }
    touch "$FLAG_DIR/.python_installed.flag"
fi

# --- Step 6: Download Ripcord and SYNterloper from Git ---
if [ ! -f "$FLAG_DIR/.scripts_downloaded.flag" ]; then
    echo "Downloading Ripcord and SYNterloper scripts"
    curl -fsSL -o /usr/local/sbin/ripcord.sh https://git.r21.io/primemover/user-data/raw/branch/master/ripcord/ripcord.sh || { echo "Ripcord download failed"; exit 1; }
    curl -fsSL -o /usr/local/sbin/synterloper.sh https://git.r21.io/primemover/user-data/raw/branch/master/synterloper/synterloper.sh || { echo "SYNterloper download failed"; exit 1; }
    chmod +x /usr/local/sbin/ripcord.sh /usr/local/sbin/synterloper.sh || { echo "chmod failed"; exit 1; }
    touch "$FLAG_DIR/.scripts_downloaded.flag"
fi

# --- Step 7: Setup Ripcord service (init-system aware) ---
if [ ! -f "$FLAG_DIR/.ripcord_setup.flag" ]; then
    echo "Setting up Ripcord service (detecting init system)"
    
    # Detect init system
    if [ -f /run/systemd/system ]; then
        INIT_SYSTEM="systemd"
    elif [ -d /etc/init.d ] && [ ! -L /etc/init.d ]; then
        INIT_SYSTEM="sysvinit"
    elif [ -d /etc/rc.d ] && [ -x /etc/rc.d/rc ]; then
        INIT_SYSTEM="openrc"
    else
        INIT_SYSTEM="unknown"
    fi
    
    echo "Detected init system: $INIT_SYSTEM"
    
    # Setup based on init system
    if [ "$INIT_SYSTEM" = "systemd" ]; then
        cat << EOF | sudo tee /etc/systemd/system/ripcord.service
[Unit]
Description=Ripcord Emergency Disk Space Reserve
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/ripcord.sh
ExecStartPost=/bin/sh -c "systemctl disable ripcord.service"
[Install]
WantedBy=multi-user.target
EOF
        systemctl enable ripcord.service
    elif [ "$INIT_SYSTEM" = "sysvinit" ]; then
        # Devuan/OpenRC-compatible init script
        cat << 'EOF' | sudo tee /etc/init.d/ripcord
#!/bin/sh
### BEGIN INIT INFO
# Provides:          ripcord
# Required-Start:    $local_fs $network
# Required-Stop:     $local_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Ripcord Emergency Disk Space Reserve
# Description:       Creates emergency disk space on first boot
### END INIT INFO

case "$1" in
  start)
    if [ ! -f "/var/log/ripcord-deployed.flag" ]; then
      /usr/local/sbin/ripcord.sh
      touch /var/log/ripcord-deployed.flag
    fi
    ;;
  *)
    echo "Usage: /etc/init.d/ripcord start" >&2
    exit 3
    ;;
esac

exit 0
EOF
        chmod +x /etc/init.d/ripcord
        update-rc.d ripcord defaults
    else
        echo "Unsupported init system. Running Ripcord once now."
        /usr/local/sbin/ripcord.sh
        touch /var/log/ripcord-deployed.flag
    fi
    
    touch "$FLAG_DIR/.ripcord_setup.flag"
fi

# --- Step 8: Install SYNterloper ---
if [ ! -f "$FLAG_DIR/.synterloper_installed.flag" ]; then
    echo "Installing SYNterloper"
    /usr/local/sbin/synterloper.sh install || { echo "SYNterloper install failed"; exit 1; }
    touch "$FLAG_DIR/.synterloper_installed.flag"
fi

# --- Final Step: Mark completion ---
if [ ! -f "$FLAG_DIR/.user_data_complete.flag" ]; then
    echo "Marking user-data completion"
    touch /root/user_data_completed.txt
    touch "$FLAG_DIR/.user_data_complete.flag"
fi

echo "All steps completed successfully."
exit 0

What Gets Installed

Component Purpose Validation Flag
User: fastsoul Primary admin account /var/lib/cloud/scripts/user/.user_created.flag
SSH Keys Cert-authority and/or deployment keys /var/lib/cloud/scripts/user/.ssh_keys_added.flag
Sudoers NOPASSWD:ALL access /var/lib/cloud/scripts/user/.sudoers_set.flag
Python 3-apt Ansible dependency /var/lib/cloud/scripts/user/.python_installed.flag
Ripcord.sh Emergency disk ballast /var/lib/cloud/scripts/user/.ripcord_setup.flag
Synterloper.sh Rolling SSH connection logger /var/lib/cloud/scripts/user/.synterloper_installed.flag

⚙️ Configuration

Edit these variables at the top of the script to customize:

# Change these to match your needs
USRNAME=fastsoul
SSH_KEYS="
cert-authority ssh-rsa AAAAB3NzaXXXXXXXXXXXXXXXXXX...user@example.com
ssh-rsa AAAAB3NzaYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYQ==
ssh-rsa AAAAB3NzaXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXQ==

🔍 Validation

All steps are validated with .flag files in /var/lib/cloud/scripts/user/:

ls -la /var/lib/cloud/scripts/user/

Expected output:

.user_created.flag
.ssh_keys_added.flag
.sudoers_set.flag
.python_installed.flag
.scripts_downloaded.flag
.ripcord_setup.flag
.synterloper_installed.flag
.user_data_complete.flag

🧹 Re-running

To re-run the script (e.g., for testing):

  1. Delete specific .flag files:
    rm /var/lib/cloud/scripts/user/.sudoers_set.flag
    
  2. Re-run the script:
    sudo /var/lib/cloud/scripts/user/user-data.sh
    

⚠️ Known Limitations

  • Debian/Ubuntu only (FreeBSD support would require separate logic)
  • Root access required (must be run via cloud-init)
  • Hardcoded SSH keys (rotate credentials for production use)

📚 For More Details


📄 License

This script is released under the MIT License. See LICENSE for details.