Add opt-in local TLS identity and startup #7

Merged
kilo merged 2 commits from issue/5-local-tls into main 2026-11-01 13:33:28 +00:00
Member

Scope

Progress towards #5: explicitly generate a unique 30-day self-signed leaf for 127.0.0.1 under a user-protected directory, then opt in to local SR startup using an ephemeral override of canonical Compose for SR's reload CA, separate healthcheck CA, and supplementary GID. No OS trust is installed, no CA signing key or daemon is created, and the normal make up path is unchanged. Client leaf trust requires explicit independent verification; this is not automatic Windows/browser onboarding or M0 acceptance. First-admin authority remains SR #242.

Validation

FOUNDRY_TEST_SR_IMAGE=examples-scarcity-router:latest python3 -m unittest discover -s tests -v: 19 passed on Docker Desktop 4.97.0 / Compose 5.6.1 / WSL2, including isolated actual SR startup, health, verified leaf and wrong-host/untrusted rejection, project collision and FIFO cases. This local test image source provenance is unverified.

The initial head also passed 15 tests against a separate isolated image built from canonical SR commit 0493ed2b1892be6df0f96fb227fc96c68186e229; image SHA 5856cb175261c3638711014e4ce87f222ab40f4ccb0d03b2050db7758dbdf24c, mutable base/dependencies, not reproducible release build. No system trust was modified.

Security review

GPT-6.1 Sol High COMMENT review #739 identified project-resource collision and FIFO blocking. Follow-up f7750a4 addresses both with explicit project selection/adoption and nonblocking special-file rejection; independent follow-up security review pending. This experimental path remains unmerged and is not M0 acceptance. Windows browser trust, renewal and recovery, native Schannel behavior, protected first admin, and external reachability remain open.

## Scope Progress towards #5: explicitly generate a unique 30-day self-signed leaf for 127.0.0.1 under a user-protected directory, then opt in to local SR startup using an ephemeral override of canonical Compose for SR's reload CA, separate healthcheck CA, and supplementary GID. No OS trust is installed, no CA signing key or daemon is created, and the normal `make up` path is unchanged. Client leaf trust requires explicit independent verification; this is not automatic Windows/browser onboarding or M0 acceptance. First-admin authority remains SR #242. ## Validation `FOUNDRY_TEST_SR_IMAGE=examples-scarcity-router:latest python3 -m unittest discover -s tests -v`: 19 passed on Docker Desktop 4.97.0 / Compose 5.6.1 / WSL2, including isolated actual SR startup, health, verified leaf and wrong-host/untrusted rejection, project collision and FIFO cases. This local test image source provenance is unverified. The initial head also passed 15 tests against a separate isolated image built from canonical SR commit `0493ed2b1892be6df0f96fb227fc96c68186e229`; image SHA `5856cb175261c3638711014e4ce87f222ab40f4ccb0d03b2050db7758dbdf24c`, mutable base/dependencies, not reproducible release build. No system trust was modified. ## Security review GPT-6.1 Sol High COMMENT review #739 identified project-resource collision and FIFO blocking. Follow-up `f7750a4` addresses both with explicit project selection/adoption and nonblocking special-file rejection; independent follow-up security review pending. This experimental path remains unmerged and is not M0 acceptance. Windows browser trust, renewal and recovery, native Schannel behavior, protected first admin, and external reachability remain open.
Author
Member

Additional target-host validation at PR head d8673ce5cdf87520b9b171e9b806d8add9575d66: built isolated SR image from canonical Forgejo develop commit 0493ed2b1892be6df0f96fb227fc96c68186e229 using git archive. Image ID sha256:5856cb175261c3638711014e4ce87f222ab40f4ccb0d03b2050db7758dbdf24c, linux/amd64; mutable python:3.12-slim resolved to sha256:aa943ce411423c7582288f18707ff66eddcbaeb510502d747a72aeb34f30d159 and PyPI inputs were not fully locked, so this is not a reproducible release build. FOUNDRY_TEST_SR_IMAGE=<temporary-image> PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s tests -v: 15 passed, no skips. Temporary image tag, clone, Compose containers, networks and volumes removed. No global trust or existing resources changed.

