SSH environment
The SSH environment terminates the incoming SSH connection at Bifröst and creates a separately authenticated SSH connection to a target server. All sessions and direct TCP forwarding channels of one incoming connection share one target SSH transport.
Each target transport allows up to 64 concurrently active or pending shell, SFTP, forwarding and agent channels. Additional locally requested channels wait for capacity; excess agent channels initiated by the target are rejected.
If a channel-open request is still unanswered when its source operation is canceled, Bifröst closes the target transport so the pending request cannot retain a channel slot. This also interrupts other active channels of the same incoming connection; later requests can establish a new transport.
If the open result has already arrived, Bifröst normally closes only that operation's channel or forwarded connection. A subsystem target that does not finish within two seconds after its source is canceled causes the shared target transport to close. An open result arriving concurrently with cancellation may also cause the shared transport to close.
Configuration
type
Environment Type = "ssh"
Has to be set to ssh to enable the SSH environment.
variables
Defines environment variables sent to the target for commands, shells and SFTP. They override values received from the SSH client, authorized_keys and authorization. Bifröst-generated runtime variables take precedence. Variables are sent as best-effort SSH env requests; the target server decides which variables it accepts, commonly through OpenSSH AcceptEnv.
address
string Authorization
Target SSH address. The port defaults to 22. DNS names, IPv4 addresses and IPv6 addresses can omit it; an explicit IPv6 port uses [address]:port form.
user
string Authorization
User used to authenticate at the target SSH server.
os
Os = "linux"
Operating system of the target. This controls case-sensitive or case-insensitive environment-variable precedence.
knownHosts
string
Inline OpenSSH known_hosts content. A YAML block string can contain multiple entries.
knownHostsFile
string
Explicit path to one OpenSSH known_hosts file. Bifröst never implicitly reads user or system known_hosts files. knownHosts and knownHostsFile can be used together.
acceptAllHostKeys
bool = false
Disables target host-key verification. This cannot be combined with knownHosts or knownHostsFile and should only be used in controlled test environments.
Danger
Setting acceptAllHostKeys: true makes the target connection vulnerable to on-path attacks.
identityFiles
list of strings Authorization
Private SSH keys offered to the target. Every configured file has to exist and contain an unencrypted private key. If the list is absent or empty, Bifröst offers all configured server host keys as client identities. An invalid explicit entry never activates this fallback.
identityFiles and certificate are mutually exclusive. If neither is configured, the server-host-key fallback remains active.
certificate
Enables a persistent OpenSSH user certificate for the target connection. See below.
connectTimeout
duration Authorization = "10s"
Maximum duration for TCP connection establishment and SSH handshake. 0 disables this timeout.
loginAllowed
bool Authorization = true
Controls whether the environment accepts an authorization.
banner
string Authorization = ""
Displayed before an interactive target shell is opened.
portForwardingAllowed
bool Authorization = true
Controls local and dynamic forwarding after the applicable authorized-key policy has also been checked. Must also be true for reverse forwarding.
reversePortForwardingAllowed
bool Authorization = false
Enables ssh -R only when portForwardingAllowed is also true and the applicable authorized-key policy permits the bind address. Disabled by default. The listener is requested on the target SSH server, not on Bifröst. The target sshd must permit forwarding (AllowTcpForwarding, including remote forwarding) and its GatewayPorts setting controls which requested bind addresses it accepts. The destination of each forwarded connection is reached from the SSH client.
Warning
Bifröst checks permitlisten against the requested bind address. The SSH protocol does not report the address actually bound by the target. With GatewayPorts yes, a target sshd can turn a permitted loopback request into a wildcard listener. If permitlisten is intended to restrict network exposure, ensure the target server does not widen the bind address (for example, use GatewayPorts no) before enabling reverse forwarding.
allowedSubsystems
regular expression = "^sftp$"
Only matching SSH subsystem names are forwarded to the target. The regular expression must match the entire name, even if no anchors are written. By default, only sftp is allowed; allowedSubsystems: '' denies all subsystems, while YAML null is invalid. For multiple names, use an expression such as sftp|netconf|powershell. Shell and exec are unaffected. An authorized-key forced command is executed instead of forwarding the requested subsystem.
At least one host-key verification source is required unless acceptAllHostKeys is explicitly enabled.
SSH User Certificate
Bifröst issues exactly one certificate for each persistent Bifröst session. Reconnecting and restarting Bifröst reuse the byte-identical certificate until its immutable validity boundary is reached.
Configuration
identityFile
Static path to the private subject key used with the certificate. The default is /etc/engity/bifroest/client-key on Linux, /Library/Application Support/Engity/Bifroest/client-key on Darwin, and C:\ProgramData\Engity\Bifroest\client-key on Windows. The default key is shared by all certificate-enabled flows of one Bifröst instance. If the file does not exist, an Ed25519 key is generated. Existing unreadable, encrypted or invalid files cause startup to fail and are never overwritten. The private key is not copied into session storage.
authorityIdentityFile
Static path to the private SSH certificate-authority key. The default is /etc/engity/bifroest/ca on Linux, /Library/Application Support/Engity/Bifroest/ca on Darwin, and C:\ProgramData\Engity\Bifroest\ca on Windows. The default CA is shared by all certificate-enabled flows of one Bifröst instance. If the file does not exist, an Ed25519 key is generated. Existing unreadable, encrypted or invalid files cause startup to fail and are never overwritten. The server host key is never used as certificate authority. Existing certificates remain bound to their original CA after a configured CA rotation.
Bifröst does not create a .pub companion file for either private key. The CA public key is logged at every startup and can be exported for a specific flow with bifroest key export ca.
validity
duration = "15m"
Positive lifetime of a newly issued certificate. The first issuance persists MaxValidUntil, and reconnects, activity and later configuration increases never move that boundary. This lifetime is separate from the dynamic Bifröst session idle timeout.
Certificate expiry does not disconnect an already authenticated downstream SSH transport, the incoming client connection or the Bifröst session. It only prevents the expired certificate from authenticating a new or re-established downstream SSH connection. Connection and session timeouts control their respective lifetimes independently.
validAfterSkew
duration = "30s"
Non-negative clock skew subtracted from the issuance time for the OpenSSH ValidAfter field.
audience
string Authorization
Enables the Bifröst delegation profile and identifies the intended downstream authorization, normally its flow name. If absent, the certificate uses the interoperable audit profile. The two profiles are described below.
principals
list of strings Authorization
Additional OpenSSH principals. The rendered target user is always included and empty rendered principals are rejected.
extensions
map of strings Authorization
OpenSSH certificate extensions and their values. Standard extensions such as permit-pty, permit-port-forwarding and permit-agent-forwarding are removed when the effective incoming authorization policy denies the corresponding capability. Names ending in @bifroest.engity.org are reserved for Bifröst metadata.
Certificate metadata uses only the reserved evidence-v1@bifroest.engity.org extension. The size-limited evidence document contains allowlisted origin, hop, target and capability fields; passwords, OAuth tokens and unrestricted authorization data are never included.
Certificate profiles
Without audience, Bifröst issues an audit-profile certificate. Its evidence is a non-critical extension, so standard OpenSSH and Bifröst's simple and local authorizations can authenticate it while ignoring the metadata.
With audience, Bifröst additionally sets the critical option bifroest-delegation@bifroest.engity.org. Standard OpenSSH and authorization types that do not understand this option reject the certificate. Only authorization.type: bifroest validates the signed evidence and accepts the delegation.
When a bifroest authorization forwards to another SSH environment, the original identity and validated hop history are retained. The new hop cannot move ValidAfter earlier, extend ValidBefore, or restore a PTY, port-forwarding or agent-forwarding capability denied upstream. Because destination-specific permitopen and permitlisten rules are hop-local, their presence conservatively disables delegated port forwarding rather than broadening it downstream.
Supported operations
| Operation | Behavior |
|---|---|
| Shell and exec | Opened as separate channels on the shared target transport |
| stdin, stdout and stderr | Forwarded without merging stderr into stdout |
| Exit status | Returned from the target command |
| PTY and resize | Forwarded for shell and exec sessions; subsystem requests with a PTY are rejected |
| Signals | Forwarded for shell and exec sessions |
| Allowed SSH subsystems (including SFTP) | The requested subsystem name is sent unchanged to the target; stdin, stdout and stderr are streamed without interpreting the protocol. The client receives success only after the target accepts the subsystem request. |
| SCP | Modern SCP uses SFTP; legacy SCP is handled as an exec command |
| Agent forwarding | Forwarded for shell, exec and non-SFTP subsystems only when requested and permitted by the authorization policy |
ssh -L and ssh -D |
Connections originate from the target SSH server's network |
ssh -R |
When enabled, the target SSH server binds the listener according to its GatewayPorts setting; each forwarded connection reaches its destination from the SSH client |
The environment allowlist is checked before connecting to the target. A subsystem denied by the allowlist or rejected by the target receives a failed SSH subsystem request. The target has 30 seconds by default to answer an allowed subsystem request before the incoming request is rejected. Subsystem names must be non-empty, valid UTF-8, contain no NUL byte and be at most 256 bytes long. Subsystem requests with a PTY are rejected to prevent terminal newline conversion from changing protocol data. Agent forwarding must be requested before the subsystem starts. Audit events record the requested subsystem name. Subsystem streams are not terminal-recorded, and no login notification is written into their stdout. SSH break requests and other arbitrary session requests are not forwarded. Target exit signals are not forwarded as SSH exit signals; a target exit status and the target output must both finish within 30 seconds after either one finishes to report a successful session completion.
If an ssh -R request is canceled while the target has not replied to the listen request, Bifröst closes the shared target SSH transport. If the target does not reply to cancel-tcpip-forward when a listener closes, Bifröst also closes that transport. Other active channels and reverse forwards sharing it are interrupted.
If the client's stdin remains blocked for 30 seconds after a subsystem session ends, Bifröst closes the entire incoming SSH connection. This also terminates other sessions and forwards on that connection. The resulting connection.closed audit event has the reason deadline-exceeded.
Warning
OpenSSH agent forwarding is scoped to an SSH connection rather than an individual session. After one permitted session enables forwarding, the target can access that source agent until the incoming SSH connection ends. Only enable agent forwarding for trusted targets.
Examples
Private key authentication
1 2 3 4 5 6 7 8 9 | |
OpenSSH certificate authentication
-
Import the running OpenSSH server's host key, then use this flow:
1 2 3 4
bifroest key import host \ --knownHostsFile /etc/engity/bifroest/known_hosts \ --address internal.example.org \ --expectedFingerprint SHA256:...Warning
A plain password and
--expectedFingerprint unknownare suitable only for testing. Verify production host-key fingerprints independently and use a password hash or another authorization. -
Configure Bifröst:
/etc/engity/bifroest/configuration.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
flows: - name: openssh authorization: type: simple entries: - name: alice password: plain:change-me environment: type: ssh address: internal.example.org user: alice knownHostsFile: /etc/engity/bifroest/known_hosts certificate: extensions: permit-pty: ""Note
The OpenSSH server needs a local
aliceaccount. Leaveaudienceunset because OpenSSH does not understand Bifröst's delegation critical option. -
Export the CA and deploy the output as
/etc/ssh/bifroest-ca.pubon the OpenSSH server:1 2 3 4
bifroest key export ca \ -c /etc/engity/bifroest/configuration.yaml \ openssh \ --output /tmp/ca.pub -
Configure
sshd:/etc/ssh/sshd_config.d/bifroest.conf 1 2
PubkeyAuthentication yes TrustedUserCAKeys /etc/ssh/bifroest-ca.pub -
Restart
sshd:1systemctl restart sshd
Bifröst delegation
For Bifröst-to-Bifröst certificate delegation, see the Bifröst authorization example.
Compatibility
linux |
darwin |
windows |
|---|---|---|
| / | / | / |