Skip to content
Draft
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
35 changes: 35 additions & 0 deletions docs/upgrades/api-v1-retirement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Retire the v1 API

This release removes every `/api/v1/**` handler. Old API calls return 404. The v2 API keeps its existing session and API-key policies. This change does not redeploy environment services, move Jobs or change Kubernetes permissions.

Before the upgrade:

- Inventory ingress and internal callers, including scheduled automation. Move v1 calls to supported v2 operations. A quiet ingress sample does not prove that internal callers are absent.
- Deploy the ticket-capable Lifecycle UI and publish a distinguishable ticket-capable CLI release. Validate live pod, build and deploy logs and archived logs against the new API image.
- Coordinate the API and WebSocket replica cutover. Mixed versions can interrupt logs. Drain every old replica and anonymous socket before declaring the endpoint closed. Users must reopen disconnected views or rerun CLI commands.
- Confirm job-only workers have no custom HTTP consumers beyond GET `/api/health` and GET `/api/jobs`.
- Update existing GitHub App settings before a future setup or installation flow: setup URL `/api/setup/installed`, redirect URL `/api/setup/callback`, and the matching OAuth callback entry. Keep the existing Keycloak/user authorization callback and `/api/webhooks/github` webhook URL. Existing configured Apps are managed through installation settings, not the fresh setup API.

The environment banner now opens the authenticated environment page. It uses the existing `LFC_DASHBOARD_URL` metadata, so newly loaded banner scripts work in existing environments without a service redeploy. Already loaded or cached old scripts may still call the retired API until users refresh. The in-page anonymous log terminal is removed.

The old `/schema/validate` page now explains CLI and authenticated v2 validation. Use `lfc schema validate lifecycle.yaml` for a local file or sign in and use `lfc schema validate --repo owner/repository --branch branch-name`. `/v1/docs` redirects to `/v2/docs`.

## Fresh GitHub App setup

Fresh setup uses `/setup` and `/api/setup/**`. It requires an operator token and reads the live bootstrap Secret before changing credentials. Any existing credentials lock new App creation. A valid, matching fresh setup flow can finish installation and configuration. There is no HTTP path to replace an existing configured App.

Provide the same `LIFECYCLE_SETUP_TOKEN` to all web replicas, or use the token derived from their shared `ENCRYPTION_KEY`. The server does not log the token and does not generate per-process credentials. Operators with access to a web pod can retrieve the token without printing the encryption key:

```sh
kubectl exec -n <namespace> <web-pod> -- node -e 'const c=require("crypto"); const token=process.env.LIFECYCLE_SETUP_TOKEN?.trim(); if(token) process.stdout.write(token+"\n"); else { const key=process.env.ENCRYPTION_KEY?.trim(); if(!key || ["changeme","not_setup","replace_me","your_value_here"].includes(key.toLowerCase())) throw new Error("Configure LIFECYCLE_SETUP_TOKEN or ENCRYPTION_KEY"); process.stdout.write(c.createHmac("sha256",key).update("lifecycle-setup-token").digest("hex").slice(0,32)+"\n"); }'
```

Treat this output as a credential. Enter it in the setup form, keep it out of URLs and shared logs, and remove an explicit setup token after completion when it is no longer needed. Existing in-flight v1 setup links are rejected. Start a fresh setup flow using the new page.

Setup callback state expires after one hour and is consumed when its phase advances. Mutations serialize on the existing `app_setup` database row. Kubernetes Secret writes use the observed resource version. Nonsecret flow annotations bind retry recovery to the exact App credentials written by that flow. This permits retry after a Secret write succeeds but the database commit fails. A conflicting operator credential update locks the flow instead of adopting the changed credentials.

The installation callback verifies that the installation belongs to the App created by setup. Setup initializes only missing or known seed domain and registry defaults and preserves other configured values. It requests a rollout restart only after fresh installation is recorded. If a restart fails, retry the configure action or use the deployment tools. A restart accepted before a lost response may be repeated on retry.

## Verification still required for an installation

