SSH Access

Mezite replaces static SSH keys with short-lived certificates issued on demand. Every connection is authenticated via a User CA and Host CA certificate pair, authorized against RBAC policies, routed through the proxy, and recorded for audit. No static keys are ever stored on servers or distributed to users.


How SSH Works Through Mezite

Traditional SSH relies on distributing public keys to ~/.ssh/authorized_keys on every server. Mezite eliminates this entirely with certificate-based authentication:

  • User CA — The Mezite auth service acts as a certificate authority. When a user logs in (via password, SSO, or MFA), Mezite issues a short-lived SSH certificate signed by the User CA. This certificate encodes the user's identity, allowed logins, and an expiration time.
  • Host CA — Each node agent receives a host certificate signed by the Host CA. Clients trust the Host CA, so there are no "unknown host" prompts and no TOFU (trust-on-first-use) vulnerabilities.
  • No static keys — Certificates expire automatically (typically within hours). There are no long-lived keys to rotate, revoke, or audit.

When you run msh ssh, the following sequence occurs:

  1. Authenticationmsh presents your user certificate (obtained during msh login) to the Mezite proxy on port 3023.
  2. Authorization — The proxy validates the certificate against the auth service and evaluates your RBAC roles to confirm you have access to the target node.
  3. Routing — The proxy locates the target node via its reverse tunnel connection (established by mezd on port 3024).
  4. Session establishment — The proxy forwards the SSH session through the reverse tunnel to the agent, which opens a local SSH connection on the target.
  5. Recording — The proxy captures the full terminal I/O stream and writes it for later playback.
Connection flow text
workstation                 Mezite proxy              target node
     |                           |                          |
     |-- msh ssh --login=user node ----->|                          |
     |   (user cert + request)   |                          |
     |                           |-- reverse tunnel ------->|
     |                           |   (proxied SSH session)  |
     |                           |                          |
     |<-- interactive session -->|<-- agent SSH session --->|
     |   (recorded by proxy)     |                          |

Prerequisites

  • A running Mezite cluster (mezhub with auth and proxy services enabled).
  • The msh client CLI installed on your workstation.
  • The mezd binary available on the target node.
  • Network connectivity from the target node to the Mezite proxy on port 3024 (reverse tunnel).

Agent Setup

Generate a Join Token

Before a node can join the cluster, you need a one-time join token. Generate one with mezctl:

Generate join token bash
# Generate a token valid for 1 hour
mezctl tokens create --roles=node --ttl=1h

# Output:
# Token: a1b2c3d4e5f6...
# Expires: 2026-03-24T15:00:00Z

Install and Configure the Agent

On the target node, install mezd and configure it to connect back to the Mezite proxy via a reverse tunnel.

Install agent bash
# Download the signed agent binary (Linux amd64)
curl -fsSL https://releases.mezite.com/latest/mezite-linux-amd64.tar.gz \
  | tar -xz -C /usr/local/bin/ mezd

Configure the agent via environment variables (e.g. in /etc/mezite/agent.env):

/etc/mezite/agent.env bash
MEZITE_AUTH_ADDR=mezite.example.com:3025
MEZITE_PROXY_ADDR=mezite.example.com:3024

# Join token (used once for initial registration)
MEZITE_JOIN_TOKEN=a1b2c3d4e5f6...

# Node metadata
MEZITE_NODE_NAME=web-server-01
MEZITE_NODE_LABELS=env=production,team=platform,os=ubuntu

Start the Agent (Reverse Tunnel)

The agent establishes a persistent reverse tunnel to the Mezite proxy on port 3024. This tunnel is how the proxy routes SSH sessions to the node, even if the node is behind a firewall or NAT.

Start the agent bash
# Start with systemd
sudo systemctl enable mezd
sudo systemctl start mezd

# Verify the agent registered
mezctl nodes ls
# NAME              ADDR            LABELS
# web-server-01     10.0.1.50:22    env=production,team=platform,os=ubuntu

