Skip to content

Configuration

Bifröst will be configured in the YAML language.

By default, the configuration is token from the following location:

  • Linux: /etc/engity/bifroest/configuration.yaml
  • Windows: C:\ProgramData\Engity\Bifroest\configuration.yaml

This location can be changed by the --configuration=<path> flag when executing:

bifroest run --configuration=/my/config.yaml

Properties

ssh

SSH

Defines how the SSH connections itself will behave.

session

Defines where and how the sessions inside Bifröst are handled.

flows

Defines which flows are evaluated for user sessions.

housekeeping

Defines how Bifröst will clean up its sessions and connections.

Examples

  1. Simple:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    ssh:
      addresses: [ ":22" ]
      # ...
    session:
      type: fs
      # ...
    flows:
      - name: local
        # ...
    housekeeping:
      # ...
    

  2. Drop in replacement for OpenSSH sshd
    sshd-dropin-replacement.yaml
     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
    ## The configuration can be used if you simply want to use Engity's Bifröst as a drop-in-replacement
    ## for the regular sshd.
    
    ssh:
      addresses: [ ":22" ]
    
    session:
      type: fs
    
    flows:
      - name: local
        authorization:
          type: local
          ## If PAM does not exist or is not supported, please comment the following line.
          pamService: "sshd"
    
        environment:
          type: local
          name: "{{.authorization.user.name}}"
          ## If you only want to allow user with group "ssh" to log in, uncomment the following lines:
          #loginAllowed: |
          #  {{ or
          #    (.authorization.user.group.name | eq "ssh" )
          #    (.authorization.user.groups     | firstMatching `{{.name | eq "ssh" }}` )
          #  }}
    
  3. Complex
    demo.yaml
      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
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    ## This is a configuration to use with Engity's Bifröst to show how to configure it the right way.
    ##
    ## If the values are marked with "@default" this means you're not required to write them into the final
    ## configuration if you want to use them as it is. They are just written here for better visualization.
    
    ssh:
        ## Where to bind the service to.
        ## @default
        ## @[]address - syntax: [<host>]:<port>
        addresses: [ ":22" ]
    
        keys:
            ## Where to store the host keys at. If they do not exist, they will be created as Ed25519 key.
            ## @default
            ## @[]path
            hostKeys: [ /etc/engity/bifroest/key ]
    
            ## Restrict which RSA keys are allowed to be used.
            ## @default
            ## @enum[none, all, at-least-1024-bits, at-least-2048-bits, at-least-3072-bits, at-least-4096-bits]
            rsaRestriction: at-least-4096-bits
    
            ## Restrict which DSA keys are allowed to be used.
            ## @default
            ## @enum[none, all, at-least-1024-bits, at-least-2048-bits, at-least-3072-bits]
            dsaRestriction: none
    
            ## Restrict which ECDSA keys are allowed to be used.
            ## @default
            ## @enum[none, all, at-least-256-bits, at-least-384-bits, at-least-521-bits]
            ecdsaRestriction: at-least-384-bits
    
            ## Restrict which ED25519 keys are allowed to be used.
            ## @default
            ## @enum[none, all, at-least-256-bits]
            ed25519Restriction: all
    
            ## 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 it is not expired and the
            ## client presents the same public key.
            ## @default
            ## @template{context: {session: Session, key: PublicKey, new: bool}}
            rememberMeNotification: "If you return until {{.session.validUntil | format `dateTimeT`}} with the same public key ({{.key | fingerprint}}), you can seamlessly login again.\n\n"
    
        ## For how long a connection can be idle before it will forcibly be closed.
        ## The client can send keep alive packages to extend the idle time.
        ## @default
        ## @duration
        idleTimeout: 10m
    
        ## The maximum duration a connection can be connected before it will be forcibly be closed,
        ## regardless if there are actions or not.
        ## @default
        ## @duration
        maxTimeout: 0
    
        ## How much different authentication methods a client can use before the connection will be
        ## rejected.
        ## @default
        ## @uint
        maxAuthTries: 6
    
        ## The maximum amount of parallel connections on this service. Each new connecting connection
        ## will be rejected.
        ## @default
        ## @uint
        maxConnections: 255
    
        ## Banner which will be shown if the client connects to the server before the first
        ## even the validation of authorizations or similar happens.
        ## @default
        ## @template{context: {}}
        banner: "{{`/etc/ssh/sshd-banner` | file `optional` | default `Transcend with Engity's Bifröst\n\n` }}"
    
    session:
        ## Defines which session type should be used.
        ## @enum[fs]
        type: fs
    
        ###################################################
        ## type: fs
        ###################################################
    
        ## For how long a session can be idle before it will forcibly be closed and will be
        ## disposed and can therefore not be used again. This can extend by actions of the
        ## client (regular interactions or keep alive) across all of client's connections.
        ## @default
        ## @duration
        idleTimeout: 30m
    
        ## The maximum duration of a session before it will be forcibly be closed and be disposed
        ## regardless if there are actions or not.
        ## @default
        ## @duration
        maxTimeout: 0
    
        ## The maximum amount of parallel connections of one session. Each new connecting connection
        ## will be instantly closed.
        ## @default
        ## @uint
        maxConnections: 10
    
        ## Where the session information are stored, locally.
        ## @default
        ## @path
        storage: /var/lib/engity/bifroest/sessions
    
        ## All files/directories inside the session storage will be stored with this mode.
        ## @default
        ## @fileMode - octal representation
        fileMode: 600
    
    ## Flows can hold one or more flows.
    ## The order is important: The flow which is accepted and the authorization is successful,
    ## will be used; otherwise the next will be tried.
    flows:
        - ## Authorize using an OpenID Connect provider and connect them to the local environment. Potentially also
            ## create the user locally if they do not exist and delete them automatically if their session disposes.
    
            ## The name of the flow. Will be used anywhere to identify and store information about it.
            ## @flowName - syntax: [a-z][a-z0-9]+
            name: sso
    
            requirement:
                ## Defines what the requesting (<requesting-name>@<host>) should match. If empty everything will be
                ## included. Important keep ^ and $ to ensure a full match, otherwise it matches only a part of it.
                ## @regex
                includedRequestingName: ^sso$
    
                ## Defines what the requesting (<requesting-name>@<host>) should NOT match. If empty everything will
                ## still be included. Important keep ^ and $ to ensure a full match, otherwise it matches only a
                ## part of it.
                ## @default
                ## @regex
                excludedRequestingName: ""
    
            authorization:
                ## In this example oidcDeviceAuth will be used. See the documentation of your OpenID Connect identity
                ## provider for more details.
                type: oidcDeviceAuth
                ## @string
                issuer: https://login.microsoftonline.com/my-great-tenant-uuid/v2.0
                ## @string
                clientId: my-great-client-uuid
                ## @string
                clientSecret: very-secret-secret
                ## @[]string
                scopes:
                    - openid
                    - email
                    - profile
    
                ## If enabled also the ID Token will be retrieved and available as port of the authentication.
                ## See: https://openid.net/specs/openid-connect-core-1_0.html#IDToken
                ## @default
                ## @bool
                retrieveIdToken: true
    
                ## If enabled also the UserInfo will be retrieved and available as port of the authentication.
                ## See: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
                ## @default
                ## @bool
                retrieveUserInfo: false
    
            environment:
                ## In this example we'll use the local environment.
                type: local
    
                ## Will create the user if it does not exist to match the provided requirements (see below).
                ## If this property is false the user has to exist, otherwise the execution will fail and
                ## the connection will be closed instantly.
                ## @bool
                createIfAbsent: true
    
                ## If an existing user does not match the provided requirements (see below) and this property
                ## is true, this user will be adjusted to match the requirements.
                ## @bool
                updateIfDifferent: true
    
                ## The username of the user that should be taken. If no user with this name does exist, it will be
                ## created.
                ## @template{context{authentication{token{..},idToken{..},userInfo{..}}}} -> username
                name: "{{.authorization.idToken.email}}"
    
                ## The display name the user should have.
                ## @template{context{authentication{token{..},idToken{..},userInfo{..}}}} -> string
                displayName: "{{.authorization.idToken.name}}"
    
                ## It is also possible to define the UID. But in dynamic environment it does not make a lot of sense.
                ## @default
                ## @template{context{authentication{token{..},idToken{..},userInfo{..}}}} -> uint
                #uid: ""
    
                ## The group the user should get. If not set, a group matching the user's name will be created.
                #group:
                #   ## The group name.
                #   ## @default
                #   ## @template{context{authentication{token{..},idToken{..},userInfo{..}}}} -> groupname
                #   #name: ""
                #
                #   ## It is also possible to define the GID. But in dynamic environment it does not make a lot of sense.
                #   ## @default
                #   ## @template{context{authentication{token{..},idToken{..},userInfo{..}}}} -> uint
                #   #gid: ""
    
                ## All other groups, that should the user have.
                groups:
                    - ## OIDC
                        ## The group name.
                        ## @template{context{authentication{token{..},idToken{..},userInfo{..}}}} -> groupname
                        name: oidc
    
                        ## It is also possible to define the GID. But in dynamic environment it does not make a lot of
                        ## sense.
                        ## @default
                        ## @template{context{authentication{token{..},idToken{..},userInfo{..}}}} -> uint
                        #gid: ""
    
                ## Shell that should be used by the user.
                ## @template{context{authentication{token{..},idToken{..},userInfo{..}}}} -> path
                shell: "/bin/bash"
    
                ## Home directory where the user should be created. If not provided it will be always "/home/<username>".
                ## @default
                ## @template{context{authentication{token{..},idToken{..},userInfo{..}}}} -> path
                #homeDir: ""
    
                ## Template directory where to create the user's home directory. Defaults to "/etc/skel".
                ## @default
                ## @template{context{authentication{token{..},idToken{..},userInfo{..}}}} -> path
                #skel: ""
    
                ## Has to be true (after being evaluated) that the user is allowed to use this environment. This is by
                ## default always true.
                ## @template{context{authentication{token{..},idToken{..},userInfo{..}}}} -> bool
                loginAllowed: |
                    {{ and
                      (.authorization.idToken.groups | has "my-great-group-uuid")
                      (.authorization.idToken.tid    | eq  "my-great-tenant-uuid")
                    }}
    
                dispose:
                    ## Tell the environment to delete this user afterward, if it's corresponding session will be disposed.
                    ## @default
                    ## @template{context{authentication{token{..},idToken{..},userInfo{..}}}} -> bool
                    deleteManagedUser: true
    
                    ## Do also delete the user's home directory in this case.
                    ## @default
                    ## @template{context{authentication{token{..},idToken{..},userInfo{..}}}} -> bool
                    deleteManagedUserHomeDir: true
    
                    ## ... and kill all eventually running processes.
                    ## @default
                    ## @template{context{authentication{token{..},idToken{..},userInfo{..}}}} -> bool
                    killManagedUserProcesses: true
    
                ## Banner which will be shown to the user upon successfully start of its environment.
                ## @default
                ## @template{context{authentication{token{..},idToken{..},userInfo{..}}}} -> string
                banner: ""
    
                ## Defines if the user is also allowed to do port forwarding in this environment or not.
                ## @default
                ## @template{context{authentication{token{..},idToken{..},userInfo{..}}}} -> bool
                portForwardingAllowed: true
    
        - ## Authorize using the local users and their public keys (if possible) and connect them to the local environment.
    
            name: local
    
            requirement:
                ## Defines what the requesting (<requesting-name>@<host>) should match. If empty everything will be
                ## included. Important keep ^ and $ to ensure a full match, otherwise it matches only a part of it.
                ## @default
                ## @regex
                includedRequestingName: ""
    
                ## Defines what the requesting (<requesting-name>@<host>) should NOT match. If empty everything will
                ## still be included. Important keep ^ and $ to ensure a full match, otherwise it matches only a
                ## part of it.
                ## @default
                ## @regex
                excludedRequestingName: ""
    
            authorization:
                type: local
    
                ## Defines which pam service to be used to validate the password (and more). If empty /etc/shadow will be
                ## used directly.
                ## @default
                ## @string
                pamService: ""
    
                password:
                    ## If enabled the user can authenticate itself via password (if available). If false it is always
                    ## rejected.
                    ## @default
                    ## @template{context{remoteName}} -> bool
                    allowed: true
    
                    ## Enables also interactive way of query the password.
                    ## @default
                    ## @template{context{remoteName}} -> bool
                    interactiveAllowed: true
    
                    ## If true also empty password are allowed. Be aware: This is highly dangerous!
                    ## @default
                    ## @template{context{remoteName}} -> bool
                    emptyAllowed: false
    
            environment:
                ## In this example we'll use the local environment.
                type: local
    
                ## As createIfAbsent and updateIfDifferent both are false by default as a consequence the user always
                ## has to exist to be able to use this environment. As the authorization is based on a local user, this
                ## makes fully sense in this example.
                #createIfAbsent: false
                #updateIfDifferent: false
    
                ## The username of the user that should be taken. In this example the user has to exist.
                ## @template{context{authentication{user{name,uid,group,..}}}} -> username
                name: "{{.authorization.user.name}}"
    
                ## Has to be true (after being evaluated) that the user is allowed to use this environment. This is by
                ## default always true.
                ## @template{context{authentication{user{name,uid,group,..}}}} -> bool
                loginAllowed: |
                    {{ or
                      (.authorization.user.group.name | eq "ssh" )
                      (.authorization.user.groups     | firstMatching `{{.name | eq "ssh"}}`)
                    }}
    
                dispose: { }
                  ## These properties have no affect, as this user is never managed (authorization: type=local) in this
                  ## case, although they are all true.
                  #deleteManagedUser: true
                  #deleteManagedUserHomeDir: true
                #killManagedUserProcesses: true
    
                ## Banner which will be shown to the user upon successfully start of its environment.
                ## @default
                ## @template{context{authentication{user{name,uid,group,..}}}} -> string
                banner: ""
    
                ## Defines if the user is also allowed to do port forwarding in this environment or not.
                ## @default
                ## @template{context{authentication{user{name,uid,group,..}}}} -> bool
                portForwardingAllowed: true
    
    ## The housekeeping is running in the background asynchronously and continuously. It does free up resources like
    ## dispose sessions, environments and authorizations.
    housekeeping:
        ## How often the housekeeping should run.
        ## @default
        ## @duration
        every: 10m
    
        ## How long should be waited upon start of the application before the first run. If 0 it is also blocking,
        ## before even the first connection will be accepted.
        ## @default
        ## @duration
        initialDelay: 0
    
        ## If true the service will try to repair maybe corrupt/broken states by itself, if safe possible.
        ## @default
        ## @bool
        autoRepair: true
    
        ## For how long a disposed session will be kept. The session will no longer be usable, but it might be
        ## helpful for audit reasons.
        ## @default
        ## @duration
        keepExpiredFor: 336h # 14 days