Skip to content
Browser-Based SSH via Bridge

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, and python3 — extend the Bridge image with a custom build if needed
  • bridge.sh present 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

VariableRequiredDefaultNotes
BRITIVE_USER_EMAILYesInjected — OS username derived from the local part
TRXYesInjected — transaction ID; tags the key and sudoers entry
BRITIVE_REMOTE_HOSTYesSSH target host
BRIDGE_URLYesPublic Bridge base URL
EXPIRATIONYesCheckout duration in seconds
REMOTE_USERNobritivebrokerPrivileged account used for provisioning
PROVISION_HOSTNoBRITIVE_REMOTE_HOSTSeparate provisioning host if needed
PROVISION_PORTNo22SSH port for provisioning
PROVISION_KEYNo/home/bridge/.ssh/id_ed25519Broker’s provisioning private key (mode 600)
BRITIVE_SUDONo0Set 1 to grant passwordless sudo for the session
BROKER_APINo/opt/britive-broker/scripts/bridge.shPath 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

SymptomLikely CauseFix
provisioning user requires root or passwordless sudoProvisioning account lacks privilegesGrant britivebroker passwordless sudo or use root
Checkout fails at SSH stepWrong PROVISION_KEY path or key not authorizedConfirm the broker’s key (mode 600) is in the provisioning user’s authorized_keys
Browser URL rejected immediatelyExpired transaction or wrong BRIDGE_URLCheck EXPIRATION; confirm BRIDGE_URL matches the deployed Bridge endpoint
Session opens but sudo deniedBRITIVE_SUDO not setSet BRITIVE_SUDO=1 on the permission
Key remains after checkinCheckin routine failed mid-runKeys are TRX-tagged — re-run checkin or remove lines matching bridge:<TRX>

Next Steps

Last updated on