Constrain Forgejo signups and runners before accepting untrusted accounts #658

Closed
opened 2026-07-27 19:43:30 +00:00 by coilyco-ops · 4 comments
Member

Outcome

Permit external Forgejo accounts to open issues without letting those accounts create execution surfaces or select privileged runners.

Parent program: coilysiren/inbox#280

Current evidence

  • deploy/forgejo.yml enables push-to-create for user and organization repositories.
  • No repository creation limit, regular-organization restriction, or fork restriction is configured.
  • The shared runner registration script creates an instance-scoped runner when SCOPE is empty.
  • The general, build, tap, and deploy runner manifests consume the shared instance registration token.
  • The deploy runner advertises deploy:host, mounts a deployer service-account token, and holds broad cluster mutation permissions.
  • The general and build runners use privileged Docker-in-Docker.
  • coilyco_ops_summon.py authorizes the summoning actor, but a trusted summon can still pass an externally authored issue body into Goose running as Kai with host credentials.

Scope

Signup capabilities

  • Set the new-account repository creation limit to zero.
  • Disable regular organization creation.
  • Disable user and organization push-to-create.
  • Disable forks, including any bypass of the repository creation limit.
  • Preserve the ability for authenticated external accounts to open issues in intended public repositories.

Runner scope

  • Replace the shared instance runner registration with explicit repository or tightly bounded organization scopes.
  • Scope the deploy host runner to the exact repositories that own approved deployment workflows.
  • Ensure a repository created or controlled by an untrusted account cannot select any general, build, tap, or deploy runner.
  • Separate runner registration tokens where scopes differ.
  • Preserve existing trusted release and deployment workflows through a planned re-registration and rollback path.

Host-side summon boundary

  • Require an approved immutable issue snapshot before external issue content reaches the host-side Goose session, or move that session into an appropriately constrained execution environment.
  • Scrub credentials and environment capabilities that the task does not require.
  • Add systemd hardening appropriate to the chosen execution model.

Operational boundary

Runner re-registration, live Forgejo configuration changes, credential rotation, and live validation belong to Ops. Engineering authors and validates the canonical configuration from repository evidence. The rollout keeps old registrations available for rollback until scoped runners prove healthy.

Acceptance

  • A newly registered test account can open an issue in an intended public repository.
  • The account cannot create a repository, organization, fork, push-created repository, or workflow execution surface.
  • The account cannot select any privileged, host, deploy, tap, build, or general runner.
  • Every runner registration has an explicit documented scope.
  • The deploy host runner is selectable only by its approved deployment repositories.
  • Existing trusted build, release, and deployment workflows pass after re-registration.
  • A malicious external issue body does not reach a credentialed host-side Goose session without a trusted snapshot approval.
  • Rollback and credential-rotation evidence are recorded before registration opens.

Codex, via advisor surface

## Outcome Permit external Forgejo accounts to open issues without letting those accounts create execution surfaces or select privileged runners. Parent program: https://forgejo.coilysiren.me/coilysiren/inbox/issues/280 ## Current evidence * `deploy/forgejo.yml` enables push-to-create for user and organization repositories. * No repository creation limit, regular-organization restriction, or fork restriction is configured. * The shared runner registration script creates an instance-scoped runner when `SCOPE` is empty. * The general, build, tap, and deploy runner manifests consume the shared instance registration token. * The deploy runner advertises `deploy:host`, mounts a deployer service-account token, and holds broad cluster mutation permissions. * The general and build runners use privileged Docker-in-Docker. * `coilyco_ops_summon.py` authorizes the summoning actor, but a trusted summon can still pass an externally authored issue body into Goose running as Kai with host credentials. ## Scope ### Signup capabilities * Set the new-account repository creation limit to zero. * Disable regular organization creation. * Disable user and organization push-to-create. * Disable forks, including any bypass of the repository creation limit. * Preserve the ability for authenticated external accounts to open issues in intended public repositories. ### Runner scope * Replace the shared instance runner registration with explicit repository or tightly bounded organization scopes. * Scope the deploy host runner to the exact repositories that own approved deployment workflows. * Ensure a repository created or controlled by an untrusted account cannot select any general, build, tap, or deploy runner. * Separate runner registration tokens where scopes differ. * Preserve existing trusted release and deployment workflows through a planned re-registration and rollback path. ### Host-side summon boundary * Require an approved immutable issue snapshot before external issue content reaches the host-side Goose session, or move that session into an appropriately constrained execution environment. * Scrub credentials and environment capabilities that the task does not require. * Add systemd hardening appropriate to the chosen execution model. ## Operational boundary Runner re-registration, live Forgejo configuration changes, credential rotation, and live validation belong to Ops. Engineering authors and validates the canonical configuration from repository evidence. The rollout keeps old registrations available for rollback until scoped runners prove healthy. ## Acceptance * A newly registered test account can open an issue in an intended public repository. * The account cannot create a repository, organization, fork, push-created repository, or workflow execution surface. * The account cannot select any privileged, host, deploy, tap, build, or general runner. * Every runner registration has an explicit documented scope. * The deploy host runner is selectable only by its approved deployment repositories. * Existing trusted build, release, and deployment workflows pass after re-registration. * A malicious external issue body does not reach a credentialed host-side Goose session without a trusted snapshot approval. * Rollback and credential-rotation evidence are recorded before registration opens. <!-- ward-agent-signature --> Codex, via advisor surface
Author
Member

