Help us shape the future of Go! Take the 2026 Go Developer Survey

kubeworkspaces

package module
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 20, 2026 License: Apache-2.0 Imports: 44 Imported by: 0

README

Kube Workspaces API

License Go Version Release CI Docker Image

REST API service for managing Kubernetes workspaces, built with the Goa framework.

Overview

The API provides:

  • Workspaces - CRUD, start/stop/reset/reboot, logs, events, pod details
  • Volumes - PVC management (create, list, delete)
  • Images - List available workspace images with proxy configuration
  • SshKeys - CRUD of user SSH public keys (used to seed VM guest access)
  • Namespaces - List available Kubernetes namespaces (supports _all for cross-namespace listing)
  • Proxy - Reverse proxy to workspace web UIs with WebSocket support
  • Console/exec bridges - WebSocket bridges to workspace terminals, VM serial consoles, VM noVNC displays, and web SSH sessions (single-session with take-over)
  • Admin - Raw CRD browsing endpoints
  • Health - Health check endpoint
  • OpenAPI - Auto-generated OpenAPI 3.0 spec (JSON and YAML)

VM access

For spec.type: vm workspaces the API bridges WebSockets to KubeVirt subresources (and guest sshd), using a session registry so each bridge is single-session with an idle TTL and take-over consent:

  • GET /v1/workspaces/{name}/console — serial console (.../virtualmachineinstances/{name}/console)
  • GET /v1/workspaces/{name}/vnc — noVNC display (.../virtualmachineinstances/{name}/vnc)
  • GET /v1/workspaces/{name}/ssh — web SSH into the guest (dials the workspace Service; the browser supplies the private key transiently)
  • GET/POST .../console/status, .../console/takeover, .../ssh/status, .../ssh/takeover — session status + take-over
  • POST /v1/workspaces/{name}/reboot — delete the VMI (persistent root disk preserved)

Authentication configuration availability

Unauthenticated mode requires an existing AuthConfig named default with spec.enabled: false. A missing or unreadable AuthConfig, a missing/non-boolean spec.enabled, or an enabled configuration whose session signing key cannot be loaded does not disable authentication. Protected requests and /auth/me return 503 with error auth_unavailable when configuration resolution fails.

Configuration results, including failures, are cached for 30 seconds. After a failed refresh, stale configuration is not used to authorize requests. The last good signing-key state is retained internally so normal key-rotation continuity is preserved when configuration access recovers. Health, public version/schema and authentication routes remain reachable; authentication handlers validate their own prerequisites.

Authenticated requests require a current, enabled User CR. Its current role and namespace access replace stale token authorization data; configured admin emails remain an explicit override but cannot bypass a missing/disabled account. OIDC users must be pre-provisioned or successfully auto-provisioned before sign-in. New login credentials include the User UID, preventing them from adopting a replacement account with the same email. Legacy credentials remain accepted by existing routes but cannot authorize agent-backed machine execution.

Scoped VM execution authorization foundation

User.spec.executionGrants holds administrator-managed, additive grants with an exact namespace, optional exact workspaces, permissions, and optional RFC3339 expiresAt. Grants do not create namespace membership. Supported names:

  • workspace.exec and workspace.shell — ordinary command/script and shell access.
  • workspace.exec.elevated — separately required for root/SYSTEM targets.
  • workspace.execution.manage — manage others' executions.
  • workspace.execution.recordings.read — separate sensitive recording access.

Authenticated administrators can update grants through PUT /admin/users/{name} using executionGrants; send [] to revoke all grants. Unknown fields, wildcards, unknown permissions and malformed expiry values are rejected. Auth-disabled mode cannot write execution grants. Ordinary editor/viewer roles imply no grants.

Execution admission/renewal must use a user-bound credential and fresh current User data. PlatformConfig.spec.vmExecution.enabled and allowElevated are separate opt-in policy gates, read fresh by the execution policy provider. These foundations do not yet expose guest process execution: authenticated transport, full recording in the selected storage mode and runner lifecycle are additional mandatory admission requirements.

Execution storage configuration

The startup configuration loader recognizes KW_EXECUTION_STORAGE_MODE values disabled (the default), ephemeral, external, and bundled. Ephemeral mode needs no database, object store or persistent key; it configures a bounded temporary directory and history that may be lost on restart. External/bundled modes use PostgreSQL metadata/state plus S3-compatible recording storage and a stable recording key. Persistent configuration errors never select ephemeral.

