Skip to content

SSH connection

Defines the behavior of the SSH protocol for a user who is connecting to Bifröst.

Configuration

addresses

[]Net Address = [":22"]

To which address the service will bind and listen to.

keys

See below.

messages

See below.

idleTimeout

Duration = "10m"

For how long a connection can be idle before it will forcibly be closed. The client can send keep-alive packets to extend the idle time. 0 disables this timeout.

maxTimeout

Duration = 0

The maximum duration a connection can be open before it will forcibly be closed, regardless of whether it is active. 0 disables this timeout.

gracefulShutdownTimeout

Duration = "30s"

How long Bifröst waits for active SSH connections and their handlers to finish after shutdown starts and the listeners have been closed. Remaining connections are forcibly closed when this single time budget expires. Shared resources remain open until their handlers have finished cleanup. 0 disables waiting and closes connections immediately.

handshakeTimeout

Duration = "2m"

The maximum duration from accepting a connection until successful SSH authentication. 0 disables this timeout.

sessionRequestTimeout

Duration = "30s"

How long an accepted session channel may wait for its initial shell, exec, or subsystem request. When the timeout expires, only the idle session channel is closed; the SSH connection and active sibling channels remain available. 0 disables this timeout.

maxAuthTries

uint8 = 6

How many authentication attempts a client can make before the connection is rejected. 0 disables this limit.

maxConnections

uint32 = 255

The maximum number of parallel connections on this service. Every additional connection is rejected.

maxStartupsStart

uint16 = 10

The number of concurrent unauthenticated connections accepted before random early dropping starts. 0 starts the early-drop calculation with the first unauthenticated connection. This setting has no effect when maxStartupsFull is 0.

maxStartupsRate

uint8 = 30

The initial probability, in percent, of dropping a new unauthenticated connection after maxStartupsStart is reached. It must be between 0 and 100; the probability increases toward 100 as maxStartupsFull is approached.

maxStartupsFull

uint16 = 100

The hard limit for concurrent unauthenticated connections per SSH listener. When greater than 0, it must be greater than or equal to maxStartupsStart. 0 disables the complete pre-authentication connection limit.

maxSessionsPerConnection

uint16 = 10

The maximum number of active SSH session channels per connection. 0 disables this limit.

maxChannelsPerConnection

uint16 = 64

The maximum number of active SSH channels of all supported types per connection. 0 disables this limit.

maxReverseForwardsPerConnection

uint16 = 16

The maximum number of active reverse-forward listeners per connection. 0 disables this limit.

maxChannels

uint16 = 64

The maximum number of active SSH channels across all connections handled by one SSH listener. 0 disables this limit.

maxReverseForwards

uint16 = 256

The maximum number of active reverse-forward listeners across all connections handled by one SSH listener. 0 disables this limit.

unauthenticatedAudit

Controls how detailed audit records produced before a client has proven authentication are rate-limited. See below.

Listener-scoped limits are tracked independently for every entry in addresses. For example, two configured listen addresses can each serve up to maxChannels active channels and maxReverseForwards reverse-forward listeners. maxConnections is different: Bifröst enforces it across the complete service and all configured addresses.

With ssh -R [bind_address:]port:destination_host:destination_port, the reverse listener binds in the selected environment: on the Bifröst host for Local, in the container network namespace for Docker, in the Pod network namespace for Kubernetes, or on the target sshd for the SSH environment. The forwarded destination is reached from the SSH client, not from the listener environment. Omitting the destination (ssh -R [bind_address:]port) lets a supporting SSH client serve SOCKS5 on that reverse listener. For Local, Docker and Kubernetes, an empty bind host uses loopback in that environment; an explicit * requests a wildcard bind, whose network reachability depends on the authorized-key and environment policies and the network configuration. For the SSH environment, the target server's GatewayPorts setting governs the actual bind address. A permitted request does not guarantee a successful bind or network access to the listener.

Note

