RDP via Bridge
Overview
This pattern combines the temporary local user approach with Britive Bridge: the broker creates a temporary Windows local user at checkout, but instead of returning the password, it registers a proxied RDP 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 RDP client (
mstsc, Remmina, Microsoft Remote Desktop), pointed at the Bridge’s native RDP listener - The browser, with no client at all
Either way the Windows account password stays server-side. The user authenticates to Bridge with the Bridge Password on their Britive profile — never with the temporary account’s password.
What you’ll accomplish:
- Create a temporary Windows local user at checkout (over WinRM or SSH)
- Register a Bridge session that accepts the user’s Bridge credentials
- Tear down the session and delete the account 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 RDP listener is enabled and reachable on
NATIVE_PORT(default3389) - A provisioning account with admin rights on each target (default
Administrator) - For WinRM transport: WinRM enabled on the target (5985/5986, NTLM auth), and
pywinrminstalled on the broker host - For SSH transport: OpenSSH Server + PowerShell 5.1+ on the target, and the broker’s key authorized
- Broker host has
python3andjq(plussshfor SSH transport) — 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)
How It Works
checkout → derive Windows username from BRITIVE_USER_EMAIL (SAM-safe, max 20 chars)
→ generate password meeting Windows complexity rules
→ WinRM or SSH: New-LocalUser (description bridge:<TRX>),
Add-LocalGroupMember "Remote Desktop Users"
→ broker-bridge-api.sh checkout-create
{ protocol: rdp, target_password, rdp_security,
native_auth: bridge_credentials, bridge_auth_password }
→ return { command, browser_session, bridge_username, ... }
checkin → broker-bridge-api.sh checkout-delete <TRX> ← session dies FIRST
→ WinRM or SSH: Remove-LocalGroupMember, Remove-LocalUserThe checkin order matters: the Bridge session is terminated before the account is deleted, so the RDP proxy tunnel closes cleanly rather than being pulled out from under a live session.
The Identity Model
The native RDP 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 RDP 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.
Environment Variables
| Variable | Required | Default | Notes |
|---|---|---|---|
BRITIVE_USER_EMAIL | Yes | — | Injected — Windows username derived from the local part |
TRX | Yes | — | Injected — transaction ID; tags the account description |
TARGET_HOST | Yes | — | RDP 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 |
TARGET_PORT | No | 3389 | RDP port on the target |
TARGET_DOMAIN | No | — | Windows/AD domain for the RDP login |
NATIVE_PORT | No | 3389 | Bridge native RDP listener port |
RDP_SECURITY | No | nla | any, nla, tls, or rdp |
RDP_ENABLE_DRIVE | No | false | true allows drive redirection |
PROVISION_TRANSPORT | No | winrm | winrm or ssh |
PROVISION_USER | No | Administrator | Provisioning account |
PROVISION_PASSWORD | WinRM only | — | Password for the provisioning account |
PROVISION_HOST | No | TARGET_HOST | Separate provisioning host if needed |
PROVISION_PORT | No | 5985/5986/22 | Auto-selected per transport and WINRM_NO_SSL |
PROVISION_KEY / PROVISION_KEY_PEM | SSH only | /home/bridge/.ssh/id_ed25519 | Key file path, or PEM content injected as a variable |
WINRM_NO_SSL | No | 1 | 1 = HTTP (5985), 0 = HTTPS (5986) |
LOCAL_GROUP | No | Remote Desktop Users | Comma-separated groups to add the user to |
BRITIVE_FIRST_NAME / BRITIVE_LAST_NAME | No | — | Used for the account display name |
BROKER_API | No | /opt/britive-broker/scripts/broker-bridge-api.sh | Path to the Bridge API helper |
Checkout Routine
Full script: Windows/permissions/temp-user-bridge/checkout_rdp_bridge.sh
Key sections — create the user, then register the session against the user’s Bridge credentials:
# SAM-safe username from the email local part (a-z0-9, max 20 chars)
USERNAME="$(python3 -c "
import re, sys
name = re.sub(r'[^a-z0-9]', '', sys.argv[1].split('@')[0].lower())
if name[0].isdigit(): name = 'brg' + name
print(name[:20])
" "$USER_EMAIL")"
# Password meeting Windows complexity: upper + lower + digit + special
RAND="$(head -c 12 /dev/urandom | base64 | tr -d "+/='\\\\" | head -c 12)"
PASSWORD="${RAND}Aa1@"
# PowerShell run over WinRM (pywinrm) or SSH (-EncodedCommand):
# New-LocalUser -Description 'bridge:<TRX>' ... ;
# Add-LocalGroupMember -Group 'Remote Desktop Users' ...
# The Windows password goes to Bridge as target_password; the user never sees
# it. What the user types is their own Bridge Password.
AUTH_FIELDS="$(jq -n --arg p "$BRIDGE_AUTH_PASSWORD" \
'{native_auth:"bridge_credentials", bridge_auth_password:$p}')"
jq -n --arg transaction_id "$TRANSACTION_ID" --arg username "$USER_EMAIL" \
--arg target_username "$USERNAME" --arg target_password "$PASSWORD" \
--arg rdp_security "$RDP_SECURITY" --argjson auth "$AUTH_FIELDS" \
'{transaction_id: $transaction_id, protocol: "rdp",
username: $username, target_username: $target_username,
target_password: $target_password, rdp_security: $rdp_security,
record_session: true} + $auth' > "$PAYLOAD_FILE"
"${BROKER_API}" checkout-create --file "$PAYLOAD_FILE" >/dev/nullThe response carries both routes in:
{
"BRIDGE_URL": "bridge.example.com",
"command": "mstsc /v:bridge.example.com:3389",
"auth_method": "password",
"bridge_username": "alice@corp%server.internal",
"bridge_port": "3389",
"target_username": "alicecorp",
"browser_session": "https://bridge.example.com/rdp/#transaction_id=<TRX>"
}The RDP client prompts for credentials on connect: the username is bridge_username, the password is the user’s Bridge Password.
Checkin Routine
Full script: Windows/permissions/temp-user-bridge/checkin_rdp_bridge.sh
# Terminate the Bridge session FIRST so the RDP tunnel closes cleanly
"${BROKER_API}" checkout-delete "${TRANSACTION_ID}"
# Then deprovision over WinRM or SSH (best-effort after the session is revoked):
# Remove-LocalGroupMember -Group 'Remote Desktop Users' ... ;
# Remove-LocalUser -Name <username>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 the provisioning variables for your transport (PROVISION_PASSWORD for WinRM, or key variables for SSH); 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 Linux SSH 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 Windows resources, and add a policy assigning members by tag.
Verify
Check out
Check out the profile. The response contains a command, a bridge_username, and a browser_session URL.
Connect with your own client
mstsc /v:bridge.example.com:3389At the credential prompt, enter the bridge_username from the response and your Bridge Password — not the temporary Windows account’s password. On the target, Get-LocalUser shows the account with description bridge:<TRX>.
Or open the browser session
The browser_session URL opens the same desktop with no client installed.
Check in
Check in. Any live session is cut and the local account is gone (Get-LocalUser no longer lists it).
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 |
pywinrm not installed | Broker host missing the library | pip install pywinrm or bake it into a custom Bridge image |
PROVISION_PASSWORD required for winrm transport | WinRM selected without a password | Set PROVISION_PASSWORD, or switch to PROVISION_TRANSPORT=ssh |
| WinRM connection refused | WinRM disabled or wrong port | Enable WinRM on the target; check WINRM_NO_SSL matches the listener (5985 vs 5986) |
| Provisioning succeeds but access denied | Non-built-in admin over WinRM | Set LocalAccountTokenFilterPolicy=1 on the target |
| RDP client cannot reach the Bridge | Native listener not enabled or not exposed | Enable the native RDP listener and expose NATIVE_PORT (3389) on the load balancer |
| Bridge rejects the login name | Username not in <email>%<target-host> form | Use the bridge_username from the response verbatim |
| Account remains after checkin | Deprovisioning failed (best-effort) | Accounts are tagged bridge:<TRX> in the description — re-run checkin or remove manually |
Next Steps
- What Is Britive Bridge — architecture and concepts
- Temporary Local User Access — the same account lifecycle without Bridge (password returned to the user)
- SSH via Bridge — the Linux equivalent
- Source: britive/access-broker-examples — temp-user-bridge