Persistent modes require KW_EXECUTION_RECORDING_KEY_FILE, containing base64 text for a random 32-byte key. External mode uses a Secret-backed KW_EXECUTION_DATABASE_URL with sslmode=verify-full; bundled mode constructs a URL from separate KW_EXECUTION_PG_HOST, _PORT, _DATABASE, _USER, _PASSWORD values without string-interpolating credentials. S3 endpoint/bucket/region and credential settings use KW_EXECUTION_S3_*. Private S3 CAs can be supplied with KW_EXECUTION_S3_CA_FILE; HTTPS certificate verification is never disabled.

The bundled test profile explicitly opts into internal PostgreSQL/TCP and S3/HTTP through KW_EXECUTION_ALLOW_INSECURE_STORAGE_TRANSPORT=true; external mode also requires this opt-in for plaintext transport. Retention defaults are 720h for recordings and 8760h for metadata, independently configurable with KW_EXECUTION_RETENTION_RECORDINGS and KW_EXECUTION_RETENTION_METADATA (metadata must cover recording retention). Credentials and recording keys are redacted from ordinary configuration formatting/errors.

This is configuration validation, not implemented database migrations, recording writes or storage readiness. Execution must remain unavailable until those stores, recording admission and authenticated guest enforcement are integrated. Deployment profiles and Secret setup are documented in VM execution storage.

Browser WebSocket handshakes and state-changing requests require an exact origin from comma-separated ALLOWED_ORIGINS; CORS headers alone are not authorization. Cookie-authenticated mutations/handshakes without an Origin are rejected. Native bearer clients may omit Origin. Set this list to the frontend's actual origin (including scheme and non-default port); host/forwarded headers and wildcard or suffix matches cannot expand the allowlist. Safe reads retain normal CORS behavior.

Container exec always targets its Workspace's conventional pod; scratch pod resolution is supplied internally by the server. Client pod query parameters cannot redirect exec into another pod, and an existing Workspace must be read successfully before the route opens any console/exec stream.

Native app authentication (RFC 8252)

Desktop clients cannot read the HttpOnly kw-session cookie, so the API also supports the loopback + PKCE flow from RFC 8252 ("OAuth 2.0 for Native Apps"). The client never talks to the IdP, so the flow works with whichever authentication the administrator configured.

  1. The client starts a loopback listener on an ephemeral port and opens the system browser at:

    GET /auth/login?native_redirect=http://127.0.0.1:<port>/cb
                   &code_challenge=<BASE64URL(SHA256(verifier))>
                   &code_challenge_method=S256
                   &state=<opaque client nonce>
    

    native_redirect must be http, on the loopback literal 127.0.0.1 or [::1] (not localhost, per RFC 8252 §8.3), with no query or fragment and no userinfo. Anything else is a 400. Only S256 is accepted.

    state is optional but recommended: it is the client's own nonce, echoed verbatim in step 3 so the client can perform the RFC 6749 §10.12 check. It must be at most 256 unreserved characters (A-Z a-z 0-9 - . _ ~). The server's internal OIDC CSRF state is never echoed — the client has not seen it and could not check it.

  2. The API runs its normal OIDC flow. The native parameters ride through the round trip inside the HMAC-signed kw-auth-state cookie.

  3. /auth/callback mints the session token exactly as for a browser login but, instead of setting the cookie, redirects to native_redirect?code=<code>&state=<client state>. The code is single-use, expires after 60s and is bound to the PKCE challenge. Failures before this point (IdP error, email not allowed) are reported to the same listener as ?error=...&error_description=...&state=... so the client fails fast.

  4. The client exchanges it:

    POST /auth/native/token
    {"code": "...", "code_verifier": "..."}
    
    200 {"token": "...", "expires_at": 1234567890, "email": "...", "role": "..."}
    

    Failures return 400 with {"error": "...", "message": "..."} where error is invalid_code, invalid_verifier or invalid_request.

  5. The token is then used as Authorization: Bearer <token>, which /auth/me and /auth/change-password accept in addition to the session cookie.

GET /auth/config advertises support as "nativeAuth": {"enabled": true, "methods": ["loopback-pkce"]}.

Omitting the native parameters leaves the browser flow completely unchanged.

Codes are held in memory on the replica that issued them, so with multiple replicas the exchange must reach that replica; a failed exchange simply restarts the login.

Design

The API is defined using Goa DSL in design/design.go. Running goa gen generates:

  • HTTP server and client code
  • OpenAPI 2.0 and 3.0 specifications
  • Type definitions and encoders/decoders

