Browser-Based 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 the private key to the user, it registers a proxied session with Bridge and returns a browser URL. The user opens the URL and gets an SSH session in the browser — no SSH client, no key handling, and all traffic flows through the Bridge proxy where it can be audited and recorded.
What you’ll accomplish:
- Provision a one-time SSH key on a remote Linux host at checkout
- Register a proxied session with Bridge and return a tokenized URL
- Remove the key, sudo grant, and session at checkin
Before You Begin
- Bridge is deployed and reachable by user browsers at
BRIDGE_URL - 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, andpython3— extend the Bridge image with a custom build if needed bridge.shpresent at/opt/britive-broker/scripts/bridge.sh
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>
→ bridge.sh checkout-create (payload includes the private key)
→ return { token, url: <BRIDGE_URL>/ssh/#token=...&transaction_id=... }
checkin → SSH as provisioning user: remove key by bridge:<TRX> marker,
delete sudoers entry
→ bridge.sh checkout-delete <TRX>The private key travels only from the broker to Bridge inside the transaction payload — the user never sees it.
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 |
BRITIVE_REMOTE_HOST | Yes | — | SSH target host |
BRIDGE_URL | Yes | — | Public Bridge base URL |
EXPIRATION | Yes | — | Checkout duration in seconds |
REMOTE_USER | No | britivebroker | Privileged account used for provisioning |
PROVISION_HOST | No | BRITIVE_REMOTE_HOST | Separate provisioning host if needed |
PROVISION_PORT | No | 22 | SSH port for provisioning |
PROVISION_KEY | No | /home/bridge/.ssh/id_ed25519 | Broker’s provisioning private key (mode 600) |
BRITIVE_SUDO | No | 0 | Set 1 to grant passwordless sudo for the session |
BROKER_API | No | /opt/britive-broker/scripts/bridge.sh | Path to the bridge helper |
Checkout Routine
Full script: Linux/permissions/temp-ssh-key-remote-bridge/checkout_remote_bridge.sh
Key sections — generate the one-time key, provision it, register the session:
# 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
ssh -i "$PROVISION_KEY" -o BatchMode=yes -p "$PROVISION_PORT" \
"${PROVISION_USER}@${PROVISION_HOST}" \
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
# Register the proxied session — the private key goes to Bridge, not the user
PRIVATE_KEY_JSON="$(python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))' < "$KEYDIR/key")"
TOKEN="$(head -c 32 /dev/urandom | base64 | tr -d '/+=' | head -c 43)"
EXPIRES_AT="$(( $(date +%s) + EXPIRATION ))"
cat > "$PAYLOAD_FILE" <<EOF
{
"transaction_id": "${TRANSACTION_ID}",
"protocol": "ssh",
"username": "${USER_EMAIL}",
"target_host": "${TARGET_HOST}",
"target_port": 22,
"target_username": "${TARGET_USERNAME}",
"private_key": ${PRIVATE_KEY_JSON},
"expires_at": ${EXPIRES_AT},
"token": "${TOKEN}"
}
EOF
"${BROKER_API}" checkout-create --file "$PAYLOAD_FILE" >/dev/null
printf '{"token": "%s", "url": "%s/ssh/#token=%s&transaction_id=%s"}\n' \
"${TOKEN}" "${BRIDGE_URL}" "${TOKEN}" "${TRANSACTION_ID}"Checkin Routine
Full script: Linux/permissions/temp-ssh-key-remote-bridge/checkin_remote_bridge.sh
# On the target: remove the injected key (matched by the bridge:<TRX> comment)
# and delete /etc/sudoers.d/bridge-<TRX> if present
ssh -i "$PROVISION_KEY" -o BatchMode=yes -p "$PROVISION_PORT" \
"${PROVISION_USER}@${PROVISION_HOST}" \
sh -s -- "$TARGET_USERNAME" "$PROVISION_SUDO" "$TRANSACTION_ID" <<'REMOTE'
# ... grep -vF "bridge:<TRX>" authorized_keys, rm sudoers entry
REMOTE
# Terminate the bridge session — invalidates the token and closes the tunnel
"${BROKER_API}" checkout-delete "${TRANSACTION_ID}"The checkin routine optionally deletes the user account entirely — a commented DELETE_USER block at the end of the remote section enables full teardown.
Configure in Britive
Create the permission
Resource Manager → Resource Type Permissions → New Permission. Language = Shell. Paste the checkout and checkin routines. Declare BRIDGE_URL, EXPIRATION, and any provisioning overrides; BRITIVE_USER_EMAIL and TRX are system-defined. Attach a response template that surfaces the returned url.
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 URL like https://<bridge>/ssh/#token=<token>&transaction_id=<trx>.
Open the session in the browser
The URL opens an SSH session to the target as the derived user. On the target, ~/.ssh/authorized_keys contains a key commented bridge:<TRX>.
Check in
Check in. The URL stops working, the key line is gone from authorized_keys, and /etc/sudoers.d/bridge-<TRX> is removed.
Troubleshoot
| Symptom | Likely Cause | Fix |
|---|---|---|
provisioning user requires root or passwordless sudo | Provisioning account lacks privileges | Grant britivebroker passwordless sudo or use root |
| Checkout fails at 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 |
| Browser URL rejected immediately | Expired transaction or wrong BRIDGE_URL | Check EXPIRATION; confirm BRIDGE_URL matches the deployed Bridge endpoint |
| Session opens but sudo denied | BRITIVE_SUDO not set | Set BRITIVE_SUDO=1 on the permission |
| Key remains after checkin | Checkin routine 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)
- Source: britive/access-broker-examples — temp-ssh-key-remote-bridge