Run a fresh install rehearsal and verify callback state, Secret writes, configured-instance lockout, and queue bootstrap with the actual chart and authentication provider. Verify the banner in an existing environment with main-era injected metadata. Verify that all old v1 API paths return 404 on the built image, including each log alias and archived-log route. Unit tests do not establish private chart compatibility, actual client versions, production routing, or the absence of old listeners.
62 changes: 62 additions & 0 deletions docs/upgrades/main-security-release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Upgrade checklist for the main-based security release

This candidate starts from API main `976e713af4dbd1b5cd1cacd06ccef91d36dc24f0`. It addresses application security without introducing a migration of existing environment namespaces, service accounts, RBAC, AWS identities, network policies, databases or queues relative to that revision. API dependencies and the lockfile are unchanged. Existing permissions are not revoked by the first new Job.

This boundary applies to the candidate diff, not to the difference between production and main. Map the production image to source and compare its migration ledger before approving an upgrade. Existing broad workload permissions and datastore exposure remain separate security work. No production closure or guarantee of uninterrupted control-plane connections follows from this checklist.

## Included behavior

- Shell injection fixes use argument arrays or quote data in fixed scripts. Native and Codefresh build/deploy/destroy paths, ingress and ExternalSecret commands retain their resource targets. Generated scripts and executable versions still require rehearsal.
- Agent Kubernetes tools require an authoritative environment namespace. A missing namespace denies dependent tools instead of accepting a caller-supplied namespace. A failed Build or database lookup can fail the agent run.
- Command-based MCP servers execute and discover tools through the session workspace gateway. Discovery runs for each agent run, even when stored tool metadata exists. Workspace tool catalogs and errors stay within that run; they do not update shared or user connection discovery state. These tools are classified as MCP writes regardless of their annotations; admission and approval for Develop are described below.
- Develop uses its existing workspace-command and MCP-read permissions to admit command-based MCP tools. It gains no HTTP/SSE write permission, but command-based servers can themselves call remote services. Compared with main, Develop can use write-annotated command tools; read-annotated command tools that previously ran without approval now follow the stricter policy.
- This workspace admission path requires MCP-write availability for the agent owner. The default `admin_only` setting excludes user-owned custom agents; creator-reserved write also blocks them. Approval uses the strictest command, MCP-read and MCP-write rule unless an exact tool rule overrides it. A configured `approvalPolicy.defaultMode` supplies each unspecified rule. Disabled capabilities cannot be overridden. Agents that explicitly declare MCP-write permission retain their existing MCP-write approval path; a command deny does not block that path.
- Denied command-based connections are excluded before discovery starts a process; an applicable exact tool exception can permit discovery. A connection can appear available in the controls while policy excludes all its tools. Review installation-specific policies before rollout.
- Workspace command capture skips directory symlink aliases after the existing path checks. Such aliases previously caused an `EISDIR` error before an approved command could start. File aliases within the workspace remain supported. Existing workspace containers retain their running image until normal session replacement.
- Log streams require a ticket issued through authenticated `/api/v2/logs/stream-tickets`. The issuer checks environment access, API-key repository restrictions, and the pod/container. Tickets bind the target and expire after five minutes. They are replayable within that period; expiration does not terminate an established stream.
- Editor bootstrap exchanges the existing authenticated link for an opaque grant tied to the session and owner. Lifecycle credentials are removed from upstream requests; upstream cookies are suppressed on HTTP and WebSocket responses. Old raw-token cookies are rejected. Reopen the editor through Lifecycle to obtain a grant. Grants expire at the earlier of one hour or the source token's expiry. A 60-second allowance for a future issue time handles clock skew; it does not extend expiry or maximum lifetime. Existing sockets require a separate drain during replacement.
- All v1 API handlers are removed. The environment banner opens the authenticated environment page using existing `LFC_DASHBOARD_URL` metadata. Newly loaded scripts do not require an environment redeploy; cached pages need refresh. Fresh setup uses protected bootstrap state at `/api/setup/**`; configured installations remain managed through installation settings. See [V1 retirement and fresh setup](api-v1-retirement.md).

## Finding accounting

Rows refer to the original 22-critical-finding CSV. A source-complete candidate has the intended implementation; release verification and reviewer acceptance remain required. The log-ticket issue is a separate High finding and does not increase the critical count.

