Security#
Production checklist#
The default configuration is meant for development. At startup, serve and worker log a warning (configuration not fit for production) for every setting that is still permissive. In production:
-
auth.mode: enabled, with OIDC for users and static tokens for CI, the latter inauth.tokens_fileand withexpires_at. See Authentication, permissions and audit. - Users and CI use the API or the
manentclient;manent-serverand the database credentials only for server processes and platform operators. -
rbac.bindingswith the minimum needed: for exampledeployeron non-production environments,tester(dry-run only) in production,operatorfor release managers. -
audit.filewithhmac_key_refandmax_size_mb, checked periodically withmanent-server audit verify, in addition to keeping the entries in the SIEM. -
governancewith mandatory promotion and approvals for production, and the company freeze windows. Theapproverrole for a group different from those who release;deployment.freeze-overrideonly for those who handle emergencies. See Release governance. -
descriptor_policyset: no commands from the descriptor, limited secret references, targets from the environment configuration. - Secrets in Vault (
secrets.vault), not in the worker's environment variables. See Secrets and artifact sources. -
artifacts.allow_unlisted: false, with the sources and their credentials inartifacts.sources. -
callbacks.allowed_hostsset. - With custom steps:
pipeline.custom.spec_key_refsset, a dedicated SSH user (ssh.user) with minimal privileges, read-only secrets fordry_run, controls declaredmandatory, and noinsecure_ignore_host_key. -
sha256for every external plugin, binaries in a directory not writable by the worker. - TLS terminated in front of
serve(reverse proxy or load balancer). - Access to the DBOS database only for workers,
serveand platform operators. -
/metrics,/healthzand/readyzrequire no authentication:observability.listenreachable only from the monitoring network and, in front of the API,/metricsnot exposed externally. -
retention.max_ageconsistent with retention requirements, and the audit kept separately (file and SIEM).
Secrets#
The principle is that a secret value is never persisted. DBOS saves in the database the descriptor, the plan, the environment configuration that was read and the output of every step, so none of these may contain secrets.
- Descriptor and environment configuration: contain only references (
env:,file:,vault:) associated with a logical name. secretsstep: checks that the references can be resolved but does not return the values.- Plugins: read a secret by logical name, through
req.Secrets, only at the moment it is needed. - Callbacks and artifact sources: authentication refers to a secret, read only at send or download time.
- Consul: the token is read from
CONSUL_HTTP_TOKENortoken_file, never from the configuration file. - API tokens: the configuration holds only their sha256.
What a descriptor can ask for#
Whoever can send a descriptor is not necessarily whoever administers the environments. The environment configuration is authoritative, but what it does not define can be added by the descriptor. Without limits, a descriptor could:
- set
post_commandand have arbitrary commands run on the targets, with the environment's credentials; - declare an
env:reference to a worker variable (for exampleenv:CONSUL_HTTP_TOKEN) and have a plugin use it; - name its own targets and receive the artifact with the environment's credentials.
descriptor_policy decides what the descriptor can add:
descriptor_policy:
params: # per plugin; "*" applies to all plugins
remote-copy: [dest_dir, verify_checksum, keep_releases]
secret_refs: # patterns with {application} and {environment}
- "vault:secret/data/{application}/*"
targets: false
post_test_hosts: ["*.example.com"]
| Key | Default | Description |
|---|---|---|
params |
{"*": ["*"]} |
Patterns of the deploy.params parameters the descriptor may set, per plugin. The entries of the selected plugin (deploy.plugin, from the environment or the descriptor) and those of "*" apply. |
secret_refs |
["*"] |
Patterns of the references the descriptor may declare in secrets. * also matches /, so a reference whose path has a . or .. segment, even percent-encoded, is always refused. |
targets |
true |
Allows deploy.targets in the descriptor when the environment does not define them. |
post_test_hosts |
["*"] |
Patterns of the hosts the descriptor's postTest.http checks may reach. The worker sends them from its network: with * a descriptor can probe internal services and read their status codes. |
What comes from the environment configuration is not subject to the policy: commands, secrets and targets stay under the control of whoever administers it. If the section is present, omitted fields keep the default; in production set all of them.
The policy is checked at enqueue time (422), again in the config step together with the merge with the environment configuration, and by manent validate. In the config step a violation makes the deploy fail immediately, without the step's retries.
Callbacks#
Callback URLs are chosen by whoever requests the deploy. To prevent a descriptor from making the worker call internal services (SSRF):
- by default only
httpsURLs are allowed (callbacks.allow_http); callbacks.allowed_hostslimits the reachable hosts;- redirects are not followed;
- a URL cannot contain credentials;
auth.secretcan only name a secret declared in the descriptor itself, so a callback never sends the secrets of the environment configuration (SSH keys, cluster tokens).
See Callbacks.
External plugins#
An external plugin is a binary run by the worker with its permissions. manent applies these protections:
| Protection | How |
|---|---|
| Binary integrity | With external.sha256, the binary is verified at activation and at every process start. |
| Isolated environment | The process does not inherit the worker's variables, except PATH, HOME, TMPDIR, TZ and LANG. Others must be passed explicitly with external.env. |
| Secrets on demand | Secrets arrive only through the gRPC secrets channel, active for the duration of a single Deploy, DryRun, CurrentRelease or Rollback call. |
| Encrypted channel | Communication with the process uses mTLS with certificates generated at every start. |
Warning
Without sha256 manent logs a warning: whoever can replace the binary can run code with the worker's permissions. In production always set the hash and install the binaries in a directory not writable by the worker's user.
SSH#
The remote-copy plugin verifies the targets' host key with known_hosts. insecure_ignore_host_key: true disables the check and is meant for development only.
API#
With auth.mode: enabled the API requires a bearer token (OIDC or static) and enforces permissions. manent does not terminate TLS: expose it behind a reverse proxy or load balancer that does. With auth.mode: none the API has no authentication: use it only locally.
Database#
The DBOS system database contains the deploy history, the queue, the deploy logs and the worker registry. Whoever has write access to it can enqueue, cancel or modify workflows without going through manent's permissions. It must therefore be treated as an administrative component: credentials only for workers, serve and platform operators, encrypted connection (sslmode=verify-full).