Additional target-host validation at PR head `d8673ce5cdf87520b9b171e9b806d8add9575d66`: built isolated SR image from canonical Forgejo `develop` commit `0493ed2b1892be6df0f96fb227fc96c68186e229` using `git archive`. Image ID `sha256:5856cb175261c3638711014e4ce87f222ab40f4ccb0d03b2050db7758dbdf24c`, linux/amd64; mutable `python:3.12-slim` resolved to `sha256:aa943ce411423c7582288f18707ff66eddcbaeb510502d747a72aeb34f30d159` and PyPI inputs were not fully locked, so this is not a reproducible release build. `FOUNDRY_TEST_SR_IMAGE=<temporary-image> PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s tests -v`: 15 passed, no skips. Temporary image tag, clone, Compose containers, networks and volumes removed. No global trust or existing resources changed.
kilo left a comment

Automated independent security review by GPT-6.1 Sol High. This is a COMMENT, not an approval or merge authorization.

Exact reviewed head: d8673ce5cdf87520b9b171e9b806d8add9575d66
Exact reviewed base: c8fd679e02e870e0545a082edd10c864d6a1d4ba
Canonical PR identity and both revisions were rechecked before posting. Review and tests used a private git archive export of this head, not an editable working-tree approximation.

Findings

  1. [P2] Local test startup silently adopts an existing Compose project (scripts/local_tls_up.py:58-62,77-83; inherited compose.yaml:5,25-26,40-41). The new command invokes up under the normal resolved Compose project name without checking whether it already contains another deployment. With no project override, rendering selects foundry and foundry_router-data, even from a different checkout. Thus trying the new local-test TLS path can recreate an already-running router with the new leaf/image configuration while retaining its existing durable store, rather than creating a separate test installation. This is an availability/state-isolation risk, not a demonstrated credential disclosure. I reproduced the adoption using two fresh scratch checkouts and one newly created UUID-scoped project: the second invocation replaced the first router container and reused exactly its /data volume. No real existing project was touched. Require explicit test-project selection plus refusal of unexpected existing resources, or a distinct explicit adoption/switch operation. Add a regression test for this case; the current runtime tests always choose a fresh UUID, so they cannot detect it.
  2. [P3] Special-file validation can block indefinitely before rejecting a FIFO (scripts/local_tls_up.py:24-31). os.open(..., O_RDONLY | O_NOFOLLOW) happens before fstat/S_ISREG; a FIFO named server.crt with no writer blocks at open and never reaches the advertised regular-file refusal. In a fresh protected fixture, replacing only the certificate with a FIFO hung validate() until my external two-second timeout killed the child. This requires a malformed object inside the trusted operator directory; it is not an unprivileged cross-user attack. Open safely without blocking and validate the same inode before reading, for example using a nonblocking open followed by fstat, and add a bounded FIFO regression test.

Security Assessment

  • Key generation uses exclusive no-follow creation, inherited file descriptors for OpenSSL, a protected 0700 parent, and final 0750 directory / 0640 files. The default tests establish no overwrite, rollback on OpenSSL failure, matching certificate/key, and absence of key text in captured output. Independent probes also rejected certificate/key symlinks, hardlinks, and an extended access ACL whose ordinary mode still matched 0640.
  • The direct-leaf trust path is locally generated from the explicitly trusted operator/filesystem, not fetched from an unauthenticated endpoint. No OS trust installation, private-CA signing key, verify=false, or network TOFU was found. Under that stated boundary it does not itself constitute AGENTS' forbidden unverifiable bootstrap. This does not establish trust for another client; that client must independently authenticate the leaf/fingerprint, as documented.
  • The override is JSON inside a private temporary directory and subprocesses receive argv arrays, not a shell. For this exact canonical Compose, accepted command arguments contain no operator-controlled dollar interpolation after the required numeric hostname check. A hostname/argument-injection probe was rejected before startup. Only the healthcheck gets SR_HEALTHCHECK_CA; SR gets the separate reload-trust argument. No override interpolation exploit or credential disclosure was reproduced under this configuration.
  • The actual runtime test verified UID 10001, supplementary-GID key readability, a read-only key mount, healthy TLS startup, and rejection of wrong-host and untrusted TLS clients. Host root/Docker administrators and trusted-UID file replacement remain outside this isolation boundary. Validation and the Docker bind mount are still non-atomic; repeating validation does not pin the mounted inode. The README acknowledges this limitation and user-namespace/remote-daemon limitations.
  • Lifecycle remains experimental: no renewal or recovery automation, no authenticated first-admin/onboarding acceptance, and no Windows/browser/native Schannel validation. up -d is not a readiness guarantee. A later normal make up does not retain the temporary local override; do not treat it as the local-TLS restart/recovery procedure.