| Classification | Rows | Remaining work |
| ----------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Source-complete candidates: 5 | 2, 8, 15, 17, 21 | Qualify v1 removal/setup and external consumers; deploy/destroy command fidelity; session-local MCP discovery/write policy; namespace denial; editor credentials and connection behavior. |
| Partial fixes: 8 | 4, 9, 11, 12, 13, 16, 18, 19 | Direct shell execution is corrected. Report-specific input validation, generated pipeline review, exact-ref/fork behavior and credential scope still require evidence or an explicitly accepted alternative. |
| Deferred: 9 | 1, 3, 5, 6, 7, 10, 14, 20, 22 | Prewarm/setup execution isolation, workload authority, Aurora helpers, Redis/PostgreSQL controls, clone credentials, OpenSandbox key handling and workspace ownership. Assign owners and follow-up releases. |

The candidate touches 13 critical rows. It does not close all 22 findings. Record each accepted criterion and unresolved condition in the release evidence; do not infer scanner closure from a passing build.

## Before deployment

- [ ] Pin API, UI and CLI source revisions, image/package digests and rollback artifacts. Give the ticket-capable CLI a distinguishable release version. Map the running production image to source and compare migrations with the candidate base; qualify any intervening main changes separately.
- [ ] Record the effective `workspaceBackend.provider` and whether it comes from stored configuration or an environment default. Local agent, editor and gateway replays used `lifecycle_kubernetes`; they do not qualify OpenSandbox. If production uses OpenSandbox, treat finding 20 as live. Withhold agent rollout approval until that exposure is fixed or the installation owner explicitly accepts it. If sessions are already enabled, record the current exposure and agree on containment separately; this checklist does not automatically disable existing sessions.
- [ ] Record the effective workspace and gateway image settings, including `agentSessionDefaults.workspaceImage` and `workspaceGatewayImage`. Publish the corrected gateway image and update the applicable setting to its immutable digest, with a recorded rollback value. Inspect prewarm pods and verify the image ID and gateway module hash inside a new session. An API image update alone does not replace workspace images; existing sessions retain their running image until normal replacement.
- [ ] Confirm the rendered deployment contains no unintended chart, identity, namespace, RBAC, network, database or queue changes. Check private Terraform/chart overrides as well as this repository. Do not run fresh setup or rotate existing credentials as an upgrade step.
- [ ] Inventory all v1 consumers: ingress, internal services, automation and scheduled tasks. Migrate them before removal. Update GitHub App callback/setup URLs before a future setup or installation flow, following the linked v1 checklist. Existing in-flight v1 setup links cannot resume.
- [ ] Verify every serving production web/gateway/all replica has `ENABLE_AUTH=true` and an appropriate `LIFECYCLE_MODE` (`web`, `gateway` or `all`). Protected API, MCP, preview and editor access fails closed without configured authentication. Log issuance/redemption also requires a recognized serving mode. An unset mode is not a log-serving mode.
- [ ] Verify worker replicas use `LIFECYCLE_MODE=job`. Their only HTTP routes are exact GET `/api/health` and GET `/api/jobs`; all upgrades are rejected. Inventory custom probes, metrics or HTTP consumers before rollout. Confirm `/api/jobs` starts queue consumers and readiness succeeds with the actual private chart.
- [ ] Check explicit local-development settings separately. Only `APP_ENV=dev` or `APP_ENV=development` with auth disabled enables local anonymous behavior. An unset value or staging value does not qualify. Never use this exception on production listeners.
- [ ] Inventory global and repository-specific MCP policies: `approvalPolicy.defaultMode`, `rules.shell_exec`, `rules.external_mcp_read`, `rules.external_mcp_write`, exact `mcp__*` tool rules, and `capabilityPolicy.availability` for MCP-write and workspace-command permissions. Identify command-based servers that call remote services. Record expected approvals and denied agents before rollout.
- [ ] Compare the selected grant secret across every issuer and redeemer without recording secret values. Selection order is `CHAT_PREVIEW_GRANT_SECRET`, `ENCRYPTION_KEY`, `NEXTAUTH_SECRET`, then `GITHUB_WEBHOOK_SECRET`. The first nonempty configured value is selected and must not be a placeholder; an unusable higher-priority value does not fall through. Preserve the existing selected key to retain valid preview grants. Fresh setup has its separate shared-token requirements in the linked guide.
- [ ] Confirm ingress, load balancers, proxies and application logs do not retain ticket/token query values. Check clock synchronization across replicas. Verify that issuer responses are not cached.
- [ ] Capture representative existing PR and static environment state, including `good-dev-0` where available: Service/Ingress identities, pod UIDs, PVC data, application traffic and current Jobs. Record pending queue work so normal deployment activity can be distinguished from an upgrade regression.
- [ ] Compare production's expiry sweep with the candidate base. Main contains a PostgreSQL parameter-inference failure that can stop expiry and stuck-teardown retries; it may be new to an older production installation. Inventory overdue environments and pending teardown work before a separate fix, because restoring the sweep can activate overdue deletions.

