Skip to content

Local environment

A local environment is executed on the host itself (same host on which Bifröst is running).

Currently, we support different variants provided by the host operating system which is executing the environment.

Type identifier is local.

Linux

The Linux variant is only supported by Linux based operating systems.

It can run as the Bifröst user itself, but can also impersonate another user.

Note

If impersonating another user Bifröst is running at, root permissions are required.

User requirement

Users have to fulfill the defined requirements (name, displayName, uid, group, groups, shell, homeDir and skel).

If a user does not fulfill this requirement they are not eligible for the environment. The environment can create a user (createIfAbsent = true) or even updates an existing one (updateIfDifferent = true) to match this requirement. This does not make a lot of sense for local users; but for users authorized via OIDC - which usually do not exist locally.

See the evaluation matrix of createIfAbsent and updateIfDifferent to see the actual reactions of the local environment per users requirement evaluation state.

Configuration

type

Environment Type = "local"

Has to be set to local to enable the local environment.

loginAllowed

bool Authorization = true

Has to be true (after being evaluated) that the user is allowed to use this environment.

Examples
  1. Require that the existing local user has the group ssh:

    1
    2
    3
    4
    5
    loginAllowed: |
       {{ or
           (.authorization.user.group.name | eq "ssh" )
           (.authorization.user.groups     | firstMatching `{{.name | eq "ssh"}}`)
       }}
    

  2. Require that the user authorized via OIDC has in the group my-great-group-uuid and the tenant ID (tid) in this OIDC ID token:

    1
    2
    3
    4
    5
    loginAllowed: |
       {{ and
          (.authorization.idToken.groups | has "my-great-group-uuid")
          (.authorization.idToken.tid    | eq  "my-great-tenant-uuid")
       }}
    

name

string Authorization

The username the user should have. Empty means this requirement won't be evaluated or applied (in case of creation/modification of a user).

Examples
  1. Use the name of the local user:
    1
    name: "{{.authorization.user.name}}"
    
  2. Use the email address of the user authorized via OIDC:
    1
    name: "{{.authorization.idToken.email}}"
    
  3. Always use foobar:
    1
    name: "foobar"
    

displayName

string Authorization

The display name (or title or GECOS) the user should have.

Examples
  1. In case of local user should be never be defined.
  2. Use the e-mail address of the user authorized via OIDC:
    1
    displayName: "{{.authorization.idToken.name}}"
    
  3. Always use Foobar:
    1
    displayName: "Foobar"
    

uid

uint32 Authorization

The UID (user identifier) the user should have. Empty means this requirement won't be evaluated or applied (in case of creation/modification of a user).

Examples
  1. Use the name of the local user:
    1
    uid: "{{.authorization.user.uid}}"
    
  2. In case of users authorized via OIDC this should usually not be defined.
  3. Always use 123:
    1
    uid: 123
    

group

The primary group the user should have. Empty means this requirement won't be evaluated or applied (in case of creation/modification of a user).

Examples
  1. If local user is used, this should usually not be defined.
  2. Assign always group with name oidc in case of users authorized via OIDC:
    1
    2
    group:
      name: "oidc"
    

groups

[]Group

The groups (do not confuse with the primary group) the user should have. Empty means this requirement won't be evaluated or applied (in case of creation/modification of a user).

Examples
  1. If local user is used, this should usually not be defined.
  2. Assign always group with name oidc in case of users authorized via OIDC:
    1
    2
    groups:
      - name: "oidc"
    

shell

File Path Authorization = "/bin/sh"

The shell the user should have. Not defined means this requirement won't be evaluated or applied (in case of creation/modification of a user).

homeDir

File Path Authorization = "/home/<user.name>"

The home directory the user should have. Not defined means this requirement won't be evaluated or applied (in case of creation/modification of a user).

skel

File Path Authorization = "/etc/skel"

