Cleanup: Remove reverse SSH tunnel code, fix documentation accuracy

Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/3941ead1-cb20-4686-92bb-46e447791ae3

Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-04-12 00:19:25 +00:00
committed by GitHub
parent af14622e45
commit 3ca15d0da4
4 changed files with 41 additions and 116 deletions

View File

@@ -44,7 +44,7 @@ This guide covers the Sovran Systems remote deployment system built on [Headscal
**Components:** **Components:**
- **`sovran-provisioner.nix`** — NixOS module deployed on a separate VPS; runs Headscale + provisioning API + Caddy. - **`sovran-provisioner.nix`** — NixOS module deployed on a separate VPS; runs Headscale + provisioning API + Caddy.
- **Live ISO** (`iso/common.nix`) — Auto-registers with the provisioning server and joins the Tailnet on boot. - **Live ISO** (`iso/common.nix`) — Auto-registers with the provisioning server and joins the Tailnet on boot.
- **`remote-deploy.nix`** — Post-install NixOS module that uses Tailscale/Headscale for ongoing access (plus the existing reverse SSH tunnel as a fallback). - **`remote-deploy.nix`** — Post-install NixOS module that uses Tailscale/Headscale for ongoing access.
--- ---
@@ -83,9 +83,6 @@ Add the following to your VPS's `/etc/nixos/configuration.nix`:
domain = "prov.yourdomain.com"; domain = "prov.yourdomain.com";
headscaleDomain = "hs.yourdomain.com"; headscaleDomain = "hs.yourdomain.com";
# Optional: set a static token instead of auto-generating one
# enrollToken = "your-secret-token-here";
# Optional: customise defaults # Optional: customise defaults
headscaleUser = "sovran-deploy"; # namespace for deploy machines headscaleUser = "sovran-deploy"; # namespace for deploy machines
adminUser = "admin"; # namespace for your workstation adminUser = "admin"; # namespace for your workstation
@@ -114,7 +111,7 @@ Caddy will automatically obtain TLS certificates via Let's Encrypt.
cat /var/lib/sovran-provisioner/enroll-token cat /var/lib/sovran-provisioner/enroll-token
``` ```
Keep this token secret — it is used to authenticate ISO registrations. If you set `enrollToken` statically in `configuration.nix`, that value is used directly (but avoid committing secrets to version control). Keep this token secret — it is used to authenticate ISO registrations. The token is auto-generated on first boot and stored at this path. You never need to set it manually. Just `cat` it from the VPS and copy it to `iso/secrets/enroll-token` before building the ISO.
--- ---
@@ -209,19 +206,18 @@ The resulting ISO is in `./result/iso/`.
``` ```
5. **Run the headless installer**: 5. **Run the headless installer**:
```bash
# Basic install (relay tunnel)
sudo sovran-install-headless.sh \
--disk /dev/sda \
--role server \
--deploy-key "ssh-ed25519 AAAA..." \
--relay-host relay.yourdomain.com
# With Tailscale for post-install access The `--deploy-key` is your SSH public key that gets injected into `root`'s `authorized_keys` on the deployed machine. This grants full root access for initial setup. Generate it once on your workstation if you haven't already:
```bash
ssh-keygen -t ed25519 -f ~/.ssh/sovran-deploy -C "sovran-deploy"
```
After deployment is complete and you disable deploy mode, this key is removed.
```bash
sudo sovran-install-headless.sh \ sudo sovran-install-headless.sh \
--disk /dev/sda \ --disk /dev/sda \
--role server \ --role server \
--deploy-key "ssh-ed25519 AAAA..." \ --deploy-key "$(cat ~/.ssh/sovran-deploy.pub)" \
--headscale-server "https://hs.yourdomain.com" \ --headscale-server "https://hs.yourdomain.com" \
--headscale-key "$(headscale preauthkeys create --user sovran-deploy --expiration 2h --output json | jq -r '.key')" --headscale-key "$(headscale preauthkeys create --user sovran-deploy --expiration 2h --output json | jq -r '.key')"
``` ```
@@ -229,15 +225,17 @@ The resulting ISO is in `./result/iso/`.
6. **Machine reboots into Sovran_SystemsOS** — `deploy-tailscale-connect.service` runs: 6. **Machine reboots into Sovran_SystemsOS** — `deploy-tailscale-connect.service` runs:
- Reads `/var/lib/secrets/headscale-authkey` - Reads `/var/lib/secrets/headscale-authkey`
- Joins the Tailnet with a deterministic hostname (`sovran-<hostname>`) - Joins the Tailnet with a deterministic hostname (`sovran-<hostname>`)
- The reverse SSH tunnel also activates if `relayHost` was set
7. **Post-install SSH and RDP**: 7. **Post-install SSH and RDP**:
```bash ```bash
# SSH over Tailnet # SSH over Tailnet
ssh root@<tailscale-ip> ssh root@<tailscale-ip>
# RDP over Tailnet (if desktop role) # RDP over Tailnet (desktop role) — Sovran_SystemsOS uses GNOME Remote Desktop (native Wayland RDP)
xfreerdp /v:<tailscale-ip> /u:free /p:free # Retrieve the auto-generated RDP password:
ssh root@<tailscale-ip> cat /var/lib/gnome-remote-desktop/rdp-password
# Then connect with any RDP client (Remmina, GNOME Connections, Microsoft Remote Desktop):
# Host: <tailscale-ip>:3389 User: sovran Password: <from above>
``` ```
8. **Disable deploy mode** — edit `/etc/nixos/custom.nix` on the target, set `enable = false`, then: 8. **Disable deploy mode** — edit `/etc/nixos/custom.nix` on the target, set `enable = false`, then:
@@ -254,18 +252,20 @@ The resulting ISO is in `./result/iso/`.
```bash ```bash
# Over Tailnet # Over Tailnet
ssh root@100.64.x.x ssh root@100.64.x.x
# Over reverse tunnel (if configured)
ssh -p 2222 root@relay.yourdomain.com
``` ```
### RDP (desktop/server roles) ### RDP (desktop/server roles)
Sovran_SystemsOS uses **GNOME Remote Desktop** (native Wayland RDP — not xfreerdp). The RDP service auto-generates credentials on first boot.
**Username:** `sovran`
**Password:** auto-generated — retrieve it via SSH:
```bash ```bash
# Over Tailnet ssh root@<tailscale-ip> cat /var/lib/gnome-remote-desktop/rdp-password
xfreerdp /v:100.64.x.x /u:free /p:free /dynamic-resolution
``` ```
Connect using any RDP client (Remmina, GNOME Connections, Microsoft Remote Desktop) to `<tailscale-ip>:3389`.
--- ---
## Security Model ## Security Model
@@ -355,7 +355,7 @@ Then rebuild:
nixos-rebuild switch nixos-rebuild switch
``` ```
This stops the reverse tunnel and Tailscale connect services. This stops the Tailscale connect service.
### Revoke All Active Pre-Auth Keys ### Revoke All Active Pre-Auth Keys

View File

@@ -10,10 +10,6 @@ Options:
--data-disk /dev/sdb Data disk for Bitcoin (optional) --data-disk /dev/sdb Data disk for Bitcoin (optional)
--role server|desktop|node Installation role (default: server) --role server|desktop|node Installation role (default: server)
--deploy-key "ssh-ed25519 AAAA..." SSH pubkey for remote access after install --deploy-key "ssh-ed25519 AAAA..." SSH pubkey for remote access after install
--relay-host HOST Reverse tunnel relay hostname
--relay-user USER Relay username (default: deploy)
--relay-port PORT Relay SSH port (default: 22)
--tunnel-port PORT Reverse tunnel port on relay (default: 2222)
--headscale-server URL Headscale login server for post-install Tailnet --headscale-server URL Headscale login server for post-install Tailnet
--headscale-key KEY Headscale pre-auth key for the installed OS --headscale-key KEY Headscale pre-auth key for the installed OS
USAGE USAGE
@@ -26,10 +22,6 @@ DISK=""
DATA_DISK="" DATA_DISK=""
ROLE="server" ROLE="server"
DEPLOY_KEY="" DEPLOY_KEY=""
RELAY_HOST=""
RELAY_USER="deploy"
RELAY_PORT="22"
TUNNEL_PORT="2222"
HEADSCALE_SERVER="" HEADSCALE_SERVER=""
HEADSCALE_KEY="" HEADSCALE_KEY=""
@@ -58,10 +50,6 @@ while [[ $# -gt 0 ]]; do
--data-disk) DATA_DISK="$2"; shift 2 ;; --data-disk) DATA_DISK="$2"; shift 2 ;;
--role) ROLE="$2"; shift 2 ;; --role) ROLE="$2"; shift 2 ;;
--deploy-key) DEPLOY_KEY="$2"; shift 2 ;; --deploy-key) DEPLOY_KEY="$2"; shift 2 ;;
--relay-host) RELAY_HOST="$2"; shift 2 ;;
--relay-user) RELAY_USER="$2"; shift 2 ;;
--relay-port) RELAY_PORT="$2"; shift 2 ;;
--tunnel-port) TUNNEL_PORT="$2"; shift 2 ;;
--headscale-server) HEADSCALE_SERVER="$2"; shift 2 ;; --headscale-server) HEADSCALE_SERVER="$2"; shift 2 ;;
--headscale-key) HEADSCALE_KEY="$2"; shift 2 ;; --headscale-key) HEADSCALE_KEY="$2"; shift 2 ;;
-h|--help) -h|--help)
@@ -220,21 +208,17 @@ EOF
# ── Step 10: Write custom.nix with deploy config ────────────────────────────── # ── Step 10: Write custom.nix with deploy config ──────────────────────────────
log "=== Writing custom.nix ===" log "=== Writing custom.nix ==="
if [[ -n "$DEPLOY_KEY" ]]; then if [[ -n "$DEPLOY_KEY" || -n "$HEADSCALE_SERVER" ]]; then
cat > /mnt/etc/nixos/custom.nix <<EOF {
{ config, lib, ... }: echo '{ config, lib, ... }:'
{ echo '{'
sovran_systemsOS.deploy = { echo ' sovran_systemsOS.deploy = {'
enable = true; echo ' enable = true;'
authorizedKey = "${DEPLOY_KEY}"; [[ -n "$DEPLOY_KEY" ]] && echo " authorizedKey = \"${DEPLOY_KEY}\";"
relayHost = "${RELAY_HOST}"; [[ -n "$HEADSCALE_SERVER" ]] && echo " headscaleServer = \"${HEADSCALE_SERVER}\";"
relayUser = "${RELAY_USER}"; echo ' };'
relayPort = ${RELAY_PORT}; echo '}'
reverseTunnelPort = ${TUNNEL_PORT}; } > /mnt/etc/nixos/custom.nix
$([ -n "${HEADSCALE_SERVER}" ] && echo " headscaleServer = \"${HEADSCALE_SERVER}\";")
};
}
EOF
else else
cp /mnt/etc/nixos/custom.template.nix /mnt/etc/nixos/custom.nix cp /mnt/etc/nixos/custom.template.nix /mnt/etc/nixos/custom.nix
fi fi
@@ -265,7 +249,5 @@ nixos-install \
log "=== Installation complete! ===" log "=== Installation complete! ==="
log "You can now reboot into Sovran_SystemsOS." log "You can now reboot into Sovran_SystemsOS."
log "After reboot, the machine will be accessible via SSH on port 22 (if --deploy-key was provided)." log "After reboot, the machine will be accessible via SSH on port 22 (if --deploy-key was provided)."
[[ -n "$RELAY_HOST" ]] && \
log "Reverse tunnel will connect to ${RELAY_USER}@${RELAY_HOST}:${RELAY_PORT} — forward port ${TUNNEL_PORT} maps to the machine's SSH."
[[ -n "$HEADSCALE_SERVER" ]] && \ [[ -n "$HEADSCALE_SERVER" ]] && \
log "Tailscale will connect to Headscale at ${HEADSCALE_SERVER} on first boot." log "Tailscale will connect to Headscale at ${HEADSCALE_SERVER} on first boot."