A client must acknowledge a new forwarded-tcpip channel within one second. Otherwise only the affected forwarded connection fails; the SSH connection and unrelated active channels remain available.

proxyProtocol

bool = false

If enabled, Bifröst supports incoming connections using PROXY protocol versions 1 and 2. Only enable this when the SSH listener is exclusively reachable through trusted proxies. The current boolean configuration trusts the source addresses supplied by every peer that can reach the listener.

OpenSSH Unix-socket forwarding (direct-streamlocal@openssh.com and streamlocal-forward@openssh.com) is not enabled. Bifröst currently has no cross-environment policy for socket paths, ownership, permissions, and cleanup.

banner

string Connection =

"{{ \`/etc/ssh/sshd-banner\` | file \`optional\` | default \`Transcend with Engity's Bifröst\n\n\` }}"

Banner which will be shown when the client connects to the server even before the first validation of authorizations or similar happens.

preparationMessages

See below.

Examples

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
addresses: [ ":22" ]
keys:
  hostKeys: [ /etc/engity/bifroest/key ]
  # ...
messages:
  # ...
idleTimeout: 10m
maxTimeout: 0
gracefulShutdownTimeout: 30s
handshakeTimeout: 2m
sessionRequestTimeout: 30s
maxAuthTries: 6
maxConnections: 255
maxStartupsStart: 10
maxStartupsRate: 30
maxStartupsFull: 100
maxSessionsPerConnection: 10
maxChannelsPerConnection: 64
maxReverseForwardsPerConnection: 16
maxChannels: 64
maxReverseForwards: 256
unauthenticatedAudit:
  interval: 1m
  perSourceLimit: 6
  globalLimit: 24
proxyProtocol: false
banner: "Yeah!"

Unauthenticated audit

Unauthenticated flow evaluations can be triggered repeatedly by remote clients. Bifröst therefore limits only their detailed audit records; exhaustion of either bucket never rejects authentication. Accepted password and keyboard-interactive results and verified public-key results bypass these limits. A public-key candidate has not yet proved possession of the private key and remains limited even if a flow accepts it.

Configuration

Suppressed details are represented by signed authentication.flow.evaluations-suppressed aggregate events. Aggregates contain counts but no source address. See audit events for their exact semantics.

interval

Duration = "1m"

The token refill and aggregate reporting interval. It must be greater than zero.

perSourceLimit

uint16 = 6

Both the per-source token bucket capacity and the number of tokens refilled over one interval. It must be greater than zero. IPv4-mapped IPv6 addresses share their IPv4 bucket; IPv6 sources are grouped by /64.

globalLimit

uint16 = 24

Both the service-wide token bucket capacity and the number of tokens refilled over one interval. It must be greater than or equal to perSourceLimit. This is the authoritative bound against distributed source addresses; the per-source bucket only preserves fairness.

The effective remote address from the SSH context is used. With proxyProtocol: true, this is the source supplied by the trusted proxy. The source cache is bounded; rotating IPv6 prefixes or sending many PROXY source addresses cannot bypass the global bucket or grow memory without bound.

Keys

Configuration

hostKeys

[]File Path = ["<defaultLocation>"]

Where to store the host keys at. If they do not exist, they will be created as Ed25519 key.

Default Locations:

  • Linux: /etc/engity/bifroest/key
  • Darwin: /Library/Application Support/Engity/Bifroest/key
  • Windows: C:\ProgramData\Engity\Bifroest\key

exchanges

["curve25519-sha256@libssh.org", "curve25519-sha256", "diffie-hellman-group16-sha512", "mlkem768x25519-sha256"]

Restrict which key exchanges are allowed to be used.

rsaRestriction

RSA Restriction = "at-least-4096-bits"

Restrict which RSA keys are allowed to be used.

dsaRestriction

DSA Restriction = "none"

Restrict which DSA keys are allowed to be used.

ecdsaRestriction