## Rehearse and cut over

- [ ] Run the named test-repository scenarios for native build/Helm and Codefresh build/deploy/destroy, ExternalSecrets, Jobs, CronJobs, webhooks and after-build actions. Exercise valid complex branch/argument values and denied hostile input. The API image installs Codefresh CLI `0.81.5`; generated pipelines use `codefresh/cli:0.87.4`. Verify both execution environments, cleanup and error behavior. Record the resolved image digests used in rehearsal for generated Job and pipeline helpers, including `alpine/git:latest`, `alpine/helm:3.7.2` and `codefresh/cli:0.87.4`; a tag alone does not identify the tested image.
- [ ] Rehearse fresh setup separately with actual PostgreSQL locking, the bootstrap Secret, GitHub callbacks and multiple web replicas. Test stale/replayed state, Secret-write/DB-failure recovery and configured-install lockout. Do not use production as the fresh-install fixture.
- [ ] Test pending and previously cached stdio connections across two sessions/runs. Confirm discovery uses each workspace, does not persist its catalog/errors globally, and cannot obtain read-only treatment through tool annotations. Verify absent namespace and failed build lookup deny Kubernetes tools before a request is made.
- [ ] Check Develop's default and saved MCP selections. Command-based tools must retain write classification, respect command/read/write approval rules and disabled capabilities, and require the existing workspace-command and MCP-read permissions. Confirm HTTP/SSE write tools remain unavailable in Develop, including under existing allow rules. Verify workspace commands on a new session using the released gateway image. Rehearse a configured default approval mode and repository override. Confirm a user-owned custom agent under `admin_only` has no command tools. For the workspace admission path, verify a command deny prevents discovery and an exact exception exposes only the permitted tool.
- [ ] Test editor reopen, old-cookie rejection, grant expiry, clock skew, wrong owner/session and upstream credential/cookie filtering for supported backends. Test valid existing preview grants with the unchanged selected key.
- [ ] Deploy the ticket-capable UI before the enforcing backend if using its missing-route fallback. Activate the ticket-capable CLI only after both issuer and enforcing WebSocket server are available; that CLI has no anonymous fallback. Test live pod/build/deploy logs and archived logs with the released artifacts.
- [ ] Coordinate API/WebSocket replacement so ticket issuance and redemption reach compatible replicas. Remove every old anonymous listener, including internal worker endpoints, and drain existing sockets. Mixed replicas can interrupt logs. Users may need to reopen terminals or rerun CLI commands; do not assume automatic reconnect.
- [ ] On the built image, verify anonymous issuance/stream denial, expired/tampered/wrong-target tickets, key scope/repository restrictions, worker HTTP/upgrade denial and v1 404s. Verify the banner in an existing environment with main-era metadata and a refreshed page.
- [ ] Monitor the captured PR/static environments through control-plane replacement without changing their application configuration or permissions. Compare traffic, resource identities and PVC data. Confirm readiness, queue progress, error rates and new deploy operations before broad rollout.

## Release decision

Keep automated results, image digests, operator evidence and reviewer verdicts together in the release record. Unit tests establish specific code contracts; they do not establish private infrastructure compatibility, live queue behavior or the absence of old listeners. Approve production rollout only after the installation-specific gates pass. If rollback is required, record that the old image reopens the corrected security paths; availability recovery is not security closure.
Loading
Loading