Additional endpoints (logs, events, pod, proxy, admin) are implemented directly in cmd/kube_workspaces/http.go, along with the WebSocket bridges (exec/console/vnc/ssh) and their session registry.

Development

# Regenerate code from design
go run goa.design/goa/v3/cmd/goa gen github.com/kube-workspaces/api/design

# Sync the embedded OpenAPI copy served at /openapi3.{json,yaml}
# (go:embed cannot reference ../gen, so this is a manual copy)
cp gen/http/openapi3.json gen/http/openapi3.yaml cmd/kube_workspaces/

# Build
go build -o bin/kube-workspaces-api ./cmd/kube_workspaces/

# Run locally (uses current kubeconfig)
go run ./cmd/kube_workspaces/ --http-port=8090

Docker

docker build -t kube-workspaces-api:latest -f Dockerfile .

Requires Go 1.26+ (for k8s.io/client-go@v0.36.2 compatibility).

Workspace Proxy

The API includes a built-in reverse proxy at /proxy/{namespace}/{name}/{path...}:

  • Full WebSocket passthrough
  • Location header rewriting for redirects
  • Referer-based catch for escaped absolute-path requests
  • No-op ServiceWorker at /sw.js
  • Per-image proxy configuration (see internal/proxy/proxy.go)

Image Defaults

When creating workspaces, the API auto-injects default settings for known images:

  • codercom/code-server:latest - Adds --bind-addr 0.0.0.0:8080 --auth none args

Key Files

File Description
design/design.go Goa API design DSL
cmd/kube_workspaces/http.go HTTP server setup, proxy, admin, WebSocket bridge routes
cmd/kube_workspaces/main.go Entry point, client initialization
internal/k8s/client.go CoreClient for pods, logs, events
internal/k8s/workspace.go Workspace CR dynamic client
internal/k8s/sshkey.go SshKey CR client
internal/k8s/image.go Image CR client (workspace types, defaults)
internal/auth/oidc.go OIDC login/callback, /auth/me, /auth/config
internal/auth/native.go RFC 8252 native-app flow (loopback redirect validation, PKCE, code store)
internal/exec/session.go Session registry (single-session, TTL, take-over)
internal/exec/vmconsole.go VM serial console WebSocket bridge
internal/exec/vmvnc.go VM noVNC display WebSocket bridge
internal/exec/ssh.go Web SSH console WebSocket bridge
internal/proxy/proxy.go Reverse proxy handler
workspaces.go Workspace service implementation
volumes.go Volume service implementation
images.go Images service (static registry)
sshkeys.go SshKeys service

Configuration

The API uses the current kubeconfig or in-cluster service account for Kubernetes access.

Env Var Description Default
KUBECONFIG Path to kubeconfig file ~/.kube/config
HTTP_HOST Listen address localhost:8080
Repository Description
kube-workspaces/controller Kubernetes controller (CRD reconciliation)
kube-workspaces/proxy Workspace reverse proxy
kube-workspaces/frontend Next.js web UI
kube-workspaces/deploy Deployment manifests and documentation

License

Apache License 2.0

Execution history and protected playback

The execution management API requires a current User-UID-bound credential even when ordinary API authentication is disabled. Legacy sessions must sign in again. Current User grants and namespace membership are reloaded by authentication on each HTTP request; token role claims do not grant management authority.

Route Permission / behavior
GET /v1/executions/status Authenticated user-bound identity; policy/storage status and opt-in issuer_ready, not a guest-execution capability promise.
GET /v1/executions?namespace=team&workspace_name=vm Exact workspace scope and workspace.execution.manage; optional actor/since/limit filters only narrow that scope.
GET /v1/executions/{id} workspace.execution.manage on the recorded scope; inaccessible IDs return 404.
GET /v1/executions/{id}/recording Independent workspace.execution.recordings.read; verify the authenticated manifest and full chunk chain, then audit access before returning recording metadata.
GET /v1/executions/{id}/recording/chunks/{index} Fresh recording-read authorization and integrity verification; returns base64 plaintext data in JSON.

Recording responses use Cache-Control: no-store; management payloads are excluded from debug body logging. Recording chunks are capped at 1 MiB. Manifests are HMAC-SHA-256 authenticated with domain separation in addition to AES-256-GCM chunk encryption and the ciphertext hash chain. Unsigned manifests fail verification. Replay currently verifies the entire recording on each request; it is intended as a correctness-first protected playback surface, not a streaming optimization.

