Repository navigation
fix(ansible/vnc): offer VncAuth so clients can connect, give VNC its own TLS key, stop provisioning on unselected hosts (#13060) - #13061
Merged
Conversation
…own TLS key, stop provisioning on unselected hosts (#13060) Three defects in the Ansible vnc role, all reproduced on a live deploy. 1. SecurityTypes=VeNCrypt made the server unreachable by every client. The role provisions VncAuth credentials (vncpasswd -f) and then advertised only security type 19. Bare "VeNCrypt" names the wrapper with no sub-type, so the sub-type list is empty and even a VeNCrypt-capable client has nothing to negotiate; noVNC < 1.4.0 (apt novnc ships 1.0.0) cannot do VeNCrypt at all. Every other launcher in the repo already uses VncAuth -- api/vnc_manager.py, browser-vnc.service, setup_browser_vnc.sh, fix-vnc-desktop.sh, vision-vnc-ui-testing.md -- so the role was the lone outlier. Default is now VncAuth,TLSVnc,X509Vnc. This also explains intermittency: the backend API path passes -SecurityTypes on the CLI and worked, while the systemd unit passes none, so ~/.vnc/config won at boot and the server came up unreachable. 2. websockify could not read its own TLS key. The unit runs as vnc_user but pointed --cert/--key at the shared node keypair, which is root:root 0600 (setup-internal-ca.yml) and is also consumed by nginx, Redis and PostgreSQL. The --ssl-only unit could never start. VNC now gets a dedicated keypair owned by vnc_user instead of widening access to the shared key; the same pair backs the X509Vnc security type. 3. role_vnc_active pulled tigervnc, x11vnc, novnc, websockify and xfce4 onto every host in groups['main'] or groups['backend'], including hosts with no 'vnc' in node_roles. Both duplicated copies now gate on explicit selection only, matching role_xrdp_active. Migration: a backend/main host that relied on implicit activation must list 'vnc' in node_roles (or join vnc_nodes) to keep its desktop provisioned. Verification: both role-active-facts test playbooks pass across all 9 synthetic hosts (group_vars and vars_files paths); the flipped expectation is a live assertion, proven by a negative control forcing role_vnc_active=true; templates render SecurityTypes=VncAuth,TLSVnc,X509Vnc and a vnc_user-readable key path; deploy.yml passes --syntax-check; the openssl argv produces a valid keypair with the expected subject.
Contributor
✅ SSOT Configuration Compliance: Passing🎉 No hardcoded values detected that have SSOT config equivalents! |
This was referenced Jul 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #13060
Thinking Path
Started from a live deploy symptom: the server offered VeNCrypt only (type 19) while the
novncapt package ships 1.0.0, which supportsstd_vnc_auth/tight_auth/xvp_auth— VeNCrypt only arrived in noVNC 1.4.0.The obvious read is "upgrade noVNC". That would not have fixed it. Tracing the config chain showed the role sets
SecurityTypes=VeNCrypt, which names the VeNCrypt wrapper without any sub-type (TLSVnc/X509Vnc/Plain), leaving the sub-type list empty — so even a VeNCrypt-capable client has nothing to negotiate. The role also provisions a VncAuth password (vncpasswd -f), so it was configuring a security type that never reads the credentials it creates.Confirming it as divergence rather than intent: every other VNC launcher in the repo already uses VncAuth —
api/vnc_manager.py:143,browser-vnc.service:18,setup_browser_vnc.sh:57,fix-vnc-desktop.sh:58, anddocs/guides/vision-vnc-ui-testing.md:2316. The Ansible role was the lone outlier.That also explains any intermittency: starting VNC through the backend API passes
-SecurityTypes VncAuth,TLSVncon the CLI and works, while the systemd unit passes none — so~/.vnc/configwins at boot and the server comes up unreachable.Two further defects surfaced while fixing this and are folded in, per the fix-what-you-find rule.
What Changed
1. Security type —
VeNCrypt→VncAuth,TLSVnc,X509Vncroles/vnc/defaults/main.yml. noVNC connects via plain VncAuth; native viewers can negotiate the TLS-wrapped sub-types. VncAuth on the raw RFB hop is safe here because websockify already terminates TLS for the browser and reaches VNC over loopback, andregister-credentials.ymlregisters the websockify port, not the raw RFB port.2. Dedicated VNC TLS keypair
vnc-websockify.service.j2runs as{{ vnc_user }}but pointed--cert/--keyat/etc/autobot/certs/server-key.pem— documentedroot:root 0600insetup-internal-ca.yml:435and chmodded0600with no chown by this role. An unprivileged user cannot read it, so the--ssl-onlyunit could never start.Fixed with a dedicated pair (
vnc-cert.pem/vnc-key.pem) owned byvnc_user, rather than loosening the shared node key that nginx, Redis and PostgreSQL also consume. The same pair backs the newX509Vnctype. Node-level cert generation is left in place — a vnc-only node still needs one.3.
role_vnc_active— explicit selection onlyReported from the field: the VNC stack landed on a host with no
vncinnode_roles. Both duplicated copies (playbooks/vars/role_active_facts.yml,inventory/group_vars/all.yml) hadgroups['main']andgroups['backend']fallbacks, pulling in tigervnc, x11vnc, novnc, websockify and xfce4. VNC was the outlier —role_xrdp_active, defined immediately below, has no such fallback. Both now gate on'vnc' in node_rolesorvnc_nodesmembership.Important
Migration. A backend/main host that relied on implicit activation must now list
vncin itsnode_roles(or joinvnc_nodes) to keep its desktop provisioned. This matters whereapi/vnc_manager.py:85pgreps forXtigervncon the canonical display.Verification
Role-active-facts tests — both loading paths, all 9 synthetic hosts,
failed=0:Negative control — proves the flipped expectation is a live assertion, not a silently-skipped one:
Rendered templates:
Syntax / YAML:
ansible-playbook --syntax-check deploy.ymlpasses; all 6 changed YAML files parse.openssl argv smoke test (exact string the
commandmodule shlex-splits):Notes
vncpasswd -fwrites a VncAuth password, and the RFB VncAuth scheme truncates to 8 characters — so the ~16-char generated secret in/etc/autobot/vnc-secrets.envyields ~47 bits of effective strength regardless of its length. That is inherent to VncAuth, not introduced here, and is a further reason the TLS sub-types stay in the offer list. Noted on #13060 rather than expanded into this PR.Model Used
Claude Opus 5 (1M context)