compman manages Docker/Podman Compose stacks. It is supply-chain-sensitive: it
downloads deploy artifacts, resolves secrets from AWS Secrets Manager, and runs
container commands as the user. This policy covers the credential model, secret
handling, and vulnerability reporting.
Only the latest release line receives security fixes; there are no backports.
| Version | Supported |
|---|---|
| 1.x | yes |
| < 1.0 | no |
compman has no accounts or API of its own. Authentication is delegated to the
services it talks to:
-
S3 / AWS Secrets Manager — the boto3 credential chain:
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN,AWS_DEFAULT_REGION.AWS_ENDPOINT_URL_S3(orAWS_ENDPOINT_URL) redirects the client for S3-compatible endpoints (Ministack/LocalStack athttp://localhost:4566). Never commit real credentials:export AWS_ACCESS_KEY_ID=<your-access-key-id> export AWS_SECRET_ACCESS_KEY=<your-secret-access-key> export AWS_DEFAULT_REGION=ap-northeast-2
compman doctorwarns (non-failing) when secrets are configured but credentials or region are missing. -
Docker / Podman runtimes —
compmanshells out as the invoking user. Authorization is whatever the runtime grants that user;compmanadds and bypasses no permission layer. -
SSH backup stores (
ssh://[user@]host[:port]/path) — transfer drivesscpandsshwithBatchMode=yesandStrictHostKeyChecking=accept-new. Keys are assumed pre-provisioned in the agent's keyring;compmannever reads, writes, or generates key material. Because host keys are accepted on first contact, provision them out of band first on hosts you care about. -
Slack notifications (
notify.slack) — an Incoming Webhook URL is a write-capable credential for one channel. Preferwebhook_env, which names an environment variable read at send time so the URL never enterscompman.ymlor the repository; a literalwebhookkeeps the secret in a tracked file. The URL is never echoed and failures name only the variable. Treat it as a secret everywhere — shell history and CI logs included — and rotate it after exposure. -
Authenticated HTTP deploys (
deploy.auth: { header, value_env }) — one caller-supplied header on configured HTTPS fetches. The value is read fromvalue_envat fetch time, never stored, echoed, or logged; errors name only the variable. The loader rejects a non-https://URL and a value containing CR/LF. During redirects the header is dropped whenever the target leaves the original host (compared case-insensitively and port-agnostically; an unparsable host counts as cross-host) or downgrades tohttp, so a token never travels to another host or over plaintext. A--pathdeploy whose URL differs from the configureddeployURL runs unauthenticated.
There is no Basic, JWT, or API-key handling in compman itself. ${secrets:NAME}
injects values into containers; it does not authenticate to compman.
- Secrets are declared in
compman.ymlundersecretsas{ arn, key }pairs (an ARN plus the JSON key inside it). - Values are injected only where a profile
envcontains a${secrets:NAME}marker — never as standalone compose variables, and markers are never expanded intodocker-compose.yml. - Each ARN is fetched once per invocation, lazily, when a compose context is
built. A marker naming an undeclared secret fails clearly; other
${VAR}markers are left for docker compose to resolve from the system environment. - Never hardcode real tokens, keys, or ARNs in docs, tests, or examples — use
placeholders (
<your-secret-access-key>,...:secret:example).compman.ymlbelongs in version control; credentials do not.
Pin a source with a SHA-256 digest (deploy: { url, sha256 } or --sha256). It
is verified after download and before extraction, build, or tree replacement; a
mismatch aborts and leaves the managed tree untouched. This protects against
artifacts altered at a trusted-but-compromisable location — it does not
authenticate the publisher. Compute the digest yourself and publish it through a
channel independent of storage. .sha256 sidecars are not auto-fetched, and
endpoints redirected via AWS_ENDPOINT_URL_S3/AWS_ENDPOINT_URL are out of
scope for this control.
compman writes a little state outside the project directory, under
%APPDATA%\compman when APPDATA is set (always on Windows), otherwise
~/.config/compman:
| File | Contents |
|---|---|
schedules.json |
registered backup jobs and their config paths |
history.jsonl |
append-only deploy/rollback/backup/restore log |
runs/<name>.jsonl |
per-run start/finish records |
schedule.log |
scheduled job output (journald under systemd) |
stacks.json |
multi-stack registry: name and directory per stack |
These hold paths, stack names, timestamps, and exit codes — never secret values. They exist so an operator can audit a host nobody was watching; delete them freely. Do not commit them if your directory layout is itself sensitive.
Report privately, before public disclosure:
- Open a private report at
https://github.com/allbegray/compman/security/advisories/new(preferred), or email the maintainer with the subject prefix[compman-security]. - Include the affected version, a minimal reproduction with credentials redacted, and the impact you observed or suspect.
- Expect acknowledgment within 5 business days and a status update with the fix plan. Accepted reports are fixed and published, then disclosed; declined reports come with the reason.
Please do not open public issues for active vulnerabilities before a fix ships.
- Archive extraction safety — reject absolute paths,
..traversal, and links; flatten a single top-level directory. Extraction goes to a temporary tree, and whenlimits.max_archive_mbis set the cap is enforced during download and on uncompressed member totals before extraction begins; without a configured limit no cap applies. - Path containment — managed backup/volume/project paths must never escape the config directory; a destructive managed directory may not equal the config root.
- No secret leakage — never echo, log, or embed secret values in errors or
diagnostics. All output goes through
typer.echo(..., err=True)(no stdliblogging), and exception messages stay free of credentials. - Fail before mutation — fallible work (image builds, archive validation) runs before irreversible filesystem changes, so a failure leaves the previous state untouched.
- No swallowing errors — destructive operations must not suppress failures
with
|| true/2>/dev/null. A silent destructive step is both a correctness and a security bug. - No hardcoded credentials anywhere, including tests, docs, and examples.