Engineering checkpoint landed on canonical main as 6f8beb3.

Completed in the tracked state:

  • Forgejo signup lockdown keeps external OAuth registration and issue participation while disabling repository creation, regular organization creation, push-to-create, and forks.
  • Side-by-side organization and repository scoped runner resources render outside the active Flux overlay, with per-scope SSM token paths and rollback instructions.
  • coilyco-ops summon now requires immutable two-stage snapshot approval. The poller consumes and unlinks the runtime token before Goose starts, and Goose receives an isolated home plus a credential-scrubbed environment.
  • The scoped render, 34 security tests, and the complete pre-commit suite pass.

Live rollout and acceptance remain open under interactive Ops issue #664. That issue owns runner registration, SSM mutation, server apply, external-account denial checks, summon host verification, rollback evidence, and promotion of the proven resources into Flux. Keep #658 open until #664 returns that evidence.

Engineering checkpoint landed on canonical main as 6f8beb3. Completed in the tracked state: - Forgejo signup lockdown keeps external OAuth registration and issue participation while disabling repository creation, regular organization creation, push-to-create, and forks. - Side-by-side organization and repository scoped runner resources render outside the active Flux overlay, with per-scope SSM token paths and rollback instructions. - coilyco-ops summon now requires immutable two-stage snapshot approval. The poller consumes and unlinks the runtime token before Goose starts, and Goose receives an isolated home plus a credential-scrubbed environment. - The scoped render, 34 security tests, and the complete pre-commit suite pass. Live rollout and acceptance remain open under interactive Ops issue #664. That issue owns runner registration, SSM mutation, server apply, external-account denial checks, summon host verification, rollback evidence, and promotion of the proven resources into Flux. Keep #658 open until #664 returns that evidence.
Author
Member

Ops rollout #664 is at the final interactive wall.\n\nLanded and verified at 9c24a88:\n\n* Forgejo account-creation and fork restrictions are live while external-only registration remains enabled.\n* Seven organization- or repository-scoped runners are healthy under Flux.\n* Flight Deck, Bridge, Gaming, image-build, and deploy canaries passed. Tap-writer is visible only to homebrew-tap.\n* An unrelated user repository remains waiting with no eligible runner.\n* Every global registration was deleted, all legacy StatefulSets are 0/0, and the old SSM token was invalidated.\n* The repository gate, Flux readiness, and final infrastructure checks pass.\n* coilyco-gaming/sirens-discord-ops#43 tracks a workflow-specific failure found during canary testing. Another Gaming workflow passed on the same scoped runner.\n\nThe complete before-state and rollback evidence is on #664. Kai still needs to complete the sudo-backed summon installation and the real external OAuth account acceptance test. Keep this issue open until those two checks return.

Ops rollout #664 is at the final interactive wall.\n\nLanded and verified at 9c24a88:\n\n* Forgejo account-creation and fork restrictions are live while external-only registration remains enabled.\n* Seven organization- or repository-scoped runners are healthy under Flux.\n* Flight Deck, Bridge, Gaming, image-build, and deploy canaries passed. Tap-writer is visible only to homebrew-tap.\n* An unrelated user repository remains waiting with no eligible runner.\n* Every global registration was deleted, all legacy StatefulSets are 0/0, and the old SSM token was invalidated.\n* The repository gate, Flux readiness, and final infrastructure checks pass.\n* coilyco-gaming/sirens-discord-ops#43 tracks a workflow-specific failure found during canary testing. Another Gaming workflow passed on the same scoped runner.\n\nThe complete before-state and rollback evidence is on #664. Kai still needs to complete the sudo-backed summon installation and the real external OAuth account acceptance test. Keep this issue open until those two checks return.
Author
Member

Final verification found a host-installer defect after Kai completed the interactive checks. The tracked fix is on canonical main at 6e30cc3, and kai-server has the commit, but the sudo-installed unit files still need the installer rerun. The summon bot token family was also rotated after a verbose diagnostic disclosure, its consumers were refreshed, and the scoped deploy runner is Ready again. Detailed evidence is on #664. Keep this issue open until the corrected summon oneshot completes successfully.

Final verification found a host-installer defect after Kai completed the interactive checks. The tracked fix is on canonical main at `6e30cc3`, and kai-server has the commit, but the sudo-installed unit files still need the installer rerun. The summon bot token family was also rotated after a verbose diagnostic disclosure, its consumers were refreshed, and the scoped deploy runner is Ready again. Detailed evidence is on #664. Keep this issue open until the corrected summon oneshot completes successfully.
Author
Member

Acceptance is complete through Ops rollout #664. Forgejo external-account restrictions and scoped runners are live, every global runner registration is gone, trusted canaries passed, and Kai completed the external OAuth denial checks. The native @coilyco-ops host-side Goose surface was retired entirely at dd9d903; live units, unit files, runtime state, and source are all absent. #664 holds the complete before-state, rollback, canary, credential-rotation, and final-retirement evidence.

Acceptance is complete through Ops rollout #664. Forgejo external-account restrictions and scoped runners are live, every global runner registration is gone, trusted canaries passed, and Kai completed the external OAuth denial checks. The native `@coilyco-ops` host-side Goose surface was retired entirely at `dd9d903`; live units, unit files, runtime state, and source are all absent. #664 holds the complete before-state, rollback, canary, credential-rotation, and final-retirement evidence.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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
coilyco-flight-deck/infrastructure#658
No description provided.