If a new user needs to be created in a directory on the Bifröst hosts, it will receive its initial files of its home directory from (= user's home skeleton/template directory).

createIfAbsent

string Authorization = false

Will create the local 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 immediately.

This property (together with updateIfDifferent) has to be true if you're using authorizations like OIDC, where the user is not expected to exist locally, and you don't want to create each user individually.

Evaluation
createIfAbsent = false = true
Exists and matches Accepted Accepted
Exists, but does not match Does not apply Does not apply
Does not exist Rejected Created and accepted

updateIfDifferent

bool Authorization = false

If an existing user does not match the provided requirements (see below) and the property is true, this user is asked to match the requirements.

This property (together with createIfAbsent) should be true if you're using authorizations like OIDC, where the user is not expected to exist locally, and you don't want to create each user individually.

Evaluation
updateIfDifferent = false = true
Exists and matches Accepted Accepted
Exists but does not match Rejected Modified and accepted
Does not exist Does not apply Does not apply

banner

string Authorization = ""

Will be displayed to the user upon connection to its environment.

Examples
  1. If local user is used, show its name in a message:
    1
    banner: "Hello, {{.authorization.user.name}}!\n"
    
  2. If users authorized via OIDC is used, show its name in a message:
    1
    banner: "Hello, {{.authorization.idToken.name}}!\n"
    

portForwardingAllowed

bool Authorization = true

If true, users are allowed to use SSH's port forwarding mechanism.

dispose

Defines what happens if an environment is disposed.

Examples

  1. Use existing UNIX user:
    1
    2
    type: local
    name: "{{.authorization.user.name}}"
    
  2. OIDC - create/modify user if absent/different and cleanup automatically:
     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
    type: local
    
    ## Ensure users get created/modified if absent/different...
    createIfAbsent: true
    updateIfDifferent: true
    
    ## Use the email address of the OIDC's ID token
    name: "{{.authorization.idToken.email}}"
    
    ## Use the display name of the OIDC's ID token
    displayName: "{{.authorization.idToken.name}}"
    
    groups:
      ## Ensure user has always the group `oidc` assigned for better access control
      ## on the host itself.
      - name: oidc
    
    shell: "/bin/bash"
    
    ## Only allow login if the OIDC's groups has "my-great-group-uuid"
    ## ...and the tid (tenant ID) is "my-great-tenant-uuid"
    loginAllowed: |
        {{ and
          (.authorization.idToken.groups | has "my-great-group-uuid")
          (.authorization.idToken.tid    | eq  "my-great-tenant-uuid")
        }}
    

Group

name

string Authorization

The name the group should have. Empty means this requirement won't be evaluated or applied (in case of creation/modification of a user).

Examples
  1. In case of local user this should usually not be used.
  2. Use the email address of the user authorized via OIDC always set the name oidc:
    1
    name: "oidc"
    

gid

uint32 Authorization

The GID (group identifier) the group should have. Empty means this requirement won't be evaluated or applied (in case of creation/modification of a user).

Examples
  1. Always use 123
    1
    gid: 123
    

Dispose

Defines the behavior of an environment on disposal (cleanup).

deleteManagedUser

bool Authorization = true

If true the environment will also delete users, created/managed by it. Usually, if createIfAbsent and updateIfDifferent is both false this has no effect.

deleteManagedUserHomeDir

bool Authorization = true

In combination with deleteManagedUser, if true the environment will also delete the user's home directory.

killManagedUserProcesses

bool Authorization = true

In combination with deleteManagedUser, if true the environment will also kill all user's running processes.

Windows

The Windows variant is only supported by Windows 7+ based operating systems.

Warning

In contrast to the Linux version this variant CANNOT impersonate. As a consequence, each user session always executes as the user the Bifröst process itself runs with.

Impersonating on a Windows machine requires either full credentials (password) or another running process the session tokens can be cloned from. As both conflicts how we intend Bifröst to work, both solutions leave a lot of use-cases behind. Since it is very "hacky", we decided to stick with the simple approach.

Configuration

type

Environment Type = "local"

Has to be set to local to enable the local environment.

loginAllowed

bool Authorization = true

Has to be true (after being evaluated) that the user is allowed to use this environment.

Examples
  1. Require that the user authorized via OIDC has in the group my-great-group-uuid and the tenant ID (tid) in this OIDC ID token:
    1
    2
    3
    4
    5
    loginAllowed: |
       {{ and
          (.authorization.idToken.groups | has "my-great-group-uuid")
          (.authorization.idToken.tid    | eq  "my-great-tenant-uuid")
       }}
    

banner

string Authorization

Will be displayed to the user upon connection to its environment.

Examples
  1. If users authorized via OIDC is used, show its name in a message:
    1
    banner: "Hello, {{.authorization.idToken.name}}!\n"
    

shellCommand

string Authorization =

["C:\\WINDOWS\\system32\\cmd.exe"]

The shell which is used to execute the user's session.

execCommandPrefix

string Authorization =

["C:\\WINDOWS\\system32\\cmd.exe", "/C"]

The executor command prefix which is used when a user executes a command instead of executing into a shell.

If the user will execute ssh foo@bar.com echo "bar" on the host C:\WINDOWS\system32\cmd.exe /C 'echo "bar"' will be executed.

directory

"<working directory of Bifröst>"

The working directory in which the command will be executed in.

portForwardingAllowed

bool Authorization = true

If true, users are allowed to use SSH's port forwarding mechanism.

Examples

  1. Simple:
    1
    type: local
    
  2. OIDC:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    type: local
    
    ## Use the PowerShell Core without banner as Shell
    shellCommand: ["pwsh.exe", "-NoLogo"]
    directory: "C:\\my\\home"
    
    ## Only allow login if the OIDC's groups has "my-great-group-uuid"
    ## ...and the tid (tenant ID) is "my-great-tenant-uuid"
    loginAllowed: |
        {{ and
          (.authorization.idToken.groups | has "my-great-group-uuid")
          (.authorization.idToken.tid    | eq  "my-great-tenant-uuid")
        }}
    

`## Compatibility

linux windows
/ /
`