Skip to content

Security: bytestrix/InfraCanvas

Security

SECURITY.md

Security Policy

Supported versions

Version Supported
Latest release
Older releases

Reporting a vulnerability

Please do not open a public GitHub issue for security vulnerabilities.

Report security issues by emailing security@bytestrix.com with:

  • Description of the vulnerability
  • Steps to reproduce
  • Potential impact
  • Any suggested fix (optional)

You will receive an acknowledgement within 48 hours and a full response within 7 days. We will coordinate a fix and disclosure timeline with you.

Scope

The following are in scope:

  • Authentication bypass in the relay server
  • Arbitrary command execution via the agent API
  • Sensitive data leakage (secrets not redacted before leaving the VM)
  • WebSocket message injection or spoofing

The following are out of scope:

  • Attacks that require physical access to the VM
  • Social engineering
  • Vulnerabilities in third-party dependencies (report those upstream)

Security model

InfraCanvas (OSS) runs as a single binary on each machine. The dashboard, relay, and agent all live in the same process. The two surfaces that need protection:

  • Exposed URL: by default the binary binds 127.0.0.1:7777 and a bundled cloudflared opens an outbound-only Cloudflare quick-tunnel that publishes a random https://*.trycloudflare.com URL. Traffic is HTTPS-terminated at Cloudflare's edge; no inbound firewall rule is required. The URL is unguessable but not secret; pair it with the auth token below. --no-tunnel binds 0.0.0.0:7777 directly (you allow the port in your cloud security group). --private binds 127.0.0.1 and you reach it through SSH tunnel.
  • UI auth token: every install generates a random 24-character token (saved in /etc/infracanvas/config.env). Required as a query param on first load, then stored in an HTTP-only cookie. WebSocket calls require it too.

Once authenticated, the dashboard can:

  • See the full topology of the host
  • Read container logs
  • Open a shell inside any container or on the host
  • Run Docker / Kubernetes actions (restart, scale, update image)

Treat the URL+token like an SSH key for the box.

  • Secret redaction: env vars whose names contain SECRET, TOKEN, KEY, PASSWORD, CREDENTIAL, AUTH, PASSWD are replaced with [REDACTED] before they leave the discovery layer.
  • No persistent storage of graph state: graphs are recomputed every refresh; the relay does not persist snapshots between restarts. The one exception is Clusters (see below), which by design persists a credential locally so the connection survives a restart.
  • Pair codes: generated by the agent for legacy shared-relay deployments. In the default local serve-mode the browser auto-pairs from loopback and pair codes are unused.
  • Clusters (kubeconfig direct-connect): an uploaded kubeconfig is written to <state dir>/clusters/<id>.kubeconfig at 0600, readable only by the user running infracanvas serve, and is never transmitted anywhere: it's read directly by the same process serving your dashboard. Deleting a cluster from the UI deletes this file. Anyone with shell access to the box as that user (or root) can read it, same as they already could read ~/.kube/config or any other credential on the machine; this introduces no new privilege beyond what local shell access already grants.
  • Read-only mode. --read-only blocks every mutating action, terminal/exec session, and (as of v0.18.1) cluster connect/disconnect, at the relay before it reaches the agent or any credential-executing code path. Viewing topology and logs still works. Use this for public demos.
  • Per-cluster read-only (v0.19.0+). Independent of the global --read-only flag, each Clusters connection has its own read-only toggle (set it when connecting, or flip it later), so you can run some clusters view-only while others stay fully interactive on the same dashboard. Enforced at the same relay layer as global read-only mode.
  • Permission preview (v0.19.0+). Before you commit to connecting a kubeconfig, POST /api/clusters/preview (surfaced in the Add Cluster dialog) runs a handful of SelfSubjectAccessReview checks against it (a standard Kubernetes API any authenticated credential can call about its own permissions) and shows what it can actually do (view, exec, restart, scale, read Secrets) before anything is persisted or connected.
  • Audit log (v0.19.0+). Every write action, terminal session, and read-only-blocked attempt is recorded to <state dir>/audit.log (append-only, 0600) and viewable from the dashboard's Audit tab or GET /api/audit. Entries are attributed by session/machine; OSS has one shared UI token authenticating the whole dashboard, no per-user login, so that's the honest ceiling on what can be logged here (the hosted SaaS product has real user accounts and its own per-user audit log).
  • Hub mode (--join). Joining agents authenticate with a shared join token, but that token alone no longer proves which machine a connection is. As of v0.18.1, the first time a machine ID connects it's issued a random resume secret; every later reconnect claiming that same machine ID must present it, or the connection is rejected outright rather than silently swapped in over a session with browsers already attached. /api/sessions (the machine roster) requires the dashboard's own UI token, not the shared join token.
  • Hardening (v0.19.1+). All token/secret comparisons (UI token, agent token, hub resume secret) are constant-time. Token generation fails closed instead of falling back to a hardcoded value if the system's secure random source ever errors. Every auth-checking endpoint has a per-IP rate limit (20 failed attempts/minute). WebSocket frames have a size cap to prevent a memory-exhaustion DoS. Every HTTP response carries CSP, X-Frame-Options, X-Content-Type-Options, and HSTS-when-TLS headers.

There aren't any published security advisories