04 Joining Servers
This chapter explains how to register Linux, macOS, and Windows servers with SSH Teams using the built-in join scripts.
4.1 Prerequisites
| Requirement | Lab | Staging | Production |
|---|---|---|---|
| Server can reach SSH Teams over HTTPS | Required | Required | Required |
| Operator can approve device flow in browser | Required | Required | Required |
| Elevated privileges for sshd config update | Recommended | Required | Required |
Stable tagging plan (prod, db, bastion) |
Optional | Recommended | Required |
| Trusted TLS certificate on SSH Teams host | Optional | Recommended | Required |
Important gotcha:
- If your SSH Teams endpoint uses a self-signed certificate, use the dashboard-provided join command variant with TLS bypass flags for bootstrap only, then move to trusted certificates.
4.2 Join Script Endpoints and When to Use Them
SSH Teams exposes two script endpoints:
graph TD
A[Run join script on target host] --> B[Request device code scope=join]
B --> C[Operator approves in browser]
C --> D[Script exchanges code for access token]
D --> E[Script calls /api/v1/ssh/join]
E --> F[Write TrustedUserCAKeys and server-id]
F --> G[Update sshd config and restart service]
https://<hostname>/join.shfor Linux and macOS hosts.https://<hostname>/join.ps1for Windows hosts.
Both scripts execute an OAuth device authorization flow with scope=join, then call:
POST /oauth2/device_authorizationPOST /oauth2/tokenPOST /api/v1/ssh/join
Use the join commands shown in the dashboard so the right TLS flags are included for your certificate type.
From the dashboard:
- Open
https://<hostname>/dashboard. - Locate the Join Script panel.
- Click the copy icon next to Linux/macOS or Windows command.
The copied command is generated by the server and includes the correct SSH Teams hostname (and HTTPS port when non-default), so operators do not need to hand-edit the base URL. On Windows, you still need to supply the required target host argument when executing the script.
Dashboard join command panel:

Typical command patterns:
curl -fsSL https://<hostname>/join.sh | bash -s -- <host>:22 [tags]
irm https://<hostname>/join.ps1 -OutFile .\join.ps1
.\join.ps1 "<host>:22" "prod,windows"
4.3 Linux and macOS Join Flow
Run the join script on the target server:
curl -fsSL https://<hostname>/join.sh | bash -s -- server-a.example.internal:22 prod,db --name "server-a"
Parameters:
- Positional
target:<hostname[:port]>. - Optional
tags: comma-separated tags such asprod,db. - Optional
--name <name>: friendly display name in SSH Teams. - Optional
--config <path>: sshd config path (default/etc/ssh/sshd_config). - Optional
--bypass-install: complete join and CA key retrieval without modifying local sshd config.
What the script does:
- Collects SSH host-key fingerprints from the target host.
- Prints a device verification URL with
user_code. - Waits for operator approval in browser.
- Registers or updates server via
/api/v1/ssh/join. - Writes trusted user CA keys to
~/.ssh/sshteam_trusted_user_ca_keys. - Adds or updates
TrustedUserCAKeysin sshd config unless--bypass-installis used. - Restarts sshd (
launchctlon macOS,systemctlon Linux when available).
Re-join behavior:
- If an existing server ID is found (
/etc/sshteam/server-idor~/.ssh/sshteam_server_id), the script updates that server registration instead of creating a duplicate.
4.4 Windows Join Flow
Run from an elevated PowerShell session on the target host with explicit arguments:
irm https://<hostname>/join.ps1 -OutFile .\join.ps1
.\join.ps1 "server-win.example.internal:22" "prod,windows" -Name "server-win"
Windows script behavior mirrors Linux/macOS flow:
- Performs OAuth device authorization (
scope=join). - Submits join payload to
/api/v1/ssh/join. - Saves trusted user CA keys at
~/.ssh/sshteam_trusted_user_ca_keys. - Updates sshd config (default
C:\ProgramData\ssh\sshd_config) unless-BypassInstallis set. - Restarts
sshdservice when present.
Note:
- Current Windows script uses a placeholder fingerprint value. Validate resulting registration details and policy matching after join.
4.5 Self-Signed Certificate Handling
If SSH Teams uses a self-signed certificate, use the dashboard-provided command variants:
- Linux/macOS:
curl -kfsSL .../join.sh | bash ... - Windows:
irm -SkipCertificateCheck https://<hostname>:9443/join.ps1 -OutFile .\join.ps1
.\join.ps1 "<host>:22" "prod,windows"
Guidance:
- Use bypass flags only for initial bootstrap when needed.
- Move to a trusted certificate as soon as practical.
- Re-run join commands after certificate changes if operators are still using stale command snippets.
4.6 Verifying Joined Server Inventory
After join completes:
- Open
https://<hostname>/dashboard. - Confirm server count increased.
- Open server inventory page and validate host fields:
- hostname
- port
- OS family/version
- tags
- Confirm expected policy targeting (server IDs or tags) can match this server.
CLI-level verification on the target host:
- Confirm CA keys file exists and is populated:
~/.ssh/sshteam_trusted_user_ca_keys. - Confirm sshd config contains
TrustedUserCAKeysdirective. - Confirm sshd restarted cleanly.
4.7 Join Troubleshooting
Common issues and fixes:
-
Script download fails:
- Verify HTTPS reachability to
https://<hostname>/join.shor/join.ps1. - Check reverse proxy/TLS configuration.
- Verify HTTPS reachability to
-
Device authorization does not complete:
- Ensure browser verification URL is reachable and approved by an authenticated user in the same team.
- Retry if token polling returned
authorization_pendingfor too long.
-
Join API returns non-2xx:
- Capture response body from script output.
- Validate join payload inputs (hostname, port, tags, server ID reuse).
-
Permission errors updating sshd config:
- Use elevated privileges (
sudo -von Linux/macOS, admin PowerShell on Windows). - Use
--bypass-install/-BypassInstallto complete registration first, then update sshd config manually.
- Use elevated privileges (
-
Duplicate server records:
- Preserve
/etc/sshteam/server-id(or user-profile fallback file) during rebuilds so re-joins update existing registration.
- Preserve
-
Certificates still denied after successful join:
- Check policy matching (tags/server selectors, user constraints, time windows).
- Validate server appears in the correct team inventory.