Skip to main content

Terminal RBAC

Terminal access is controlled by the terminal_spec field on roles. This follows Teleport's access control model.

Configuring via the Admin UI

  1. Go to Admin > Roles and edit a role
  2. Click the Terminal Access tab
  3. Click Enable Terminal Access (if not already enabled)
  4. Use the Form editor to configure:
    • Session Options — max TTL, idle timeout, concurrent session limit
    • Allow Rules — which Linux logins and host labels this role can use
    • Deny Rules — logins and hosts to explicitly block (overrides allow)
  5. Switch to JSON mode for advanced editing or to paste a spec directly
  6. Save the role — the backend validates the spec before storing
tip

When a user has multiple roles with terminal access, specs are merged automatically: options use the most restrictive values, allow rules are unioned (any role grants access), and deny rules are unioned (any role can deny).

Role Configuration

Each role can have a terminal_spec JSON object with three sections:

Options

FieldTypeDefaultDescription
max_session_ttlduration string8hMaximum session duration
idle_timeoutduration string30mDisconnect after inactivity
max_sessionsinteger10Max concurrent sessions per user
disconnect_timeoutduration string30sHow long PTY stays alive after disconnect (5s–5m)
create_host_user_modestringoffUser creation mode: off, keep (persist), drop (delete on disconnect)
host_groupsstring array[]Linux groups to add auto-created users to
host_sudoersstring array[]Sudoers rules written to /etc/sudoers.d/proxima-<username> (mode 0440, visudo-validated)
default_shellstring""Login shell for auto-created users (falls back to /bin/bash if binary not found)
uid_mininteger0Lower bound of deterministic UID range (must be ≥ 1000; 0 = disabled)
uid_maxinteger0Upper bound of deterministic UID range (must be > uid_min; range must span ≥ 100)
record_sessionsbooltrueRecord terminal sessions. When true, PTY output is captured in asciicast v2 format and uploaded to object storage.

Allow Rules

FieldTypeDescription
loginsstring arrayLinux users this role can assume
host_labelsobjectLabel matching rules (key → string or string array)

Deny Rules

Same structure as allow. Deny always overrides allow.

Example Configurations

Full access (admin)

{
"options": {"max_session_ttl": "12h", "idle_timeout": "1h", "max_sessions": 10, "disconnect_timeout": "60s"},
"allow": {"logins": ["root", "ubuntu", "deploy"], "host_labels": {"*": "*"}},
"deny": {}
}

Staging/dev only (developer)

{
"options": {"max_session_ttl": "4h", "idle_timeout": "15m", "max_sessions": 10, "disconnect_timeout": "30s"},
"allow": {"logins": ["deploy", "app-user"], "host_labels": {"env": ["staging", "dev"]}},
"deny": {"logins": ["root"], "host_labels": {"env": "production"}}
}

No terminal access (viewer)

Don't set terminal_spec, or set allow.logins to empty array.

Permissions

PermissionDescription
terminal:writeConnect to host terminals
terminal_sessions:readView all active and historical terminal sessions
terminal_sessions:writeForce-terminate active terminal sessions

Validation Rules

The backend validates terminal_spec on save:

FieldConstraint
max_session_ttlValid Go duration, 1m–24h
idle_timeoutValid Go duration, 1m–12h
max_sessionsInteger 1–100 (0 = default 10)
disconnect_timeoutValid Go duration, 5s–5m (0 = default 30s)
allow.logins / deny.loginsNon-empty strings, no whitespace, max 64 chars. * wildcard allowed
host_labels keysNon-empty strings
host_labels valuesString or array of strings. *: * wildcard allowed

Host User Auto-Creation

When create_host_user_mode is set to keep or drop, the agent automatically creates the Linux user on the target host if it doesn't exist.

Minimal K8s Nodes

Minimal Kubernetes distributions (RKE2, k3s, Talos) lack user management binaries. Host user creation, sudoers management, and custom shells are automatically disabled on these nodes. Connect as root or another existing user instead.

