Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,11 @@ insufficiently protected policy fails closed before non-bootstrap work.

Schema 1 can require an exact image lock, reject local image-ID locks unless
explicitly allowed, and allowlist canonical registry/repository boundaries.
Lower-precedence registry or command-line choices cannot weaken it. See
Policy schema 2 can also require a strict detached Ed25519 signature over the
exact `container-bin.toml` bytes, with machine-owned key validity, revocation
and overlap rotation. Signed registries are read-only to `cb`; updates must be
provisioned with a matching signature by the administrator. Lower-precedence
registry or command-line choices cannot weaken policy. See
[enterprise machine policy](docs/enterprise-policy.md) for the schema,
ownership rules, normalization behavior and stable diagnostic codes.

Expand Down Expand Up @@ -750,6 +754,10 @@ configuration, or host project files. Output archives are created exclusively:
choose a new filename instead of overwriting an existing backup. Volume data can
itself contain package credentials or other secrets, so store and transfer the
archive as sensitive data even though ContainerBin requests owner-only file mode.
When valid and bounded, the detached `container-bin.toml.sig` envelope is
included; a required signed-registry snapshot is re-authenticated before
backup. An invalid optional envelope is skipped with a warning in unmanaged
mode.

See [proxies, private registries, and air-gapped operation](docs/proxy-airgap.md)
for mirror identity rules, disconnected image preparation, and the complete
Expand Down
12 changes: 11 additions & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,14 @@ This preserves the precedence boundary: user/project/CLI layers may choose a
request, but only the machine layer can authorize it. Full schema and ownership
rules are in [enterprise-policy.md](enterprise-policy.md).

When policy schema 2 requires registry authentication, `registry.Load` passes
the exact file bytes to the policy verifier before parsing. The verifier accepts
only a strict detached Ed25519 envelope beside the registry and a currently
active, non-revoked machine-policy key. Missing signed files do not trigger the
built-in default or `.bak` recovery. Registry-mutating commands are disabled in
this mode because ContainerBin never possesses the administrator's signing key;
lockfile-only operations remain separate.

## Atomic writes

