Skip to content

Instantly share code, notes, and snippets.

@mastervash
Created May 3, 2026 16:04
Show Gist options
  • Select an option

  • Save mastervash/f61a968e6bee1866d928c2f31eb068f0 to your computer and use it in GitHub Desktop.

Select an option

Save mastervash/f61a968e6bee1866d928c2f31eb068f0 to your computer and use it in GitHub Desktop.
debian-desktop-tailscale
#!/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