Created
May 3, 2026 16:04
-
-
Save mastervash/f61a968e6bee1866d928c2f31eb068f0 to your computer and use it in GitHub Desktop.
debian-desktop-tailscale
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
| #!/usr/bin/env bash | |
| # ============================================================================= | |
| # Debian 12 VPS Desktop Setup | |
| # Stack: XFCE4 + xrdp + Tailscale + Brave Browser | |
| # Access: RDP over Tailscale only (port 3389 not exposed publicly) | |
| # Run as: root on a fresh Debian 12 instance | |
| # ============================================================================= | |
| set -euo pipefail | |
| # --- Colors --- | |
| RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m' | |
| info() { echo -e "${GREEN}[INFO]${NC} $*"; } | |
| warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } | |
| die() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; } | |
| [[ $EUID -ne 0 ]] && die "Run this script as root." | |
| # ============================================================================= | |
| # 0. CONFIGURATION — Interactive prompts | |
| # ============================================================================= | |
| echo "" | |
| echo -e "${GREEN}============================================================${NC}" | |
| echo -e "${GREEN} Debian 12 VPS Desktop Setup — Interactive Configuration${NC}" | |
| echo -e "${GREEN}============================================================${NC}" | |
| echo "" | |
| read -rp "Desktop username: " DESKTOP_USER | |
| [[ -z "$DESKTOP_USER" ]] && die "Username cannot be empty." | |
| while true; do | |
| read -rsp "Desktop password: " DESKTOP_PASS; echo "" | |
| read -rsp "Confirm password: " DESKTOP_PASS2; echo "" | |
| [[ "$DESKTOP_PASS" == "$DESKTOP_PASS2" ]] && break | |
| warn "Passwords do not match, try again." | |
| done | |
| [[ -z "$DESKTOP_PASS" ]] && die "Password cannot be empty." | |
| read -rp "Hostname [vps-desktop]: " HOSTNAME_NEW | |
| HOSTNAME_NEW="${HOSTNAME_NEW:-vps-desktop}" | |
| echo "" | |
| echo -e "${YELLOW}Tailscale auth key (leave blank to authenticate manually after setup)${NC}" | |
| echo -e "${YELLOW}Generate one at: https://login.tailscale.com/admin/settings/keys${NC}" | |
| read -rsp "Tailscale auth key: " TS_AUTHKEY; echo "" | |
| echo "" | |
| echo -e "${GREEN}--- Configuration Summary ---${NC}" | |
| echo " Hostname: $HOSTNAME_NEW" | |
| echo " Desktop user: $DESKTOP_USER" | |
| echo " Tailscale auth: $([ -n "$TS_AUTHKEY" ] && echo "provided" || echo "manual (post-setup)")" | |
| echo "" | |
| read -rp "Proceed with installation? [y/N]: " CONFIRM | |
| [[ "${CONFIRM,,}" != "y" ]] && die "Aborted by user." | |
| echo "" | |
| # ============================================================================= | |
| # 1. SYSTEM BASE | |
| # ============================================================================= | |
| info "Setting hostname..." | |
| hostnamectl set-hostname "$HOSTNAME_NEW" | |
| info "Updating system packages..." | |
| apt-get update -qq | |
| apt-get upgrade -y -qq | |
| info "Installing base utilities..." | |
| apt-get install -y -qq \ | |
| curl wget gnupg apt-transport-https ca-certificates \ | |
| software-properties-common lsb-release \ | |
| ufw sudo nano htop net-tools dbus-x11 | |
| # ============================================================================= | |
| # 2. DESKTOP USER | |
| # ============================================================================= | |
| if ! id "$DESKTOP_USER" &>/dev/null; then | |
| info "Creating desktop user: $DESKTOP_USER..." | |
| useradd -m -s /bin/bash -G sudo "$DESKTOP_USER" | |
| echo "$DESKTOP_USER:$DESKTOP_PASS" | chpasswd | |
| else | |
| warn "User $DESKTOP_USER already exists, skipping creation." | |
| fi | |
| # ============================================================================= | |
| # 3. XFCE4 DESKTOP (minimal, no recommended bloat) | |
| # ============================================================================= | |
| info "Installing XFCE4 (minimal)..." | |
| apt-get install -y -qq --no-install-recommends \ | |
| xfce4 \ | |
| xfce4-terminal \ | |
| xfce4-taskmanager \ | |
| xfce4-screenshooter \ | |
| thunar \ | |
| mousepad \ | |
| xorg \ | |
| xorgxrdp \ | |
| lightdm | |
| # Disable LightDM from starting on boot — xrdp manages sessions directly | |
| systemctl disable lightdm 2>/dev/null || true | |
| # Disable XFCE compositor (software rendering, no GPU acceleration) | |
| info "Disabling XFCE compositor for software-rendered GPU..." | |
| XFCE_CFG_DIR="/home/$DESKTOP_USER/.config/xfce4/xfconf/xfce-perchannel-xml" | |
| mkdir -p "$XFCE_CFG_DIR" | |
| cat > "$XFCE_CFG_DIR/xfwm4.xml" <<'EOF' | |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <channel name="xfwm4" version="1.0"> | |
| <property name="general" type="empty"> | |
| <property name="use_compositing" type="bool" value="false"/> | |
| <property name="vblank_mode" type="string" value="off"/> | |
| </property> | |
| </channel> | |
| EOF | |
| chown -R "$DESKTOP_USER:$DESKTOP_USER" "/home/$DESKTOP_USER/.config" | |
| # ============================================================================= | |
| # 4. XRDP | |
| # ============================================================================= | |
| info "Installing xrdp..." | |
| apt-get install -y -qq xrdp | |
| # Tell xrdp to launch an XFCE session | |
| cat > /etc/xrdp/startwm.sh <<'EOF' | |
| #!/bin/sh | |
| # xrdp session launcher — XFCE4 | |
| unset DBUS_SESSION_BUS_ADDRESS | |
| unset XDG_RUNTIME_DIR | |
| exec /usr/bin/startxfce4 | |
| EOF | |
| chmod +x /etc/xrdp/startwm.sh | |
| # Restrict xrdp to localhost + Tailscale only (Tailscale binds on 100.x.x.x) | |
| # We achieve this via UFW rules later; xrdp itself listens on 0.0.0.0 by default | |
| # but UFW will block all non-Tailscale access to 3389. | |
| # Add xrdp user to ssl-cert group (prevents certificate warnings) | |
| adduser xrdp ssl-cert 2>/dev/null || true | |
| systemctl enable xrdp | |
| systemctl restart xrdp | |
| # ============================================================================= | |
| # 5. TAILSCALE | |
| # ============================================================================= | |
| info "Installing Tailscale..." | |
| curl -fsSL https://pkgs.tailscale.com/stable/debian/bookworm.noarmor.gpg \ | |
| | tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null | |
| curl -fsSL https://pkgs.tailscale.com/stable/debian/bookworm.tailscale-keyring.list \ | |
| | tee /etc/apt/sources.list.d/tailscale.list | |
| apt-get update -qq | |
| apt-get install -y -qq tailscale | |
| systemctl enable --now tailscaled | |
| if [[ -n "$TS_AUTHKEY" ]]; then | |
| info "Authenticating Tailscale..." | |
| tailscale up --authkey="$TS_AUTHKEY" --accept-routes | |
| info "Tailscale connected. IP: $(tailscale ip -4 2>/dev/null || echo 'run: tailscale ip -4')" | |
| else | |
| warn "No auth key provided. Run 'tailscale up' manually after setup to authenticate." | |
| fi | |
| # ============================================================================= | |
| # 6. FIREWALL (UFW) | |
| # ============================================================================= | |
| info "Configuring UFW firewall..." | |
| ufw --force reset | |
| ufw default deny incoming | |
| ufw default allow outgoing | |
| # Allow SSH from anywhere (keep your management access!) | |
| ufw allow 22/tcp comment 'SSH' | |
| # Allow RDP only from the Tailscale network range (100.64.0.0/10) | |
| # This covers all Tailscale IPs and keeps 3389 off the public internet entirely | |
| ufw allow in on tailscale0 to any port 3389 proto tcp comment 'RDP via Tailscale' | |
| # Allow Tailscale UDP port for direct connections | |
| ufw allow 41641/udp comment 'Tailscale direct' | |
| ufw --force enable | |
| ufw status verbose | |
| info "Firewall configured. RDP (3389) is restricted to tailscale0 interface only." | |
| # ============================================================================= | |
| # 7. BRAVE BROWSER | |
| # ============================================================================= | |
| info "Installing Brave browser..." | |
| curl -fsSL https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg \ | |
| | tee /usr/share/keyrings/brave-browser-archive-keyring.gpg >/dev/null | |
| echo "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg arch=amd64] \ | |
| https://brave-browser-apt-release.s3.brave.com/ stable main" \ | |
| | tee /etc/apt/sources.list.d/brave-browser.list | |
| apt-get update -qq | |
| apt-get install -y -qq brave-browser | |
| # ============================================================================= | |
| # 8. XRDP SESSION POLISH | |
| # ============================================================================= | |
| info "Applying session environment fixes..." | |
| # Silence the common pam_env warning in xrdp sessions | |
| sed -i 's/^.*pam_env.*user_readenv=1.*$//' /etc/pam.d/xrdp-sesman 2>/dev/null || true | |
| # Ensure .profile and .bashrc exist for the desktop user | |
| touch "/home/$DESKTOP_USER/.profile" "/home/$DESKTOP_USER/.bashrc" | |
| chown "$DESKTOP_USER:$DESKTOP_USER" \ | |
| "/home/$DESKTOP_USER/.profile" \ | |
| "/home/$DESKTOP_USER/.bashrc" | |
| # Create a basic .xsessionrc so xrdp picks up the environment properly | |
| cat > "/home/$DESKTOP_USER/.xsessionrc" <<'EOF' | |
| export XDG_SESSION_TYPE=x11 | |
| export XDG_CURRENT_DESKTOP=XFCE | |
| export DBUS_SESSION_BUS_ADDRESS="" | |
| EOF | |
| chown "$DESKTOP_USER:$DESKTOP_USER" "/home/$DESKTOP_USER/.xsessionrc" | |
| # ============================================================================= | |
| # 9. DONE | |
| # ============================================================================= | |
| echo "" | |
| echo -e "${GREEN}============================================================${NC}" | |
| echo -e "${GREEN} Setup complete!${NC}" | |
| echo -e "${GREEN}============================================================${NC}" | |
| echo "" | |
| echo " Next steps:" | |
| echo " 1. Get your Tailscale IP: tailscale ip -4" | |
| echo " (or check https://login.tailscale.com/admin/machines)" | |
| echo " 2. If you skipped auth key: tailscale up" | |
| echo " 3. Connect via RDP client to: <tailscale-ip>:3389" | |
| echo " Username: $DESKTOP_USER" | |
| echo " Password: (what you set in DESKTOP_PASS)" | |
| echo "" | |
| echo " Recommended RDP client settings:" | |
| echo " Color depth: 16-bit (faster over Tailscale)" | |
| echo " Resolution: 1920x1080 or your monitor native" | |
| echo " Experience: LAN (you're sub-15ms, use it)" | |
| echo "" | |
| echo -e "${YELLOW} Don't forget to change DESKTOP_PASS before running!${NC}" | |
| echo "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment