Repository navigation
bug(ansible/vnc): SecurityTypes=VeNCrypt makes VNC unreachable by every client; websockify can't read its TLS key; role provisions on unselected hosts #13060
Description
Activity
- addedbugSomething isn't workingSomething isn't working
on Jul 30, 2026 Follow-up note: VncAuth truncates the password to 8 characters
Recording this alongside the switch to
VncAuthin #13061 so it is not lost.The role generates a ~16-character secret (
openssl rand -base64 12 | tr -dc 'a-zA-Z0-9' | head -c 16,tasks/main.yml:60) and stores it in/etc/autobot/vnc-secrets.env. Butvncpasswd -fwrites an RFB VncAuth password, and that scheme silently keeps only the first 8 characters — a DES key-schedule limit in the protocol, not a bug in this role.Effective strength is therefore ~47 bits, not what the stored length suggests. Two consequences:
- The full 16-char value in
vnc-secrets.envand in the SLM credential record is not what actually guards the port; only its first 8 chars are. - This is a further argument for keeping
TLSVnc/X509Vncin the offer list (fix(ansible/vnc): offer VncAuth so clients can connect, give VNC its own TLS key, stop provisioning on unselected hosts (#13060) #13061) so capable native viewers get a TLS-wrapped channel, and for not exposing the raw RFB port more widely than needed.
Not expanded into #13061 to keep that PR scoped to the three reproduced defects. Worth a separate decision on whether to move the desktop path off VncAuth entirely (X509Vnc-only, or front it with the already-TLS websockify hop exclusively).
- The full 16-char value in
Fix opened as #13061 (branch
issue-13060, baseDev_new_gui) — all three defects addressed in one PR.- added a commit that references this issue
on Jul 30, 2026 github-actions commented
on Jul 30, 2026 on Jul 30, 2026 – with GitHub ActionsContributorMore actionsClosed by #13061 — squash-merged to
Dev_new_guiasdf0c203a4.Closing manually because
Closes #Nonly fires on the default branch.Verified on
Dev_new_guiafter merge$ git log --oneline -1 origin/Dev_new_gui df0c203a4 fix(ansible/vnc): offer VncAuth so clients can connect, give VNC its own TLS key, stop provisioning on unselected hosts (#13060) (#13061) # 1. security type roles/vnc/defaults/main.yml:36 vnc_security_type: "{{ lookup('env','VNC_SECURITY_TYPE') | default('VncAuth,TLSVnc,X509Vnc', true) }}" # 2. dedicated VNC keypair roles/vnc/templates/vnc-websockify.service.j2:22-23 --cert={{ vnc_tls_cert }} \ --key={{ vnc_tls_key }} \ # 3. explicit activation (both duplicated copies) role_vnc_active: >- {{ ('vnc' in (node_roles | default([]))) or (inventory_hostname in (groups.get('vnc_nodes', []) | default([]))) }}CI
Head
57f825bc7, latest-run-per-check: 0 checks non-success and non-skipped. Merged via--squash --admin— the only blocker wasREVIEW_REQUIREDfrom CODEOWNERS* @mrveiss, which makes self-review impossible.Tests
Both role-active-facts playbooks passed across all 9 synthetic hosts (
failed=0) on the group_vars and vars_files loading paths. A negative control forcingrole_vnc_active=truefailed the flipped assertion, proving it is live rather than silently skipped.Still open from this issue
- Migration: any backend/main host that relied on implicit activation must now list
vncinnode_roles(or joinvnc_nodes) to keep its desktop provisioned —api/vnc_manager.py:85pgreps forXtigervncon the canonical display. Not applied to any live inventory here; do that per-fleet. - VncAuth 8-char truncation (comment above) — deliberately not folded into fix(ansible/vnc): offer VncAuth so clients can connect, give VNC its own TLS key, stop provisioning on unselected hosts (#13060) #13061. Open as a separate decision if the desktop path should move off VncAuth entirely.
- Migration: any backend/main host that relied on implicit activation must now list
- added a commit that references this issue
on Jul 31, 2026 Correction to finding #2 — the "websockify could never start" claim was overstated
While investigating an unrelated incident I observed the live host directly, and it contradicts what I asserted here.
I claimed websockify could never start because it runs as
vnc_userwhile the shared node key isroot:root 0600. I took the ownership from documentation —playbooks/setup-internal-ca.yml:435describes node keys asroot:root 0600— rather than from the filesystem.Observed reality on a provisioned host:
$ ls -l /etc/autobot/certs/server-key.pem -rw------- 1 autobot autobot 1704 /etc/autobot/certs/server-key.pem $ ps -eo user,cmd | grep websockify autobot /usr/bin/python3 /usr/bin/websockify --web=/usr/share/novnc \ --cert=/etc/autobot/certs/server-cert.pem \ --key=/etc/autobot/certs/server-key.pem --ssl-only 0.0.0.0:6080 localhost:5901The key is owned by
autobot, notroot, and websockify is running fine with--ssl-only.What was accurate: the vnc role's own generation path (
roles/vnc/tasks/main.yml) runschmod 0600underbecome: truewith nochown, so when that path creates the file it landsroot:rootand websockify genuinely cannot read it. On this host something else —setup-internal-caor the backend role — created it asautobotfirst, so the role'screates:-guarded generation never ran and the latent problem never surfaced.So the correct statement is: the failure is conditional on which path generates the shared key first, not the universal "could never start" I wrote. Any host where the vnc role wins that race gets a broken
--ssl-onlyunit; hosts where another role wins are fine.No change to the fix. The dedicated
vnc_user-owned keypair shipped in #13061 makes this deterministic regardless of ordering, and still avoids widening access to a key that nginx, Redis and PostgreSQL share. The remedy stands; only my justification for it was wrong.Consolidation follow-up filed as #13148 — the divergence that caused this issue (Ansible role on
VeNCryptwhile all five other launchers were onVncAuth) is one of three hand-synchronised duplications, including therole_*_activefacts this fix had to edit in two places.- added a commit that references this issue
on Sep 8, 2026
Summary
Deploying the Ansible
vncrole produces a VNC server no client can connect to, a websockify unit that cannot read its own TLS key, and installs the whole desktop stack on hosts that never selected the role. Three defects, all inautobot-slm-backend/ansible/.1.
SecurityTypes=VeNCrypt— no client can complete the handshake (blocking)roles/vnc/defaults/main.yml:23defaultsvnc_security_typetoVeNCrypt, rendered byroles/vnc/templates/vnc-config.j2:12into~/.vnc/config. TigerVNC then advertises only security type 19.Observed on a real deploy:
novnc)std_vnc_auth,tight_auth,xvp_authTwo independent reasons this is wrong, not just an old-client problem:
roles/vnc/tasks/main.yml:79runsvncpasswd -f, which writes a VncAuth-format password file. The role sets up VncAuth auth and then configures a security type that never reads it.VeNCryptnames only the wrapper, not a sub-type.TLSVnc/X509Vnc/Plainare the sub-types. With none enabled the sub-type list is empty, so even a VeNCrypt-capable client (noVNC >= 1.4.0, TigerVNC viewer) has nothing to negotiate. Upgrading noVNC alone does not fix this.This is a canonical divergence — the Ansible role is the only launcher in the repo that does this. Every other one uses VncAuth:
autobot-backend/api/vnc_manager.py:143—VncAuth,TLSVncautobot-infrastructure/shared/scripts/infrastructure/browser-vnc.service:18—VncAuth,TLSVncautobot-infrastructure/shared/scripts/setup_browser_vnc.sh:57—VncAuth,TLSVncautobot-infrastructure/shared/scripts/utilities/fix-vnc-desktop.sh:58—VncAuthdocs/guides/vision-vnc-ui-testing.md:2316—VncAuth,TLSVncThis also explains intermittent behaviour: starting VNC through the backend API passes
-SecurityTypes VncAuth,TLSVncon the CLI and works, whileroles/vnc/templates/vnc-server.service.j2:21passes no-SecurityTypes, so~/.vnc/configwins at boot and the server comes up unreachable.VeNCrypt buys nothing on this hop regardless:
roles/vnc/templates/vnc-websockify.service.j2:19-21already terminates TLS for the browser (--cert/--key/--ssl-only), androles/vnc/tasks/register-credentials.yml:70registers the websockify port, not the raw RFB port. The raw hop is loopback websockify -> VNC.Fix: default
vnc_security_typetoVncAuth,TLSVnc,X509Vnc.2. websockify cannot read its own TLS key —
--ssl-onlyunit fails to startroles/vnc/templates/vnc-websockify.service.j2runs asUser={{ vnc_user }}(line 14) but reads/etc/autobot/certs/server-key.pem(line 20). That is the shared node key:roles/vnc/tasks/main.yml:125chmods it0600with no chown, andplaybooks/setup-internal-ca.yml:435documents node keys asroot:root 0600.An unprivileged
vnc_usercannot read a root-owned0600key, so the--ssl-onlywebsockify unit dies on startup.Fix: generate a dedicated VNC keypair owned by
vnc_user, rather than loosening permissions on the shared node key that nginx, Redis, and PostgreSQL also consume. This also givesX509Vncfrom #1 a cert/key the VNC server can actually read.3.
role_vnc_activeprovisions VNC on hosts that never selected itReported from a live deploy: the VNC stack landed on a host without
vncin itsnode_roles.Duplicated in two places —
playbooks/vars/role_active_facts.yml:71-75andinventory/group_vars/all.yml:346-350:Any host in the
mainorbackendgroup pulls in tigervnc, x11vnc, novnc, websockify and xfce4 withvncabsent fromnode_roles. VNC is the outlier:role_xrdp_active, defined immediately below, has no equivalent group fallback.Fix (owner decision: explicit only): drop both the
mainandbackendfallbacks in both copies, so VNC provisions only on'vnc' in node_rolesor membership invnc_nodes— matchingrole_xrdp_active.Migration note: backend/main hosts that relied on implicit activation must add
vncto theirnode_rolesto keep the desktop provisioned.autobot-backend/api/vnc_manager.py:85pgreps forXtigervncon the canonical display, so AdvancedControl/desktop on such a host needs the role explicitly selected from now on.Verification plan
ansible-lint+yamllinton the changed role and vars filesvnc-config.j2/vnc-websockify.service.j2and assertSecurityTypes=VncAuth,TLSVnc,X509Vncand avnc_user-readable key pathrole_vnc_activeis false for a host inbackendwithoutvncinnode_roles, and true whenvncis present (thetests/playbooks/test_role_active_facts*.ymlharness already covers this shape)