Configured persistent storage initialization failures stop API startup; disabled storage leaves the ordinary API running and management admission unavailable. Every admission reserves both execution and recording IDs atomically before opening its sink. Recording-write failures latch, cannot be finalized as success, and preserve available evidence on abort. There is no guest process-start route until authenticated guest transport and runners are implemented.

The optional management issuer now supports protected mounted CA credentials, shared-ledger initialization and a metadata-only protected-bootstrap import worker. Its configuration, trust rotation and readiness contract are documented in internal/execution/management/README.md. It remains disabled by default and exposes no public enrollment/start route.

Disposable PostgreSQL/S3 integration checks are opt-in:

KW_EXECUTION_TEST_DATABASE_URL='postgres://test:test@127.0.0.1:15432/test?sslmode=disable' \
  go test -race ./internal/execution/storage -run TestPostgresIntegration
KW_EXECUTION_TEST_S3_ENDPOINT='http://127.0.0.1:19000' \
KW_EXECUTION_TEST_S3_ACCESS_KEY='test-access' \
KW_EXECUTION_TEST_S3_SECRET_KEY='test-secret' \
  go test -race ./internal/execution/storage -run TestS3Integration

Use isolated disposable services: these tests apply migrations and write test execution records and recording objects. They never discover cluster credentials.

Documentation

Overview

Package kubeworkspaces implements the kube-workspaces REST API service layer. It provides HTTP endpoints for managing workspaces, volumes, images, and namespaces within a Kubernetes cluster. The API is designed using the Goa framework and supports both authenticated and unauthenticated operation modes.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewAgent added in v0.8.0

func NewAgent(wsClient *k8s.WorkspaceClient, sessions *agent.Store, keys func(ctx context.Context) ([][]byte, error)) genagent.Service

NewAgent returns the agent service implementation.

func NewAgentUpgrades added in v0.10.0

func NewAgentUpgrades(svc *upgrades.Service, plat *platform.ConfigProvider, resolver UpgradeEligibility) genagentupgrades.Service

NewAgentUpgrades returns the agent-upgrades service implementation. A nil coordinator, policy provider or resolver leaves every method unavailable; the surface never degrades to in-memory best-effort behavior.

func NewDisplay added in v0.6.0

func NewDisplay(wsClient *k8s.WorkspaceClient, sessions *display.Sessions, enabled bool) gendisplay.Service

NewDisplay returns the display service implementation.

func NewExecutions added in v0.10.0

func NewExecutions(execSvc *execution.Service, plat *platform.ConfigProvider, options ...ExecutionsOption) executions.Service

NewExecutions returns the executions service implementation.

func NewHealth

func NewHealth() health.Service

NewHealth returns the health service implementation.

func NewImages

func NewImages(imageClient *k8s.ImageClient) images.Service

NewImages returns the images service implementation.

func NewNamespaces

func NewNamespaces(authProvider *auth.ConfigProvider) namespaces.Service

NewNamespaces returns the namespaces service implementation.

func NewSSHKeys added in v0.4.0

func NewSSHKeys(authProvider *auth.ConfigProvider) sshkeys.Service

NewSSHKeys returns the sshkeys service implementation.

func NewVolumes

func NewVolumes(authProvider *auth.ConfigProvider) volumes.Service

NewVolumes returns the volumes service implementation.

func NewWorkspaces

func NewWorkspaces(client *k8s.WorkspaceClient, imageClient *k8s.ImageClient, coreClient *k8s.CoreClient, authProvider *auth.ConfigProvider, podDefaultClient *k8s.PodDefaultClient) workspaces.Service

NewWorkspaces returns the workspaces service implementation.

func SharedDisplayEnabledFromEnv added in v0.6.0

func SharedDisplayEnabledFromEnv() bool

SharedDisplayEnabledFromEnv reads the pilot gate: the shared display ships opt-in until the platform plan's acceptance package passes, so it must be explicitly switched on with KW_DISPLAY_SHARED=on. Rolling back is the inverse: unset it and restart — generations end with the process, claims are released, and clients fall back to the legacy exclusive /vnc path.

Types

type ExecutionsOption added in v0.10.0

type ExecutionsOption func(*executionssrvc)

ExecutionsOption configures optional executions-service dependencies.

func WithIssuerReadiness added in v0.10.0

func WithIssuerReadiness(probe func(context.Context) error) ExecutionsOption

WithIssuerReadiness supplies the management issuer readiness probe reported by the status endpoint.

func WithLifecycleContext added in v0.10.0

func WithLifecycleContext(lifecycle context.Context) ExecutionsOption

