RBAC Configuration

Mezite uses role-based access control (RBAC) to govern who can SSH into which nodes. Roles define allow and deny rules that match against node labels, specify allowed OS logins, and set session options. This guide covers the role system in depth: structure, label matching, deny-overrides-allow semantics, template variables, built-in roles, and practical examples.


Role Structure

A Mezite role has three main sections: allow, deny, and options. The allow and deny sections specify which nodes a user can or cannot access. Options control session behavior.

Role anatomy yaml
kind: role
version: v1
metadata:
  name: example-role
  description: "Human-readable description of this role"
spec:
  options:
    # Maximum session TTL for users with this role
    max_session_ttl: 12h
    # Whether to require MFA for SSH sessions ("", "off", "totp", "hardware_key", ...)
    require_session_mfa: ""
    # Allow port forwarding
    port_forwarding: true
    # Allow SCP/SFTP file transfers
    file_copy: true

  allow:
    # Label selectors — which nodes this role grants access to
    node_labels:
      env: staging
    # Which OS logins are permitted
    logins:
      - ubuntu
      - deploy

    # Roles this user may request via an access request (optional)
    request_roles:
      - ssh-production

    # Roles whose requests this user may review/approve (optional)
    review_roles:
      - ssh-production

  deny:
    # Deny rules override allow rules
    node_labels:
      sensitivity: restricted
    logins:
      - root

Label-Based Matching

Labels are key-value pairs attached to nodes via the agent configuration. Roles grant or deny SSH access based on label selectors.

Exact Match

Exact label match yaml
# Matches nodes with env=production AND team=backend
node_labels:
  env: production
  team: backend

Wildcard Match

Wildcard label match yaml
# Matches any value for the env label
node_labels:
  env: "*"

# Matches all nodes (any label, any value)
node_labels:
  "*": "*"

Multi-Value Match

Multi-value label match yaml
# Matches nodes where env is staging OR development
node_labels:
  env:
    - staging
    - development

Regex Match

Regex label match yaml
# Matches nodes where team starts with "eng-"
node_labels:
  team: "^eng-.*$"

When multiple labels are specified, all must match (AND logic). When multiple values are specified for a single label, any can match (OR logic).


Deny Overrides Allow

Mezite uses a deny-overrides-allow evaluation model. When a user has multiple roles, the system:

  1. Collects all allow rules from every role assigned to the user.
  2. Collects all deny rules from every role assigned to the user.
  3. Grants access only if at least one allow rule matches AND no deny rule matches.

This means a single deny rule in any role will block access, even if other roles explicitly allow it. Use deny rules sparingly and intentionally.

Deny overrides example yaml
# Role A: allows SSH to all production nodes
kind: role
metadata:
  name: ssh-all-production
spec:
  allow:
    node_labels:
      env: production
    logins:
      - ubuntu
      - deploy
  deny: {}

---
# Role B: denies SSH to PCI nodes
kind: role
metadata:
  name: deny-pci
spec:
  allow: {}
  deny:
    node_labels:
      compliance: pci
    logins:
      - root
      - ubuntu
      - deploy

# If a user has both Role A and Role B:
# - They CAN access env=production nodes (allowed by A)
# - They CANNOT access compliance=pci nodes (denied by B)
# - A production node with compliance=pci is DENIED (deny wins)

Allowed Logins

The logins field in a role specifies which OS-level usernames the Mezite user can authenticate as on remote nodes. The login requested at connection time (via msh ssh --login=) must appear in the allow.logins list and must not appear in the deny.logins list.

Allowed logins yaml
spec:
  allow:
    node_labels:
      env: production
    logins:
      - ubuntu
      - deploy
      - "{{internal.logins}}"  # Expands to the user's own Mezite username
  deny:
    logins:
      - root  # Never allow root, even if another role permits it

Template Variables

Roles support template variables that are expanded at evaluation time. This allows you to write generic roles that adapt to each user based on their identity and traits. The implementation lives in server/rbac/templates.go.