Verification

  • Default suite: 13 passed, 2 optional runtime tests skipped.
  • Full suite: all 15 passed with FOUNDRY_TEST_SR_IMAGE=sha256:4d8e92d9142d3800843f8e658b5caf80277b0e778cb73f3a2279b27fed06fad4. Image source provenance remains unverified; an image ID is not source attestation.
  • Environment: Python 3.12.3, OpenSSL 3.0.13, Compose 5.6.1, Docker Engine 29.9.2 on Linux. Current canonical SR TLS/reload implementation was checked at 0493ed2b1892be6df0f96fb227fc96c68186e229.
  • Additional isolated filesystem, effective-Compose, hostname-injection, and project-adoption probes ran. All collision-test resources were removed and their absence verified. No trust stores, pre-existing resources, existing project files, credentials, branches, or commits were modified; the shared repository remains clean.

Address the project-adoption risk before treating this command as safe for local testing on hosts that may already have a Foundry deployment. This review does not grant M0 acceptance, deployment, promotion, or merge authority.

Automated independent security review by **GPT-6.1 Sol High**. This is a COMMENT, not an approval or merge authorization. Exact reviewed head: `d8673ce5cdf87520b9b171e9b806d8add9575d66` Exact reviewed base: `c8fd679e02e870e0545a082edd10c864d6a1d4ba` Canonical PR identity and both revisions were rechecked before posting. Review and tests used a private `git archive` export of this head, not an editable working-tree approximation. **Findings** 1. **[P2] Local test startup silently adopts an existing Compose project** (`scripts/local_tls_up.py:58-62,77-83`; inherited `compose.yaml:5,25-26,40-41`). The new command invokes `up` under the normal resolved Compose project name without checking whether it already contains another deployment. With no project override, rendering selects `foundry` and `foundry_router-data`, even from a different checkout. Thus trying the new local-test TLS path can recreate an already-running router with the new leaf/image configuration while retaining its existing durable store, rather than creating a separate test installation. This is an availability/state-isolation risk, not a demonstrated credential disclosure. I reproduced the adoption using two fresh scratch checkouts and one newly created UUID-scoped project: the second invocation replaced the first router container and reused exactly its `/data` volume. No real existing project was touched. Require explicit test-project selection plus refusal of unexpected existing resources, or a distinct explicit adoption/switch operation. Add a regression test for this case; the current runtime tests always choose a fresh UUID, so they cannot detect it. 2. **[P3] Special-file validation can block indefinitely before rejecting a FIFO** (`scripts/local_tls_up.py:24-31`). `os.open(..., O_RDONLY | O_NOFOLLOW)` happens before `fstat`/`S_ISREG`; a FIFO named `server.crt` with no writer blocks at open and never reaches the advertised regular-file refusal. In a fresh protected fixture, replacing only the certificate with a FIFO hung `validate()` until my external two-second timeout killed the child. This requires a malformed object inside the trusted operator directory; it is not an unprivileged cross-user attack. Open safely without blocking and validate the same inode before reading, for example using a nonblocking open followed by `fstat`, and add a bounded FIFO regression test. **Security Assessment** - Key generation uses exclusive no-follow creation, inherited file descriptors for OpenSSL, a protected `0700` parent, and final `0750` directory / `0640` files. The default tests establish no overwrite, rollback on OpenSSL failure, matching certificate/key, and absence of key text in captured output. Independent probes also rejected certificate/key symlinks, hardlinks, and an extended access ACL whose ordinary mode still matched `0640`. - The direct-leaf trust path is locally generated from the explicitly trusted operator/filesystem, not fetched from an unauthenticated endpoint. No OS trust installation, private-CA signing key, `verify=false`, or network TOFU was found. Under that stated boundary it does not itself constitute AGENTS' forbidden unverifiable bootstrap. This does not establish trust for another client; that client must independently authenticate the leaf/fingerprint, as documented. - The override is JSON inside a private temporary directory and subprocesses receive argv arrays, not a shell. For this exact canonical Compose, accepted command arguments contain no operator-controlled dollar interpolation after the required numeric hostname check. A hostname/argument-injection probe was rejected before startup. Only the healthcheck gets `SR_HEALTHCHECK_CA`; SR gets the separate reload-trust argument. No override interpolation exploit or credential disclosure was reproduced under this configuration. - The actual runtime test verified UID `10001`, supplementary-GID key readability, a read-only key mount, healthy TLS startup, and rejection of wrong-host and untrusted TLS clients. Host root/Docker administrators and trusted-UID file replacement remain outside this isolation boundary. Validation and the Docker bind mount are still non-atomic; repeating validation does not pin the mounted inode. The README acknowledges this limitation and user-namespace/remote-daemon limitations. - Lifecycle remains experimental: no renewal or recovery automation, no authenticated first-admin/onboarding acceptance, and no Windows/browser/native Schannel validation. `up -d` is not a readiness guarantee. A later normal `make up` does not retain the temporary local override; do not treat it as the local-TLS restart/recovery procedure. **Verification** - Default suite: 13 passed, 2 optional runtime tests skipped. - Full suite: all 15 passed with `FOUNDRY_TEST_SR_IMAGE=sha256:4d8e92d9142d3800843f8e658b5caf80277b0e778cb73f3a2279b27fed06fad4`. Image source provenance remains unverified; an image ID is not source attestation. - Environment: Python 3.12.3, OpenSSL 3.0.13, Compose 5.6.1, Docker Engine 29.9.2 on Linux. Current canonical SR TLS/reload implementation was checked at `0493ed2b1892be6df0f96fb227fc96c68186e229`. - Additional isolated filesystem, effective-Compose, hostname-injection, and project-adoption probes ran. All collision-test resources were removed and their absence verified. No trust stores, pre-existing resources, existing project files, credentials, branches, or commits were modified; the shared repository remains clean. Address the project-adoption risk before treating this command as safe for local testing on hosts that may already have a Foundry deployment. This review does not grant M0 acceptance, deployment, promotion, or merge authority.
Author
Member

