SSH via Bridge
Overview
This pattern combines the remote SSH access approach with Britive Bridge: the broker provisions a one-time ed25519 key on the target host, but instead of returning that key to the user, it registers a proxied session with Bridge. All traffic flows through the Bridge proxy where it is audited and recorded.
One checkout gives the user two ways in:
- Their own SSH client, pointed at the Bridge’s native SSH listener
- The browser, with no client at all
Either way the target’s credential stays server-side. The user authenticates to Bridge with the Bridge Password on their Britive profile — never with the one-time key.
What you’ll accomplish:
- Provision a one-time SSH key on a remote Linux host at checkout
- Register a Bridge session that accepts the user’s Bridge credentials
- Remove the key, sudo grant, and session at checkin
This guide covers Bridge v2 only. v2 scripts call broker-bridge-api.sh and register the checkout with native_auth=bridge_credentials. The retired v1 scripts called bridge.sh and returned a tokenized browser URL. A v1 script on a v2 Bridge fails at the API call with nothing to indicate the version is the reason.
Before You Begin
- Bridge v2 is deployed and reachable by users
- The Bridge native SSH listener is enabled and reachable on
NATIVE_PORT(default2222) - A privileged provisioning account (default
britivebroker) on each target host, reachable with the broker’s SSH key (root or passwordless sudo) - Broker host has
ssh,ssh-keygen,base64, andjq— extend the Bridge image with a custom build if needed broker-bridge-api.shpresent at/opt/britive-broker/scripts/broker-bridge-api.sh- Each user has a Bridge Password set on their Britive profile (Manage Account → Bridge Attributes). Optionally a Bridge SSH Key
How It Works
checkout → derive username from BRITIVE_USER_EMAIL (local part, alphanumeric)
→ generate one-time ed25519 keypair tagged bridge:<TRX>
→ SSH as provisioning user: create user, install public key,
optional sudoers entry /etc/sudoers.d/bridge-<TRX>
→ broker-bridge-api.sh checkout-create
{ protocol: ssh, private_key, record_session: true,
native_auth: bridge_credentials, bridge_auth_password }
→ return { command, browser_session, bridge_username, ... }
checkin → broker-bridge-api.sh checkout-delete <TRX> ← session dies FIRST
→ SSH as provisioning user: remove key by bridge:<TRX> marker,
delete sudoers entry, optionally delete the accountThe one-time private key travels only from the broker to Bridge inside the checkout payload — the user never sees it. If Bridge registration fails, the checkout rolls the provisioned key back off the target.
The Identity Model
This is the part that most often trips people up.
The native SSH login is <email>%<target-host>, where <email> is the user’s Britive identity — the same value as the checkout owner. Bridge matches both the native SSH username (the part before %) and the browser SSO identity against the checkout’s username field, so they must be identical.
The profile’s Bridge Username field is not used for this matching. Setting it does not change the login name.
The username is passed with -l rather than as user@host because it contains both @ (the email) and % (the target separator), which would be ambiguous before a hostname.
Environment Variables
| Variable | Required | Default | Notes |
|---|---|---|---|
BRITIVE_USER_EMAIL | Yes | — | Injected — OS username derived from the local part |
TRX | Yes | — | Injected — transaction ID; tags the key and sudoers entry |
TARGET_HOST | Yes | — | SSH target host |
BRIDGE_URL | Yes | — | Bridge hostname. One NLB serves both browser and native sessions |
EXPIRATION | Yes | — | Checkout duration in seconds |
BRIDGE_AUTH_PASSWORD | Yes | — | Injected from the user’s profile. Bridge rejects a bridge_credentials checkout without it |
BRIDGE_AUTH_PUBKEY | No | — | User’s Bridge SSH public key. When set, added as user_public_key so they may use their own key as well as the password |
TARGET_PORT | No | 22 | SSH port on the target |
NATIVE_PORT | No | 2222 | Bridge native SSH listener port |
BRITIVE_SUDO | No | 0 | 1 grants passwordless sudo for the session |
PROVISION_USER | No | britivebroker | Privileged account used for provisioning |
PROVISION_HOST | No | TARGET_HOST | Separate provisioning host if needed |
PROVISION_PORT | No | TARGET_PORT | SSH port for provisioning |
PROVISION_KEY | No | /home/bridge/.ssh/id_ed25519 | Broker’s provisioning private key (mode 600) |
PROVISION_KEY_PEM | No | — | Inline PEM content, preferred over PROVISION_KEY |
DELETE_USER | No | 0 | Checkin only — 1 also deletes the temp account |
BROKER_API | No | /opt/britive-broker/scripts/broker-bridge-api.sh | Path to the Bridge API helper |
Checkout Routine
Full script: Linux/permissions/temp-user-bridge/checkout_ssh_bridge.sh
Key sections — provision the one-time key, then register the session against the user’s Bridge credentials:
# One-time keypair, tagged with the transaction ID for cleanup
ssh-keygen -t ed25519 -f "$KEYDIR/key" -N "" -C "bridge:${TRANSACTION_ID}" >/dev/null 2>&1
# Provision the user and public key on the target via the privileged account
run_provision sh -s -- "$TARGET_USERNAME" "$PUBKEY_B64" "$PROVISION_SUDO" "$TRANSACTION_ID" <<'REMOTE'
# ... creates the user if missing, appends the key to authorized_keys,
# ... writes /etc/sudoers.d/bridge-<TRX> when sudo is requested
REMOTE
# The bridge password is always registered — Bridge rejects the checkout
# without it. A Bridge SSH key, when present, is added alongside it.
if [ -n "$BRIDGE_AUTH_PUBKEY" ]; then
AUTH_METHOD="pubkey"
AUTH_FIELDS="$(jq -n --arg p "$BRIDGE_AUTH_PASSWORD" --arg k "$BRIDGE_AUTH_PUBKEY" \
'{native_auth:"bridge_credentials", bridge_auth_password:$p, user_public_key:$k}')"
else
AUTH_METHOD="password"
AUTH_FIELDS="$(jq -n --arg p "$BRIDGE_AUTH_PASSWORD" \
'{native_auth:"bridge_credentials", bridge_auth_password:$p}')"
fi
jq -n --arg transaction_id "$TRANSACTION_ID" --arg username "$USER_EMAIL" \
--argjson private_key "$PRIVATE_KEY_JSON" --argjson auth "$AUTH_FIELDS" \
'{transaction_id: $transaction_id, protocol: "ssh",
username: $username, private_key: $private_key,
record_session: true} + $auth' > "$PAYLOAD_FILE"
if ! "${BROKER_API}" checkout-create --file "$PAYLOAD_FILE" >/dev/null; then
rollback # take the injected key back off the target
fail "Bridge checkout registration failed"
fiThe response carries both routes in:
{
"BRIDGE_URL": "bridge.example.com",
"command": "ssh -p 2222 -l 'alice@corp%server.internal' bridge.example.com",
"auth_method": "password",
"bridge_username": "alice@corp%server.internal",
"bridge_port": "2222",
"target_username": "alicecorp",
"browser_session": "https://bridge.example.com/ssh/#transaction_id=<TRX>"
}Checkin Routine
Full script: Linux/permissions/temp-user-bridge/checkin_ssh_bridge.sh
# Session dies FIRST — revokes the proxy credential and cuts any live session
"${BROKER_API}" checkout-delete "${TRANSACTION_ID}"
# Then deprovision: remove the key (matched by the bridge:<TRX> comment)
# and delete /etc/sudoers.d/bridge-<TRX>
run_provision sh -s -- "$TARGET_USERNAME" "$TRANSACTION_ID" "$DELETE_USER" <<'REMOTE'
# ... grep -vF "bridge:<TRX>" authorized_keys, rm sudoers entry,
# ... and userdel when DELETE_USER=1
REMOTEThe order matters: revoking the Bridge checkout first means an active session is cut immediately, rather than lingering while the key is removed.
Configure in Britive
Create the permission
Resource Manager → Resource Type Permissions → New Permission. Language = Shell. Paste the checkout and checkin routines. Declare TARGET_HOST, BRIDGE_URL, EXPIRATION, and any provisioning overrides; BRITIVE_USER_EMAIL, TRX, and the BRIDGE_AUTH_* values are system-defined.
Attach a response template
Surface {{command}} and {{bridge_username}} for native clients, and {{browser_session}} for the browser. The same template works for the Windows RDP and database bridge patterns — they return the same keys.
Create a profile and policy
Create a profile (e.g. 1h), add the permission, associate it with the Linux resources, and add a policy assigning members by tag.
Verify
Check out
Check out the profile. The response contains a ready-to-paste command and a browser_session URL.
Connect with your own client
ssh -p 2222 -l 'alice@corp%server.internal' bridge.example.comAt the password prompt, enter the Bridge Password from your Britive profile — not the target account’s password. On the target, ~/.ssh/authorized_keys contains a key commented bridge:<TRX>.
Or open the browser session
The browser_session URL opens the same session with no client installed.
Check in
Check in. Any live session is cut, the key line is gone from authorized_keys, and /etc/sudoers.d/bridge-<TRX> is removed.
Troubleshoot
| Symptom | Likely Cause | Fix |
|---|---|---|
required env var missing: BRIDGE_AUTH_PASSWORD | User has no Bridge Password on their profile | Set one under Manage Account → Bridge Attributes |
| Checkout registration fails | Script is v1, or Bridge is v1 | v2 scripts call broker-bridge-api.sh; confirm both ends are v2 |
provisioning user requires root or passwordless sudo | Provisioning account lacks privileges | Grant britivebroker passwordless sudo or use root |
| Checkout fails at the SSH step | Wrong PROVISION_KEY path or key not authorized | Confirm the broker’s key (mode 600) is in the provisioning user’s authorized_keys |
| SSH to the Bridge is refused | Native listener not enabled or not exposed | Enable the native SSH listener and expose NATIVE_PORT (2222) on the load balancer |
| Bridge rejects the login name | Username not in <email>%<target-host> form, or profile Bridge Username assumed | Use -l with the full bridge_username from the response |
| Session opens but sudo denied | BRITIVE_SUDO not set | Set BRITIVE_SUDO=1 on the permission |
| Key remains after checkin | Checkin failed mid-run | Keys are TRX-tagged — re-run checkin or remove lines matching bridge:<TRX> |
Next Steps
- What Is Britive Bridge — architecture and concepts
- Remote SSH Access — the same provisioning pattern without Bridge (key returned to the user)
- RDP via Bridge — the Windows equivalent
- Source: britive/access-broker-examples — temp-user-bridge