Registry and lock mutations (add, expose, unexpose, uninstall, lock, update,
Expand All @@ -196,7 +204,9 @@ The next `cb` load automatically recovers `container-bin.toml` or
`container-bin.lock` from its `.bak` if the live file is missing, after
validating the backup. If the backup is unreadable or otherwise unusable,
loading stops with a hard error rather than falling back to defaults or an
unlocked state.
unlocked state. Required signed-registry mode is the intentional exception: a
missing live registry is never restored from an unauthenticated `.bak`; the
administrator must provision the registry/signature pair.

Atomic replacement protects file integrity, but it does not protect against
lost updates when two `cb` processes read, modify and write the same file.
Expand Down
100 changes: 97 additions & 3 deletions docs/enterprise-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ root and may not be group- or world-writable.
ContainerBin never creates or edits this file. Provision it and its ACL/mode
with the machine's normal administrator configuration-management mechanism.

## Schema 1
## Schema 1 — image-origin and lock constraints

```toml
policy_version = 1
Expand Down Expand Up @@ -110,5 +110,99 @@ Policy failures have a stable bracketed code suitable for log processing:
- `policy.repository_denied`

The fingerprint hashes the exact policy bytes. It is an audit correlation
value, not a signature. Registry-signature and image-signature policy are
separate roadmap stages and are not implied by schema 1.
value, not a signature. Image-signature policy remains a separate roadmap
stage and is not implied by either schema.

## Schema 2 — authenticated registry bytes

Schema 2 retains every schema 1 control and can additionally require a detached
Ed25519 signature for `container-bin.toml`:

```toml
policy_version = 2
require_lock = true
allowed_repositories = ["docker.io/library", "ghcr.io/acme"]
require_registry_signature = true
registry_signing_keys = [
"ops-2026|BASE64_OF_RAW_32_BYTE_ED25519_PUBLIC_KEY|2026-01-01T00:00:00Z|2027-01-01T00:00:00Z",
"ops-2027|BASE64_OF_RAW_32_BYTE_ED25519_PUBLIC_KEY|2026-12-01T00:00:00Z|2028-01-01T00:00:00Z",
]
revoked_registry_key_ids = ["compromised-2025"]
expires_at = "2027-06-01T00:00:00Z"
```

`registry_signing_keys` entries are
`KEY_ID|PUBLIC_KEY_BASE64|NOT_BEFORE|EXPIRES_AT`. Key IDs are case-sensitive,
start with a lowercase ASCII letter and then contain only lowercase letters,
digits, `.`, `_` or `-` (64 characters maximum). The public key is canonical
padded base64 of the raw 32-byte Ed25519 public key. Key timestamps are
whole-second UTC RFC 3339 values ending in `Z`; expiry is exclusive.

Enabling `require_registry_signature` requires at least one currently active,
non-revoked key. Duplicate IDs, duplicate public keys, malformed validity
windows and duplicate revocations reject the complete policy. Revocation wins
over presence in the trusted-key list. Multiple active keys are the supported
rotation window: provision overlapping old/new keys in policy, deploy that
policy, re-sign the registry with the new key, then revoke or remove the old
identity. Never remove the only signer before the new signature is deployed.

The detached file is exactly `container-bin.toml.sig` beside the registry and
uses this strict envelope:

```toml
signature_version = 1
algorithm = "ed25519"
key_id = "ops-2027"
signature = "BASE64_OF_RAW_64_BYTE_ED25519_SIGNATURE"
```

The Ed25519 message is the complete byte sequence of `container-bin.toml`
itself—no prehash, canonicalization, newline conversion, BOM removal or parsed
representation. Any comment, whitespace or line-ending change therefore needs
a new signature. The envelope is bounded to 16 KiB, must be a regular
non-symlink file and rejects unknown/duplicate fields, unsupported versions,
other algorithms and noncanonical base64.

ContainerBin does not generate keys, read a private key or sign registries.
Create the raw Ed25519 signature in the administrator's protected signing
system, construct the envelope, then provision the registry and envelope as one
configuration-management transaction. Private keys must never live beside the
registry or in the machine policy.

Authentication happens on the raw bytes before TOML parsing, default fallback,
`.bak` recovery, shim reconciliation or Docker use. A missing signed registry
does not fall back to the built-in registry and does not auto-restore an
unsigned backup. The parser acts only on the already authenticated in-memory
bytes, so a later on-disk change cannot alter that invocation's effective
registry.

Signed mode deliberately makes the registry read-only to ContainerBin.
`cb add`, `cb default set`, `cb expose`, `cb unexpose`, `cb uninstall` and
`cb restore --apply` fail before mutation. An administrator must produce and
provision the new registry/signature pair. `cb install` and `cb setup` skip
registry creation/upgrades but may reconcile shims from an already authenticated
registry; a missing signed registry still fails closed. Read-only commands and
lockfile-only operations remain available. `cb backup` includes a valid bounded
detached envelope and re-verifies the exact snapshot when signed mode is active.
An invalid optional envelope is skipped with a warning when policy is unmanaged;
a required invalid envelope still fails. Signed-policy `cb restore` can verify
and preview that archive, but applying it remains an administrator provisioning
operation. An unmanaged restore of an unsigned archive removes any stale
envelope and its backup.

Schema 1 remains supported unchanged. Registry-signature fields in schema 1
are rejected, and policy versions newer than 2 fail closed. This makes rollback
to a ContainerBin build that predates schema 2 fail visibly instead of silently
ignoring the authentication requirement.

Additional stable error codes are:

- `policy.registry_signature_missing`
- `policy.registry_signature_invalid`
- `policy.registry_signer_unauthorized`
- `policy.registry_signer_inactive`
- `policy.registry_signed_readonly`

Policy summaries report whether registry signatures are required plus trusted
and revoked key counts. They never print public-key material or signature
contents.
9 changes: 6 additions & 3 deletions docs/proxy-airgap.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,12 @@ appears in `docker image ls`.

## What `cb backup` protects

Plain `cb backup` archives `container-bin.toml`, the lockfile when present, and
informational metadata. Add `--state` followed by explicit names from `cb state`
to include selected ContainerBin-managed Docker volumes:
Plain `cb backup` archives `container-bin.toml`, a valid bounded detached
signature when present, the lockfile when present, and informational metadata.
Under signed registry policy the exact registry/signature snapshot is
re-authenticated before the archive is created. An invalid optional signature is
skipped with a warning in unmanaged mode. Add `--state` followed by explicit
names from `cb state` to include selected ContainerBin-managed Docker volumes:

```powershell
cb state
Expand Down
7 changes: 4 additions & 3 deletions docs/security-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ Inside the boundary (whoever controls these controls execution):

An optional administrator-owned machine policy sits above this user-controlled
boundary. Its fixed path, owner and permissions are validated before use. It
can require locking and restrict image origins, but schema 1 does not constrain
mounts, environment allowlists or commands and does not authenticate registry
or image signatures. See [enterprise machine policy](enterprise-policy.md).
can require locking, restrict image origins and authenticate exact registry
bytes through a detached Ed25519 signature. It cannot grant mounts, environment
access or commands, and it does not yet authenticate image signatures. See
[enterprise machine policy](enterprise-policy.md).

Treat the registry and lockfile like your PowerShell `$PROFILE`: yours,
readable, and dangerous to let others edit.
Expand Down
Loading
Loading