Merge pull request #188 from naturallaw777/copilot/cleanup-remove-reverse-ssh-tunnel-code
Remove reverse SSH tunnel, fix enrollToken footgun, fix RDP/deploy-key docs
This commit is contained in:
@@ -44,7 +44,7 @@ This guide covers the Sovran Systems remote deployment system built on [Headscal
|
||||
**Components:**
|
||||
- **`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.
|
||||
- **`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";
|
||||
headscaleDomain = "hs.yourdomain.com";
|
||||
|
||||
# Optional: set a static token instead of auto-generating one
|
||||
# enrollToken = "your-secret-token-here";
|
||||
|
||||
# Optional: customise defaults
|
||||
headscaleUser = "sovran-deploy"; # namespace for deploy machines
|
||||
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
|
||||
```
|
||||
|
||||
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**:
|
||||
```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 \
|
||||
--disk /dev/sda \
|
||||
--role server \
|
||||
--deploy-key "ssh-ed25519 AAAA..." \
|
||||
--deploy-key "$(cat ~/.ssh/sovran-deploy.pub)" \
|
||||
--headscale-server "https://hs.yourdomain.com" \
|
||||
--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:
|
||||
- Reads `/var/lib/secrets/headscale-authkey`
|
||||
- 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**:
|
||||
```bash
|
||||
# SSH over Tailnet
|
||||
ssh root@<tailscale-ip>
|
||||
|
||||
# RDP over Tailnet (if desktop role)
|
||||
xfreerdp /v:<tailscale-ip> /u:free /p:free
|
||||
# RDP over Tailnet (desktop role) — Sovran_SystemsOS uses GNOME Remote Desktop (native Wayland RDP)
|
||||
# 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:
|
||||
@@ -254,18 +252,20 @@ The resulting ISO is in `./result/iso/`.
|
||||
```bash
|
||||
# Over Tailnet
|
||||
ssh root@100.64.x.x
|
||||
|
||||
# Over reverse tunnel (if configured)
|
||||
ssh -p 2222 root@relay.yourdomain.com
|
||||
```
|
||||
|
||||
### 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
|
||||
# Over Tailnet
|
||||
xfreerdp /v:100.64.x.x /u:free /p:free /dynamic-resolution
|
||||
ssh root@<tailscale-ip> cat /var/lib/gnome-remote-desktop/rdp-password
|
||||
```
|
||||
|
||||
Connect using any RDP client (Remmina, GNOME Connections, Microsoft Remote Desktop) to `<tailscale-ip>:3389`.
|
||||
|
||||
---
|
||||
|
||||
## Security Model
|
||||
@@ -355,7 +355,7 @@ Then rebuild:
|
||||
nixos-rebuild switch
|
||||
```
|
||||
|
||||
This stops the reverse tunnel and Tailscale connect services.
|
||||
This stops the Tailscale connect service.
|
||||
|
||||
### Revoke All Active Pre-Auth Keys
|
||||
|
||||
|
||||
@@ -10,10 +10,6 @@ Options:
|
||||
--data-disk /dev/sdb Data disk for Bitcoin (optional)
|
||||
--role server|desktop|node Installation role (default: server)
|
||||
--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-key KEY Headscale pre-auth key for the installed OS
|
||||
USAGE
|
||||
@@ -26,10 +22,6 @@ DISK=""
|
||||
DATA_DISK=""
|
||||
ROLE="server"
|
||||
DEPLOY_KEY=""
|
||||
RELAY_HOST=""
|
||||
RELAY_USER="deploy"
|
||||
RELAY_PORT="22"
|
||||
TUNNEL_PORT="2222"
|
||||
HEADSCALE_SERVER=""
|
||||
HEADSCALE_KEY=""
|
||||
|
||||
@@ -58,10 +50,6 @@ while [[ $# -gt 0 ]]; do
|
||||
--data-disk) DATA_DISK="$2"; shift 2 ;;
|
||||
--role) ROLE="$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-key) HEADSCALE_KEY="$2"; shift 2 ;;
|
||||
-h|--help)
|
||||
@@ -220,21 +208,17 @@ EOF
|
||||
# ── Step 10: Write custom.nix with deploy config ──────────────────────────────
|
||||
log "=== Writing custom.nix ==="
|
||||
|
||||
if [[ -n "$DEPLOY_KEY" ]]; then
|
||||
cat > /mnt/etc/nixos/custom.nix <<EOF
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
sovran_systemsOS.deploy = {
|
||||
enable = true;
|
||||
authorizedKey = "${DEPLOY_KEY}";
|
||||
relayHost = "${RELAY_HOST}";
|
||||
relayUser = "${RELAY_USER}";
|
||||
relayPort = ${RELAY_PORT};
|
||||
reverseTunnelPort = ${TUNNEL_PORT};
|
||||
$([ -n "${HEADSCALE_SERVER}" ] && echo " headscaleServer = \"${HEADSCALE_SERVER}\";")
|
||||
};
|
||||
}
|
||||
EOF
|
||||
if [[ -n "$DEPLOY_KEY" || -n "$HEADSCALE_SERVER" ]]; then
|
||||
{
|
||||
echo '{ config, lib, ... }:'
|
||||
echo '{'
|
||||
echo ' sovran_systemsOS.deploy = {'
|
||||
echo ' enable = true;'
|
||||
[[ -n "$DEPLOY_KEY" ]] && echo " authorizedKey = \"${DEPLOY_KEY}\";"
|
||||
[[ -n "$HEADSCALE_SERVER" ]] && echo " headscaleServer = \"${HEADSCALE_SERVER}\";"
|
||||
echo ' };'
|
||||
echo '}'
|
||||
} > /mnt/etc/nixos/custom.nix
|
||||
else
|
||||
cp /mnt/etc/nixos/custom.template.nix /mnt/etc/nixos/custom.nix
|
||||
fi
|
||||
@@ -265,7 +249,5 @@ nixos-install \
|
||||
log "=== Installation complete! ==="
|
||||
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)."
|
||||
[[ -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" ]] && \
|
||||
log "Tailscale will connect to Headscale at ${HEADSCALE_SERVER} on first boot."
|
||||
|
||||
@@ -8,30 +8,6 @@ in
|
||||
options.sovran_systemsOS.deploy = {
|
||||
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 {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
@@ -120,29 +96,6 @@ in
|
||||
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 ────────────────────────────────────────────
|
||||
systemd.services.deploy-auto-expire = {
|
||||
description = "Auto-expire remote deploy mode after 48 hours";
|
||||
@@ -155,7 +108,7 @@ in
|
||||
script = ''
|
||||
# 48 hours = 172800 seconds
|
||||
sleep $((48 * 60 * 60))
|
||||
systemctl stop deploy-reverse-tunnel || true
|
||||
systemctl stop deploy-tailscale-connect || true
|
||||
mkdir -p /etc/sovran
|
||||
echo "expired" > /etc/sovran/deploy-mode
|
||||
'';
|
||||
|
||||
@@ -151,12 +151,6 @@ in
|
||||
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 {
|
||||
type = lib.types.str;
|
||||
default = "sovran-deploy";
|
||||
@@ -251,17 +245,13 @@ in
|
||||
script = ''
|
||||
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"
|
||||
${if cfg.enrollToken != "" then ''
|
||||
echo "${cfg.enrollToken}" > "$TOKEN_FILE"
|
||||
'' else ''
|
||||
if [ ! -f "$TOKEN_FILE" ]; then
|
||||
${pkgs.openssl}/bin/openssl rand -hex 32 > "$TOKEN_FILE"
|
||||
chmod 600 "$TOKEN_FILE"
|
||||
echo "Generated new enrollment token: $(cat $TOKEN_FILE)"
|
||||
fi
|
||||
''}
|
||||
if [ ! -f "$TOKEN_FILE" ]; then
|
||||
${pkgs.openssl}/bin/openssl rand -hex 32 > "$TOKEN_FILE"
|
||||
chmod 600 "$TOKEN_FILE"
|
||||
echo "Generated new enrollment token: $(cat $TOKEN_FILE)"
|
||||
fi
|
||||
|
||||
# Ensure headscale users exist
|
||||
${pkgs.headscale}/bin/headscale users create ${cfg.headscaleUser} 2>/dev/null || true
|
||||
|
||||
Reference in New Issue
Block a user