Modes

ModeBehavior
offNo auto-creation (default). User must exist on the host.
keepCreate user if missing. User persists after disconnect.
dropCreate user if missing. User deleted when all sessions end.

Host Groups

The host_groups option specifies which Linux groups the auto-created user is added to (e.g., docker, sudo). Non-existent groups are auto-created.

Tracking

Auto-created users are tracked via system groups:

  • proxima-keep — users created in keep mode
  • proxima-drop — users created in drop mode (cleaned up by agent)

Host User Options

Sudoers Management

Roles can define sudoers rules that are automatically written to /etc/sudoers.d/proxima-<username> when a user connects:

terminal_spec:
options:
create_host_user_mode: keep
host_sudoers:
- "ALL=(ALL) NOPASSWD: ALL"
- "ALL=(ALL) /usr/bin/systemctl"

Each rule is prefixed with the login username. The agent validates the file with visudo -cf before activating it. If validation fails, the file is removed and a warning is logged — the session is not blocked.

In drop mode, the sudoers file is deleted alongside the user.

Default Shell

Configure the login shell for auto-created users:

terminal_spec:
options:
default_shell: "/bin/zsh"

The agent checks that the shell binary exists before using it. If not found, falls back to /bin/bash with a warning.

Stable UID Ranges

Assign deterministic UIDs from a range so the same username always gets the same UID across all hosts (important for NFS/shared storage):

terminal_spec:
options:
uid_min: 10000
uid_max: 15000

UIDs are computed via FNV-1a hash of the username. If a UID collision occurs, the agent falls back to natural allocation.

Multi-Role Merge Semantics

When a user has multiple roles, options merge with least-permissive semantics:

FieldMerge Rule
create_host_user_modeMost restrictive: off > drop > keep
host_sudoersUnion of all entries (deduplicated)
host_groupsUnion of all groups
default_shellFirst non-empty value
uid_min / uid_maxIntersection (narrowest overlap)
max_session_ttlShortest
idle_timeoutShortest
max_sessionsSmallest
record_sessionsAny role enables → recording on (security wins)

Login Templates

Role terminal specs support Go template syntax in allow.logins and deny.logins to derive Linux usernames from SSO identity claims. Templates are resolved at connect time using the user's OIDC traits.

Template Variables

VariableDerivationExample
.emailUser's email from OIDC claims[email protected]
.email_prefixEverything before @j.yerzakov
.email_domainEverything after @proximaops.io
.usernameEmail prefix, dots/hyphens removedjyerzakov
.nameUser's display nameJamshid Yerzakov
.external.<claim>Any raw OIDC claim.external.hdproximaops.io

Template Functions

FunctionUsageDescription
lower{{lower .email_prefix}}Lowercase
upper{{upper .email_prefix}}Uppercase
replace{{replace .email_prefix "." "_"}}String replacement
trimPrefix{{trimPrefix "svc-" .username}}Trim prefix
trimSuffix{{trimSuffix "-admin" .username}}Trim suffix
default{{default "deploy" .username}}Fallback value
printf{{printf "prxm-%s" .username}}Formatted string

Example

terminal_spec:
allow:
logins:
- "{{.email_prefix}}" # j.yerzakov
- "{{.username}}" # jyerzakov
- "deploy" # static
- '{{printf "prxm-%s" .username}}' # prxm-jyerzakov
host_labels:
env: [staging, dev]
deny:
logins:
- "root"

Templates are validated at role save time (parse + dry-run with dummy data). Invalid templates are rejected with a clear error message. At connect time, resolved logins that don't match the Linux username pattern ([a-z_][a-z0-9_.-]*) are silently filtered out.

Authorization Flow

  1. User requests terminal access to a host
  2. Backend resolves user's role and terminal_spec
  3. Deny rules checked first — if login or host labels match deny, access denied
  4. Allow rules checked — login must be in allow list AND host labels must match
  5. If no rule matches, default deny