Using msh ssh

Log In and List Nodes

Login and list nodes bash
# Log in to Mezite (obtain user certificate from the User CA)
msh login --proxy=mezite.example.com:3080 --user=alice

# List available nodes (filtered by your RBAC permissions)
msh ls
# NAME              ADDR            LABELS
# web-server-01     10.0.1.50:22    env=production,team=platform,os=ubuntu
# web-server-02     10.0.1.51:22    env=production,team=platform,os=ubuntu
# staging-01        10.0.2.10:22    env=staging,team=platform,os=ubuntu

Connect by Name

SSH by node name bash
# Connect to a specific node
msh ssh --login=ubuntu web-server-01

# Run a one-off remote command
msh ssh --login=ubuntu web-server-01 -- uptime

# Connect using the user@host shorthand
msh ssh ubuntu@web-server-01

Connect by Label

Use the --labels flag to target nodes by their metadata labels instead of by name. If multiple nodes match, msh will prompt you to choose.

SSH by label bash
# Connect to any node with env=staging
msh ssh --login=ubuntu --labels=env=staging

# Connect with multiple label selectors (AND logic)
msh ssh --login=deploy --labels=env=production,team=platform

# List nodes matching labels first
msh ls --labels=env=production,team=platform
# NAME              ADDR            LABELS
# web-server-01     10.0.1.50:22    env=production,team=platform,os=ubuntu
# web-server-02     10.0.1.51:22    env=production,team=platform,os=ubuntu

Specify Login User

The --login flag specifies which OS user to authenticate as on the remote node. The login must be listed in your role's allowed_logins field.

Login flag examples bash
# Explicit login flag
msh ssh --login=deploy web-server-01

# user@host shorthand
msh ssh deploy@web-server-01

# If your role uses template variables, your Mezite username may work:
msh ssh --login=alice web-server-01

SCP File Transfers

Use msh scp to transfer files through the Mezite proxy. All transfers are authenticated with your certificate and logged in the audit trail.

File transfer with msh scp bash
# Upload a file to the remote node
msh scp ./deploy.tar.gz ubuntu@web-server-01:/tmp/

# Download a file from the remote node
msh scp ubuntu@web-server-01:/var/log/app.log ./

# Recursive directory upload
msh scp -r ./config/ ubuntu@web-server-01:/etc/app/

# Transfer between two remote nodes
msh scp ubuntu@web-server-01:/tmp/data.tar.gz deploy@staging-01:/tmp/

SSH ProxyCommand Integration

If you prefer to use the native ssh client (for editor integration, Ansible, or other tooling), configure msh as a ProxyCommand. This routes your native SSH sessions through the Mezite proxy with full certificate auth and audit.

~/.ssh/config bash
# Route all connections to *.mezite through the Mezite proxy
Host *.mezite
    ProxyCommand msh proxy ssh --proxy=mezite.example.com:3080 %r@%h:%p
    UserKnownHostsFile /dev/null
    StrictHostKeyChecking no

# Then use native ssh:
# ssh ubuntu@web-server-01.mezite

You can also generate this configuration automatically:

Generate SSH config bash
# Generate SSH config for all nodes you have access to
msh config --proxy=mezite.example.com:3080 >> ~/.ssh/config

# Now use native ssh directly
ssh ubuntu@web-server-01

This is particularly useful for tools like rsync, sshfs, and Ansible that rely on the native ssh binary.


Environment Variables in Sessions

Mezite injects several environment variables into SSH sessions that identify the user and session context:

VariableDescriptionExample
MEZITE_USERAuthenticated Mezite usernamealice
MEZITE_SESSIONUnique session IDa1b2c3d4-e5f6-7890
MEZITE_CLUSTERCluster nameproduction
MEZITE_LOGINOS login used for the sessionubuntu

These variables are useful for audit scripts, shell prompts, and logging within the session.