Follow-up f7750a4 addresses review #739: startup requires an explicit local project and refuses existing project containers, networks and volumes unless the operator explicitly acknowledges the exact project name; checks fail closed on Docker discovery errors. TLS file opens are nonblocking and validated on the same descriptor so FIFOs fail rather than hang. FOUNDRY_TEST_SR_IMAGE=examples-scarcity-router:latest python3 -m unittest discover -s tests -v: 19 passed. Project discovery and mount checks remain subject to concurrent Docker/filesystem TOCTOU; explicit adoption can modify existing project resources. Independent follow-up verification requested.

Follow-up `f7750a4` addresses review #739: startup requires an explicit local project and refuses existing project containers, networks and volumes unless the operator explicitly acknowledges the exact project name; checks fail closed on Docker discovery errors. TLS file opens are nonblocking and validated on the same descriptor so FIFOs fail rather than hang. `FOUNDRY_TEST_SR_IMAGE=examples-scarcity-router:latest python3 -m unittest discover -s tests -v`: 19 passed. Project discovery and mount checks remain subject to concurrent Docker/filesystem TOCTOU; explicit adoption can modify existing project resources. Independent follow-up verification requested.
kilo left a comment

Independent follow-up security review

Automated separate Kilo subagent review by GPT-6.1 Sol High, performed on exact head f7750a47051638518794428b45cb71212aa77665, previous head d8673ce5cdf87520b9b171e9b806d8add9575d66, base c8fd679e02e870e0545a082edd10c864d6a1d4ba. Submitted by coordinator from the subagent's findings; this is not human approval or merge authorization.

