-
Notifications
You must be signed in to change notification settings - Fork 0
Add isolated test-environment host installer #76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Nickfost
wants to merge
44
commits into
main
Choose a base branch
from
feat/issue-23-tester-host
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
2c2448f
feat: add isolated tester host installer
6196867
fix: make tester lifecycle recoverable and scoped
de515af
test: cover tester cleanup failures
4866a84
docs: clarify tester credential boundary
50905e0
test: support read-only releases as non-root
beff43c
fix: close remaining tester isolation channels
b9242fb
fix: make tester lifecycle repairable
25fbac1
fix: serialize tester lifecycle updates
3e5c7e4
fix: contain tester recovery failures
9b09cfa
fix: quiesce tester timers during activation
e4fca5b
fix: close tester isolation gaps
e2c33af
fix: route tester lifecycle through stable launcher
7db5f25
test: follow dedicated tester lifecycle lock
034bf0d
fix: make tester uninstall idempotent
9e8dc3c
Merge remote-tracking branch 'origin/main' into pr-76
d003568
fix: close final tester lifecycle gaps
4be273e
fix: restore tester when rollback record fails
25b4306
fix: repair PR #76 rewrite corruption and resolve 8 Codex findings
Nickfost a3793b7
Merge origin/main into pr76 repair (no history rewrite)
Nickfost c661e72
test: reproduce archive symlink dereference
Nickfost 3ef9fbf
fix: reject symlinked tester release members
Nickfost 6fcdb67
test: reproduce escaped explicit include key
Nickfost c1650ac
fix: reject explicit Compose mapping keys
Nickfost 054d159
test: reproduce source label file loading
Nickfost 1269edd
fix: reject Compose label files before rendering
Nickfost 5113982
test: reproduce broken deployed tester launcher
Nickfost e3f425b
fix: validate activation through deployed launcher
Nickfost bfa705b
fix: preserve inherited tester lifecycle lock
Nickfost df14fa5
test: require activation service execution
Nickfost d3cc48d
fix: exercise tester maintenance services on activation
Nickfost 7516fa5
fix: close tester maintenance review findings
Nickfost 08be87d
fix: avoid tester maintenance lock deadlock
Nickfost 9654ace
fix: harden tester Compose policy validation
Nickfost 55db13a
fix: close tester activation policy gaps
Nickfost e8a517a
fix: close tester verification gaps
Nickfost 1892638
fix: close tester lifecycle verification gaps
Nickfost 818b780
fix: close remaining tester policy gaps
Nickfost 8087c37
fix: close tester lifecycle verification gaps
Nickfost f183587
fix: close tester policy and health gaps
Nickfost 5428783
fix: close tester maintenance policy gaps
Nickfost a91697b
fix: harden tester maintenance validation
Nickfost 589fc4e
test: preserve rollback route fixture
Nickfost 57db054
fix: permit tester health probe sockets
Nickfost de02123
fix: scope tester runtime prerequisites to each action
Nickfost File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| # Test-environment host | ||
|
|
||
| Status: repository implementation complete; prepared isolated-host acceptance remains required. | ||
|
|
||
| This role runs persistent or expiring deployed application test environments. It is deliberately separate from ephemeral CI workers and production deployers. It cannot register ordinary CI runners or promote production releases. | ||
|
|
||
| ## Boundary | ||
|
|
||
| A tester host accepts only: | ||
|
|
||
| - a reviewed `ci-fleet` source commit for the tester service; | ||
| - root-owned host configuration and environment declarations; | ||
| - application images addressed by an immutable `sha256` digest; | ||
| - environment secrets stored below that environment's fixed host-local secret directory. | ||
|
|
||
| It rejects mutable images, public port binds, host bind mounts, external/unscoped Docker resources, custom volume drivers/options, privileged containers, added capabilities, host or shared namespaces, Docker API access, global container names, and credentials outside the environment secret boundary. Compose environment variables, env files, and configs are forbidden credential channels; use only fixed mode-`0600` Compose secrets. Every service must define positive CPU, memory, and PID limits, remain OOM-killable, be read-only, drop all capabilities, and set `no-new-privileges=true`. The validated rendered Compose model is copied into protected runtime state before activation, so partial starts remain tracked and later cleanup does not depend on a mutable or deleted source definition. Test identity, networks, storage, routes, domains, data, and credentials must have no production authority. Host/network isolation is an external acceptance gate, not something this repository-only change can prove. | ||
|
|
||
| ## Prepare host-local configuration | ||
|
|
||
| The installer supports only `/etc/ci-fleet-tester/tester.env`. Create it and all protected directories as root; never commit them: | ||
|
|
||
| ```bash | ||
| sudo install -d -m 0700 \ | ||
| /etc/ci-fleet-tester \ | ||
| /etc/ci-fleet-tester/environments \ | ||
| /etc/ci-fleet-tester/definitions \ | ||
| /etc/ci-fleet-tester/secrets | ||
| sudo install -m 0600 /dev/null /etc/ci-fleet-tester/tester.env | ||
| ``` | ||
|
|
||
| Example non-secret settings: | ||
|
|
||
| ```text | ||
| CI_FLEET_TESTER_DEFAULT_TTL_SECONDS=86400 | ||
| CI_FLEET_TESTER_MAX_ENVIRONMENTS=20 | ||
| CI_FLEET_TESTER_DISK_WARN_PERCENT=80 | ||
| CI_FLEET_TESTER_NETWORK_PROBE_HOST=tester-probe.invalid | ||
| CI_FLEET_TESTER_HTTPS_PROBE_URL=https://tester-probe.invalid/health | ||
| CI_FLEET_TESTER_ISOLATION_ACK=test-only-no-production-authority | ||
| ``` | ||
|
|
||
| Set both probe values to a test-only host whose DNS resolution and HTTPS HEAD response exercise the intended local proxy path without carrying credentials. The acknowledgement is required but is not proof: an authorized operator must still verify that the prepared host has no production identity or network authority. | ||
|
|
||
| For environment `example-preview`, create root-owned mode-`0600` `/etc/ci-fleet-tester/environments/example-preview.env`: | ||
|
|
||
| ```text | ||
| CI_FLEET_TESTER_PROJECT=example-project | ||
| CI_FLEET_TESTER_OWNER=example-owner | ||
| CI_FLEET_TESTER_COMPOSE_FILE=/etc/ci-fleet-tester/definitions/example-preview.yaml | ||
| CI_FLEET_TESTER_EXPIRES_AT=REVIEWED_FUTURE_UNIX_TIME | ||
| CI_FLEET_TESTER_ROUTE_SERVICE=web | ||
| CI_FLEET_TESTER_ROUTE_PORT=18080 | ||
| ``` | ||
|
|
||
| The Compose file is root-owned mode `0644` and may contain no credential value. Each image must use `registry/path@sha256:REVIEWED_64_HEX_DIGEST`, and each service must set positive `cpus`, `mem_limit`, and `pids_limit` values. Exactly one route is published, on loopback only, at the declared port. Compose-generated network and volume names must remain below `ci-fleet-test-<environment>_...`; explicit external names are rejected. | ||
|
|
||
| If credentials are required, create `/etc/ci-fleet-tester/secrets/example-preview` as root-owned mode `0700`, put only test-scope regular files there as root-owned mode `0600`, and reference them through Compose `secrets.file`. Symlinks, external secrets, production credentials, environment-variable secret transport, and files outside that exact directory are unsupported. | ||
|
|
||
| ## Fresh install or repair | ||
|
|
||
| Use a clean reviewed checkout at the exact commit: | ||
|
|
||
| ```bash | ||
| ref=$(git rev-parse 'HEAD^{commit}') | ||
| sudo ./scripts/install-tester.sh --install \ | ||
| --config /etc/ci-fleet-tester/tester.env \ | ||
| --ref "$ref" | ||
| ``` | ||
|
|
||
| The command fails before mutation unless it sees Debian 12 or newer, root, the local default Docker context/socket/root, Compose v2, required generic tools, protected paths, and Docker storage below 80%. It stages an immutable source release, validates it, switches the `current` symlink, installs health and expiration timers, and verifies both configuration and active environments. Repeating the same command is idempotent. | ||
|
|
||
| ## Environment lifecycle | ||
|
|
||
| The runtime command is the one interface for create/update, inspect, reset, and removal: | ||
|
|
||
| ```bash | ||
| sudo /opt/ci-fleet-tester/tester-runtime --converge --environment example-preview | ||
| sudo /opt/ci-fleet-tester/tester-runtime --inspect --environment example-preview | ||
| sudo /opt/ci-fleet-tester/tester-runtime --reset --environment example-preview | ||
| sudo /opt/ci-fleet-tester/tester-runtime --remove --environment example-preview | ||
| ``` | ||
|
|
||
| `--converge` validates the full resolved Compose model before `up --wait`. `--reset` removes only that exact Compose project and its volumes, then recreates it from the approved definition and digest. `--remove` uses the same scoped `compose down --volumes`; no global Docker prune is used. State reports only environment/project/owner, loopback route, expiry, source revision, timestamps, and health—not Compose environment values or secret content. | ||
|
|
||
| `ci-fleet-tester-health.timer` checks every five minutes. `ci-fleet-tester-cleanup.timer` checks expiration every fifteen minutes. Expired environments are removed through the same scoped path. Disposable environment data is intentionally removed on reset/expiry. Reviewed definitions and fixture sources belong outside runtime state and need ordinary configuration backups; credentials and disposable volumes are never backed up by this service. | ||
|
|
||
| ## Upgrade, validation, and rollback | ||
|
|
||
| ```bash | ||
| ref=$(git rev-parse 'HEAD^{commit}') | ||
| sudo ./scripts/install-tester.sh --upgrade --config /etc/ci-fleet-tester/tester.env --ref "$ref" | ||
| sudo ./scripts/install-tester.sh --check --config /etc/ci-fleet-tester/tester.env | ||
| sudo ./scripts/install-tester.sh --rollback --config /etc/ci-fleet-tester/tester.env | ||
| ``` | ||
|
|
||
| Upgrade validates the candidate before activation and restores the complete previous release if post-switch checks fail. A successful switch records only the previous complete source revision as last known good. Rollback changes the tester service release; it does not rewrite an application's immutable image digest or reset environment data. | ||
|
|
||
| ## Removal | ||
|
|
||
| Remove every environment explicitly, verify no state remains, then uninstall: | ||
|
|
||
| ```bash | ||
| sudo ./scripts/install-tester.sh --uninstall --config /etc/ci-fleet-tester/tester.env | ||
| ``` | ||
|
|
||
| Uninstall fails while any managed environment exists. It removes only tester units and immutable service releases. Host configuration, definitions, and secrets remain for explicit operator disposition; the script never guesses which credential may be deleted. | ||
|
|
||
| ## External acceptance gate | ||
|
|
||
| Before this draft can merge, an authorized operator must use a prepared isolated Debian Docker host with test-only identity/network/storage/domain boundaries and provide evidence for: fresh install, unchanged second install, immutable application converge, health, scoped reset, expiration cleanup, upgrade, failed-upgrade restoration, rollback, and removal. No production credentials, database, network authority, ordinary CI runner, or live production system may participate. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| [Unit] | ||
| Description=Remove expired ci-fleet test environments | ||
| After=docker.service | ||
| Requires=docker.service | ||
|
|
||
| [Service] | ||
| Type=oneshot | ||
| ExecStart=/opt/ci-fleet-tester/tester-runtime --cleanup | ||
| User=root | ||
| Group=root | ||
| UMask=0077 | ||
| NoNewPrivileges=yes | ||
| PrivateTmp=yes | ||
| ProtectHome=yes | ||
| ProtectSystem=strict | ||
| ReadWritePaths=/var/lib/ci-fleet-tester -/run/lock/ci-fleet-tester | ||
|
Nickfost marked this conversation as resolved.
|
||
| TimeoutStartSec=300 | ||
| RestrictAddressFamilies=AF_UNIX | ||
| LockPersonality=yes | ||
| MemoryDenyWriteExecute=yes | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| [Unit] | ||
| Description=Expire abandoned ci-fleet test environments | ||
|
|
||
| [Timer] | ||
| OnBootSec=5m | ||
| OnUnitActiveSec=15m | ||
| Persistent=true | ||
| RandomizedDelaySec=1m | ||
| Unit=ci-fleet-tester-cleanup.service | ||
|
|
||
| [Install] | ||
| WantedBy=timers.target |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| [Unit] | ||
| Description=Validate ci-fleet test environments | ||
| After=docker.service | ||
| Requires=docker.service | ||
|
|
||
| [Service] | ||
| Type=oneshot | ||
| ExecStart=/opt/ci-fleet-tester/tester-runtime --health | ||
| User=root | ||
| Group=root | ||
| UMask=0077 | ||
| NoNewPrivileges=yes | ||
| PrivateTmp=yes | ||
| ProtectHome=yes | ||
| ProtectSystem=strict | ||
| ReadWritePaths=/var/lib/ci-fleet-tester -/run/lock/ci-fleet-tester | ||
| TimeoutStartSec=300 | ||
| RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 | ||
| LockPersonality=yes | ||
| MemoryDenyWriteExecute=yes |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| [Unit] | ||
| Description=Check ci-fleet test environments every five minutes | ||
|
|
||
| [Timer] | ||
| OnBootSec=2m | ||
| OnUnitActiveSec=5m | ||
| Persistent=true | ||
| RandomizedDelaySec=30s | ||
| Unit=ci-fleet-tester-health.service | ||
|
|
||
| [Install] | ||
| WantedBy=timers.target |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Recreate the volatile tester lifecycle lock directory at boot and on | ||
| # tmpfiles --create, so the maintenance units and stable launcher can open it | ||
| # after a reboot even though /run is cleared. | ||
| d /run/lock/ci-fleet-tester 0755 root root - |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.