Security
Authentication
The WorkManager's gRPC and HTTP endpoints carry no authentication or TLS of their own -- Kestrel binds plaintext, and neither ApiKeyInterceptor-style auth nor Dapr mTLS is wired to the primary listener or to instance-to-instance peer forwarding (ForwardOrReclaimAsync, which dials peers directly, bypassing the Dapr sidecar entirely). The trust boundary is the cluster network: WorkManager has no ingress and is only reachable from within the cluster, where every workload is assumed trusted. This is a deliberate scope decision, not Dapr-provided protection -- if that trust assumption ever changes (e.g. multi-tenant workloads sharing the cluster), this endpoint needs its own auth layer.
For external client access, authentication is handled at the API Gateway layer via its x-api-key scheme -- the only externally-reachable surface in this platform. See API Gateway Security for configuration.
Code Execution Safety
Python Workers
Python workers execute in a persistent sandboxed subprocess with the following protections:
- Launched with
-E -sflags (disablesPYTHON*environment variable injection and user site-packages) - Configurable blocked module list (blocks
subprocess,os,socket,requests, and other I/O modules by default) - Configurable package allow-list via
PYTHON_ALLOWED_PACKAGESenvironment variable - Code is injected directly via stdin — no temporary files written to disk
- Stderr output is captured as warning-level logs, not treated as errors
The subprocess communicates with the host via a JSON-line message loop over stdin/stdout and stays alive for the worker's lifetime (no per-message process spawn overhead).
C# Workers
C# workers run in-process within the WorkManager host. They share the host process's permissions. For maximum safety, use Python workers with the module blocklist or consider containerizing C# workers as sidecar containers in Kubernetes.
Native DLL Workers (AOT)
Native DLL workers (application/x-native-dll, loaded via NativeDllEngine) run in-process within the WorkManager host with no sandbox whatsoever. A native segfault, stack overflow, or P/Invoke that corrupts the heap will terminate the WorkManager and all of its other in-process workers. The native DLL has root-equivalent access to the host process.
Because of this, native DLL workers must come from a fully trusted source — the registered NuGet feed with content-addressed versioning, the same feed the build pipeline publishes to. Native workers should be treated as root-equivalent in the trust model.
The worker source file (WebSocketManagerControllerNative.cs) carries a class-docstring caveat to this effect; operators should read it before deploying native workers.
URL Validation
When loading worker code from a URL, the WorkManager validates:
- The URL host is in the
ALLOWED_CODE_SOURCE_HOSTSlist (if configured) - The URL does not resolve to a private/internal IP address (blocking SSRF attacks against
10.x,172.16.x,192.168.x,127.x,::1,fc00::/7,fe80::/10) - Optional SHA-256 content integrity verification via
ContentSha256field
Dapr Component Security
The WorkManager relies on Dapr for state storage and pub/sub messaging. Ensure Dapr components are configured with:
- State store: Authentication enabled (Redis password for Valkey)
- Pub/sub: Authentication enabled on the message broker
- mTLS: Enabled in Dapr configuration (default since Dapr 1.0)
Configuration & Secrets
The WorkManager reads its configuration from the standard .NET configuration sources (in order: appsettings.json, environment variables, command-line). The following keys control runtime behavior and are the only ones that carry security-sensitive values:
| Key | Default | Security role |
|---|---|---|
AllowedCodeSourceHosts |
(empty) | Host allow-list for worker code loaded from a URL. If empty, all hosts are allowed. Always set explicitly in production to prevent SSRF. |
ALLOWED_CODE_SOURCE_HOSTS |
(empty) | Environment-variable equivalent of the above (used by Program.cs:57). |
Logging:LogLevel:Default |
Information |
Set to Warning or higher in production to avoid leaking sensitive worker code or input data into logs. |
Worker Dapr component values (Redis password, broker credentials) are mounted at runtime via Dapr's secret management component (secretstores/kubernetes) — never commit them to source control. Dapr resolves them by key (e.g., stateStore.password) and the application reads them via DaprClient.GetSecretAsync.
Audit & Logging
The WorkManager emits structured logs for every state-mutating operation (CreateWorker, StartWorker, StopWorker, LoadCode, DeleteWorker, RecoverWorkers) and every worker lifecycle event. Each log record includes the worker ID, the engine MIME type, and a correlation ID derived from the incoming gRPC request.
Fire-and-forget logging calls (sweeper events, completion continuations) are wrapped in try/catch and downgrade to LogLevel.Warning if the underlying stream write fails — this prevents observability failures from cascading into service failures.
Reporting Issues
Report security concerns to the Virtufin security team.