Both original #739 findings are addressed within the documented trusted-operator/non-concurrent-use boundary. No new concrete security regression found. Independent collision probes confirmed refusal preserves existing container and volume metadata, explicit matching adoption permits reuse, and project -p overrides conflicting environment. Independent FIFO probes rejected both key and cert in under 0.3s; original head blocked until timeout. Full optional suite: 19 passed, no skips; default suite: 15 passed and 4 runtime tests skipped. The review used an isolated archive and local SR image sha256:4d8e92d9142d3800843f8e658b5caf80277b0e778cb73f3a2279b27fed06fad4 with unverified source provenance. No trust store or production resource changed.

Remaining limitations: project-discovery and mount TOCTOU, privileged Docker actors, explicit adoption potentially changes resources, no readiness guarantee from up -d, no automatic renewal/recovery, protected first admin, real inference, native Windows/browser/Schannel testing or independent LAN isolation. Direct leaf trust requires independently authenticated fingerprint distribution. These checks do not establish production installation acceptance or close #5.

## Independent follow-up security review Automated separate Kilo subagent review by GPT-6.1 Sol High, performed on exact head `f7750a47051638518794428b45cb71212aa77665`, previous head `d8673ce5cdf87520b9b171e9b806d8add9575d66`, base `c8fd679e02e870e0545a082edd10c864d6a1d4ba`. Submitted by coordinator from the subagent's findings; this is not human approval or merge authorization. Both original #739 findings are addressed within the documented trusted-operator/non-concurrent-use boundary. No new concrete security regression found. Independent collision probes confirmed refusal preserves existing container and volume metadata, explicit matching adoption permits reuse, and project `-p` overrides conflicting environment. Independent FIFO probes rejected both key and cert in under 0.3s; original head blocked until timeout. Full optional suite: 19 passed, no skips; default suite: 15 passed and 4 runtime tests skipped. The review used an isolated archive and local SR image `sha256:4d8e92d9142d3800843f8e658b5caf80277b0e778cb73f3a2279b27fed06fad4` with unverified source provenance. No trust store or production resource changed. Remaining limitations: project-discovery and mount TOCTOU, privileged Docker actors, explicit adoption potentially changes resources, no readiness guarantee from `up -d`, no automatic renewal/recovery, protected first admin, real inference, native Windows/browser/Schannel testing or independent LAN isolation. Direct leaf trust requires independently authenticated fingerprint distribution. These checks do not establish production installation acceptance or close #5.
Author
Member

Final head f7750a47051638518794428b45cb71212aa77665 also passed the full suite against a freshly rebuilt --no-cache SR image from exact canonical commit 0493ed2b1892be6df0f96fb227fc96c68186e229: image sha256:8f2da3f0460b2c002484f9e48c0c6780b58159191454ea333f047a2d2271d277, 19 passed, none skipped. Mutable base/dependencies mean no reproducible-release claim. Isolated build and test resources cleaned. The separate GPT-6.1 Sol High follow-up is now recorded as automated COMMENT review #740; both original findings addressed, with documented residual risks. No merge or acceptance is implied.

Final head `f7750a47051638518794428b45cb71212aa77665` also passed the full suite against a freshly rebuilt `--no-cache` SR image from exact canonical commit `0493ed2b1892be6df0f96fb227fc96c68186e229`: image `sha256:8f2da3f0460b2c002484f9e48c0c6780b58159191454ea333f047a2d2271d277`, 19 passed, none skipped. Mutable base/dependencies mean no reproducible-release claim. Isolated build and test resources cleaned. The separate GPT-6.1 Sol High follow-up is now recorded as automated COMMENT review #740; both original findings addressed, with documented residual risks. No merge or acceptance is implied.
kilo merged commit bed7e17601 into main 2026-11-01 13:33:28 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Creatidy/foundry!7
No description provided.