Port Forwarding

Mezite supports local and remote port forwarding through the SSH tunnel. Port forwarding must be enabled in the user's role (see the port_forwarding session option).

Port forwarding bash
# Local port forwarding: access remote port 5432 on localhost:15432
msh ssh -L 15432:localhost:5432 ubuntu@db-server-01

# Remote port forwarding: expose local port 3000 on the remote node
msh ssh -R 8080:localhost:3000 ubuntu@web-server-01

# Dynamic SOCKS proxy
msh ssh -D 1080 ubuntu@web-server-01

File Transfer (SCP and SFTP)

Use msh scp to transfer files through the Mezite proxy. The syntax mirrors standard scp.

File transfer bash
# Copy a file to the remote node
msh scp ./deploy.tar.gz ubuntu@web-server-01:/tmp/

# Copy a file from the remote node
msh scp ubuntu@web-server-01:/var/log/app.log ./

# Recursive directory copy
msh scp -r ./config/ ubuntu@web-server-01:/etc/app/

# SFTP interactive session
msh sftp ubuntu@web-server-01

Session Recording and Playback

All SSH sessions are automatically recorded by the agent (mezd). The agent captures terminal I/O at the PTY level — the clean text you see in your terminal, not encrypted SSH protocol bytes. Recordings are uploaded to the auth service via gRPC and stored in the configured backend (local filesystem or S3).

No additional configuration is required — recording is on by default in node-sync mode, which streams recordings to the auth service in real-time.

Session playback bash
# List recent sessions
msh sessions ls
# SESSION ID                            USER   NODE          LOGIN  STARTED               ENDED
# a1b2c3d4-e5f6-7890-abcd-ef1234567890  alice  web-server-01  ubuntu 2026-04-11T10:28:35Z  2026-04-11T10:41:09Z

# Play back a session in your terminal
msh play a1b2c3d4-e5f6-7890-abcd-ef1234567890

# Play back at 2x speed
msh play --speed=2 a1b2c3d4-e5f6-7890-abcd-ef1234567890

Administrators can view all sessions; regular users can only view their own. See the Session Recording guide for recording modes, storage backends, and encryption configuration.


Advanced Configuration

Custom SSH Port

If your target node runs SSH on a non-standard port, configure it in the agent:

Custom SSH port yaml
# In /etc/mezd/mezite.yaml
ssh_port: 2222

Enhanced Session Recording (eBPF)

For compliance requirements, you can enable enhanced session recording which uses eBPF to capture individual commands executed within the session. This requires Linux and a privileged container or root access.

Enhanced recording with eBPF bash
# Enable eBPF command capture on the agent
MEZITE_BPF_ENABLED=true mezd start

Session Moderated Access

Status: Available — Moderated sessions are implemented. Sessions with require_session_join policies block until the required moderators join via the web API. Moderator leave terminates the session.

For sensitive environments, require a second user to observe or approve sessions in real time:

Moderated sessions role yaml
kind: role
metadata:
  name: ssh-sensitive
spec:
  allow:
    node_labels:
      env: production
      sensitivity: high
    logins:
      - root
    require_session_mfa: "totp"
    require_session_join:
      - name: observer
        roles:
          - auditor
        count: 1
        modes:
          - observer

Troubleshooting

Agent fails to join

  • Verify the join token has not expired: mezctl tokens ls
  • Check network connectivity from the agent to the proxy on port 3024.
  • Review agent logs: journalctl -u mezd -f

Connection refused or timeout

  • Confirm the node appears in msh ls. If not, the agent may not be connected.
  • Check that your role grants access to the node's labels and the login you are using.
  • Verify your user certificate is valid: msh status

Permission denied

  • The login (root, ubuntu, etc.) must be listed in your role's logins field.
  • The node's labels must match your role's node_labels selector.
  • Check if a deny rule is overriding your allow rule: mezctl users show alice

Next Steps