Agentless OpenSSH Nodes
Mezite can broker SSH sessions to machines that run plain OpenSSH — no mezd agent installed. The proxy reaches the node directly on its sshd port, presents the user's Mezite-issued SSH certificate, and records the
session. The node keeps its existing sshd and just needs to trust
the cluster's User CA.
This is the right path when:
- The host is a legacy or appliance you cannot install a Go binary on (an embedded device, a vendor-managed jumpbox, a router).
- A regulated environment forbids new daemons on production hosts.
- You want to bring an existing fleet of OpenSSH servers under Mezite RBAC/recording without changing the on-host process model.
Agent-based mezd nodes are still the default — they give you
reverse-tunnel reachability (no inbound port 22), session moderation, host-user
provisioning, and PAM hooks. Use agentless only when those trade-offs are
worth it.
How agentless mode works
-
The node's
sshdis configured withTrustedUserCAKeyspointing at the Mezite User CA public key, so it accepts SSH user certificates issued by the cluster. -
The node is registered in the cluster with
mezctl nodes add --openssh, which records the dial-address (host:port), labels, and the node's SSH host-key fingerprint. -
When a user runs
msh ssh node-name, the proxy opens a TCP connection to the node'ssshd, presents the user's certificate, and verifies the node's host key against the pinned fingerprint. - Session recording still happens — the proxy is the in-line MITM for agentless flows, so terminal I/O is captured at the proxy and uploaded to the auth service.
Prerequisites
- A running Mezite cluster with a reachable proxy.
-
Network path from the proxy to the node's
sshd(the proxy must be able to dial the node — agentless mode does not use a reverse tunnel). -
mezctland an admin session token. -
Root or sudo on the node to update
sshd_configand drop a CA file.
Step 1: Export the cluster's User CA bundle
sshd needs to trust the cluster's User CA so it accepts user
certificates signed by Mezite. Export the public key from the proxy and copy
it to the node.
# Plain SSH authorized_keys format, suitable for TrustedUserCAKeys.
# Works without authentication — this is intentionally a public endpoint.
curl -fsSL https://mezite.example.com/v1/ca/user.pub \
-o /tmp/mezite-user-ca.pub
# Sanity check: should be one line starting with 'ssh-...'
cat /tmp/mezite-user-ca.pub On a cluster that rotates the User CA, the endpoint returns the full current trust bundle (one key per line) — both the active key and any keys still in the standby set during a multi-phase rotation. Always pull the current bundle when you re-deploy.
Step 2: Configure sshd on the node
Copy the CA bundle to the node and reference it from sshd_config.
# Install the CA bundle
sudo install -m 0644 /tmp/mezite-user-ca.pub /etc/ssh/mezite-user-ca.pub
# Tell sshd to trust certs signed by this CA
sudo sed -i \
-e '/^TrustedUserCAKeys/d' \
-e '$a\TrustedUserCAKeys /etc/ssh/mezite-user-ca.pub' \
/etc/ssh/sshd_config
# (Optional) accept the cluster's host-CA-signed host cert if you mint one
# Otherwise the proxy uses a pinned host key (Step 3).
# Reload sshd so the new TrustedUserCAKeys takes effect
sudo systemctl reload sshd
# or: sudo service ssh reload Once sshd is reloaded, a user holding a Mezite certificate can SSH directly to the node — but you still want everything routed through the proxy for RBAC enforcement and recording, so do not open port 22 to user networks. Restrict inbound 22 at the firewall so only the proxy can reach it.
Step 3: Capture the node's host key
Mezite pins the node's sshd host-key fingerprint at enrollment
time. On reconnect the proxy verifies the live key matches the pin — a mismatch
is treated as the node being replaced (or a MITM) and the connection is refused.
Grab the key before enrolling.
# Most distributions ship Ed25519 host keys today
sudo cat /etc/ssh/ssh_host_ed25519_key.pub
# Older boxes or hardened images may have RSA only
sudo cat /etc/ssh/ssh_host_rsa_key.pub Step 4: Register the node
Use mezctl nodes add --openssh to record the node in the cluster.
Pass either the host key inline or via a file.
# Inline (authorized_keys format)
mezctl nodes add \
--openssh \
--name=db-legacy-01 \
--host=db-legacy-01.internal \
--port=22 \
--labels=env=production,role=db,managed-by=ansible \
--host-key="ssh-ed25519 AAAAC3Nz...comment" \
--verify-host-key
# Or from a file copied off the node
mezctl nodes add \
--openssh \
--name=db-legacy-01 \
--host=db-legacy-01.internal \
--port=22 \
--labels=env=production,role=db \
--host-key-file=/tmp/db-legacy-01-host.pub \
--verify-host-key
# Confirm
mezctl nodes get db-legacy-01
Labels are the same shape as agent-based nodes — RBAC roles match
against them in allow.node_labels. Pick a labelling
convention that lets you write
allow.node_labels: { env: production }-style rules
without referencing the agent-vs-agentless distinction (Mezite treats
them as one inventory).
Step 5: Connect
From a workstation, the agentless node behaves like any other registered node:
# Show up in inventory
msh ls
# Open a session — same flags as agent-based nodes
msh ssh ubuntu@db-legacy-01
# One-shot remote command
msh ssh db-legacy-01 -- systemctl status postgresql
# Recursive copy
msh scp -r ./schema/ ubuntu@db-legacy-01:/var/lib/postgresql/migrations/ Rotating the host key
If the node's sshd host key is regenerated (e.g. re-imaged VM,
host-key rotation script), the pinned fingerprint in the cluster no longer
matches and the proxy refuses to dial the node. Re-pin with mezctl nodes rotate-host-key.
# Inline
mezctl nodes rotate-host-key db-legacy-01 \
--host-key="ssh-ed25519 AAAAC3Nz...new-comment"
# Or from a file
mezctl nodes rotate-host-key db-legacy-01 \
--host-key-file=/tmp/db-legacy-01-host-new.pub
# Confirm the new fingerprint is recorded
mezctl nodes get db-legacy-01 Treat host-key rotation as an operational event: it should coincide with an explicit change-management entry, not happen silently. The audit log records the rotation with the operator identity that performed it.
Removing an agentless node
mezctl nodes rm db-legacy-01
After deregistration, revert the node's sshd_config
(remove the TrustedUserCAKeys line) and remove the CA file at
/etc/ssh/mezite-user-ca.pub. The firewall rule that limited
port 22 to the proxy should stay — you don't want to re-open SSH to user
networks.
Limitations vs agent-based nodes
Agentless mode trades flexibility for "no daemon on the box". What you lose:
- Reverse tunnel. The proxy has to dial the node, so the
node must accept inbound TCP on its
sshdport from the proxy. Behind NAT or restrictive egress, this might not be possible. - Host-user auto-provisioning.
create_host_userneeds an agent on the node to runuseradd/usermod/sudoconfiguration. Agentless nodes are stuck with whichever local accounts already exist on the box. - PAM hooks. Mezite's
MEZITE_PAM_SERVICEhook fires inside the agent's session-setup path. Agentless sessions terminate at the node's ownsshd, which runs PAM independently — the cluster cannot push PAM configuration onto the node. - Enhanced (BPF) command capture. The per-command capture loop runs in the agent process and watches the session shell's process tree. Agentless sessions still record the full terminal I/O at the proxy, but not the per-command structured stream.
- Moderated sessions / session join.
require_session_joinandrequire_session_mfaare enforced at the proxy and work for agentless flows;session-joinfor a peer to actively share the PTY is best supported via the agent-based path.
Troubleshooting
-
proxy: dial tcp: connection refused— The proxy could not reach the node's port. Check the recorded address withmezctl nodes getand verify firewall rules between the proxy and the node. -
ssh: handshake failed: host key mismatch— The node's host key has changed since enrollment. Confirm withssh-keyscanfrom the proxy host, then re-pin withmezctl nodes rotate-host-key. -
Permission denied (publickey)— The node'ssshddoes not yet trust the cluster CA. Re-checkTrustedUserCAKeysinsshd_configand reloadsshd. Confirm the requested login exists on the box and is allowed byAllowUsers/AllowGroups. - See the Agentless OpenSSH section of the Troubleshooting guide for the longer recipe list.
Next steps
- SSH Access — Day-to-day usage, flags, and config that applies to agent and agentless nodes alike.
- RBAC — Restrict which nodes the agentless inventory exposes to which roles.
- Audit Logging — Query proxy-side audit events for agentless sessions.