View File

@@ -8,30 +8,6 @@ in
options.sovran_systemsOS.deploy = { options.sovran_systemsOS.deploy = {
enable = lib.mkEnableOption "Remote deploy mode"; enable = lib.mkEnableOption "Remote deploy mode";
relayHost = lib.mkOption {
type = lib.types.str;
default = "";
description = "SSH relay server hostname or IP for the reverse tunnel";
};
relayPort = lib.mkOption {
type = lib.types.port;
default = 22;
description = "SSH port on the relay server";
};
relayUser = lib.mkOption {
type = lib.types.str;
default = "deploy";
description = "Username on the relay server";
};
reverseTunnelPort = lib.mkOption {
type = lib.types.port;
default = 2222;
description = "Port on the relay that maps back to this machine's SSH (port 22)";
};
authorizedKey = lib.mkOption { authorizedKey = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = ""; default = "";
@@ -120,29 +96,6 @@ in
path = [ pkgs.tailscale pkgs.coreutils ]; path = [ pkgs.tailscale pkgs.coreutils ];
}; };
# ── Reverse tunnel service (only when relayHost is configured) ───────────
systemd.services.deploy-reverse-tunnel = lib.mkIf (cfg.relayHost != "") {
description = "Deploy reverse SSH tunnel to ${cfg.relayHost}";
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" "sshd.service" ];
wants = [ "network-online.target" ];
serviceConfig = {
Restart = "always";
RestartSec = "10s";
ExecStart = "${pkgs.openssh}/bin/ssh"
+ " -o StrictHostKeyChecking=accept-new"
+ " -o ServerAliveInterval=30"
+ " -o ServerAliveCountMax=3"
+ " -o ExitOnForwardFailure=yes"
+ " -i /var/lib/secrets/deploy-relay-key"
+ " -N"
+ " -R ${toString cfg.reverseTunnelPort}:localhost:22"
+ " -p ${toString cfg.relayPort}"
+ " ${cfg.relayUser}@${cfg.relayHost}";
};
path = [ pkgs.openssh ];
};
# ── Safety auto-expiry service ──────────────────────────────────────────── # ── Safety auto-expiry service ────────────────────────────────────────────
systemd.services.deploy-auto-expire = { systemd.services.deploy-auto-expire = {
description = "Auto-expire remote deploy mode after 48 hours"; description = "Auto-expire remote deploy mode after 48 hours";
@@ -155,7 +108,7 @@ in
script = '' script = ''
# 48 hours = 172800 seconds # 48 hours = 172800 seconds
sleep $((48 * 60 * 60)) sleep $((48 * 60 * 60))
systemctl stop deploy-reverse-tunnel || true systemctl stop deploy-tailscale-connect || true
mkdir -p /etc/sovran mkdir -p /etc/sovran
echo "expired" > /etc/sovran/deploy-mode echo "expired" > /etc/sovran/deploy-mode
''; '';

View File

@@ -151,12 +151,6 @@ in
description = "Domain for the Headscale coordination server (e.g. hs.sovransystems.com)"; description = "Domain for the Headscale coordination server (e.g. hs.sovransystems.com)";
}; };
enrollToken = lib.mkOption {
type = lib.types.str;
default = "";
description = "Static enrollment token. If empty, one is auto-generated on first boot.";
};
headscaleUser = lib.mkOption { headscaleUser = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = "sovran-deploy"; default = "sovran-deploy";
@@ -251,17 +245,13 @@ in
script = '' script = ''
mkdir -p ${cfg.stateDir} mkdir -p ${cfg.stateDir}
# Generate enrollment token if not exists and not set statically # Auto-generate enrollment token on first boot if not already present
TOKEN_FILE="${cfg.stateDir}/enroll-token" TOKEN_FILE="${cfg.stateDir}/enroll-token"
${if cfg.enrollToken != "" then '' if [ ! -f "$TOKEN_FILE" ]; then
echo "${cfg.enrollToken}" > "$TOKEN_FILE" ${pkgs.openssl}/bin/openssl rand -hex 32 > "$TOKEN_FILE"
'' else '' chmod 600 "$TOKEN_FILE"
if [ ! -f "$TOKEN_FILE" ]; then echo "Generated new enrollment token: $(cat $TOKEN_FILE)"
${pkgs.openssl}/bin/openssl rand -hex 32 > "$TOKEN_FILE" fi
chmod 600 "$TOKEN_FILE"
echo "Generated new enrollment token: $(cat $TOKEN_FILE)"
fi
''}
# Ensure headscale users exist # Ensure headscale users exist
${pkgs.headscale}/bin/headscale users create ${cfg.headscaleUser} 2>/dev/null || true ${pkgs.headscale}/bin/headscale users create ${cfg.headscaleUser} 2>/dev/null || true