This release includes 4 security fixes for security teams reviewing exposed deployments.
Published 3mo
Containers & Orchestration
✓ No known CVEs patched
This release patches 4 known CVEs
Topics
agplv3
docker
docker-deployment
docker-management
docker-management-tool
docker-manager
+12 more
docker-swarm
docker-ui
go
goland
moby
postgresql
self-hosted
swarm
templ
ui
usulnet
webui
Affected surfaces
auth
Summary
AI summaryGuacamole‑based full RDP web sessions are added with WebSocket proxy and defaults enabled.
Full changelog
Changelog
usulnet v26.2.2 — Beta — 2026-02-13
Added
- RDP Web Sessions: Full web-based RDP client via Apache Guacamole daemon (guacd), with WebSocket proxy in Go backend, guacamole-common-js canvas rendering, fullscreen mode, Ctrl+Alt+Del injection, clipboard sync, and auto-resize
- Configurable Docker Socket Path: New
docker.socketconfig option,USULNET_DOCKER_SOCKET/DOCKER_SOCKETenv vars, and automatic socket detection for rootless Docker setups. When not explicitly configured, usulnet auto-detects the socket by checkingDOCKER_HOST,/var/run/docker.sock,$XDG_RUNTIME_DIR/docker.sock,/run/user/<UID>/docker.sock, anddocker context inspect— eliminating the hard-coded/var/run/docker.sockdependency that broke rootless Docker deployments - guacd Core Service: Apache Guacamole daemon now ships as a default service alongside PostgreSQL, Redis, and NATS — no
--profile rdpflag required
Fixed
- RDP Session Drops After ~7 Seconds (close 1005): Two causes — (1) The guacd→WebSocket relay forwarded raw TCP chunks that could end mid-instruction; the Guacamole JS parser calls
close("Incomplete instruction.")on any partial data, killing the tunnel immediately. Fixed by buffering reads and only sending complete instructions (up to the last;boundary). (2) The Guacamole JS tunnel sends internal ping instructions (0.,4.ping,…) every 500ms to keep its 15-second receive-timeout alive; these were forwarded to guacd (which ignores them) and never echoed back. Fixed by intercepting empty-opcode messages and echoing them to the browser - RDP Canvas Hidden Behind Background in Non-Fullscreen Mode: The
#rdp-displaycontainer useddisplay: blockin windowed mode butdisplay: flexonly in fullscreen, causing the Guacamole canvas element to be obscured by the parent's background. Fixed by applyingdisplay: flex; align-items: center; justify-content: centerin both modes, addingz-index: 1to.guac-display, and usingx-cloakon overlays to prevent a flash of loading/error UI before Alpine.js initializes - Node Detail Page Missing Docker Engine Fields: Version, Storage Driver, Logging Driver, Cgroup Driver, Default Runtime, OS Type, and Architecture showed "—" because these fields were never mapped through the chain: Docker SDK
system.Info→docker.DockerInfo→models.HostDockerInfo→DockerInfoView→ template. Added field mapping across all four layers and fallback to live Docker info when database values are empty - False Update Alerts for
latest-Tagged Containers: Registry clients (Docker Hub, GHCR, Generic) now return the digest of thelatesttag itself instead of resolving it to the highest semver tag, eliminating false positives where "latest" vs "1.27.0" always triggered an update notification - Container Digest Not Populated in Batch Checks:
ContainerInfo.Digestnow sourced from Docker'sImageIDfield, enabling accurate digest-based comparison forlatest-tagged images - CSRF Validation on Git Integration Forms: The Gitea list and repo detail handlers used
ctx.Value("csrf_token")with a plain string key instead of the typedContextKey, causing the CSRF token to always be empty; replaced withh.getCSRFToken(r) - Container Bulk Action Form Missing CSRF Token: Added
csrf_tokenhidden input to the bulk-action form for defense in depth (was relying solely on HTMX global header injection) - Guacamole JS "module is not defined" in Browser: The bundled
guacamole-common.min.jswas the CJS (Node.js) build ending withmodule.exports=Guacamole;— browsers have nomoduleglobal, causingReferenceError. Stripped the CJS export since the file already setsGuacamoleas a global via IIFE - RDP WebSocket Rejected by Browser (missing subprotocol):
guacamole-common-jsopens WebSocket with"guacamole"subprotocol but the Go upgrader didn't negotiate it, causing the browser to drop the connection immediately. Added dedicatedrdpUpgraderwithSubprotocols: ["guacamole"] - RDP Session: guacd Response Not Forwarded to Browser: After completing the Guacamole handshake (select→args→connect), the Go handler started the relay loop but never forwarded guacd's initial
readyresponse to the JS client, so the client never transitioned to CONNECTED state - RDP Session Dies After ~5 Seconds: Two causes — (1) Missing
VERSION_1_5_0protocol version parameter caused guacd to fall back to a legacy protocol incompatible with the 1.5.0 JS client; (2)http.Server.WriteTimeoutdeadline was inherited by the hijacked WebSocketnet.Conn, killing the connection after the server's 30s write timeout. Fixed by adding the version parameter to the handshake and clearing deadlines withSetDeadline(time.Time{})after upgrade - Monaco Editor "Save & Commit" CSRF Failure: The
doCommit()fetch POST in the Monaco editor did not include a CSRF token, causing the server to return a 403 plain-text response which the JS then failed to parse as JSON (Unexpected token 'C'). Added CSRF token from<meta name="csrf-token">to both theX-CSRF-Tokenheader andcsrf_tokenform field, and improved error handling to checkresp.okbefore callingresp.json() - Backup Creation Fails with NOT NULL Constraint: Migration 006 defined
name VARCHAR(255) NOT NULLandtarget_type VARCHAR(50) NOT NULLon thebackupstable, but the Go model and repository layer usetarget_nameandtypecolumns instead — the legacy columns were never populated, causing every INSERT to fail. Added migration 030 to drop orphaned columns (name,target_type,storage_path,storage_type) frombackupsand (name,target_type,storage_type,retention_count) frombackup_schedules. Also fixed the backup creation form to sendtarget_name(human-readable name) instead of only the raw container/volume/stack ID getUserID()Always Returned Nil: The helper usedr.Context().Value("user_id")with a plain string key, but the auth middleware stores the user under the typedContextKey("user")as*UserContext; replaced withGetUserFromContext()— fixes Git connection creation, GitOps sync, and enterprise handlers all receiving zero UUID forcreated_byunknown provider type: Gitea:NewProvider()factory matched provider types case-sensitively; any casing variation (e.g."Gitea"vs"gitea") fell through to the error default — now normalizes to lowercase before matching- Guacamole JS Not Copied into Docker Image: Dockerfile only copied
style.cssandfavicon.icointo the runtime stage;web/static/js/(containingguacamole-common.min.js) was never included, causing a 404 in production
Changed
- HOST_TERMINAL_ENABLED defaults to
true: Host terminal and host files browser are now enabled by default without requiring manual environment variable configuration - GUACD_ENABLED defaults to
true: RDP web access is enabled by default across all deployment modes (docker-compose.yml, docker-compose.dev.yml, docker-compose.prod.yml) NeedsUpdate()Logic: Handleslatest=latestwith no digests as "no update needed" instead of always reporting an update- Guacamole JS Bundled Locally:
[email protected]now served from/static/js/guacamole-common.min.jsinstead of an external CDN, eliminating external dependency for RDP web sessions
Security
- CSRF token extraction in Git integration handlers now uses the proper typed context key, preventing token bypass on connection creation, sync, test, and delete operations
getUserID()now uses the typedContextKeyviaGetUserFromContext(), preventing user identity bypass that causedcreated_byforeign key violations- Monaco editor
doCommit()andsaveSnippet()now include CSRF tokens, preventing form submission bypass from the code editor - Database migration 030 removes legacy NOT NULL columns that were never populated, eliminating a denial-of-service vector on backup creation
Full Changelog: https://github.com/fr4nsys/usulnet/compare/v26.2.1...v26.2.2
Full Changelog: https://github.com/fr4nsys/usulnet/compare/v26.2.1...v26.2.2
Security Fixes
- CSRF token extraction fixed in Git integration handlers – prevents bypass on connection creation/sync/delete.
- getUserID() now uses typed context key – prevents user identity bypass causing foreign‑key violations.
- Monaco editor commit/save actions now include CSRF tokens – blocks form submission bypass.
- Migration 030 removes unused NOT NULL columns, eliminating a denial‑of‑service vector on backup creation.
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About fran-olivares/usulnet
All releases →Related context
Related tools
Beta — feedback welcome: [email protected]