OpsNexus 1.4: full-screen Remote Desktop and terminal in the browser
OpsNexus 1.4 opens SSH terminals and Windows Remote Desktop sessions in a browser tab, in full screen, without passwords or keys ever reaching the browser.
Most teams that run servers keep a second set of tools just to reach them: an SSH client with a pile of keys, a Remote Desktop client, a VPN, maybe a jump host. Credentials end up on laptops, and nobody can say who connected to which server last Tuesday.
OpsNexus 1.4 brings both kinds of access into the control room. Add a server under Servers & SSH, click it, and you get a terminal or a Windows desktop in a browser tab. The release also makes both of them full screen. This post explains how it works and why it is built the way it is.
Two kinds of server, one list
Every server now has a type: SSH terminal or Remote Desktop (RDP). The default port follows the type (22 or 3389). You sign in with a credential from Secrets & variables, or with a password or private key stored for that server in the secret store.
For Remote Desktop you can also set:
- a domain. If you type
CORP\svc-buildas the username, OpsNexus splits off the domain for you; - the security mode: any, NLA, TLS or RDP;
- whether to accept a self-signed certificate, which most Windows servers use.
Remote Desktop signs in with a password, so the form only offers password credentials for RDP servers.
How the SSH terminal works
The terminal is xterm.js in the browser. When you open it, the browser asks the API for a session:
POST /api/remote/hosts/{id}/sessions -> single-use ticket, valid 60 seconds
WebSocket /api/ws/terminal/{ticket} -> API --SSH--> server:22The API is the SSH client. It reads the password or key from the secret store, signs in to the server, and relays bytes between the WebSocket and a real PTY, including window resizes.
Two choices here are about security:
- The browser never sees the secret. Not the password, not the key. It only ever holds a ticket.
- The access token never appears in a WebSocket URL. URLs end up in proxy logs. The ticket works once, for 60 seconds, and is useless after that.
Host keys are trusted on first use and pinned, like known_hosts. If a server later presents a different key, the connection is blocked with a message that shows the trusted and the new fingerprint. Someone with full access to Cloud has to reset the key on the server's page. That is a deliberate speed bump: a changed key means the server was rebuilt, or something is sitting in the middle.
How Remote Desktop works
RDP is a much bigger protocol than SSH, so we didn't write our own client. OpsNexus uses Apache Guacamole's guacd, which now runs as a service in the Docker Compose stack, and guacamole-common-js draws the desktop in the browser.
The interesting part is where the handshake happens. A plain Guacamole setup lets the browser send the connection parameters. In OpsNexus, the API is the tunnel:
- The browser opens the same kind of ticketed WebSocket, using the
guacamolesubprotocol. - The API connects to
guacd, selects RDP, and answers its argument list with the host, port, username, domain, security mode, screen size and the password from the secret store. - After
guacdreports ready, the API relays instructions in both directions.
So the password goes from the secret store to guacd inside your network, and never to the browser. guacd has no published port; only the API talks to it.
The desktop tab follows the window size, copies text from the server's clipboard, and has Paste to server and Ctrl+Alt+Del buttons.
Full screen, done properly
Full screen is the headline of 1.4, and it took more than calling requestFullscreen().
On a desktop, the toolbar hides in full screen so you get the whole display. Move the mouse to the top edge and it slides back. The terminal gets a full-screen button too.
The harder problem is the keyboard. In a normal browser tab, Esc leaves full screen and the Windows key opens your own Start menu, not the server's. For Remote Desktop, OpsNexus uses the Keyboard Lock API in Chromium-based browsers when you enter full screen. Esc, the Windows key and shortcuts such as Alt+Tab then go to the server. To leave full screen, hold Esc. In browsers without Keyboard Lock, full screen still works, but those keys stay with your computer.
A small fix for a common mistake
A very common first attempt is to add a Windows server as SSH on port 3389. That fails, because nothing speaks SSH there. The error now says so: port 3389 is Remote Desktop, so either change the server's type, or keep SSH and use port 22 with the Windows OpenSSH Server feature.
If you have permission to edit the server, the failed terminal also shows a Switch to Remote Desktop button. One click changes the type, and the page reloads as a desktop. The saved password works for both.
Every session is accounted for
Every terminal and desktop is a session record: who opened it, from which address, to which server, when it started and ended, and how much data moved. Opening and ending sessions go to the audit log.
Sessions close on their own:
# .env
TERMINAL_IDLE_MINUTES=30 # no keyboard input for this long
TERMINAL_MAX_HOURS=8 # maximum length, whatever happens
TERMINAL_CONNECT_TIMEOUT_SECONDS=15Admins can see open sessions and end other people's. Each person can only have a limited number open at once, and sessions whose API process died are cleaned up automatically.
Permissions use the Cloud module:
| Level | What it allows |
|---|---|
| read | See servers and sessions |
| modify | Add and edit servers, connect |
| full | Delete servers, reset host keys, end other people's sessions |
Upgrading
Remote Desktop needs the new guacd service, so pull the new release and start the stack again:
docker compose up -d --buildIf a desktop fails with "The remote desktop gateway (guacd ...) isn't reachable", start it with docker compose up -d guacd.
The Servers and SSH and Remote Desktop docs cover the setup in detail. The changelog has the full list of changes.