ECDSA Restriction = "at-least-384-bits"

Restrict which ECDSA keys are allowed to be used.

ed25519Restriction

Restrict which ED25519 keys are allowed to be used.

rememberMeNotification

string Authorization =

"If you return until {{.session.validUntil | format \`dateTimeT\`}} with the same public key ({{.key | fingerprint}}), you can seamlessly log in again.\n\n"

Banner which will be shown if the connection was based on an authentication method (like OIDC) which does not have its own public key authentication. At this point, the authentication was successful AND the client submitted at least one public key (as authentication try). This key will be used and this message will be shown to the client to inform that this key will be used for the session from now on. As a result, the original authentication will be skipped (like OIDC) as long as it is not expired and the client presents the same public key. For OIDC, a remembered key does not bypass the session's refresh-token checks or prevent its disposal after lost access.

Examples

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
hostKeys: [ /etc/engity/bifroest/key ]
exchanges:
  - curve25519-sha256@libssh.org
  - curve25519-sha256
  - diffie-hellman-group16-sha512
  - mlkem768x25519-sha256
rsaRestriction: at-least-4096-bits
dsaRestriction: none
ecdsaRestriction: at-least-384-bits
ed25519Restriction: all
rememberMeNotification: "If you return until {{.session.validUntil | format `dateTimeT`}} with the same public key {{.key | fingerprint}}), you can seamlessly login again.\n\n"

Messages

Configuration

authentications

["hmac-sha2-512-etm@openssh.com", "hmac-sha2-256-etm@openssh.com"]

Restrict which message authentications are allowed to be used.

ciphers

["aes256-gcm@openssh.com", "aes256-ctr", "aes192-ctr"]

Restrict which ciphers are allowed to be used.

Examples

1
2
3
4
5
6
7
authentications:
  - hmac-sha2-512-etm@openssh.com
  - hmac-sha2-256-etm@openssh.com
ciphers:
  - aes256-gcm@openssh.com
  - aes256-ctr
  - aes192-ctr

Preparation Messages

In some cases the connection will not be available instantly. For example if the docker environment is used and an image needs to be downloaded first, this could take some seconds. In these cases different parts of Bifröst might trigger these messages being displayed. By default, all of them are displayed as described below.

As this is an array of preparation messages, the first which matches, wins.

Configuration

id

Regex = ".*"

Each preparation proces has a unique ID (like pull-image of the docker environment).

This property defines a regular expression this ID has to match together with flow.

flow

Regex = ".*"

Each preparation process will be produces by a flow.

This property defines a regular expression the name of this flow has to match together with id.

start

string Preparation Process = "{{.title}}..."

This message is shown when a preparation process starts.

update

"\r{{.title}}... {{.percentage | printf \`%.0f%%\`}}"

This message is shown on each status change of a preparation process.

end

string Preparation Process = "\r{{.title}}... DONE!\n"

This message is shown if the preparation process finishes successful.

error

"\r{{.title}}... FAILED! Contact server operator for more information. Disconnecting now...\n"

This message is shown if the preparation process finishes with an error. The direct consequence will be that the connection will be closed by Bifröst immediately.

Examples

Show special message for pull-image process (all flows), but default for the rest
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
preparationMessages:
  - id: ^pull-image$
    # {{.image}} is NOT part of the common set of properties of
    # a Preparation Message it is specific to this message.
    # Please visit the details of each Preparation Message type
    # for details.
    start: "Going to download image {{.image}}..."
    update: "\rGoing to download image {{.image}}... {{.percentage | printf `%.0f%%`}}"
    end: "\rImage {{.image}} downloaded.\n"
    error: "\rFailed to download image {{.image}}.\n"
  - {} # Entry with all default values as mentioned above
Disable messages completely, for all preparation processes
1
2
3
4
5
preparationMessages:
  - start: ""
    update: ""
    end: ""
    error: ""

Compatibility

linux darwin windows
/ / /