diff --git a/charts/currents/templates/_common.tpl b/charts/currents/templates/_common.tpl index 8ca44ad..12195b3 100644 --- a/charts/currents/templates/_common.tpl +++ b/charts/currents/templates/_common.tpl @@ -299,6 +299,11 @@ ClickHouse data is loaded from an external export (scripts/org-import). - name: SSO_SAML_SP_KEY_FILE value: {{ printf "/etc/currents/sso/%s" .Values.currents.sso.saml.spKeyKey | quote }} {{- end }} +{{- /* toString, so a quoted "false" stays off: any non-empty string is true to `if`. */}} +{{- if eq (toString .Values.currents.sso.saml.allowIdpInitiated) "true" }} +- name: SSO_SAML_ALLOW_IDP_INITIATED + value: "true" +{{- end }} {{- end }} {{- end -}} diff --git a/charts/currents/values.yaml b/charts/currents/values.yaml index 64d1158..3260723 100644 --- a/charts/currents/values.yaml +++ b/charts/currents/values.yaml @@ -90,7 +90,7 @@ currents: # -- SP entityID / audience presented to your IdP. A stable opaque identifier (e.g. `currents-onprem:your-org`), NOT a URL. Must match the Audience / SP Entity ID configured in your IdP. # @section -- SAML SSO issuer: "" - # -- Provider id; recommend your IdP service name (e.g. `okta`). Becomes the last path segment of the ACS callback URL. + # -- Provider id; recommend your IdP service name (e.g. `okta`). Becomes the last path segment of the ACS URL, `https:///api/auth/sso/saml2/sp/acs/`. # @section -- SAML SSO providerId: onprem-saml # -- K8s secret holding the IdP metadata XML (and the SP key, when signing requests). Mounted read-only at /etc/currents/sso. See the SAML SSO guide for how to create it. @@ -111,6 +111,9 @@ currents: # -- Secret key (file name) of the SP private key PEM (only when authnRequestsSigned=true) # @section -- SAML SSO spKeyKey: sp-key.pem + # -- Accept sign-ins started from the IdP (for example its app dashboard), which carry no request from Currents to match. Off: such a sign-in is refused, and users start from the Currents sign-in page. + # @section -- SAML SSO + allowIdpInitiated: false mcp: # -- Serve remote MCP at `/mcp`, so coding agents can use Currents tools over HTTP. Clients # authenticate with an API key or personal access token, or with OAuth (below). Requests diff --git a/docs/README.md b/docs/README.md index 1ee8b4b..04b7655 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,6 +8,8 @@ The Currents Helm Chart is stateless, so depends on being connected to stateful - [🚀 Start Here: EKS Quickstart](./eks/quickstart.md) - [EKS Upgrade Guide](./eks/upgrading.md) +- [Upgrading to Chart 1.0](./eks/upgrade-1.0.md) +- [Docs for chart 0.8.x](https://currents-dev.github.io/helm-charts/0.8/docs/) - [Importing Organization Data (Cloud → Self-Hosted Migration)](./org-data-import.md) - [Development Guide](./developer-guide/README.md) - [Support Policy](./support.md) diff --git a/docs/configuration.md b/docs/configuration.md index 1678a47..5c4542d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -75,13 +75,14 @@ The following table lists the configurable parameters of the `currents` chart an |-----|------|---------|-------------| | currents.sso.saml.enabled | bool | `false` | Enable SAML SSO. Requires `issuer` and `metadataSecretName` below (a K8s secret holding your IdP metadata XML). Turns on email-first SSO login. | | currents.sso.saml.issuer | string | `""` | SP entityID / audience presented to your IdP. A stable opaque identifier (e.g. `currents-onprem:your-org`), NOT a URL. Must match the Audience / SP Entity ID configured in your IdP. | -| currents.sso.saml.providerId | string | `"onprem-saml"` | Provider id; recommend your IdP service name (e.g. `okta`). Becomes the last path segment of the ACS callback URL. | +| currents.sso.saml.providerId | string | `"onprem-saml"` | Provider id; recommend your IdP service name (e.g. `okta`). Becomes the last path segment of the ACS URL, `https:///api/auth/sso/saml2/sp/acs/`. | | currents.sso.saml.metadataSecretName | string | `""` | K8s secret holding the IdP metadata XML (and the SP key, when signing requests). Mounted read-only at /etc/currents/sso. See the SAML SSO guide for how to create it. | | currents.sso.saml.metadataKey | string | `"idp-metadata.xml"` | The secret key (file name) of the IdP metadata XML | | currents.sso.saml.defaultRole | string | `"member"` | Role granted to auto-provisioned SSO users | | currents.sso.saml.allowedDomains | string | `""` | Comma-separated email domains allowed to sign in via SSO (optional allow-list) | | currents.sso.saml.authnRequestsSigned | bool | `false` | Sign outbound AuthnRequests. Requires the SP key (below) in the same secret; upload the matching certificate to your IdP. | | currents.sso.saml.spKeyKey | string | `"sp-key.pem"` | Secret key (file name) of the SP private key PEM (only when authnRequestsSigned=true) | +| currents.sso.saml.allowIdpInitiated | bool | `false` | Accept sign-ins started from the IdP (for example its app dashboard), which carry no request from Currents to match. Off: such a sign-in is refused, and users start from the Currents sign-in page. | ### Remote MCP and OAuth diff --git a/docs/eks/quickstart.md b/docs/eks/quickstart.md index 511c938..c85e56d 100644 --- a/docs/eks/quickstart.md +++ b/docs/eks/quickstart.md @@ -222,7 +222,7 @@ CURRENTS_API_URL=https://currents-record.eks.example.com npx pwc --key /api/auth/sso/saml2/callback/okta` + - **Single Sign-On / ACS URL:** `https:///api/auth/sso/saml2/sp/acs/okta` — the last segment is your `providerId`; `` is your `currents.domains.appHost`. + Use the same URL as the Recipient and Destination. In Okta, leave "Use this for Recipient URL and Destination URL" checked (the default) and both follow the Single Sign-On URL. Currents rejects an assertion whose Recipient or Destination is any other URL. - **Audience / SP Entity ID:** `currents-onprem:your-org` — a stable opaque identifier, **not a URL**. - **Name ID format:** `Persistent` - **Application username:** Email @@ -271,6 +272,8 @@ Delegate sign-in to your SAML 2.0 identity provider (Okta, Microsoft Entra ID, e # Optional: # defaultRole: member # allowedDomains: "acme.com,acme.io" + # Accept sign-ins started from the IdP's app dashboard + # allowIdpInitiated: true ``` 4. **Apply:** @@ -280,6 +283,10 @@ Delegate sign-in to your SAML 2.0 identity provider (Okta, Microsoft Entra ID, e Sign in from the dashboard by entering your email — you'll be redirected to your IdP. New SSO users join your organization automatically with the role from `defaultRole` (default `member`); the root admin continues to sign in with email + password. +A sign-in started from the IdP's app dashboard (an Okta tile, the Entra My Apps portal) is refused unless `allowIdpInitiated` is `true`. Currents can't match it to a request of its own, so leave it off unless your users need the tile. + +Once SSO is on, Currents serves its SP metadata at `https:///api/auth/sso/saml2/sp/metadata?providerId=okta` (with your `providerId`), for IdPs that import it. + To rotate the IdP signing certificate, replace the secret with the refreshed metadata and restart the server pod: ```sh kubectl create secret generic currents-sso-saml --from-file=idp-metadata.xml=./idp-metadata.xml \ diff --git a/docs/eks/upgrade-1.0.md b/docs/eks/upgrade-1.0.md new file mode 100644 index 0000000..cdde526 --- /dev/null +++ b/docs/eks/upgrade-1.0.md @@ -0,0 +1,136 @@ +# Upgrading to Chart 1.0 + +This guide covers upgrading from chart `0.8.x` (images `2026-07-26-00x`) to `1.0.0`. Read it before you upgrade: one change breaks SAML SSO sign-in until you update your identity provider, and the app content bucket has to exist first. + +For the general upgrade flow, see the [EKS Upgrade Guide](./upgrading.md). The docs for the chart you are upgrading from are at [chart 0.8.x docs](https://currents-dev.github.io/helm-charts/0.8/docs/). + +## What Changes + +| Change | Action | +|--------|--------| +| [SAML SSO ACS URL moves](#saml-sso) | Required if you use SSO: update your IdP when you upgrade | +| [SAML sign-ins started from the IdP are refused](#saml-sso) | Set `currents.sso.saml.allowIdpInitiated: true` if your users sign in from an IdP tile | +| [App content bucket](#app-content-bucket) | Create a second bucket and set `currents.appContentStorage.bucket` | +| [Remote MCP and OAuth](#remote-mcp-and-oauth) | None: on by default. Set the values to `false` to keep them off | +| [Database migrations](#database-migrations) | None: they run when the scheduler starts. Check they finish | +| [`currents.sso.saml.spCertKey` removed](#removed-values) | Remove it from your values file | +| [Artifact expiry](#artifact-expiry) | Optional: add a lifecycle rule to the artifact bucket | + +## Before You Upgrade + +1. Back up MongoDB and ClickHouse. The upgrade runs database migrations, and `helm rollback` doesn't undo them. +2. If you use SAML SSO, schedule the IdP change for the upgrade window. See [SAML SSO](#saml-sso). +3. Create the app content bucket. See [App content bucket](#app-content-bucket). +4. Update your values file: + + ```yaml + currents: + appContentStorage: + bucket: currents-app-content + # Only to keep them off: + # mcp: + # enabled: false + # oauth: + # enabled: false + sso: + saml: + # Only if users sign in from the IdP's app dashboard: + # allowIdpInitiated: true + ``` + +## Run the Upgrade + +```bash +helm upgrade --install currents currents \ + --repo https://currents-dev.github.io/helm-charts/ \ + --version 1.0.0 \ + -f currents-helm-config.yaml +``` + +Then follow the scheduler until its migrations finish. See [Database migrations](#database-migrations). + +## SAML SSO + +The ACS URL moves: + +| | URL | +|--|-----| +| `0.8.x` | `https:///api/auth/sso/saml2/callback/` | +| `1.0.0` | `https:///api/auth/sso/saml2/sp/acs/` | + +The old path answers `404`, so SSO sign-in fails from the upgrade until your IdP sends assertions to the new URL. The root admin's email and password sign-in still works. When the upgrade is done: + +1. In your IdP's SAML app, set the Single Sign-On / ACS URL (Entra ID: Reply URL) to the new URL. +2. Set the Recipient and Destination URLs to the new URL as well. In Okta, leave "Use this for Recipient URL and Destination URL" checked (the default) and both follow the Single Sign-On URL. Currents rejects an assertion whose Recipient or Destination is any other URL. +3. Leave the Audience / SP Entity ID as it is. It must still equal `currents.sso.saml.issuer`, which doesn't change. +4. Sign in from the Currents sign-in page. + +A sign-in started from the IdP's app dashboard (an Okta tile, the Entra My Apps portal) is refused unless `currents.sso.saml.allowIdpInitiated` is `true`. Currents can't match such a sign-in to a request of its own. If your users don't need the tile, leave it off and have them start from the Currents sign-in page. + +Currents also serves its SP metadata at `https:///api/auth/sso/saml2/sp/metadata?providerId=`, for IdPs that import it. + +## App Content Bucket + +Context shares, organization logos and session files go to a second bucket. Without `currents.appContentStorage.bucket`, context sharing and logo editing are off and session file uploads answer `503`. + +1. Create the bucket, set its CORS rule and add its lifecycle rules as described in [App content bucket](./dependencies.md#app-content-bucket). The lifecycle rules must be scoped to the share prefixes: the bucket also holds logos and session files. +2. If the pods reach storage through IRSA, add the bucket to the role's policy. See [IRSA](./iam.md#irsa). +3. Set `currents.appContentStorage.bucket`. The other `appContentStorage` values default to the `objectStorage` ones, so the same endpoint and secret serve both buckets. + +If you run RustFS in the cluster from [Alternative Object Storage](./dependencies.md#alternative-object-storage-in-cluster), upgrade it to `1.0.1` and run the bucket Job from that section again: it creates the app content bucket and its rules. Delete the earlier Job first: while it exists, applying the file doesn't start a new run. + +```bash +kubectl delete job rustfs-create-bucket --ignore-not-found +``` + +RustFS releases before `1.0.0`, such as `1.0.0-alpha.79`, accept lifecycle rules without enforcing them. + +## Remote MCP and OAuth + +Remote MCP is served at `https:///mcp`, and the OAuth provider lets MCP clients sign in with a Currents account. Both are on by default after the upgrade. OAuth turns on only when `currents.domains.https` is `true` (or `appHost` is `localhost` or a `*.localhost` name). + +To keep either off, set `currents.mcp.enabled: false` or `currents.oauth.enabled: false`. See [Remote MCP and OAuth](./quickstart.md#remote-mcp-and-oauth). + +To check them after the upgrade: + +```bash +# 401 without credentials, 503 when MCP is off +curl -s -o /dev/null -w '%{http_code}\n' -X POST https:///mcp +# The OAuth provider's metadata; 404 when OAuth is off +curl -s https:///.well-known/oauth-authorization-server +``` + +## Database Migrations + +The scheduler runs this release's migrations when it starts: + +| Migration | What it does | +|-----------|--------------| +| `20260918-backfill-run-status` | Stores each run's status on its MongoDB document | +| `20260928-drop-non-unique-plans-price-id-index` | Drops a duplicate MongoDB index | +| `050`, `060` | Add columns to `run_metric_v2` in ClickHouse | +| `070-backfill-run-metric-search-columns` | Re-inserts every run into `run_metric_v2` to fill those columns | + +With 100,000 runs, the scheduler's startup took under a minute in testing: about 25 seconds for the run status backfill and 10 seconds for `070`. They scale with the number of runs, so expect several minutes for a million. + +This is not downtime: the dashboard, API and run recording keep working while the scheduler migrates. Until the migrations finish, filtering runs by status and searching by CI build ID can leave out runs recorded before the upgrade. The change-streams pod also copies every updated run to ClickHouse, during the migrations and for a few minutes after, so new results can take longer to process. + +The scheduler pod reports ready while the migrations run, so a finished rollout doesn't mean they are done. Follow the scheduler's log: + +```bash +kubectl logs -n currents deploy/currents-scheduler -f +``` + +The migrations are done when it logs `Startup tasks complete`. A few `Kept the existing index` warnings on the way are expected. If a migration logs an error instead, such as a ClickHouse `Timeout error.`, restart the scheduler as described in [Database Migrations and Startup Tasks](./upgrading.md#database-migrations-and-startup-tasks). + +## Removed Values + +`currents.sso.saml.spCertKey` is removed; the API never read the certificate it pointed to. Signed AuthnRequests need only `spKeyKey`; upload the matching certificate to your IdP. + +## Artifact Expiry + +Currents does not delete artifacts. To limit how long they are kept, add a lifecycle rule to the artifact bucket as described in [Artifact expiry](./dependencies.md#artifact-expiry). + +## Rollback + +`helm rollback` returns the templates and images to `0.8.x`, but not the database: the migrations stay applied. To return the data to its state before the upgrade, restore the backup you took. Point your IdP back at the old ACS URL as well. diff --git a/docs/eks/upgrading.md b/docs/eks/upgrading.md index 626576d..cc794b6 100644 --- a/docs/eks/upgrading.md +++ b/docs/eks/upgrading.md @@ -8,6 +8,7 @@ For reference, the Docker Compose environment has a separate upgrade flow: [Upgr 1. Check the chart [CHANGELOG](https://github.com/currents-dev/helm-charts/blob/main/CHANGELOG.md) for the target release. 2. Review any breaking changes and required values or secrets updates. +3. Upgrading from `0.8.x` to `1.0.0`? Follow [Upgrading to Chart 1.0](./upgrade-1.0.md) first: it changes SAML SSO and needs a second bucket. Infrastructure backups and third-party dependencies are customer-managed. See [Currents Service Dependencies](./dependencies.md) and [Support Policy](../support.md).