Variable Description Example Value
{{internal.logins}}Currently resolves to a single-element list containing the user's Mezite username — the auth server overrides any operator-supplied logins trait at certificate-issue time. Multi-login support via traits is not implemented yet.["alice"]
{{internal.<trait>}}Any other trait recorded on the user record{{internal.team}}platform
{{external.<trait>}}A trait propagated from an SSO connector (e.g. external.username, external.email)alice@example.com
{{email.local(external.email)}}Function: returns the local part of an email addressalice
Template variable usage yaml
kind: role
version: v1
metadata:
  name: team-scoped-ssh
  description: "SSH access scoped to the user's team nodes"
spec:
  allow:
    # Only access nodes labeled with the user's team trait
    node_labels:
      team: "{{internal.team}}"
    # Login as one of the user's recorded logins, or as the SSO username
    logins:
      - "{{internal.logins}}"
      - "{{external.username}}"
  deny: {}

When a user with the team=platform trait uses this role, the label selector expands to team: platform, restricting them to nodes owned by their team.


Session Options

The options section of a role controls session behavior. When a user has multiple roles, the most restrictive option value wins.

Option Type Default Description
max_session_ttlduration12hMaximum lifetime for user certificates
require_session_mfastring""MFA type required per-session
port_forwardingboolfalseAllow SSH port forwarding. Disabled unless at least one of the user's roles sets this to true.
file_copyboolfalseAllow SCP and SFTP file transfers. Disabled unless at least one of the user's roles sets this to true.
Restrictive session options yaml
kind: role
metadata:
  name: ssh-restricted
spec:
  options:
    max_session_ttl: 4h
    require_session_mfa: "totp"
    port_forwarding: false
    file_copy: false
  allow:
    node_labels:
      env: production
      sensitivity: high
    logins:
      - ubuntu
  deny:
    logins:
      - root

Built-in Roles

Mezite bootstraps four built-in roles on startup (see server/rbac/defaults.go). You can assign these directly or use them as templates for custom roles.

admin

Full cluster administrator. SSH access to every node, all administrative resources, and the ability to join any moderated session in any mode.

Built-in: admin yaml
kind: role
version: v1
metadata:
  name: admin
  description: "Full cluster administrator"
spec:
  options:
    max_session_ttl: 12h
    forward_agent: true
    port_forwarding: true
    file_copy: true
    record_session: best_effort
  allow:
    logins: [root, "{{internal.logins}}", testuser, ubuntu]
    node_labels:
      "*": "*"
    join_sessions:
      - name: admin-any-session
    rules:
      - resources: ["*"]
        verbs: ["*"]

editor

Can manage users, roles, tokens, and connectors. Read access to sessions, audit events, and nodes — but no SSH access by default.

Built-in: editor yaml
kind: role
version: v1
metadata:
  name: editor
  description: "Can manage users and roles but limited server access"
spec:
  options:
    max_session_ttl: 8h
  allow:
    rules:
      - resources: [user, role, token, connector]
        verbs: ["*"]
      - resources: [session, event, node]
        verbs: [read, list]

viewer

Read-only access to cluster state — users, roles, tokens, nodes, sessions, and audit events. Cannot SSH into nodes.

Built-in: viewer yaml
kind: role
version: v1
metadata:
  name: viewer
  description: "Read-only access to cluster state"
spec:
  options:
    max_session_ttl: 4h
  allow:
    rules:
      - resources: [user, role, token, node, session, event]
        verbs: [read, list]

ssh-access

Basic SSH access to non-production nodes. Allows logging in as the external SSO username or ubuntu on nodes labelled env=staging or env=dev; denies env=production.

Built-in: ssh-access yaml
kind: role
version: v1
metadata:
  name: ssh-access
  description: "Basic SSH access to non-production nodes"
spec:
  options:
    max_session_ttl: 8h
    forward_agent: true
    file_copy: true
    record_session: best_effort
  allow:
    logins: ["{{external.username}}", ubuntu]
    node_labels:
      env: [staging, dev]
  deny:
    node_labels:
      env: production