WithLifecycleContext fences a dispatched guest run against process shutdown. The caller's request context deliberately does not do that: a disconnected client would otherwise fabricate a transport-loss unknown and a recording gap for a command that is still running.

func WithQGA added in v0.10.0

func WithQGA(executor qgaExecutor) ExecutionsOption

WithQGA installs the opt-in QGA pilot transport behind the start method. A nil executor leaves start unavailable; the transport is never a fallback.

type UpgradeEligibility added in v0.10.0

type UpgradeEligibility interface {
	Resolve(ctx context.Context, namespace, name string) (qga.Target, error)
}

UpgradeEligibility resolves the current controller-owned identity of a workspace: the same fail-closed QGA resolver the execution pilot uses, which internally requires the platform execution policy to admit SYSTEM elevation for the workspace.

Directories

Path Synopsis
cmd
kube_workspaces command
gen
internal
auth
Package auth provides authentication and authorization middleware for the kube-workspaces API.
Package auth provides authentication and authorization middleware for the kube-workspaces API.
broker
Package broker implements the read-only RFB multi-viewer feasibility spike.
Package broker implements the read-only RFB multi-viewer feasibility spike.
display
Package display coordinates the single interactive VM display across API replicas.
Package display coordinates the single interactive VM display across API replicas.
exec
Package exec provides WebSocket-based remote command execution in workspace pods.
Package exec provides WebSocket-based remote command execution in workspace pods.
execution
Package execution owns agent-backed machine management, independently of the desktop/display grant and the existing serial/SSH/container exec bridges.
Package execution owns agent-backed machine management, independently of the desktop/display grant and the existing serial/SSH/container exec bridges.
execution/enrollment
Package enrollment implements one-use, CSR-bound management bootstrap issuance.
Package enrollment implements one-use, CSR-bound management bootstrap issuance.
execution/grants
Package grants defines the independently verifiable management-v1 grant envelope.
Package grants defines the independently verifiable management-v1 grant envelope.
execution/management
Package management wires the opt-in management issuer and trusted bootstrap importer.
Package management wires the opt-in management issuer and trusted bootstrap importer.
execution/pki
Package pki defines management-only certificate identities and mutual TLS.
Package pki defines management-only certificate identities and mutual TLS.
execution/qga
Package qga implements the server side of the approved QGA pod-exec pilot transport (`qga-pod-exec/v1`): a Kubernetes-authenticated pod exec into the current KubeVirt virt-launcher, driving the fixed installed runner through the QEMU guest agent.
Package qga implements the server side of the approved QGA pod-exec pilot transport (`qga-pod-exec/v1`): a Kubernetes-authenticated pod exec into the current KubeVirt virt-launcher, driving the fixed installed runner through the QEMU guest agent.
execution/storage
Package storage implements the durable-and-ephemeral execution store: a searchable metadata/audit layer (PostgreSQL in persistent modes, bounded in-memory in ephemeral mode) plus an encrypted, hash-chained recording chunk store (S3-compatible in persistent modes, bounded encrypted temp files in ephemeral mode).
Package storage implements the durable-and-ephemeral execution store: a searchable metadata/audit layer (PostgreSQL in persistent modes, bounded in-memory in ephemeral mode) plus an encrypted, hash-chained recording chunk store (S3-compatible in persistent modes, bounded encrypted temp files in ephemeral mode).
execution/updates
Package updates verifies publisher-authenticated agent-update manifests, authenticated machine inventory reports and the write-ahead upgrade journal, plus bounded payload streams.
Package updates verifies publisher-authenticated agent-update manifests, authenticated machine inventory reports and the write-ahead upgrade journal, plus bounded payload streams.
execution/upgrades
Package upgrades implements durable off-guest agent-upgrade admission: an exclusive per-VM reservation, idempotent start, safe cancellation, explicit rollback admission and the recording/audit evidence trail.
Package upgrades implements durable off-guest agent-upgrade admission: an exclusive per-VM reservation, idempotent start, safe cancellation, explicit rollback admission and the recording/audit evidence trail.
k8s
Package k8s provides Kubernetes client wrappers for the kube-workspaces API server.
Package k8s provides Kubernetes client wrappers for the kube-workspaces API server.
monitoring
Package monitoring owns process-local, client-facing bridge telemetry.
Package monitoring owns process-local, client-facing bridge telemetry.
proxy
Package proxy implements HTTP reverse proxying to workspace pods.
Package proxy implements HTTP reverse proxying to workspace pods.
rfb
Package rfb contains protocol primitives for the shared-display broker.
Package rfb contains protocol primitives for the shared-display broker.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL