Documentation
¶
Overview ¶
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. It is an explicit opt-in backend for bounded VM command-only pilot and is never a fallback from a failed mTLS management connection; the dedicated mTLS management transport remains the follow-up.
Trust shape: the API signs one short-lived, single-use Ed25519 grant bound to the fresh request bytes, actor/workspace/provisioning-generation identity and a digest of the server-resolved launcher target. Sensitive grant and request bytes travel only over pod-exec stdin through a fixed wrapper, never as pod-exec URL arguments. The guest independently verifies the grant, its own enrolled workspace UID and the request bounds before creating any process, and reports one bounded JSON result the API treats as authoritative; loss of that result is an unknown outcome, never success.
Index ¶
- Constants
- Variables
- func LoadSigner(path string) (*grants.Signer, error)
- func RunBudget(deadlineSeconds int) time.Duration
- func ScriptPath(target Target, executionID, name string) string
- type Bridge
- type Command
- type Config
- type ExecFunc
- type GuestResult
- type PolicyReader
- type Resolver
- type Runtime
- type Script
- type Target
- type TargetResolver
Constants ¶
const ( // Pilot contract bounds, kept in step with kw-management's Rust constants. MaxDeadlineSeconds = 20 MaxOutputBytes = 16 * 1024 MaxArgs = 64 MaxArgBytes = 8 * 1024 MaxExecutableBytes = 1024 MaxCwdBytes = 1024 MaxEnvelopeBytes = 96 * 1024 )
Fixed installed-guest paths. The runner is the installed kw-agent binary; staged files live in the agent data dir, which exists on every enrolled guest (the runner independently denies a missing enrolled identity).
const ( EnvEnabled = "KW_EXECUTION_QGA_ENABLED" EnvKeyFile = "KW_MANAGEMENT_GRANT_KEY_FILE" )
Environment configuration for the opt-in pilot transport.
Variables ¶
var ( ErrDenied = errors.New("QGA target denied") )
Fail-closed resolver outcomes. ErrDenied means the named workspace is not an eligible running, controller-bound agent VM right now; ErrUnavailable means a required read could not be completed.
var ErrConfiguration = errors.New("QGA pilot transport configuration is invalid")
ErrConfiguration reports unusable opt-in pilot configuration. It is never logged with private material.
var ErrNotStarted = errors.New("runner could not be started")
ErrNotStarted reports a guest refusal before any runner process exists: the installed runner is missing or could not be spawned. Nothing ran, so the caller records a failed outcome rather than an uncertain transport loss.
var ErrRequestTooLarge = errors.New("QGA pilot request is too large")
ErrRequestTooLarge reports a framed request plus grant beyond the staged envelope bound; nothing was staged, so the caller records a failed pre-dispatch outcome, never an uncertain transport loss or a success.
var ErrTransport = errors.New("QGA transport failed")
ErrTransport reports a pod-exec/QGA protocol failure with no trustworthy runner result. The caller must record an unknown outcome, never success.
Functions ¶
func LoadSigner ¶
LoadSigner reads exactly one unencrypted PKCS#8 Ed25519 private key, either PEM ("BEGIN PRIVATE KEY") or raw 64-byte DER, from a bounded regular file. No inline/environment key or automatic generation is available.
func RunBudget ¶
RunBudget is the wall-clock bound for one bounded guest command: staging, the guest deadline, the poll grace and margin for the reply. The API derives its execution context from this budget so a disconnected caller cannot abandon a command mid-flight.
func ScriptPath ¶ added in v0.11.0
ScriptPath builds the script staging path for a target.
Types ¶
type Bridge ¶
type Bridge struct {
// contains filtered or unexported fields
}
Bridge drives one launcher's QEMU guest agent through pod exec.
func (*Bridge) Run ¶
func (b *Bridge) Run(ctx context.Context, target Target, executionID string, envelope, keysJSON []byte, deadlineSeconds int) (*GuestResult, error)
Run stages the pinned keys and the signed envelope, invokes the fixed installed runner through the QEMU guest agent, polls for its bounded JSON result and best-effort clears the staged envelope. A guest refusal to spawn the runner returns ErrNotStarted: a deterministic "nothing ran" failure. Any other protocol failure without a parsed result returns ErrTransport: an unknown outcome.
type Command ¶
type Command struct {
ActorUID string
ExecutionID string
RecordingID string
Executable string
Args []string
Cwd *string
DeadlineSeconds int
Script *Script
}
Command is one bounded elevated command or script. ExecutionID/RecordingID/ActorUID are server-minted or authenticated values, never raw client input.
type Config ¶
Config is the validated opt-in pilot configuration.
func ConfigFromEnv ¶
ConfigFromEnv reads the explicit opt-in flag and signing-key location. The transport is disabled unless explicitly enabled, and enabling without a key file is a startup error rather than a silent downgrade.
type ExecFunc ¶
type ExecFunc func(ctx context.Context, namespace, pod, container string, command []string, stdin []byte) (stdout, stderr []byte, err error)
ExecFunc runs one Kubernetes pod exec carrying the QGA JSON on stdin and returns bounded stdout/stderr. The injectable seam keeps the bridge unit-testable without a cluster.
func KubernetesExecFunc ¶
KubernetesExecFunc builds the production SPDY pod-exec transport. Grant and request bytes travel only on exec stdin through a fixed wrapper, so Kubernetes audit logs never carry them as command arguments.
type GuestResult ¶
type GuestResult struct {
Outcome string
ExitCode *int
Stdout []byte
Stderr []byte
Truncated bool
DurationMS uint64
Denial string
}
GuestResult is one parsed runner JSON result. It is the authoritative guest outcome; the API must never invent success when it is absent.
type PolicyReader ¶
type PolicyReader interface {
GetVMExecutionPolicy(context.Context) (platform.VMExecutionPolicy, error)
}
PolicyReader supplies the fresh controller-owned VM execution policy.
type Resolver ¶
type Resolver struct {
// contains filtered or unexported fields
}
Resolver performs uncached, bounded reads through the Kubernetes API and fail-closes on every ownership, lifecycle, profile, launcher and policy check, including a final snapshot re-verification before returning.
func NewResolver ¶
func NewResolver(client dynamic.Interface, policy PolicyReader) (*Resolver, error)
NewResolver builds a resolver over the shared dynamic client.
type Runtime ¶
type Runtime struct {
// contains filtered or unexported fields
}
Runtime is the opt-in QGA pilot executor behind the API's start endpoint.
func NewRuntime ¶
func NewRuntime(cfg Config, dyn dynamic.Interface, policy PolicyReader, restConfig *rest.Config, client kubernetes.Interface) (*Runtime, error)
NewRuntime wires the production pilot stack: the configured signing key, an uncached dynamic-client resolver and the SPDY pod-exec bridge. Every dependency must be present; enabling the pilot never degrades silently.
func NewRuntimeWithExec ¶
func NewRuntimeWithExec(signer *grants.Signer, resolver TargetResolver, exec ExecFunc) (*Runtime, error)
NewRuntimeWithExec wires a runtime over an injected exec transport for tests; the guest-side contract is exercised end to end through fakes.
func (*Runtime) Execute ¶
func (r *Runtime) Execute(ctx context.Context, target Target, command Command) (*GuestResult, error)
Execute issues one single-use QGA grant bound to the resolved target and the exact request bytes, stages both through the bridge and returns the runner's authoritative result. Resolver errors pass through (ErrDenied and ErrUnavailable are pre-dispatch states, not losses); framing failures are ErrTransport (unknown outcome); oversized framing is ErrRequestTooLarge, which is deterministic and staged nothing. A GuestResult is returned only from a parsed runner reply, never invented.
type Script ¶ added in v0.11.0
Script describes an inline script to be written to a temp file and executed via shebang.
type Target ¶
type Target struct {
Namespace string
WorkspaceName string
WorkspaceUID string
// VMUID is the controller-owned VirtualMachine UID: the management
// provisioning generation (reset/recreation changes it; VMI reboot does not).
VMUID string
PodName string
PodUID string
Domain string
RevocationEpoch uint64
// Linux is set only for controller-bound Linux agent workspaces. The zero
// value retains the original Windows target contract.
Linux bool
}
Target is the server-resolved, re-verified launcher identity for one execution. Callers must never build it from client-supplied values.