Creating Roles with mezctl

Use mezctl to create, update, and manage roles.

Role management commands bash
# Create a role from a JSON file (the --from-file flag is required)
mezctl roles create --from-file=ssh-production.json

# Or inline with --name and --spec
mezctl roles create --name=ssh-production --spec='{"options":{},"allow":{}}'

# List all roles
mezctl roles ls
# NAME            VERSION  DESCRIPTION
# admin           v1       Full cluster administrator
# editor          v1       Can manage users and roles but limited server access
# viewer          v1       Read-only access to cluster state
# ssh-access      v1       Basic SSH access to non-production nodes
# ssh-production  v1       SSH access to production nodes

# Show a specific role (full spec rendered as JSON)
mezctl roles get ssh-production

# Update a role from a file
mezctl roles update --from-file=ssh-production.json

# Delete a role
mezctl roles delete ssh-production

# Assigning roles to a user
# There is no in-place "update user roles" CLI today. The roles list is
# set when the user is created (mezctl users create --roles=...); to
# change a user's roles, modify the user via the gRPC API directly or
# recreate the user with the new role set.

Example Role Definitions

SSH to Production (Non-Root)

ssh-production.yaml yaml
kind: role
metadata:
  name: ssh-production
  description: "SSH access to production nodes, non-root"
spec:
  options:
    max_session_ttl: 8h
  allow:
    node_labels:
      env: production
    logins:
      - ubuntu
      - deploy
  deny:
    logins:
      - root
    node_labels:
      sensitivity: restricted

Team-Scoped Access with Template Variables

ssh-team-scoped.yaml yaml
kind: role
version: v1
metadata:
  name: ssh-team-scoped
  description: "SSH access restricted to the user's team nodes"
spec:
  options:
    max_session_ttl: 12h
  allow:
    node_labels:
      team: "{{internal.team}}"
    logins:
      - "{{internal.logins}}"
      - ubuntu
  deny:
    logins:
      - root

Staging-Only with File Copy Disabled

ssh-staging-readonly.yaml yaml
kind: role
metadata:
  name: ssh-staging-readonly
  description: "SSH to staging, no file transfers or port forwarding"
spec:
  options:
    max_session_ttl: 4h
    port_forwarding: false
    file_copy: false
  allow:
    node_labels:
      env: staging
    logins:
      - ubuntu
  deny: {}

Requestable Production Access

can-request-production.yaml yaml
kind: role
version: v1
metadata:
  name: can-request-production
  description: "Can request temporary production SSH access"
spec:
  allow:
    # No direct node access — only request permissions
    request_roles:
      - ssh-production
  deny: {}

Role Evaluation Order

When a user attempts to SSH into a node, Mezite evaluates roles in this order:

  1. Collect all roles assigned to the user.
  2. Merge all allow rules — the union of all allowed node labels and logins.
  3. Merge all deny rules — the union of all denied node labels and logins.
  4. Check if any allow rule matches the target node and requested login.
  5. Check if any deny rule matches the target node and requested login.
  6. Grant access only if step 4 is true and step 5 is false.

Debugging Role Evaluation

There is no dedicated mezctl access check command yet — debug role evaluation by inspecting the user and each of their assigned roles, and by watching the audit log for denial events (any access.denied* event includes the role and rule that caused the denial).

Inspect users and roles bash
# List users and the roles each one holds
mezctl users list
# USERNAME  ROLES                       STATUS
# alice     ssh-access,ssh-production   active

# Print the full spec for a role
mezctl roles get ssh-production

# Watch the audit log for denial events emitted at session setup
mezctl audit ls --type=access.denied --since=1h
mezctl audit ls --type=access.denied.port_forwarding --since=1h
mezctl audit ls --type=access.denied.file_copy --since=1h

Next Steps

  • SSH Access — Apply SSH-specific RBAC policies.
  • Access Requests — Set up approval workflows for elevated access.
  • SSO Setup — Map SSO attributes to Mezite roles automatically.
  • Audit Logging — Monitor role evaluation in audit logs.