Proxied SSH sessions
The gateway fetches the password from the vault and signs in to the server for you, so the person doing the work never learns it. How to connect, and the limits.
This is the capability the privileged access module exists for. When an engineer needs a root shell on a Linux server, the usual arrangement is that somebody tells them the password. From that moment the password is in a person's head, a password manager, a chat message and possibly a text file on a laptop, and no rotation schedule can get it back. A proxied session removes that step: Qorionix opens the connection to the server on your behalf, using a secret you never see.
There is one thing to know before you invest ten minutes in this article, because it changes how you use the feature: the operator side of an SSH session is a plain TCP socket, not an SSH server. You do not connect to it with an SSH client. The reason is explained below, along with what it costs you.
What happens when you start a session
A privileged session is not a password lookup with extra steps. Four separate things happen, in this order, and the credential only comes out of the vault at the third.
You ask for a session
You name the credential you want to use and, if the policy on it requires one, an approved access request. See Access requests and approvals for that half.
Qorionix opens a listener just for you
It binds a fresh TCP port on the gateway host — an ephemeral port chosen by the operating system, one per session, and by default bound to the loopback address rather than to every interface. You get back a host, a port, an address and an expiry time.
You connect, and the gateway signs in upstream
The moment your connection is accepted, Qorionix decrypts the vaulted password, opens its own SSH connection to the target server, authenticates as the stored account, and starts a shell. Your bytes go into that shell's standard input and the shell's output comes back to you.
Everything is recorded, then sealed
The full session is buffered, encrypted with a per-session key, and stored alongside an index of what was typed and when. See Session recording and transcripts.
The password never travels over the connection you are holding. It travels from the vault, inside the gateway process, to the target. If you run a packet capture on your own laptop during a privileged session you will find your keystrokes and the server's output, and no credential.
How to actually connect
The endpoint you receive looks like a host and a port, and every instinct says to point an SSH client at it. Do not. There is no SSH server on that port. Qorionix does not present a host key to you, does not ask you for a username, and does not run an authentication exchange with you — the authentication that matters already happened when you asked for the session.
What is on the port is a byte pipe into a shell that is already open. So you connect with something that speaks raw TCP:
# correct
nc 127.0.0.1 49821
# wrong - the SSH client's version banner becomes the first line
# of shell input on the target
ssh [email protected] -p 49821That second command does not fail cleanly. An SSH client's first act is to send a version string, so the target's shell receives SSH-2.0-OpenSSH_9.6 as a command, reports that it cannot find it, and you are left staring at an error that appears to be about SSH when it is about the shell. If you see something like that, you used the wrong tool.
This design has an obvious question attached: if the gateway does not authenticate the operator in-protocol, what stops somebody else connecting to that port? Four things, none of them in the protocol:
- The session had to be granted. The access grant and the policy on the credential were evaluated before the port existed.
- The port is bound to loopback by default. Unless your deployment sets
PAM_GATEWAY_BIND_HOSTto something else, nothing off the gateway host can reach it at all. - The port is one-shot. The listener accepts exactly one connection and is then closed, so a second connection is refused by the operating system rather than queued.
- The port expires. If nobody connects within the connect window — two minutes by default — the listener is closed and the session ends with
gateway: no operator connected within 2m0s.
The terminal: a real PTY now exists, but plain nc still will not resize it
This section used to say the gateway opens a shell on the target with no pseudo-terminal at all, and that full-screen programs and password prompts that insist on a tty simply did not work. That has changed. The gateway now sends a real PTY request — with a terminal type and a starting size, xterm-256color at 80×24 by default — before it starts the shell, and fails the session closed if the target refuses the PTY rather than silently downgrading. A pseudo-terminal is what gives a remote shell line editing, job control and the ability of a program to take over the screen, and having one is what makes the following now work:
- Full-screen programs.
vi,nano,top,htop,lessand anything else that draws to the whole terminal now draw correctly, where before you got garbage or a hang. - Password prompts that insist on a terminal. A plain
sudonow has a tty to read from, so it prompts and accepts a password the way it would locally. - Ctrl-C, Ctrl-Z and ordinary job control, which need a real terminal's line discipline to deliver a signal at all — there was none to deliver one to before.
One gap in the same area is unrelated to the PTY and remains open: the target's error output is not captured. The gateway reads the shell's standard output and does not read its standard error, so error messages from commands you run are dropped — they do not reach you and they do not reach the recording. A command that fails can look, from inside the session and afterwards in the transcript, as though it produced nothing.
Proxied SSH sessions are now noticeably more usable for interactive work than they were, but for anything genuinely long and interactive — a diagnostic session where you will resize your terminal repeatedly, or work where you need to see a command's stderr — you may still prefer to check the credential out through the approval flow and connect directly, accepting that the audit trail is then weaker.
Also absent: SFTP and other SSH subsystems, port forwarding, agent forwarding, and running a single command as an exec channel rather than in a shell. Authentication to the target is by password only — the gateway does not offer a public key, a keyboard-interactive exchange or a certificate upstream, so an account that has password authentication disabled cannot be proxied.
The target's host key is checked
A host key is how an SSH client knows it is talking to the server it meant to talk to rather than to something in the middle. Earlier releases of the gateway did not check it, and earlier versions of this article said so. It does now.
The short version: the first time Qorionix reaches a given machine, the host key that machine presents is recorded. Every connection afterwards is checked against it, and a machine that ever presents a different key is refused — before any credential is decrypted or sent. A rebuilt server produces the same refusal as an interception, so a mismatch is something you have to account for rather than click past.
That is the whole protection in three sentences; the details of confirming a fingerprint, reading a mismatch and resolving a genuine rebuild are in SSH host-key pinning. The one navigational thing to carry over: the host keys live in an SSH host keys section on the Privileged access → Targets screen, below the targets table, not on a page of their own.
Database and web sessions are relayed, not injected
The same session machinery can front a database or an HTTPS target, and it is important to understand that these are a different, much weaker thing. For a database or https session the gateway opens a plain TCP tunnel to the target and copies bytes. It does not understand the protocol, so it cannot inject the credential — you still have to authenticate yourself, which means you still need the secret. The recording for those sessions is a byte count and a direction, and the session is marked with a negotiated security value of tcp-relay rather than an injected credential.
That difference is documented in the code rather than papered over, and we will not describe database sessions as credential-injected until they are. If your goal is to stop database passwords circulating, rotation is the lever that works today, not proxying.
Limits, clocks and concurrency
| Limit | Default | What happens |
|---|---|---|
| Connect window | 2 minutes | The listener closes and the session ends unused. This is the expires_at on the endpoint. |
| Maximum session duration | 8 hours | The session's clock runs out. The clock starts when the session is created, not when you connect. Treat this as a backstop rather than a guillotine: an idle session may not actually close until the next byte crosses it. |
| Idle timeout | None | An abandoned session stays open until the maximum duration expires or someone terminates it. There is no idle timeout at any layer. |
| Concurrent sessions per credential | 1 | A second request is refused with maximum concurrent sessions reached for this credential. |
Because there is no idle timeout, and because the eight-hour backstop does not reliably close an idle connection on its own, treat termination as your job rather than the platform's. Terminating a session is immediate and real — it drops the live sockets rather than only marking a row closed — so use it. Do not leave a privileged session to expire.
Checking the gateway is actually armed
The session gateway is a separate piece of wiring from the rest of the module, and a deployment can run with the vault and rotation working while nothing listens for sessions. The API logs which state it is in at start-up, and these are the three lines to look for:
pam: session gateway armed; privileged sessions are brokered
pam: session gateway unavailable; SSH/RDP proxying is NOT reachable
pam: NO SESSION BROKER - privileged sessions will be recorded but nothing will listenThe first is the healthy state. The third is the dangerous one to misread: session rows are created and look correct in the console, and no port is ever opened. If you are self-hosting and sessions never accept a connection, check this before anything else.
What to do next
- Read how a credential is requested and released — the approval side of the same workflow.
- Read what a recording contains before you rely on one as evidence.
- Confirm your recorded host-key fingerprints on the Targets screen, so that a future mismatch means something.
- Set the policy on the credential — duration, approval and recording are all policy, not per-session choices.