bash -c "$(curl -sSL https://gist.githubusercontent.com/arjenzhou/beec5a72453f7ca0bc3553f1302dadf9/raw/setup_debian.sh)"
Last active
March 25, 2026 03:07
-
-
Save arjenzhou/beec5a72453f7ca0bc3553f1302dadf9 to your computer and use it in GitHub Desktop.
setup_debian.sh
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 | |
| # ================= CONFIGURATION ================= | |
| VERSION="V6.7-2026.03.23" | |
| USER_NAME="arjenzhou" | |
| GIT_USER_NAME="arjenzhou" | |
| GIT_USER_EMAIL="github@arjenzhou.com" | |
| MAC_PUBLIC_KEY="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCxvvxGGZmqH+L/aV3ppsCzYVKqBKtlxl11k/guc+l7zziL+0Hn9sBO2gLOfr3YvMwwJYAwwr1pj1SrqaBVJyu9tXrC2AH8k52WeXn1AtJpWTBV5ZO4CtER9jbaelB21Axf5dWvWgbFRVaw3k2FZc2gpKTeJlvHLH+tMh+jZG9ten18W1FdAKES9SIaIU+DEx5nKC58iov3Qa7bDCI6PjqHIr0xDuoVuIoRNBSzXGWAKl/LpMmh9wh/G9ntgxIAdw0M7oJB6eVRct6CMJRkAQU2a1AGfMwWD/ccL60fzoaYyP1qC8KmE5B9vCsXvS5KaRX0o8/FprJ/s9rvX2QLR9MOBqzt7U5tHHhm1YnZfJ6CrTIa5bA5G7lobRL97vTclwURBwHd/88DtyvsIZDeMBrPy6+28+woRd5iIUaYNLj1FUtJeKc56uOY2fkQU2jpz063pDL9KclXUTR8C2kcuUmBU+ZG8/qAdn9uHMLozmJLrC0BHrf7FdDNXzsPXIUs/Sc= arjenzhou@localhost" | |
| CLUSTER_KEY_NAME="id_ed25519_debian_cluster" | |
| # ================================================= | |
| # 0. Header & Permissions Check | |
| echo "--- [ π Pro Node $VERSION Full Master ] ---" | |
| if [ "$EUID" -ne 0 ]; then | |
| echo "β Error: Please run as root (sudo bash setup_debian.sh)" | |
| exit 1 | |
| fi | |
| # 1. SSH Private Key Injection | |
| echo "--- 1. SSH Private Key Injection ---" | |
| echo "Please paste your cluster private key, then press [Enter] and [Ctrl + D]." | |
| if [ ! -t 0 ]; then | |
| CLUSTER_PRIVATE_KEY=$(cat < /dev/tty) | |
| else | |
| CLUSTER_PRIVATE_KEY=$(cat) | |
| fi | |
| echo "--- 2. System Update & Base APT Tools ---" | |
| export DEBIAN_FRONTEND=noninteractive | |
| echo "iperf3 iperf3/start_daemon boolean false" | debconf-set-selections | |
| # Anti-conflict Patch: Temporarily block service actions to prevent iperf3 install errors | |
| echo -e '#!/bin/sh\nexit 101' > /usr/sbin/policy-rc.d | |
| chmod +x /usr/sbin/policy-rc.d | |
| apt update && apt upgrade -y | |
| apt install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" \ | |
| vim tmux curl git sudo wget openssh-server qemu-guest-agent \ | |
| htop btop iperf3 zoxide build-essential libssl-dev zlib1g-dev \ | |
| duf ncdu net-tools sudo | |
| # Restore service action permissions | |
| rm -f /usr/sbin/policy-rc.d | |
| # System-wide Starship prompt | |
| command -v starship >/dev/null || curl -sS https://starship.rs/install.sh | sh -s -- -y | |
| echo "--- 3. User & Cluster-Aware Configuration ---" | |
| # Set unique hostname based on IP (e.g., 192.168.6.15 -> node-15) | |
| IP_SUFFIX=$(hostname -I | awk '{print $1}' | cut -d. -f4) | |
| NEW_HOSTNAME="node-$IP_SUFFIX" | |
| hostnamectl set-hostname "$NEW_HOSTNAME" | |
| sed -i "s/127.0.1.1.*/127.0.1.1 $NEW_HOSTNAME/g" /etc/hosts | |
| # Create the user if they do not exist | |
| if ! id "$USER_NAME" &>/dev/null; then | |
| echo "User '$USER_NAME' not found. Creating user..." | |
| adduser --disabled-password --gecos "" "$USER_NAME" | |
| else | |
| echo "User '$USER_NAME' already exists." | |
| fi | |
| # Grant sudo privileges if not already configured | |
| # This checks for the specific sudoers.d file to prevent duplicates | |
| SUDO_FILE="/etc/sudoers.d/$USER_NAME" | |
| if [ ! -f "$SUDO_FILE" ]; then | |
| echo "Granting sudo privileges to '$USER_NAME'..." | |
| echo "$USER_NAME ALL=(ALL) NOPASSWD:ALL" > "$SUDO_FILE" | |
| chmod 0440 "$SUDO_FILE" | |
| else | |
| echo "User '$USER_NAME' already has sudo privileges configured." | |
| fi | |
| USER_HOME="/home/$USER_NAME" | |
| mkdir -p "$USER_HOME/.ssh" | |
| echo "$MAC_PUBLIC_KEY" > "$USER_HOME/.ssh/authorized_keys" | |
| if [ -n "$CLUSTER_PRIVATE_KEY" ]; then | |
| CLUSTER_KEY_PATH="$USER_HOME/.ssh/$CLUSTER_KEY_NAME" | |
| echo "$CLUSTER_PRIVATE_KEY" > "$CLUSTER_KEY_PATH" | |
| chmod 600 "$CLUSTER_KEY_PATH" | |
| # Cluster Trust: Derive public key and authorize it for intra-cluster SSH | |
| CLUSTER_PUB=$(ssh-keygen -y -f "$CLUSTER_KEY_PATH") | |
| grep -q "$CLUSTER_PUB" "$USER_HOME/.ssh/authorized_keys" || echo "$CLUSTER_PUB" >> "$USER_HOME/.ssh/authorized_keys" | |
| # Configure global SSH config for all hosts | |
| if ! grep -q "$CLUSTER_KEY_NAME" /etc/ssh/ssh_config; then | |
| cat >> /etc/ssh/ssh_config <<EOF | |
| # Universal SSH Identity | |
| Host * | |
| IdentityFile ~/.ssh/$CLUSTER_KEY_NAME | |
| StrictHostKeyChecking no | |
| UserKnownHostsFile /dev/null | |
| EOF | |
| fi | |
| fi | |
| chown -R "$USER_NAME:$USER_NAME" "$USER_HOME/.ssh" | |
| chmod 700 "$USER_HOME/.ssh" | |
| # Hardening: Key-based login only | |
| sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/g' /etc/ssh/sshd_config | |
| systemctl restart ssh 2>/dev/null || true | |
| echo "--- 4. UI & XDG Development Environment ---" | |
| sudo -u "$USER_NAME" -i bash -s "$GIT_USER_NAME" "$GIT_USER_EMAIL" <<'EOF' | |
| G_NAME="$1" | |
| G_EMAIL="$2" | |
| # Ensure .bashrc exists to avoid grep error | |
| touch ~/.bashrc | |
| # Enforce XDG Base Directory Specification | |
| export XDG_CONFIG_HOME="$HOME/.config" | |
| export XDG_DATA_HOME="$HOME/.local/share" | |
| mkdir -p "$XDG_CONFIG_HOME" "$XDG_DATA_HOME" "$HOME/.local/bin" | |
| # Git Global Configuration | |
| git config --global user.name "$G_NAME" | |
| git config --global user.email "$G_EMAIL" | |
| git config --global core.quotepath false | |
| # Install Mise | |
| if [ ! -f "$HOME/.local/bin/mise" ]; then | |
| curl https://mise.jdx.dev/install.sh | sh | |
| fi | |
| export PATH="$HOME/.local/bin:$PATH" | |
| eval "$(mise activate bash)" | |
| # Provision tools via Mise (XDG Compliant) | |
| echo "Provisioning tools via Mise..." | |
| mise use --global python@latest node@lts eza@latest gping@latest bat@latest fzf@latest uv@latest | |
| eval "$(mise activate bash)" | |
| # NPM XDG hygiene | |
| npm config set prefix "$HOME/.local" | |
| export PATH="$HOME/.local/bin:$PATH" | |
| # Install tldr | |
| echo "Installing tldr..." | |
| npm install -g tldr --loglevel=error | |
| # Starship Layout (XDG Location) | |
| cat > "$XDG_CONFIG_HOME/starship.toml" <<'STARSHIP_CONF' | |
| format = """ | |
| [$username](bold blue)\ | |
| ${custom.address}\ | |
| $directory\ | |
| $git_branch\ | |
| $git_status\ | |
| $python\ | |
| $nodejs\ | |
| $fill\ | |
| $cmd_duration $line_break\ | |
| $character""" | |
| [custom.address] | |
| command = "hostname -I | awk '{print $1}'" | |
| when = "true" | |
| shell = ["bash", "--noprofile", "--norc"] | |
| format = "[π $output](bold yellow) " | |
| [fill] | |
| symbol = " " | |
| STARSHIP_CONF | |
| # Idempotent Bashrc Injection | |
| if ! grep -q "Custom Config START" ~/.bashrc; then | |
| cat >> ~/.bashrc <<'BASHRC_BLOCK' | |
| # >>> Custom Config START >>> | |
| # 1. XDG Base Directory Support | |
| export XDG_CONFIG_HOME="$HOME/.config" | |
| export XDG_DATA_HOME="$HOME/.local/share" | |
| # 2. Path Configuration | |
| export PATH="$HOME/.local/bin:$PATH" | |
| # 3. Tool Initialization | |
| [ -f "$HOME/.local/bin/mise" ] && eval "$($HOME/.local/bin/mise activate bash)" | |
| command -v zoxide >/dev/null && eval "$(zoxide init bash)" | |
| command -v starship >/dev/null && eval "$(starship init bash)" | |
| command -v fzf >/dev/null && eval "$(fzf --bash)" | |
| # 4. Productivity Aliases | |
| command -v eza >/dev/null && alias ls='eza --icons' | |
| command -v eza >/dev/null && alias ll='eza -lh --icons' | |
| command -v eza >/dev/null && alias l='eza -1 --icons' | |
| command -v btop >/dev/null && alias top='btop' | |
| command -v duf >/dev/null && alias df='duf' | |
| command -v bat >/dev/null && alias cat='bat --paging=never' | |
| command -v uv >/dev/null && alias venv='uv venv' | |
| command -v uv >/dev/null && alias pip='uv pip' | |
| # 5. Pure Dynamic Quick Jump Function | |
| s() { | |
| if [ -z "$1" ]; then | |
| echo "Usage: s <ip_suffix> (e.g., s 13)" | |
| return 1 | |
| fi | |
| # 1. Get current username | |
| local CURRENT_USER=$(whoami) | |
| # 2. Extract subnet prefix dynamically | |
| # Use 'hostname -I' and pick the first non-loopback IP | |
| local PRIMARY_IP=$(hostname -I | awk '{print $1}') | |
| if [ -z "$PRIMARY_IP" ]; then | |
| echo "β Error: Could not detect a valid internal IP address." | |
| return 1 | |
| fi | |
| local SUBNET=$(echo "$PRIMARY_IP" | cut -d. -f1-3) | |
| echo "π Jumping to ${SUBNET}.$1 as $CURRENT_USER..." | |
| # 3. Execute SSH | |
| ssh "${CURRENT_USER}@${SUBNET}.$1" | |
| } | |
| # <<< Custom Config END <<< | |
| BASHRC_BLOCK | |
| fi | |
| EOF | |
| echo "--- 5. Cleanup ---" | |
| apt autoremove -y && apt clean | |
| history -c | |
| echo "" | |
| echo "--- [ β $NEW_HOSTNAME Setup Complete ] ---" | |
| echo "π‘ User: $USER_NAME" | |
| echo "π‘ SSH Auth: Key-based only" | |
| exec su - "$USER_NAME" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment