diff --git a/content/operate/iris/agent-playbook/self-managed/authentication.md b/content/operate/iris/agent-playbook/self-managed/authentication.md index ddb090c4fb..d99a0f19ce 100644 --- a/content/operate/iris/agent-playbook/self-managed/authentication.md +++ b/content/operate/iris/agent-playbook/self-managed/authentication.md @@ -140,4 +140,4 @@ The planes cache the JWKS for 10 minutes and allow 5 minutes of clock skew. If a ## OIDC access to agent-key administration -The Identity Service has its own OIDC policy for its control API, configured on the Identity Service release with `controlAuth.oidc` and `controlAuth.roleBindings`. Configure it when operators must manage agent keys without the Identity Service control token. The control token remains available as a deployment-administration credential. See [operator sign-in with your identity provider](/content/operate/iris/identity-service/authentication.md#operator-sign-in-with-your-identity-provider) in the Identity Service guide. +The Identity Service control API accepts the same IdP access tokens, configured once for the suite with `global.iris.auth.oidc` and `global.iris.auth.roleBindings`. Configure it when operators must manage agent keys without the Identity Service control token. The control token remains available as a deployment-administration credential. See [operator sign-in with your identity provider](/content/operate/iris/identity-service/self-managed/authentication.md#operator-sign-in-with-your-identity-provider) in the Identity Service guide. diff --git a/content/operate/iris/context-gateway/self-managed/authentication.md b/content/operate/iris/context-gateway/self-managed/authentication.md index 55121b94ac..2ca0a06fa3 100644 --- a/content/operate/iris/context-gateway/self-managed/authentication.md +++ b/content/operate/iris/context-gateway/self-managed/authentication.md @@ -20,7 +20,7 @@ hideListLinks: true | Data Plane runtime token | Data Plane → Identity Service | The Secret in `dataplane.runtimeToken` / `token` | No | Checking agent keys for `context-gateway` | | Identity Service control token | Operator → Identity Service (`:9200`) | Set on the Identity Service release. By default the Identity Service chart generates it (`control.autoGenerate: true`), so read it from the Secret the chart generates | Yes, by default | Creating, listing, rotating, and revoking agent keys | | Agent key | Agent → Data Plane (`:8083`) | None. Returned once when minted or rotated | No | Listing and calling tools on the Gateways its grants name | -| Identity provider access token | Agent → Data Plane (`:8083`) | None. Issued by your identity provider | No | The Gateways its role or subject grants name | +| Identity provider access token | Agent → Data Plane (`:8083`) | None. Issued by your identity provider | No | With the `gateway_user` role, every Gateway in the tenant. Without it, none | The admin, internal, and runtime tokens must be distinct. An agent key gets `401` from the Control Plane. No credential that an agent presents is sent to an Upstream. @@ -86,12 +86,9 @@ MCP clients then need to sign in again to receive tokens for the new audience. ### Grant signed-in users access -Grant signed-in users access in either way: +Map an IdP group to the `gateway_user` role in `role_mappings`. The role grants `use` on every Gateway in the tenant. -- Map an IdP group to the `gateway_user` role. The role grants `use` on every Gateway in the tenant. -- Assign Identity Service subject grants to individual users for selected Gateways, with `POST /v1/subject-grants/apply` on the Identity Service. See [subject grants](/content/operate/iris/identity-service/authentication.md#subject-grants) for the request. A subject grant names the IdP's issuer, the user's `user_id_claim` value, and the same product, resource type, Gateway ID, and `use` action as an agent-key grant. - -A specific subject grant cannot narrow the all-Gateway role, and removing a subject grant does not remove access that a role gives. To restrict a user to selected Gateways, don't map them to `gateway_user`. +In a `redis-iris` installation the Data Plane can't read Identity Service [subject grants](/content/operate/iris/identity-service/self-managed/authentication.md#subject-grants), so they have no effect. Signed-in users can't be limited to selected Gateways, and a signed-in user without `gateway_user` can't use any Gateway. To give an agent access to selected Gateways only, give it an agent key with a grant for each one. diff --git a/content/operate/iris/context-gateway/self-managed/deploy.md b/content/operate/iris/context-gateway/self-managed/deploy.md index e31178e7f3..f5bc226bf2 100644 --- a/content/operate/iris/context-gateway/self-managed/deploy.md +++ b/content/operate/iris/context-gateway/self-managed/deploy.md @@ -30,13 +30,13 @@ kubectl create namespace ### Configure the Identity Service release first -For every Identity Service setting, and for running one release that serves several products, see the [Identity Service guide](/content/operate/iris/identity-service/_index.md). +These values apply to a standalone Identity Service release. The [`redis-iris` suite chart](/content/operate/iris/self-managed/_index.md) sets them for you, and fails the render if a value you set differs from the one it derives. See [What the chart sets for you](/content/operate/iris/identity-service/self-managed/deploy.md#what-the-chart-sets-for-you). Set these values on the Identity Service release so that it checks Gateway grants and accepts the Data Plane's runtime token: | Identity Service value | Setting | | --- | --- | -| `runtime.serviceCredentials[]` | A credential for the Data Plane, `name: context-gateway-dp` and `subject: context-gateway-dataplane`, with `allowedOperations: [api-key-introspect]` and `allowedProducts: [context-gateway]`, from the same Secret as `dataplane.runtimeToken`. With Data Plane sign-in on, add `subject-grant-lookup` to `allowedOperations`. | +| `runtime.serviceCredentials[]` | A credential for the Data Plane, `name: context-gateway-dp` and `subject: context-gateway-dataplane`, with `allowedOperations: [api-key-introspect]` and `allowedProducts: [context-gateway]`, from the same Secret as `dataplane.runtimeToken`. The `redis-iris` suite sets this credential and fails the render if you set it. | | `productValidation.products.context-gateway.baseURL` | The Control Plane Service, for example `http://gateway-redis-context-gateway-controlplane:9100`. | | `productValidation.products.context-gateway.credential` | The same Secret as `controlplane.internalToken`. | | `productValidation.products.context-gateway.enabled` | `true` | diff --git a/content/operate/iris/identity-service/_index.md b/content/operate/iris/identity-service/_index.md index 493eba13b3..d874cc192d 100644 --- a/content/operate/iris/identity-service/_index.md +++ b/content/operate/iris/identity-service/_index.md @@ -1,6 +1,6 @@ --- title: Identity Service -description: Deploy and operate the Identity Service, which issues and checks agent keys for self-managed Redis Iris services. +description: Issue and check the agent keys that applications and agents present to Redis Iris services. linkTitle: Identity Service alwaysopen: false categories: @@ -11,16 +11,7 @@ weight: 29 hideListLinks: true --- -> [!NOTE] -> The [`redis-iris` suite chart](/content/operate/iris/self-managed/_index.md), the supported self-managed install, always installs the Identity Service and registers each service with it. Installing the `iris-identity-service` chart on its own isn't a supported install path. These guides document its values, which you set under the suite chart's `identityService:` key. - -The Identity Service issues the agent keys that applications and agents present to self-managed Redis Iris services, and checks those keys for each service's data plane. Every self-managed Iris service connects to it, and one Identity Service release can serve all of them. - -This guide is for the platform administrator who installs the Identity Service, registers each service with it, and manages agent keys and operator access. - -> [!NOTE] -> The Identity Service needs no license key of its own. Each Iris service you connect to it needs a license. Contact your Redis representative or -> [contact sales](https://redis.io/contact/). +The Identity Service issues the agent keys that applications and agents present to Redis Iris services, and checks those keys for each service's data plane. It keeps the keys and their grants, and no service data. ## Deployment options @@ -30,58 +21,6 @@ On Redis Cloud, agent keys are managed for you: there is no Identity Service to ### Self-managed -Deploy the Identity Service on Kubernetes to run it on your own infrastructure, before or alongside the services that use it. - -[Start with the deployment guide](/content/operate/iris/identity-service/deploy.md). - -## Which services use it - -Each of these services' self-managed guides installs the Identity Service as a separate release before the service: - -| Service | `product` in grants | `resourceType` in grants | Guide | -| --- | --- | --- | --- | -| Agent Memory | `memory` | `mem-store` | [Self-managed Agent Memory](/content/operate/iris/agent-memory/self-managed/_index.md) | -| Agent Playbooks | `playbook` | `playbook` | [Self-managed Agent Playbooks](/content/operate/iris/agent-playbook/self-managed/_index.md) | -| Context Gateway | `context-gateway` | `gateway` | [Self-managed Context Gateway](/content/operate/iris/context-gateway/self-managed/_index.md) | -| Intent Router | `intent` | `intent-router` | [Self-managed Intent Router](/content/operate/iris/intent-router/self-managed/_index.md) | -| Lens | `lens` | `lens-instance` | [Self-managed Lens](/content/operate/iris/lens/self-managed/_index.md) | - -If you already run an Identity Service release for one service, register the next service on that release instead of installing a second one. See [Register services](/content/operate/iris/identity-service/configuration.md#register-services). - -## What you deploy - -The `iris-identity-service` Helm chart deploys one Deployment and one ClusterIP Service on port `9200`. The guides install it as release `ids`, so its Service is `ids-iris-identity-service`. - -| Component | Purpose | Provided by | -| --- | --- | --- | -| Identity Service | Issues, lists, rotates, and revokes agent keys, and checks them for the service data planes. | The chart | -| Control token Secret | The rescue credential for the Identity Service control API. | The chart generates it, or you supply it | -| Metadata Redis | Stores the hashed agent keys, their grants, and subject grants. | You | -| Runtime credentials and internal tokens | Shared Secrets between the Identity Service and each service. | You | - -The chart deploys no Redis, no service, no Ingress, and no NetworkPolicy. The Identity Service has no TLS listener: terminate Transport Layer Security (TLS) in your ingress, service mesh, or load balancer. - -## How it fits with the services - -The Identity Service listens on one port and serves three groups of routes. Each group accepts a different credential, sent as `Authorization: Bearer `. - -| Route group | Routes | Caller | Credential | -| --- | --- | --- | --- | -| Control | `/v1/api-keys`, `/v1/products`, `/v1/subject-grants` | Operators and automation | The control token, or an operator's identity provider (IdP) access token | -| Runtime | `/v1/auth/*` | Service data planes | The service's runtime credential | -| Operational | `/health/liveness`, `/health/readiness` | Kubernetes probes and monitoring | None | - -1. An operator creates a resource in a service's control plane, such as an Agent Memory store or a Router in Intent Router. -1. The operator mints an agent key for that resource through the Identity Service control API. -1. Before it stores the key, the Identity Service asks the service's control plane to confirm that the resource exists and that the actions are valid. This step is called grant validation. It uses the control plane's internal token. -1. The Identity Service returns the key once. It stores only a hash of the key. -1. The agent sends the key to the service's data plane. The data plane checks it with the Identity Service, using its runtime credential, and caches the answer. - -The Identity Service keeps no service data. - -## Pages in this guide +The [`redis-iris` suite chart](/content/operate/iris/self-managed/_index.md) always installs the Identity Service, and registers each service you enable with it. -- [Deploy](/content/operate/iris/identity-service/deploy.md): prerequisites, install, and verification. -- [Configuration](/content/operate/iris/identity-service/configuration.md): the metadata Redis, the tenant, service registration, and the other Helm values. -- [Authentication and authorization](/content/operate/iris/identity-service/authentication.md): credentials, roles, operator sign-in with your IdP, and subject grants. -- [Operations](/content/operate/iris/identity-service/operations.md): the agent-key lifecycle, credential rotation, upgrades, backups, and troubleshooting. +[Start with the self-managed guide](/content/operate/iris/identity-service/self-managed/_index.md). diff --git a/content/operate/iris/identity-service/authentication.md b/content/operate/iris/identity-service/authentication.md deleted file mode 100644 index 149c5ed653..0000000000 --- a/content/operate/iris/identity-service/authentication.md +++ /dev/null @@ -1,213 +0,0 @@ ---- -Title: Authentication and authorization -alwaysopen: false -categories: -- docs -- operate -- iris -description: Understand the Identity Service credentials and roles, let operators sign in with your identity provider, and give IdP users subject grants. -linkTitle: Authentication and authorization -weight: 60 -hideListLinks: true ---- - -The Identity Service uses separate credentials for each job: operators manage agent keys through its control API, service planes call its runtime API, and agents present agent keys to service data planes. This page lists each credential and the roles on the control API. It then shows how to let operators sign in with your identity provider (IdP) and how to give IdP users access with subject grants. - -## Credentials - -| Credential | Who presents it to whom | Secret / key | What it unlocks | -| --- | --- | --- | --- | -| Control token | Operator → Identity Service control API (`:9200`) | `control.existingSecret` / `token`, or the generated `-iris-identity-service-control-token` | Every control operation: create, list, update, rotate, and revoke agent keys, and manage subject grants. | -| Operator access token | Operator → Identity Service control API | None. Issued by your IdP | The control operations of the operator's roles, from `controlAuth.roleBindings`. | -| Workload token | Kubernetes workload → Identity Service control API | None. A projected service account token | Subject grant operations only, from `controlAuth.workloadIdentity`. | -| Runtime credential | Service data plane → Identity Service runtime API | The shared Secret in a `runtime.serviceCredentials[]` entry | Checking agent keys, and reading subject grants when allowed, for the entry's services. | -| Internal token | Identity Service → service control plane | The shared Secret in `productValidation.products..credential` | Grant validation: confirming that a resource exists before a key is stored. | -| Agent key | Agent → service data plane | None. Returned once when minted or rotated | The resources and actions its grants name. | - -Each credential needs its own value. The Identity Service doesn't start when two credentials have the same value, and the chart refuses to render when two reference the same Secret key. - -The control token is a shared secret, and the audit log can't name a person for it. Keep it for setup and recovery, and let operators sign in with your IdP for daily work. - -## Agent keys and grants - -An agent key looks like `iris_.`. The Identity Service returns the full key only in the response to a mint or a rotation, and stores only a SHA-256 hash of it. If you lose a key, rotate it or mint a new one. - -Each key carries one or more grants: - -| Grant field | Meaning | Example | -| --- | --- | --- | -| `tenant` | The installation's tenant. If you omit it, the Identity Service adds `runtime.apiKeyTenant.singleTenantID`. | `default` | -| `product` | The Iris service. | `memory` | -| `resourceType` | The type of resource in that service. | `mem-store` | -| `resourceId` | One resource. Wildcards aren't accepted. | `` | -| `actions` | What the key can do with the resource. | `["read", "write"]` | - -| Service | `product` | `resourceType` | Actions | -| --- | --- | --- | --- | -| Agent Memory | `memory` | `mem-store` | `read`; `write` (includes `read`); `full` (includes `write`) | -| Agent Playbooks | `playbook` | `playbook` | `read`; `write`, `publish`, and `propose` (each includes `read`); `full` (includes all) | -| Context Gateway | `context-gateway` | `gateway` | `use`; `full` (includes `use`) | -| Intent Router | `intent` | `intent-router` | `read`; `write` (includes `read`); `full` (includes `write`) | -| Lens | `lens` | `lens-instance` | `ingest` and `read`, independent of each other; `full` (includes both) | - -All the grants of one key use the same tenant. Each service's authentication page explains what its actions allow. To mint and manage keys, see [Manage agent keys](/content/operate/iris/identity-service/operations.md#manage-agent-keys). - -## Roles on the control API - -| Caller | How it gets the role | What it can do | -| --- | --- | --- | -| Control token | It holds the token | Every operation. | -| `suite_admin` | A role binding with `scope: global` | Every operation for every service, in its own tenant only. | -| `product_admin` | A role binding with `scope: product` and a `product` | Every operation for that service, in its own tenant only. | -| `subject_grant_admin` | A workload identity subject | Subject grant operations only, in its own tenant. | -| `deployment_admin` | A role binding with `scope: deployment` and an `issuer` | Support work across tenants: list, read, and revoke keys, and list and delete subject grants. It can't create, update, or rotate keys, or add grants. An installation with one tenant doesn't need it. | -| Any other caller | None | Nothing. The request gets `403`. | - -The service control-plane roles, such as `operator` and `viewer`, give no access to the Identity Service control API. An operator who must mint keys needs `suite_admin` or `product_admin` here, in addition to their role on the service's control plane. - -If one of an operator's roles is `deployment_admin`, that operator can't create, update, or rotate keys, also in their own tenant and also when they have another role. Give `deployment_admin` only to people who don't need those operations. - -## Operator sign-in with your identity provider - -Operators can call the control API with an access token from your IdP, using OpenID Connect (OIDC). The audit log then names each operator, and you can grant access by IdP group. The control token keeps working. - -### 1. Register Iris in your IdP - -1. Register one API for the whole Iris suite with one audience, for example `api://redis-iris`. On Microsoft Entra ID v2, the audience is the API's application (client) ID. Use the same audience that the service control planes accept. -1. Create groups or app roles for key administrators, for example `iris-suite-admins` and `memory-admins`, and assign operators to them. -1. Make sure the group or role values reach the access token. On Entra ID, prefer app roles: raw `groups` claims arrive as IDs and are dropped for users in many groups. On Okta, add a groups claim with a filter. -1. Restrict the application to the assigned groups. On Entra ID, set **User assignment required** to **Yes**. - -### 2. Configure `controlAuth` - -Add these values to `values-ids.yaml`: - -```yaml -controlAuth: - oidc: - enabled: true - issuer: https:/// - jwks_uri: https:/// - audience: [api://redis-iris] - roleBindings: - - {claim_value: iris-suite-admins, scope: global, role: suite_admin} - - {claim_value: memory-admins, scope: product, product: memory, role: product_admin} -``` - -- The fields under `oidc` use snake_case, because the chart copies them into the service configuration as written. -- `issuer` must equal the token's `iss` claim exactly, including its scheme. The Identity Service never fetches it. On Active Directory Federation Services (AD FS), use the discovery document's `access_token_issuer`. -- `jwks_uri`, the JSON Web Key Set (JWKS) URI, must use `https://`. The pod must reach it and trust its CA. See [TLS, FIPS, and network](/content/operate/iris/identity-service/configuration.md#tls-fips-and-network). -- Role bindings match the values of the `roles` and `groups` claims by default. Set `role_claims` under `oidc` to read other claims. A `claim_value` matches without regard to letter case. -- With `runtime.apiKeyTenant.singleTenantID` set, the chart adds the tenant for you. Rendering fails if you also set `tenant_id_claim` or a different `static_tenant_id`. -- The control API refuses `allow_unmapped_roles`. Use `roleBindings` for every role. - -### 3. Apply and check - -1. Run `helm upgrade ids redis-ai/iris-identity-service --version 1.0.0 -n -f values-ids.yaml`. -1. Get an access token for an operator from your IdP. -1. Call the control API with it: - - ```bash - curl -sS -o /dev/null -w '%{http_code}\n' \ - -H "Authorization: Bearer " \ - "$IRIS_IDENTITY_URL/v1/api-keys" - ``` - -| Status | Meaning | -| --- | --- | -| `200` | The token is valid and a role binding matches. | -| `401` | The issuer, audience, or signature is wrong, or the token expired. Compare the token's `iss` and `aud` claims with `controlAuth.oidc`. | -| `403` | The token is valid, but no role binding matches its groups, or the role doesn't cover this operation or service. | -| `503` | The Identity Service can't reach the JWKS URI. The control token still works. | - -The `iris` command-line interface (CLI) accepts an operator access token in `IRIS_IDENTITY_TOKEN` in place of the control token. - -### Remove a role binding - -To revoke a binding that gives too much access, add a [policy overlay](/content/operate/iris/identity-service/configuration.md#configuration-overlays) Secret with the same binding and `role: none`, then change `secrets.checksum` and run `helm upgrade`. - -## Subject grants - -A subject grant gives an IdP user or workload access to a service resource without an agent key. The service's data plane checks the user's token itself, then asks the Identity Service for the grants of that subject. The Identity Service never sees the user's token. - -Context Gateway reads subject grants for users who sign in with your IdP. Agent Memory and Agent Playbooks read them only for service subjects. Lens and Intent Router don't use them. - -To give IdP users access to a Context Gateway: - -1. Add `subject-grant-lookup` to the data plane's runtime credential: - - ```yaml - runtime: - serviceCredentials: - - name: context-gateway-dp - subject: context-gateway-dataplane - allowedOperations: [api-key-introspect, subject-grant-lookup] - allowedProducts: [context-gateway] - existingSecret: gateway-runtime - secretKey: token - ``` - -1. Allow the grant shape: - - ```yaml - subjectGrants: - allowed: - - {product: context-gateway, resourceType: gateway, actions: [use]} - ``` - -1. Run `helm upgrade` for the Identity Service release. -1. Give the grant to the user. The `subject` is the value of the user's `sub` claim, or of the claim that the data plane reads as the user ID: - - ```bash - curl -sS -X POST "$IRIS_IDENTITY_URL/v1/subject-grants/apply" \ - -H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" \ - -H 'Content-Type: application/json' \ - -d '{ - "issuer": "https:///", - "subject": "", - "product": "context-gateway", - "grants": {"gateway": {"": {"actions": ["use"]}}} - }' - ``` - - The control token must send `issuer`. An operator who signs in with the IdP can omit it, and the Identity Service uses the issuer of the operator's token. - -1. To remove the grant, send the same request with `"": null`. The data plane can keep using its cached grants for up to `runtime.cache.hardTtlSeconds`. - -The issuer must equal the user token's `iss` claim exactly. Don't use an email address as the subject, because a user can change it. - -These resource types accept subject grants: - -| `resourceType` | Allowed actions | -| --- | --- | -| `mem-store` | `read`, `write`, `full`, `read_all_owners` | -| `playbook` | `read`, `propose` | -| `gateway` | `use`, `full` | - -If you remove a shape from `subjectGrants.allowed`, the Identity Service stops returning grants of that shape after its next restart. It doesn't delete them. - -## Workload identity - -A Kubernetes workload can manage subject grants with its own projected service account token instead of the control token: - -```yaml -controlAuth: - workloadIdentity: - enabled: true - issuer: - jwks_uri: - audience: [] - subjects: - - subject: system:serviceaccount:: - roles: [subject_grant_admin] -``` - -Take the issuer and the JWKS URI from the cluster's `/.well-known/openid-configuration`. A subject can't get `suite_admin`. A subject without a `tenant` gets `runtime.apiKeyTenant.singleTenantID`. - -Agent Playbooks extraction from Agent Memory needs no workload identity on the Identity Service. - -## Recover from a lockout - -- **Your IdP is down, or the role bindings are wrong.** Use the control token. It always works. -- **You lost the control token, and the chart generated it.** Delete its Secret, if it still exists, and run `helm upgrade`. The chart generates a new token. If you supply the token with `control.existingSecret`, put a new value in your Secret. The Identity Service reads the token file on each request, so the new value works after Kubernetes updates the mounted file, in about a minute. -- **You lost the metadata Redis.** Restore it from your backup. See [Back up and restore](/content/operate/iris/identity-service/operations.md#back-up-and-restore). diff --git a/content/operate/iris/identity-service/configuration.md b/content/operate/iris/identity-service/configuration.md deleted file mode 100644 index d59becadeb..0000000000 --- a/content/operate/iris/identity-service/configuration.md +++ /dev/null @@ -1,221 +0,0 @@ ---- -Title: Configure the Identity Service -alwaysopen: false -categories: -- docs -- operate -- iris -description: Configure the Identity Service metadata Redis, tenant, service registration, agent-key settings, and TLS. -linkTitle: Configuration -weight: 30 -hideListLinks: true ---- - -You configure the Identity Service with the Helm values of its own release, in `values-ids.yaml`. No service chart reads these values or checks them against its own, so keep the Secret names, Service addresses, and tenant consistent across releases yourself. - -The Identity Service reads its configuration only when it starts. A changed value rolls the pod at `helm upgrade`. A changed Secret that you own doesn't roll the pod unless you also change its checksum value. See [Rotate credentials](/content/operate/iris/identity-service/operations.md#rotate-credentials). - -To see every value and its default, run: - -```bash -helm show values redis-ai/iris-identity-service --version 1.0.0 -``` - -## Metadata Redis - -The metadata Redis holds the hashed agent keys, their grants, and subject grants. If you lose it, every agent loses access. - -| Value | Default | Meaning | -| --- | --- | --- | -| `metadata.existingSecret` | `""` | Required. The Secret that holds the metadata Redis connection. | -| `metadata.secretKey` | `metadata.yaml` | The key in that Secret. | -| `metadata.existingSecretChecksum` | `""` | Optional. Change it to roll the pod after you edit the Secret. | - -The Secret's key holds a configuration fragment: - -```yaml -metadata: - urls: ["rediss://:@:"] -``` - -- The Identity Service always uses database 0. It ignores a `/` index in the URL. -- Don't change the host after the first install. The Identity Service then reads an empty database, and every key stops working. To move the data, follow [Move the metadata Redis](/content/operate/iris/identity-service/operations.md#move-the-metadata-redis). -- Turn on persistence (append-only file or RDB snapshots) and keep the data directory on durable storage. Without persistence, a Redis restart deletes every key. - -## Control token - -| Value | Default | Meaning | -| --- | --- | --- | -| `control.existingSecret` | `""` | Your own Secret with the control token. | -| `control.secretKey` | `token` | The key in that Secret. | -| `control.autoGenerate` | `true` | Generate `-iris-identity-service-control-token` when `existingSecret` is empty. | -| `control.existingSecretChecksum` | `""` | Optional. Change it to roll the pod after you edit your Secret. | - -The control token always works, also when operator sign-in is on. Give only a small group access to its Secret. For what it can do, see [Credentials](/content/operate/iris/identity-service/authentication.md#credentials). - -For GitOps tools such as Argo CD or Flux, supply the control token with `control.existingSecret`. These tools render the chart without cluster access, so a generated token changes on every render. - -## Tenant - -| Value | Default | Meaning | -| --- | --- | --- | -| `runtime.apiKeyTenant.singleTenantID` | `""` | The one tenant of the installation, for example `default`. | - -Set `singleTenantID` to the same value as the `tenantId` of every service release. The Identity Service adds this tenant to each grant that names no tenant, and rejects a grant that names another tenant with `400`. - -A tenant ID is 1 to 256 characters, without `|` or control characters. Matching is exact and case-sensitive. For a numeric ID, quote it in YAML or set it with `--set-string`, so Helm keeps it as a string. You can't change the tenant after you mint keys: keys, grants, and service data keep the old ID. - - - -## Register services - -Each service needs two entries on the Identity Service release: - -- A runtime credential in `runtime.serviceCredentials`, so the service's data plane can check agent keys. -- A grant validator in `productValidation.products.`, so the Identity Service can check the service's resources before it stores a grant. - -This table lists the entries for each service, with the release names and Secret names used in the associated deployment guide: - -{{< table-scrollable >}} -| Service | `serviceCredentials` `name` and `subject` | `allowedProducts` | Validator key | Validator `baseURL` | Runtime credential Secret | Internal token Secret | -| --- | --- | --- | --- | --- | --- | --- | -| Agent Memory | `memory-dp` | `[memory]` | `memory` | `http://redis-agent-memory-controlplane:9100` | `memory-dp-credential` | `memory-internal-token` | -| Agent Playbooks | `playbook-dp` | `[playbook]` | `playbook` | `http://playbook-controlplane:9200` | `playbook-dp-credential` | `playbook-controlplane-internal` | -| Context Gateway | `context-gateway-dp`, subject `context-gateway-dataplane` | `[context-gateway]` | `context-gateway` | `http://gateway-redis-context-gateway-controlplane:9100` | `gateway-runtime` | `gateway-grant-validation` | -| Intent Router | `intent-dp` | `[intent]` | `intent` | `http://intent-redis-intent-router-controlplane:9400` | `intent-dp-credential` | `intent-controlplane-internal` | -| Lens | `lens-dataplane` | `[lens]` | `lens` | `http://lens-redis-lens-controlplane:9300` | `lens-dp-credential` | `lens-internal-token` | -{{< /table-scrollable >}} - -The runtime credential Secret is the same Secret that the service's data plane mounts, and the internal token Secret is the same Secret that its control plane mounts. If you install a service under another release name or `fullnameOverride`, change the validator address to match. For the service-side values, see each service's deploy guide. - -### Serve several services from one release - -One release serves every service. Write all entries in one values file. Helm replaces lists instead of merging them, so each `helm upgrade` must restate every `serviceCredentials` entry, not only the new one. - -This example serves Agent Memory and Intent Router: - -```yaml -runtime: - unscopedGrantsProduct: memory - apiKeyTenant: - singleTenantID: default - serviceCredentials: - - name: memory-dp - subject: memory-dp - allowedOperations: [api-key-introspect] - allowedProducts: [memory] - existingSecret: memory-dp-credential - secretKey: token - - name: intent-dp - subject: intent-dp - allowedOperations: [api-key-introspect] - allowedProducts: [intent] - existingSecret: intent-dp-credential - secretKey: token -productValidation: - products: - memory: - baseURL: http://redis-agent-memory-controlplane:9100 - credential: - existingSecret: memory-internal-token - secretKey: token - intent: - enabled: true - baseURL: http://intent-redis-intent-router-controlplane:9400 - credential: - existingSecret: intent-controlplane-internal - secretKey: token -``` - -To add a service to a running release: - -1. Install the service, or note its control-plane Service and Secret names. -1. Add the service's runtime credential and validator to `values-ids.yaml`, next to the existing entries. -1. Run `helm upgrade ids redis-ai/iris-identity-service --version 1.0.0 -n -f values-ids.yaml`. -1. Check that `GET /v1/products` lists the new service. - -Every service on one release uses the same tenant. - -### Runtime credential fields - -| Field | Meaning | -| --- | --- | -| `name` | Required. Unique, 1 to 44 lowercase letters, digits, and dashes, with no trailing dash. It also names the mount directory. | -| `subject` | Required. The identity the data plane authenticates as. | -| `allowedOperations` | Required. One or more of the operations in the next table. | -| `allowedProducts` | Required. The services whose keys this credential may check. | -| `existingSecret`, `secretKey` | The shared Secret and its key (default `token`). | -| `existingSecretChecksum` | Optional. Change it to roll the pod after you edit the Secret. | -| `autoGenerate`, `generatedSecretName` | For development only. The chart generates the token instead of reading your Secret. | - -| Operation | Use | -| --- | --- | -| `api-key-introspect` | Check an agent key. Every data plane needs it. | -| `subject-grant-lookup` | Read subject grants. Context Gateway needs it when IdP sign-in for agents is on. See [Subject grants](/content/operate/iris/identity-service/authentication.md#subject-grants). | -| `auth-me` | Let the caller check its own credential. | -| `auth-introspect` | An earlier delegated token check. Don't use it for new installations. | - -### Grant validator fields - -| Field | Meaning | -| --- | --- | -| `enabled` | Default `true`. A disabled entry renders nothing. | -| `baseURL` | Required. An absolute `http://` or `https://` URL of the service's control plane, with no user information, query, or fragment. | -| `credential.existingSecret`, `credential.secretKey` | Required. The control plane's internal token Secret and its key (default `token`). | -| `credential.existingSecretChecksum` | Optional. Change it to roll the pod after you edit the Secret. | - -The Identity Service calls the validator with a 5-second timeout and doesn't follow redirects. If the control plane doesn't answer, minting fails with `424`. - -`runtime.unscopedGrantsProduct` names the service that owns grants stored before grants carried a service. Set it to `memory` when the release serves Agent Memory. When set, it must name an enabled validator. - -## Agent keys and the key cache - -| Value | Default | Meaning | -| --- | --- | --- | -| `apiKeys.maxRotateGraceSeconds` | `604800` (7 days) | The longest time an old key can stay valid after a rotation. | -| `apiKeys.userBinding.enabled` | `false` | Lets a mint bind a key to a user with `userId`. Agent Memory uses it for owner isolation. | -| `runtime.cache.softTtlSeconds` | `180` | When a data plane refreshes a cached key check. Must not exceed `hardTtlSeconds`. | -| `runtime.cache.hardTtlSeconds` | `300` | The longest time a data plane uses a cached key check. A revoked key can keep working for up to this time. | - -## Operator sign-in and subject grants - -| Value | Default | Meaning | -| --- | --- | --- | -| `controlAuth.oidc` | Not set | Operator sign-in to the control API with access tokens from your identity provider (IdP). | -| `controlAuth.roleBindings` | Not set | Maps IdP group or role claims to Identity Service roles. Requires `controlAuth.oidc.enabled`. | -| `controlAuth.workloadIdentity` | `{}` | Lets a Kubernetes workload call the control API with its service account token. | -| `subjectGrants.allowed` | `[]` | The grant shapes that subject grants may have. Empty allows none. | - -See [Authentication and authorization](/content/operate/iris/identity-service/authentication.md) for how to set them. - -## TLS, FIPS, and network - -- **Private certificate authority (CA).** To reach a `rediss://` metadata Redis, an `https://` validator, or an IdP signed by a private CA, put the PEM bundle in a Secret and set `tls.caCertSecret`. The key defaults to `ca.crt` (`tls.caCertKey`). The Identity Service trusts the bundle in addition to the system CAs. The chart has no option to skip certificate verification. - - ```bash - kubectl -n create secret generic ids-ca --from-file=ca.crt=./ca.crt - ``` - -- **Federal Information Processing Standards (FIPS).** Set `security.profile: fips`. The Identity Service then refuses to start unless the metadata Redis URL is `rediss://` and every validator `baseURL` is `https://`. -- **Network.** The chart deploys no NetworkPolicy and no Ingress. Let only the service planes, your operators, and your automation reach port `9200`. The health routes need no credential, so don't expose them outside the cluster. -- **Another namespace.** If the Identity Service runs in another namespace than a service, use fully qualified Service names, such as `http://redis-agent-memory-controlplane..svc:9100`, and create each shared Secret in both namespaces with the same value. - -## Availability and resources - -| Value | Default | Meaning | -| --- | --- | --- | -| `replicaCount` | `1` | Identity Service replicas. The service keeps its state in the metadata Redis. | -| `resources` | Requests `100m` CPU and `128Mi` memory; limits `500m` CPU and `512Mi` memory | Container resources. | -| `nodeSelector`, `affinity`, `tolerations` | Empty | Pod scheduling. | -| `image.repository`, `image.tag` | `redislabs/iris-identity-service`, the chart version | Set them to pull from a private registry. | -| `imagePullSecrets` | `[]` | Pull Secrets for a private registry. | -| `podDisruptionBudget.enabled` | `false` | Add a PodDisruptionBudget with `maxUnavailable: 1` when `replicaCount` is above `1`. | - -Readiness checks the metadata Redis, and liveness doesn't. When the metadata Redis is unreachable, the pod turns unready and stops receiving traffic, but Kubernetes doesn't restart it. Readiness doesn't check the service control planes, so one service's outage doesn't take the Identity Service out of service. - -## Configuration overlays - -- **`configData`** adds Identity Service settings that have no Helm value. The chart owns the credential, service, tenant, cache, and key settings, and refuses credential wiring in `configData`. Set operator policy either in `controlAuth` or in `configData.auth`, never both. -- **Policy overlays** are Secrets that hold more configuration, such as role bindings, named by `secrets.secretName` and `secrets.additionalSecrets`. Each Secret holds the overlay under the key `overlay.yaml` (`secrets.overlayKey`). The Identity Service loads them in order, after the metadata Secret. An overlay can't change the control token, the runtime credentials, or the validators. Change `secrets.checksum` to roll the pod after you edit an overlay Secret. - -Role bindings in overlays merge by issuer, claim value, scope, and service. A later binding replaces a matching one, and a binding with `role: none` removes it. diff --git a/content/operate/iris/identity-service/deploy.md b/content/operate/iris/identity-service/deploy.md deleted file mode 100644 index eb8eb36f45..0000000000 --- a/content/operate/iris/identity-service/deploy.md +++ /dev/null @@ -1,186 +0,0 @@ ---- -Title: Deploy the Identity Service -alwaysopen: false -categories: -- docs -- operate -- iris -description: Install the Identity Service as its own Helm release, register a service with it, and check that it answers. -linkTitle: Deploy -weight: 50 -hideListLinks: true ---- - -> [!NOTE] -> The [`redis-iris` suite chart](/content/operate/iris/self-managed/_index.md), the supported self-managed install, always installs the Identity Service and registers each service with it. Installing the `iris-identity-service` chart on its own isn't a supported install path. These guides document its values, which you set under the suite chart's `identityService:` key. - -Follow these steps to install the Identity Service with the `iris-identity-service` Helm chart, register your first Iris service with it, and check that its control API accepts your control token. - -The examples install chart `1.0.0` as release `ids` in the same namespace as the services, so the Service is `ids-iris-identity-service:9200`. They register Agent Memory. For the other services, swap in their entries from [Register services](/content/operate/iris/identity-service/configuration.md#register-services). - -## Prerequisites - -| Requirement | Details | -| --- | --- | -| Kubernetes | A cluster and a namespace for the release. The tested layout puts the Identity Service in the same namespace as the services, because they share Secrets. | -| Helm | Helm 3, with the Redis AI repository `https://helm.redis.io/ai`. | -| Image | `redislabs/iris-identity-service` from Docker Hub, or a copy in your private registry. By default, the chart deploys the image tag that matches its version. | -| Metadata Redis | A Redis database for the Identity Service. Turn on persistence (append-only file or RDB snapshots) and keep its data on durable storage. It holds every agent key, so back it up. See [Metadata Redis](/content/operate/iris/identity-service/configuration.md#metadata-redis). | -| Tenant | One tenant ID for the installation, for example `default`. Every service release sets the same value as its `tenantId`. You can't change it later. | -| Service details | For each service you register: its control-plane Service address, its control-plane internal token Secret, and its data-plane runtime credential Secret. Each service's deploy guide names them. | -| Transport Layer Security (TLS) | The Identity Service has no TLS listener. For production, put a TLS endpoint in front of it and of each service control plane. | - -## 1. Create the namespace - -Skip this step if the services' namespace already exists. - -```bash -kubectl create namespace -``` - -## 2. Create the metadata Secret - -1. Save the metadata Redis connection as `ids-metadata.yaml`. Use `rediss://` for TLS, and put the Redis user and password in the URL: - - ```yaml - metadata: - urls: ["rediss://:@:"] - ``` - -1. Create the Secret. The key must be `metadata.yaml`: - - ```bash - kubectl -n create secret generic ids-metadata \ - --from-file=metadata.yaml=./ids-metadata.yaml - ``` - -## 3. Create the control token and shared Secrets - -1. Create the control token Secret. Only the Identity Service mounts it: - - ```bash - kubectl -n create secret generic ids-control-token \ - --from-literal=token="$(openssl rand -hex 32)" - ``` - - To let the chart generate the token instead, skip this step and omit `control` from the values file. The chart then creates `ids-iris-identity-service-control-token` and keeps it across upgrades and uninstalls. - -1. Create the service's data-plane runtime credential. The data plane and the Identity Service both mount it: - - ```bash - kubectl -n create secret generic memory-dp-credential \ - --from-literal=token="$(openssl rand -hex 32)" - ``` - -1. Create the service's control-plane internal token. The control plane and the Identity Service both mount it: - - ```bash - kubectl -n create secret generic memory-internal-token \ - --from-literal=token="$(openssl rand -hex 32)" - ``` - -Every credential needs its own value. The Identity Service doesn't start when two credentials have the same value. If you already followed a service's deploy guide, these Secrets exist: reuse them. - -## 4. Create `values-ids.yaml` - -Save this file. It registers Agent Memory over the in-cluster Service. For production, use the control plane's TLS endpoint and set `tls.caCertSecret`, as described in [TLS, Federal Information Processing Standards (FIPS), and network](/content/operate/iris/identity-service/configuration.md#tls-fips-and-network). - -```yaml -metadata: - existingSecret: ids-metadata -control: - existingSecret: ids-control-token - autoGenerate: false -runtime: - unscopedGrantsProduct: memory - apiKeyTenant: - singleTenantID: default - serviceCredentials: - - name: memory-dp - subject: memory-dp - allowedOperations: [api-key-introspect] - allowedProducts: [memory] - existingSecret: memory-dp-credential - secretKey: token -productValidation: - products: - memory: - baseURL: http://redis-agent-memory-controlplane:9100 - credential: - existingSecret: memory-internal-token - secretKey: token -``` - -`runtime.unscopedGrantsProduct: memory` is for Agent Memory only. Omit it when the release doesn't serve Agent Memory. - -## Before you install: check your values - -The chart's values schema is closed: an unknown key, service, or operation fails the install. Check each value before you install. If one is wrong, you see the symptom in its row. - -{{< table-scrollable >}} -| Check | Symptom | Fix | -| --- | --- | --- | -| `metadata.existingSecret` is set | Rendering fails | Name the metadata Secret. | -| `runtime.serviceCredentials` has at least one entry | Rendering fails | Add an entry for each service data plane. | -| `productValidation.products` has at least one enabled service | Rendering fails | Add a validator for each service control plane. | -| The control token, runtime credentials, and internal tokens use different Secret keys and different values | Rendering fails, or the pod doesn't start | Create a separate Secret for each. | -| `runtime.apiKeyTenant.singleTenantID` equals each service's `tenantId` | Minting a key fails with `400`, or agents get `403` | Set the same tenant in every release. | -| A numeric tenant ID is a string | Rendering fails, or the tenant doesn't match | Quote it in YAML, or use `--set-string`. | -{{< /table-scrollable >}} - -## 5. Install - -```bash -helm repo add redis-ai https://helm.redis.io/ai -helm repo update redis-ai -helm install ids redis-ai/iris-identity-service \ - --version 1.0.0 \ - --namespace \ - -f values-ids.yaml -``` - -The service's control plane can be missing at this point. The Identity Service starts without it, but minting a key for that service fails until the control plane answers grant validation. - -## 6. Verify the release - -1. Wait for the Deployment: - - ```bash - kubectl -n rollout status deployment/ids-iris-identity-service - ``` - -1. Run the chart test. It checks that the Identity Service is ready, which means it reaches the metadata Redis: - - ```bash - helm test ids --namespace - ``` - -1. In a separate terminal, forward the Service port and leave it running: - - ```bash - kubectl -n port-forward svc/ids-iris-identity-service 9200:9200 - ``` - -1. Load the control token: - - ```bash - export IRIS_IDENTITY_URL='http://localhost:9200' - export IRIS_IDENTITY_TOKEN="$(kubectl -n get secret \ - ids-control-token -o jsonpath='{.data.token}' | base64 --decode)" - ``` - - If the chart generated the token, read `ids-iris-identity-service-control-token` instead. `helm status ids -n ` prints the command for your release. - -1. List the registered services. The response names each service that has a grant validator: - - ```bash - curl -sS -H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" "$IRIS_IDENTITY_URL/v1/products" - ``` - -A passing `helm test` doesn't prove that the release can mint keys. Minting needs the service's control plane. After you install the service, mint a key as its deploy guide shows, or as described in [Mint an agent key](/content/operate/iris/identity-service/operations.md#mint-an-agent-key). - -## Next steps - -- Install the service and connect its data plane to `http://ids-iris-identity-service:9200`, or to the Identity Service's TLS endpoint. -- [Register more services](/content/operate/iris/identity-service/configuration.md#register-services) on the same release. -- [Let operators sign in with your identity provider](/content/operate/iris/identity-service/authentication.md#operator-sign-in-with-your-identity-provider), so they don't share the control token. diff --git a/content/operate/iris/identity-service/operations.md b/content/operate/iris/identity-service/operations.md deleted file mode 100644 index 1e6049b86f..0000000000 --- a/content/operate/iris/identity-service/operations.md +++ /dev/null @@ -1,287 +0,0 @@ ---- -Title: Operate the Identity Service -alwaysopen: false -categories: -- docs -- operate -- iris -description: Mint, list, rotate, and revoke agent keys, rotate credentials, audit changes, upgrade, back up, and troubleshoot the Identity Service. -linkTitle: Operations -weight: 90 -hideListLinks: true ---- - -This page covers the daily work on an Identity Service release: managing agent keys for every service, rotating credentials, and finding who changed what. It also covers upgrades, backups of the metadata Redis, uninstalling, and troubleshooting. - -The examples use release `ids` and a port-forward to its Service: - -```bash -kubectl -n port-forward svc/ids-iris-identity-service 9200:9200 -``` - -```bash -export IRIS_IDENTITY_URL='http://localhost:9200' -export IRIS_IDENTITY_TOKEN='' -``` - -When operator sign-in is on, use your own access token instead of the control token, so the audit log names you. See [Operator sign-in](/content/operate/iris/identity-service/authentication.md#operator-sign-in-with-your-identity-provider). - -## Check health - -The health routes need no credential: - -```bash -curl -sS "$IRIS_IDENTITY_URL/health/readiness" -``` - -Readiness returns `503` while the Identity Service can't reach the metadata Redis. `helm test ids -n ` runs the same check in the cluster. - -## Manage agent keys - -The control API manages the keys of every service. The `iris` command-line interface (CLI) reads `IRIS_IDENTITY_URL` and `IRIS_IDENTITY_TOKEN`. To set it up, see [Set up on a self-managed deployment](/content/develop/ai/context-engine/sdk-and-cli.md#set-up-on-a-self-managed-deployment). - -### Mint an agent key - -Find the resource ID in the service's control plane first, for example an Agent Memory store ID. Then mint the key: - -```bash -curl -sS -X POST "$IRIS_IDENTITY_URL/v1/api-keys" \ - -H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" \ - -H 'Content-Type: application/json' \ - -d '{ - "name": "support-agent", - "grants": [{ - "tenant": "default", - "product": "memory", - "resourceType": "mem-store", - "resourceId": "", - "actions": ["read", "write"] - }] - }' -``` - -The response returns `keyId` and `token`. The `token` is the agent key, and it's shown only once: give it to the agent through your secret store, and record only the `keyId`. - -Optional fields: - -- `expiresAt`: when the key stops working, in Unix seconds. Omitted or `0` means never. -- `userId`: the user that owns the key. It needs `apiKeys.userBinding.enabled: true`. - -For the grant of each service, see [Agent keys and grants](/content/operate/iris/identity-service/authentication.md#agent-keys-and-grants). With the CLI, each service's own command mints its keys, because it knows the grant the service needs. For example, `iris intent-router service key` mints a key for a Router. - -Before the Identity Service stores the key, it asks the service's control plane to validate the grant. A `400` means the resource doesn't exist in the tenant or an action isn't valid. A `424` means the Identity Service can't reach the control plane or its internal token is wrong. - -### List and inspect keys - -```bash -curl -sS -H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" \ - "$IRIS_IDENTITY_URL/v1/api-keys?product=memory&resourceId=" -curl -sS -H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" \ - "$IRIS_IDENTITY_URL/v1/api-keys/" -``` - -With the CLI: - -```bash -iris identity keys list --product memory --resource-id -iris identity keys show -``` - -`iris identity keys list` also filters by `--resource-type`. Neither the API nor the CLI ever returns a key's token after it's minted. - -The list is paginated. Over HTTP, pass `maxResults` and then each response's `nextToken`. The CLI takes `--limit`, and `--cursor` with the `nextCursor` value it prints; `--all` reads every page. - -### Update, rotate, or revoke a key - -```bash -# Change the grants, the name, or expiresAt. The token stays the same. -curl -sS -X PATCH "$IRIS_IDENTITY_URL/v1/api-keys/" \ - -H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" \ - -H 'Content-Type: application/json' \ - -d '{"grants": [{"product": "memory", "resourceType": "mem-store", "resourceId": "", "actions": ["read"]}]}' -``` - -```bash -# Rotate: get a new key; the old one keeps working for graceSeconds. -curl -sS -X POST "$IRIS_IDENTITY_URL/v1/api-keys//rotate?graceSeconds=3600" \ - -H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" -``` - -```bash -# Revoke: delete the key. -curl -sS -X DELETE "$IRIS_IDENTITY_URL/v1/api-keys/" \ - -H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" -``` - -With the CLI: - -```bash -iris identity keys rotate --grace 1h --write .env --env -iris identity keys revoke -``` - -`rotate` writes the new key to the dotenv file with mode `0600` and never prints it. `revoke` asks for confirmation unless you pass `--yes`, and `--product ` refuses keys that grant another service. - -| Operation | Use it when | What happens to the token | When the agent notices | -| --- | --- | --- | --- | -| Update | The agent needs other resources or actions, a new name, or a new expiry. | It doesn't change. | After the data plane's cache refreshes, within `runtime.cache.hardTtlSeconds` (300 seconds by default). | -| Rotate | Someone else might know the token, or your rotation schedule is due. | You get a new `keyId` and token with the same grants. The old token works for `graceSeconds` more (default 3600, at most `apiKeys.maxRotateGraceSeconds`), but never after its own `expiresAt`. The response's `oldExpiresAt` says when it stops. | The agent must switch to the new token before `oldExpiresAt`. | -| Revoke | Someone else has the token, or the agent no longer needs access. | The key is deleted. A second revoke returns `404`. | Within `runtime.cache.hardTtlSeconds`. | - -## Rotate credentials - -The Identity Service reads the control token, the runtime credentials, and the internal tokens from their mounted files on each request. A changed Secret takes effect after Kubernetes updates the mounted file, in about a minute, without a restart. While the files update, some calls can fail. - -| Credential | How to rotate | -| --- | --- | -| Control token | Put a new value in your Secret. For a generated token, delete its Secret and run `helm upgrade`. | -| Runtime credential | Update the Secret that the Identity Service and the data plane share, then change `existingSecretChecksum` on both releases and run `helm upgrade` on both. To avoid failed calls, follow [Rotate a runtime credential without failed calls](#rotate-a-runtime-credential-without-failed-calls). | -| Internal token | Update the Secret that the Identity Service and the control plane share, then change `productValidation.products..credential.existingSecretChecksum` and the service's matching checksum, and run `helm upgrade` on both. | -| Policy overlays | The Identity Service reads them only at startup. Change `secrets.checksum` and run `helm upgrade`. | -| Metadata Redis credential | Follow [Rotate the metadata Redis credential](#rotate-the-metadata-redis-credential). | -| Agent key | [Rotate it](#update-rotate-or-revoke-a-key) through the control API. | - -Make every new value unique. The Identity Service doesn't start when two credentials have the same value. - -### Rotate a runtime credential without failed calls - -The Identity Service accepts more than one entry for the same `subject`, so it can accept the old and the new value for a while. This example rotates the Agent Memory credential: - -1. Create a Secret with a new value: - - ```bash - kubectl -n create secret generic memory-dp-credential-v2 \ - --from-literal=token="$(openssl rand -hex 32)" - ``` - -1. Add a second entry with the same `subject` and a new `name`, and keep the old entry: - - ```yaml - runtime: - serviceCredentials: - - name: memory-dp - subject: memory-dp - allowedOperations: [api-key-introspect] - allowedProducts: [memory] - existingSecret: memory-dp-credential - - name: memory-dp-v2 - subject: memory-dp - allowedOperations: [api-key-introspect] - allowedProducts: [memory] - existingSecret: memory-dp-credential-v2 - ``` - -1. Run `helm upgrade` for the Identity Service release. -1. Point the data plane at the new Secret. For Agent Memory, set `identityService.credential.existingSecret: memory-dp-credential-v2` and run `helm upgrade` for Agent Memory. -1. Wait until every data-plane pod uses the new Secret, and check that agent calls work. -1. Remove the old entry from `values-ids.yaml` and run `helm upgrade` for the Identity Service release. -1. Delete the `memory-dp-credential` Secret. - -## Find who did what - -The Identity Service writes one log line with the message `audit event` for each control operation. It has no audit database, so collect the pod logs with your log system. To search the logs Kubernetes still keeps: - -```bash -kubectl -n logs -l app.kubernetes.io/instance=ids --tail=-1 \ - | grep '"audit event"' | grep '' -``` - -| Field | Meaning | -| --- | --- | -| `action` | The operation, for example `api_key.create`, `api_key.revoke`, or `subject_grant.patch`. | -| `result` | `allowed`, `denied`, or `failed`. | -| `subject`, `user_id`, `issuer` | The caller. | -| `auth_type` | `admin_token` for the control token. | -| `authorized_by` | The role that allowed the operation, for example `suite_admin`. | -| `key_id`, `target_subject` | The key or subject that the operation changed. | - -The log never contains tokens. A request rejected before it reaches the service, such as one with a malformed body, has no audit line. - -## Upgrade - -1. Read the release notes for the chart version. -1. Upgrade each service's control plane first. A newer Identity Service can send grant fields that an older control plane doesn't know. -1. Upgrade the Identity Service release with the same values file: - - ```bash - helm upgrade ids redis-ai/iris-identity-service \ - --version \ - --namespace \ - -f values-ids.yaml - ``` - -1. Run `helm test ids -n `, then mint and revoke a test key for each service. - -`helm rollback` restores the workload and its values. It doesn't change generated Secrets or the keys and grants in the metadata Redis. - - - -## Back up and restore - -The metadata Redis holds every agent key, grant, and subject grant. Back up database 0 of that Redis on a schedule, with persistence turned on. Also export the Secrets you created for the release, such as the control token, the runtime credentials, and the internal tokens, and store the export as you store database passwords. - -A restore brings back the keys and grants as they were at backup time: - -- Keys minted after the backup don't work. Mint them again and give them to their agents. -- Keys revoked after the backup work again. Revoke them again. - -### Move the metadata Redis - -Stop the Identity Service during the move. If it keeps running after the copy, it changes only the old Redis. - -1. Set `replicaCount` to `0` and run `helm upgrade`. -1. Copy the data to the new Redis. -1. Put the new host in `metadata.urls` in the metadata Secret. -1. Set `replicaCount` to its earlier value and run `helm upgrade`. - -### Rotate the metadata Redis credential - -The Identity Service reads the metadata Redis connection only at startup. - -1. In Redis, add the new user or password, and keep the old one valid. -1. Update the metadata Secret, and every service Secret that uses the same Redis. -1. Restart the Identity Service, and wait until it's ready: - - ```bash - kubectl -n rollout restart deployment/ids-iris-identity-service - kubectl -n rollout status deployment/ids-iris-identity-service - ``` - -1. Restart the service control planes, then their data planes and workers, if they use the same Redis. -1. Run `helm test ids -n `. -1. Remove the old credential from Redis. - -### Planned outage of the metadata Redis - -While the metadata Redis is down, the Identity Service turns unready, and no agent key can be checked, minted, or changed. Kubernetes doesn't restart the pod. When Redis returns, readiness recovers without a restart. - -## Uninstall - -```bash -helm uninstall ids --namespace -``` - -Uninstalling keeps the Secrets that the chart generated, such as the control token, and keeps every key in the metadata Redis. Services can't check agent keys while the Identity Service is gone. To remove everything, delete the generated Secrets, the Secrets you created, and the metadata Redis data. - -## Troubleshooting - -The Identity Service returns errors as RFC 7807 problem documents. The `type` field names the error. - -{{< table-scrollable >}} -| Symptom | Possible cause | What to do | -| --- | --- | --- | -| Readiness returns `503` | The Identity Service can't reach the metadata Redis. | Check the URL, user, password, and Transport Layer Security (TLS) settings in the metadata Secret. | -| The pod doesn't start | Two credentials share a value, or a value isn't valid. | Read the first error line in the pod log. It names the field. | -| `401` on the control API | Wrong control token, or the operator token has the wrong issuer, audience, or signature, or expired. | Compare the token's `iss` and `aud` claims with `controlAuth.oidc`. | -| `403` on the control API | The caller has no role for this operation or service. | Check `controlAuth.roleBindings` and the groups in the token. | -| `503` on the control API | The Identity Service can't read its token file or reach the JSON Web Key Set (JWKS) URI. | Check the Secret mount and the network path to your identity provider (IdP). | -| `404` for a key that exists | The key is in another tenant. | Check the caller's tenant. | -| `400` that names the tenant | The grant names a tenant other than `singleTenantID`. | Omit `tenant`, or set it to `singleTenantID`. | -| `400` from grant validation | The resource doesn't exist in the tenant, or an action isn't valid. | Check the resource ID in the service's control plane, and that the service's `tenantId` equals `singleTenantID`. | -| `424` when minting | The Identity Service can't call the validator: the control plane is down, the address is wrong, or the internal token doesn't match. | Check `productValidation.products.` and the control-plane logs. | -| Agents get `403` with a valid key | The key's tenant isn't the service's tenant, or the key has no grant for the resource or action. | Compare `singleTenantID` with the service's `tenantId`, and check the key's grants. | -| Agents get `503`, and the data-plane log shows `service-credential-rejected` | The data plane's runtime credential doesn't match the Identity Service entry. | Compare the data plane's Secret with the Identity Service entry's Secret. | -| Agents get `503`, and the data-plane log shows `service-credential-insufficient-permissions` | The runtime credential lacks the operation or service. | Add it to the entry's `allowedOperations` or `allowedProducts`. | -| A user with a subject grant gets `403` | The issuer or subject differs from the token, or the shape isn't in `subjectGrants.allowed`. | Compare the token's `iss` and `sub` claims with the grant. | -| A revoked key still works | The data plane's key cache. | Wait up to `runtime.cache.hardTtlSeconds`. | -{{< /table-scrollable >}} diff --git a/content/operate/iris/identity-service/self-managed/_index.md b/content/operate/iris/identity-service/self-managed/_index.md new file mode 100644 index 0000000000..fa3ecdf4d7 --- /dev/null +++ b/content/operate/iris/identity-service/self-managed/_index.md @@ -0,0 +1,111 @@ +--- +Title: Self-managed Identity Service +alwaysopen: false +categories: +- docs +- operate +- iris +description: Install, configure, secure, and operate the Identity Service with the redis-iris suite chart. +linkTitle: Self-managed +weight: 40 +hideListLinks: true +--- + +> [!NOTE] +> The [`redis-iris` suite chart](/content/operate/iris/self-managed/_index.md), the supported self-managed install, always installs the Identity Service. Installing the `iris-identity-service` chart on its own isn't a supported install path. These guides document its values, which you set under the suite chart's `identityService:` key, and the shared values under `global.iris`. + +The Identity Service is the one place where a self-managed Redis Iris installation keeps the credentials of agents and applications. It does three things: + +1. It issues agent keys. An agent key is a secret that an agent sends to a service's data plane, for example to Agent Memory. +1. It keeps the grants of each key. A grant says which resource the caller can use, and how. +1. It answers the data planes when they ask whether a key is valid, and what it can do. + +This guide is for the platform administrator who installs the Identity Service, manages agent keys, and manages operator access. To install, start with the [prerequisites](/content/operate/iris/identity-service/self-managed/prerequisites.md). + +> [!NOTE] +> The Identity Service needs no license of its own. The suite license covers it. Contact your Redis representative or +> [contact sales](https://redis.io/contact/). + +These guides use `redis-iris` chart `1.0`. + +## What you deploy + +| Component | Purpose | Provided by | +| --- | --- | --- | +| Identity Service | Issues, lists, rotates, and revokes agent keys, and checks them for the data planes. The Service is always `iris-identity-service`, on port `9200`. | The suite chart | +| Control token Secret | The rescue credential for the control API. | The suite chart generates it, or you supply it | +| Runtime credentials and internal tokens | Shared Secrets between the Identity Service and each service. | The suite chart generates them, or you supply them | +| Metadata Redis | Stores the hashed agent keys and their grants. | You | + +The chart deploys no Redis, no Ingress, and no NetworkPolicy for the Identity Service. It has no Transport Layer Security (TLS) listener: terminate TLS in your ingress, service mesh, or load balancer. + +### Which services use it + +| Service | `product` in grants | `resourceType` in grants | Guide | +| --- | --- | --- | --- | +| Agent Memory | `memory` | `mem-store` | [Self-managed Agent Memory](/content/operate/iris/agent-memory/self-managed/_index.md) | +| Agent Playbooks | `playbook` | `playbook` | [Self-managed Agent Playbooks](/content/operate/iris/agent-playbook/self-managed/_index.md) | +| Lens | `lens` | `lens-instance` | [Self-managed Lens](/content/operate/iris/lens/self-managed/_index.md) | +| Context Gateway | `context-gateway` | `gateway` | [Self-managed Context Gateway](/content/operate/iris/context-gateway/self-managed/_index.md) | +| Intent Router | `intent` | `intent-router` | [Self-managed Intent Router](/content/operate/iris/intent-router/self-managed/_index.md) | + +### API surfaces + +The Identity Service listens on one port and serves three groups of routes. Each group accepts a different credential, sent as `Authorization: Bearer `. + +| Route group | Routes | Caller | Credential | +| --- | --- | --- | --- | +| Control | `/v1/api-keys`, `/v1/products`, `/v1/subject-grants` | Operators, the Iris console, and automation | The control token, or an operator's identity provider (IdP) access token | +| Runtime | `/v1/auth/*` | Service data planes | The service's runtime credential | +| Operational | `/`, `/build-info`, `/health/liveness`, `/health/readiness` | Kubernetes probes and monitoring | None | + +The operational routes need no credential, so don't expose them outside the cluster. + +### How the components work together + +```mermaid +sequenceDiagram + autonumber + actor Op as Operator + participant IdS as Identity Service + participant CP as Service control plane + participant R as Metadata Redis + actor Ag as Agent + participant DP as Service data plane + + Note over Op,R: Create a key + Op->>IdS: POST /v1/api-keys with grants + IdS->>CP: Validate the grants + CP-->>IdS: The grants are valid + IdS->>R: Store the hash of the key and the grants + IdS-->>Op: keyId and token, shown once + Op->>Ag: Give the token through your secret store + + Note over Ag,DP: Use the key + Ag->>DP: Request with Authorization: Bearer + DP->>IdS: Check the key + IdS-->>DP: Active, grants, cache times + DP-->>Ag: The answer, if a grant allows the request +``` + +1. An operator creates a resource in a service's control plane, such as an Agent Memory store. +1. The operator creates an agent key for that resource through the control API, or on the **API Keys** page of the Iris console. +1. Before it stores the key, the Identity Service asks the service's control plane to confirm that the resource exists and that the actions are valid. This step is called grant validation. If the control plane doesn't answer, the Identity Service refuses the grant with `424`. +1. The Identity Service returns the key once. It stores only a hash of the key. +1. The agent sends the key to the service's data plane. The data plane checks it with the Identity Service and caches the answer, so it doesn't ask for each request. + +## Get started + +1. Review the [prerequisites](/content/operate/iris/identity-service/self-managed/prerequisites.md). +1. Prepare the [configuration](/content/operate/iris/identity-service/self-managed/configuration.md). +1. [Install and check the Identity Service](/content/operate/iris/identity-service/self-managed/deploy.md) with the suite chart. +1. [Give an agent access with an agent key](/content/operate/iris/identity-service/self-managed/operations.md#give-an-agent-access-with-an-agent-key). +1. [Let operators sign in with your IdP](/content/operate/iris/identity-service/self-managed/authentication.md#operator-sign-in-with-your-identity-provider). + +## Manage the deployment + +- [Change, rotate, and revoke agent keys](/content/operate/iris/identity-service/self-managed/operations.md#change-rotate-and-revoke-a-key). +- [Rotate credentials, find who did what, upgrade, and back up](/content/operate/iris/identity-service/self-managed/operations.md). +- [Recover from a lockout](/content/operate/iris/identity-service/self-managed/authentication.md#recover-from-a-lockout). +- [Troubleshoot the deployment](/content/operate/iris/identity-service/self-managed/operations.md#troubleshooting). +- [Serve more than one tenant with Agent Memory (preview)](/content/operate/iris/identity-service/self-managed/multi-tenant.md). diff --git a/content/operate/iris/identity-service/self-managed/authentication.md b/content/operate/iris/identity-service/self-managed/authentication.md new file mode 100644 index 0000000000..a72265b353 --- /dev/null +++ b/content/operate/iris/identity-service/self-managed/authentication.md @@ -0,0 +1,175 @@ +--- +Title: Authentication and authorization +alwaysopen: false +categories: +- docs +- operate +- iris +description: Understand the Identity Service credentials, agent keys, tenants, and roles, let operators sign in with your identity provider, and recover from a lockout. +linkTitle: Authentication and authorization +weight: 60 +hideListLinks: true +aliases: +- /operate/iris/identity-service/authentication/ +--- + +The Identity Service uses a separate credential for each job: operators manage agent keys through its control API, service planes call its runtime API, and agents present agent keys to service data planes. This page lists each credential, the agent key grants, tenants, and the roles on the control API. It then shows how to let operators sign in with your identity provider (IdP). + +## Credentials + +| Credential | Held by | What it does | +| --- | --- | --- | +| Control token | The operator, in a Kubernetes Secret | The rescue credential. It can do every control operation. | +| Operator access token | An operator who signs in to your IdP | Gives the operator's control roles, from `global.iris.auth.roleBindings`. | +| Runtime credential | A service data plane, for example `memory-dp` | Lets the data plane check agent keys. | +| Internal token | A service control plane and the Identity Service | Lets the Identity Service ask the control plane whether a grant is valid. | +| Agent key | An agent | Gives the agent access to service resources. | + +Each credential has one job, so don't use one value for two jobs. The Identity Service doesn't start when two of the control token, the runtime credentials, and the internal tokens have the same value. + +The control token is a shared secret, and the audit log can't name a person for it. Keep it for setup and recovery, and let operators [sign in with your IdP](#operator-sign-in-with-your-identity-provider) for daily work. + +## Agent keys and grants + +An agent key looks like `iris_.`. The Identity Service returns the full key only once, in the response to a create or a rotation, and stores only a SHA-256 hash of it. If you lose a key, rotate it or create a new one. + +Each key has one or more grants: + +| Grant field | Meaning | Example | +| --- | --- | --- | +| `product` | The Iris service. | `memory` | +| `resourceType` | The type of resource in that service. | `mem-store` | +| `resourceId` | One resource. Wildcards aren't accepted. | `` | +| `actions` | What the key can do with the resource. | `["read", "write"]` | +| `tenant` | The resource's tenant. The Identity Service adds it. | `default` | + +| Service | `product` | `resourceType` | Actions | +| --- | --- | --- | --- | +| Agent Memory | `memory` | `mem-store` | `read`; `write` (includes `read`); `full` (includes `write`) | +| Agent Playbooks | `playbook` | `playbook` | `read`; `write`, `publish`, and `propose` (each includes `read`); `full` (includes all) | +| Lens | `lens` | `lens-instance` | `ingest` and `read`, independent of each other; `full` (includes both) | +| Context Gateway | `context-gateway` | `gateway` | `use`; `full` (includes `use`) | +| Intent Router | `intent` | `intent-router` | `read`; `write` (includes `read`); `full` (includes `write`) | + +Before the Identity Service stores a grant, it asks the service's control plane whether the resource exists and the actions are valid. Each service's authentication page explains what its actions allow. To create and manage keys, see [Give an agent access with an agent key](/content/operate/iris/identity-service/self-managed/operations.md#give-an-agent-access-with-an-agent-key). + +A data plane caches each key check. After you revoke a key, it can keep working for up to `runtime.cache.hardTtlSeconds`, 300 seconds by default. + +### Subject grants + +The control API also has routes for subject grants, `/v1/subject-grants`. They have no effect in a `redis-iris` installation. By default the chart allows no subject-grant shape, so the Identity Service refuses new subject grants. The chart also gives no data plane the `subject-grant-lookup` operation, so no data plane reads them. IdP users get access from their roles in each service, and agents use agent keys. + +## Tenants + +A tenant is a boundary for keys, grants, and service data, such as one customer or one business unit. A caller in one tenant can't see the keys of another tenant: the Identity Service returns `404` for them. + +The Identity Service has no tenant object, and no API creates, lists, or deletes tenants. A tenant is only an ID that each key, grant, and service resource carries. + +A `redis-iris` release serves one tenant, set once in `global.iris.tenantId`. The chart gives this ID to the Identity Service and to each service. To serve more than one tenant, install one release for each tenant, each in its own namespace. For a preview of several tenants in one Agent Memory installation, see [Serve more than one tenant with Agent Memory](/content/operate/iris/identity-service/self-managed/multi-tenant.md). + +| Task | What to do | +| --- | --- | +| Create a tenant | Choose the ID before the first install, and set `global.iris.tenantId`. | +| Change the ID | Not supported. Keys, grants, and service data keep the old ID, and no tool moves them. A new `global.iris.tenantId` doesn't make the old data available. | +| Remove a tenant | Uninstall its release, and delete its data. There is no tenant object to delete. | + +A grant's `tenant` field works like this: + +| `tenant` in the grant | Result | +| --- | --- | +| Not set | The Identity Service uses `global.iris.tenantId`. | +| `global.iris.tenantId` | Accepted. | +| Another tenant | `400`. | + +All the grants of one key must have the same tenant. + +## Roles on the control API + +| Caller | How it gets the role | What it can do | +| --- | --- | --- | +| Control token | It holds the token | Every operation. | +| `suite_admin` | A role binding without `product` | Every operation for every service, in its own tenant. | +| `product_admin` | A role binding with `product` | Every operation for that service, in its own tenant. | +| `operator`, `viewer` | A role binding with `product` | No key operations. These roles apply only in the service. | +| Any other caller with a valid token | None | No key operations. | + +The Identity Service also has a `deployment_admin` role, for installations with more than one tenant. A `redis-iris` release has one tenant, so it doesn't need this role, and the suite role bindings can't give it. + +For each control API request, the Identity Service finds the caller's role like this: + +1. If the bearer value is the control token, the caller can do every operation, and there are no more checks. +1. Otherwise, the Identity Service reads the value as a JSON Web Token (JWT) from your IdP. It checks the issuer, the signature, the audience, and the expiry. If a check fails, it returns `401`. +1. It compares the values of the role claim, `groups` by default, with the `group` of each role binding. Each matching binding gives its role, so an operator can have more than one role. +1. It checks the roles against the operation and the service. If no role allows the operation, it returns `403`. + +A caller with a valid token and no role can still read `GET /v1/products`. `GET /v1/api-keys` returns `200` with only the keys the caller can manage, so for such a caller the list is empty. + +For example, an operator's token has `groups: [memory-admins]`, and the binding `{group: memory-admins, role: product_admin, product: agent_memory}` matches. The operator can create keys for Agent Memory, and a key for Agent Playbooks returns `403`. + +## Operator sign-in with your identity provider + +Operators can call the control API with an access token from your IdP, using OpenID Connect (OIDC). The audit log then names each operator. The control token keeps working. + +### 1. Register Iris in your IdP + +1. Register the Iris console as a web application. +1. Register one API for the whole suite with one audience, for example `api://redis-iris`. +1. Create groups for key administrators, for example `iris-suite-admins` and `memory-admins`, and add operators to them. + +### 2. Configure `global.iris.auth` + +Add these values to `iris-values.yaml`, together with the `ui.sso` values from the [suite install](/content/operate/iris/self-managed/_index.md#3-write-iris-valuesyaml): + +```yaml +global: + iris: + publicURL: https:// + auth: + oidc: + issuer: https:/// + jwksURI: https:/// + audience: api://redis-iris + roleBindings: + - {group: iris-suite-admins, role: suite_admin} + - {group: memory-admins, role: product_admin, product: agent_memory} + - {group: memory-ops, role: operator, product: agent_memory} +``` + +- `issuer` is the issuer of the IdP discovery document. If the access tokens' `iss` claim differs, for example on Active Directory Federation Services (AD FS), set `accessTokenIssuer` to that value. The Identity Service compares it with the `iss` claim exactly. +- `jwksURI`, the JSON Web Key Set (JWKS) URI, must use `https://`. The pods must reach it and trust its certificate authority (CA). See [TLS, FIPS, and network](/content/operate/iris/identity-service/self-managed/configuration.md#tls-fips-and-network). +- Role bindings read the `groups` claim by default. Set `roleClaims` to read other claims. A `group` matches without regard to letter case. +- The service names in `roleBindings` are `agent_memory`, `playbook`, `lens`, `context_gateway`, and `intent`. +- On the Identity Service, only `suite_admin` and `product_admin` can manage keys. +- A binding for a service that isn't enabled has no effect. The chart keeps it, and uses it again when you enable the service. + +The console sends the operator's access token to the Identity Service. The chart sets the same audience and issuer in the console and the Identity Service, so they always match. + +### 3. Apply and check + +1. Run `helm upgrade` for the release. +1. Get an access token for an operator from your IdP, and save it in `OPERATOR_TOKEN`. +1. Create a key for a resource of one of the operator's services, as in [Give an agent access with an agent key](/content/operate/iris/identity-service/self-managed/operations.md#give-an-agent-access-with-an-agent-key), with the operator's token. Then delete it: + + ```bash + curl -sS -X POST "$IRIS_IDENTITY_URL/v1/api-keys" \ + -H "Authorization: Bearer $OPERATOR_TOKEN" \ + -H 'Content-Type: application/json' \ + -d '{"name": "sso-check", "grants": []}' + curl -sS -X DELETE "$IRIS_IDENTITY_URL/v1/api-keys/" \ + -H "Authorization: Bearer $OPERATOR_TOKEN" + ``` + +| Status | Meaning | +| --- | --- | +| `201` | The token is valid and a role binding allows the operation. | +| `401` | The issuer, audience, or signature is wrong, or the token expired. Compare the token's `iss` and `aud` claims with `global.iris.auth.oidc`. | +| `403` | No role binding gives `suite_admin`, or `product_admin` for that service, to the operator's groups. | + +Don't use `GET /v1/api-keys` for this check: without a role, it returns `200` with an empty list. Keep the control token for recovery. + +## Recover from a lockout + +- **Your IdP is down, or the role bindings are wrong.** Use the control token. It always works. +- **The console can't sign in.** Switch the console to [admin token mode](/content/operate/iris/self-managed/_index.md#admin-token-mode) until the IdP works again. +- **You don't have the control token Secret.** If the chart generated the token (`global.iris.secrets.generate: true`), delete the Secret, if it still exists, and run `helm upgrade`. The chart generates a new token. If you supplied the Secret, create a new one with a new token yourself. +- **You lost the metadata Redis.** Restore it from your backup. See [Back up and restore](/content/operate/iris/identity-service/self-managed/operations.md#back-up-and-restore). Without it, every agent key and grant stops working. diff --git a/content/operate/iris/identity-service/self-managed/configuration.md b/content/operate/iris/identity-service/self-managed/configuration.md new file mode 100644 index 0000000000..511fd58c9b --- /dev/null +++ b/content/operate/iris/identity-service/self-managed/configuration.md @@ -0,0 +1,127 @@ +--- +Title: Configure the Identity Service +alwaysopen: false +categories: +- docs +- operate +- iris +description: Configure the Identity Service metadata Redis, control token, tenant, services, agent-key settings, overlays, and TLS with the redis-iris suite chart. +linkTitle: Configuration +weight: 30 +hideListLinks: true +aliases: +- /operate/iris/identity-service/configuration/ +--- + +Put the Identity Service values under `identityService:` in `iris-values.yaml`, and the values the services share under `global.iris`. The suite chart derives the rest. + +The Identity Service reads its configuration only when it starts. A changed value rolls the pods at `helm upgrade`. A changed Secret that you own doesn't roll the pods. See [Rotate credentials](/content/operate/iris/identity-service/self-managed/operations.md#rotate-credentials). + +## Metadata Redis + +| Value | Default | Meaning | +| --- | --- | --- | +| `identityService.metadata.existingSecret` | None | Required. The Secret with the metadata Redis connection. | +| `identityService.metadata.secretKey` | `metadata.yaml` | The key in that Secret. | + +The key holds a configuration fragment: + +```yaml +metadata: + urls: ["rediss://:@:"] +``` + +For persistence, backups, and access control, see [Metadata Redis](/content/operate/iris/identity-service/self-managed/prerequisites.md#metadata-redis). Don't only change the host after the first install: the Identity Service then reads an empty database, and every key stops working. To move the data, see [Move the metadata Redis](/content/operate/iris/identity-service/self-managed/operations.md#move-the-metadata-redis). + +## Control token + +| Value | Default | Meaning | +| --- | --- | --- | +| `identityService.control.existingSecret` | `iris-identity-service-control-token` | The Secret with the token. The chart generates it. To use your own token, create this Secret before the install, or name another Secret. | +| `identityService.control.secretKey` | `token` | The key in that Secret. | + +The control token always works, also when operator sign-in is on. Give only a small group access to its Secret. + +## Tenant + +| Value | Default | Meaning | +| --- | --- | --- | +| `global.iris.tenantId` | `default` | The release's one tenant, in the Identity Service and in each service. | + +Don't set a tenant in a service or under `identityService`. A service value that differs stops the install and names the field. For what a tenant is and how grants get one, see [Tenants](/content/operate/iris/identity-service/self-managed/authentication.md#tenants). + +A tenant ID follows these rules: + +- 1 to 64 characters, without `|` and without control characters. +- No space at the start or the end. +- Matching is exact and case-sensitive. +- For a numeric ID, such as `91324`, use `--set-string` in Helm, so Helm keeps it as a string. + +## Services + +The Identity Service needs two entries for each service: + +- A runtime credential for the data plane, with the `api-key-introspect` operation. +- A grant validator for the control plane. + +The chart creates both entries for each service enabled in `global.iris.products..enabled`. You don't write them. The Identity Service calls these validators: + +| Service | Grant validator | +| --- | --- | +| Agent Memory | `http://redis-agent-memory-controlplane..svc.:9100` | +| Agent Playbooks | `http://redis-agent-playbook-controlplane..svc.:9200` | +| Lens | `http://redis-lens-controlplane..svc.:9300` | +| Context Gateway | `http://redis-context-gateway-controlplane..svc.:9100` | +| Intent Router | `http://redis-intent-router-controlplane..svc.:9400` | + +`` is `global.iris.clusterDomain`, `cluster.local` by default. Each call uses the control plane's internal token, with a 5-second timeout, and doesn't follow redirects. + +## Operator sign-in + +Set your IdP and the role bindings once, under `global.iris.auth`. The chart passes them to the console, the service control planes, and the Identity Service. See [Operator sign-in with your identity provider](/content/operate/iris/identity-service/self-managed/authentication.md#operator-sign-in-with-your-identity-provider). + +| Value | Default | Meaning | +| --- | --- | --- | +| `global.iris.auth.oidc.issuer` | None | The issuer of the IdP discovery document. | +| `global.iris.auth.oidc.accessTokenIssuer` | `issuer` | The `iss` claim of the access tokens, when it differs from `issuer`, for example on Active Directory Federation Services (AD FS). | +| `global.iris.auth.oidc.jwksURI` | None | The JWKS URI. Must use `https://`. | +| `global.iris.auth.oidc.audience` | `api://redis-iris` | The audience in operator access tokens. | +| `global.iris.auth.oidc.roleClaims` | `[groups]` | The token claims that carry IdP groups. | +| `global.iris.auth.roleBindings` | `[]` | IdP group to Iris role. | + +## Agent keys and the key cache + +| Value | Default | Meaning | +| --- | --- | --- | +| `identityService.apiKeys.maxRotateGraceSeconds` | `604800` (7 days) | The longest time an old key stays valid after a rotation. | +| `identityService.apiKeys.userBinding.enabled` | `false` | Lets a key name the user that owns it, with `userId`. Agent Memory uses it for owner isolation. | +| `identityService.runtime.cache.softTtlSeconds` | `180` | When a data plane refreshes a cached key check. | +| `identityService.runtime.cache.hardTtlSeconds` | `300` | The longest time a data plane uses a cached key check. A revoked key can keep working for up to this time. | + +## Configuration overlays + +- **`identityService.configData`** adds Identity Service settings that have no Helm value. The chart refuses credential and service keys in `configData`. A key that has a Helm value, such as `runtime.cache`, gets the Helm value, so use the Helm value instead. +- **Policy overlays** are Secrets with more configuration, named by `identityService.secrets.secretName` and `identityService.secrets.additionalSecrets`. The Identity Service reads them in order. Change `identityService.secrets.checksum` to roll the pods after you edit one. + +The Identity Service reads its configuration in this order, and a later layer wins over an earlier one: + +1. The rendered configuration: `configData`, then the Helm values. +1. The metadata Secret. +1. The policy overlays, in order. +1. The credentials and validators that the chart wires in. + +The chart applies the credentials and validators last, so an overlay can't change the control token, the runtime credentials, or the grant validators. + +## TLS, FIPS, and network + +- **Private certificate authority (CA).** Put your CA bundle in a Secret, under the key `ca.crt`, and set `global.tls.caCertSecret`. The Identity Service and the services then trust it for the IdP, the JWKS URI, the control planes, and the Redis databases. The chart has no option to skip certificate verification. +- **Federal Information Processing Standards (FIPS).** The suite chart doesn't support FIPS. An install with `global.security.profile: fips` fails. +- **Network.** The chart deploys no NetworkPolicy and no Ingress for the Identity Service. Let only the services, the console, and your operators reach port `9200`. + +## Availability + +| Value | Default | Meaning | +| --- | --- | --- | +| `identityService.replicaCount` | `2` | Identity Service replicas. The Identity Service keeps its state in the metadata Redis. | + +The chart adds a PodDisruptionBudget. Readiness checks the metadata Redis, and liveness doesn't. When the metadata Redis is unreachable, the pods turn unready and stop receiving traffic, but Kubernetes doesn't restart them. diff --git a/content/operate/iris/identity-service/self-managed/deploy.md b/content/operate/iris/identity-service/self-managed/deploy.md new file mode 100644 index 0000000000..44dc3c28a1 --- /dev/null +++ b/content/operate/iris/identity-service/self-managed/deploy.md @@ -0,0 +1,163 @@ +--- +Title: Deploy the Identity Service +alwaysopen: false +categories: +- docs +- operate +- iris +description: Install the Identity Service with the redis-iris suite chart, check that it answers, and find the credentials the chart generates. +linkTitle: Deploy +weight: 50 +hideListLinks: true +aliases: +- /operate/iris/identity-service/deploy/ +--- + +The `redis-iris` suite chart installs the Identity Service, the Iris console, and the services you enable as one Helm release. It always installs the Identity Service, under the fixed name `iris-identity-service`, and the services find it by that name. These steps cover the Identity Service part of the [suite install](/content/operate/iris/self-managed/_index.md). + +The examples use the release name `iris`. Before you begin, review the [prerequisites](/content/operate/iris/identity-service/self-managed/prerequisites.md). + +## 1. Create the namespace + +Use a namespace that holds only this release: + +```bash +kubectl create namespace +``` + +## 2. Create the metadata Secret + +Create a Secret with the connection of your metadata Redis, under the key `metadata.yaml`. Use `rediss://` for TLS, and put the Redis user and password in the URL: + +```bash +kubectl -n create secret generic ids-metadata \ + --from-literal=metadata.yaml='metadata: {urls: ["rediss://:@:"]}' +``` + +Also create the shared Secrets that the [suite install](/content/operate/iris/self-managed/_index.md#2-create-the-shared-secrets) lists, and each enabled service's own Secrets. + +## 3. Add the Identity Service values + +Add these values to `iris-values.yaml`: + +```yaml +global: + iris: + tenantId: default +identityService: + metadata: + existingSecret: ids-metadata +``` + +Choose the tenant ID before the first install. You can't change it later. See [Tenant](/content/operate/iris/identity-service/self-managed/configuration.md#tenant). + +For a complete installation, start from the files in the chart's `examples/` directory: `memory-only.yaml`, `playbook.yaml`, `lens.yaml`, `context-gateway.yaml`, and `intent.yaml`. `single-product.yaml` shows one service with IdP sign-in in the console. + +### What the chart sets for you + +The suite chart derives these Identity Service settings. Don't set them under `identityService:`: a value that differs from the derived one fails the install and names the field. + +- The name `iris-identity-service`, port `9200`, and a PodDisruptionBudget. +- The control token Secret, `iris-identity-service-control-token`. +- One runtime credential for each enabled data plane, for example `memory-dp`, with the `api-key-introspect` operation for its service only. +- One grant validator for each enabled control plane. See [Services](/content/operate/iris/identity-service/self-managed/configuration.md#services). +- The tenant, operator sign-in, and role bindings, from `global.iris`. +- The **API Keys** page of the Iris console, which calls the Identity Service. + +The chart runs two Identity Service replicas by default. You can change `identityService.replicaCount`. + +## 4. Run the preflight and install + +Run the [suite preflight](/content/operate/iris/self-managed/_index.md#4-run-the-preflight). Then pull the chart for its `examples/` directory, and install: + +```bash +helm repo add redis-ai https://helm.redis.io/ai +helm repo update redis-ai +helm pull redis-ai/redis-iris --version --untar +helm install iris redis-ai/redis-iris \ + --version \ + --namespace \ + -f redis-iris/examples/memory-only.yaml \ + -f iris-values.yaml +``` + +Add an `-f redis-iris/examples/.yaml` file for each other service you enable. + +## 5. Check the installation + +1. Port-forward the Identity Service in its own terminal, and leave it running: + + ```bash + kubectl -n port-forward svc/iris-identity-service 9200:9200 + ``` + +1. Check readiness. It checks the metadata Redis: + + ```bash + curl -sS http://localhost:9200/health/readiness + ``` + + The response is `{"status":"ready"}`. + +1. Read the control token. `helm status` also prints this command: + + ```bash + export IRIS_IDENTITY_TOKEN=$(kubectl -n get secret iris-identity-service-control-token \ + -o jsonpath='{.data.token}' | base64 -d) + ``` + +1. List the services that have a grant validator: + + ```bash + curl -sS -H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" http://localhost:9200/v1/products + ``` + +1. Run the suite test: + + ```bash + helm test iris --namespace --logs + ``` + + For each enabled service, the test creates a resource and a key, uses the key on the data plane, and then deletes them. For Lens, it uses the instance `default` and creates no resource. This proves grant validation and key checks. + + - The test creates its Agent Memory store in Memory database `1`. If your Memory `databases` registry uses another key, set `tests.suiteVerify.databaseId`. + - The test pod uses a public Python image. Without access to Docker Hub, set `tests.suiteVerify.image` to your mirror. + +## Credentials + +By default, the chart generates every credential once, as Secrets that Helm keeps across upgrades, rollbacks, and uninstalls. Each component mounts only its own key. + +| Secret | Key | Used by | +| --- | --- | --- | +| `iris-identity-service-control-token` | `token` | The Identity Service control API | +| `iris--dataplane-credential` | `-dp` | The service's data plane and the Identity Service | +| `iris--internal-token` | `` | The service's control plane and the Identity Service | +| `iris--admin-token` | `token` | The service's control plane admin API | +| `iris-ui-session-key` | `key` | The console's sign-in service, with IdP sign-in | + +`` is `memory`, `playbook`, `lens`, `context-gateway`, or `intent`. + +For the runtime credentials and the internal tokens, the caller and the Identity Service mount the same Secret. The console doesn't mount the control token. With IdP sign-in, the console gateway sends the operator's access token. In admin token mode, the operator pastes the control token in the browser, and the gateway sends it to the Identity Service. + +### Supply your own credentials + +For GitOps tools such as Argo CD or Flux, create the Secrets yourself. Set `global.iris.renderMode: gitops` and `global.iris.secrets.generate: false`, then create these Secrets, with their keys, before the first sync: + +| Secret | Value that names it | Keys | +| --- | --- | --- | +| Data plane credentials | `global.iris.secrets.dataplaneServiceCredentials.existingSecret` | `-dp`, for each enabled service | +| Internal tokens | `global.iris.secrets.controlplaneInternalTokens.existingSecret` | ``, for each enabled service | +| Identity Service control token | `identityService.control.existingSecret` | `token` | +| Service admin tokens | `.controlplane.adminToken.existingSecret` | `token` | +| Console session key | `ui.sso.session.existingSecret` | `key`. Required with `ui.sso.enabled`, the default. | + +- `` is `memory`, `playbook`, `lens`, `contextGateway`, or `intent`. +- The two `global.iris.secrets.*` Secrets must have different names. +- An offline render can't read the Secrets. The suite preflight checks that each key is in the two `global.iris.secrets.*` Secrets. Run it before the first sync. + +Start from `examples/byo-gitops.yaml` or `examples/full-suite-gitops.yaml` in the chart. + +## Next steps + +- [Give an agent access with an agent key](/content/operate/iris/identity-service/self-managed/operations.md#give-an-agent-access-with-an-agent-key). +- [Let operators sign in with your IdP](/content/operate/iris/identity-service/self-managed/authentication.md#operator-sign-in-with-your-identity-provider). diff --git a/content/operate/iris/identity-service/self-managed/multi-tenant.md b/content/operate/iris/identity-service/self-managed/multi-tenant.md new file mode 100644 index 0000000000..22e999ab9c --- /dev/null +++ b/content/operate/iris/identity-service/self-managed/multi-tenant.md @@ -0,0 +1,115 @@ +--- +Title: Serve more than one tenant with Agent Memory +alwaysopen: false +categories: +- docs +- operate +- iris +description: Preview. Let one Identity Service and Agent Memory installation serve several tenants, each with its own stores and keys. +linkTitle: Multiple tenants (preview) +weight: 95 +hideListLinks: true +--- + +> [!NOTE] +> This mode is a preview, and isn't part of the supported `redis-iris` installation. A `redis-iris` release always has one tenant, so this mode needs the standalone `iris-identity-service` and `redis-agent-memory` charts. Run the [isolation check](#check-the-isolation) after each change. + +In this mode, one installation of the Identity Service and Agent Memory serves several teams or customers. Each tenant has its own stores and keys, and a tenant can't see or use the stores and keys of another tenant. + +## How it works + +- An operator's tenant comes from a claim in the operator's identity provider (IdP) access token. The Identity Service and the Agent Memory control plane read the same claim. +- The Agent Memory control plane puts the operator's tenant on each new store. +- The Identity Service keeps each new agent key in the operator's tenant. Before it stores the key, the Agent Memory control plane confirms that the store belongs to that tenant. +- The Agent Memory data plane gets each agent key's tenant from the Identity Service, and compares it with the store's tenant, character for character. + +With more than one tenant, the Identity Service gives a new grant its tenant like this: + +| Caller | `tenant` in the grant | Result | +| --- | --- | --- | +| Control token | A tenant | Accepted, in that tenant. | +| Control token | Not set | `400`. | +| Operator | Not set | The operator's tenant. | +| Operator | The operator's tenant | Accepted. | +| Operator | Another tenant | `400`. | + +When an operator reads or changes a key of another tenant, the Identity Service returns `404`, not `403`, so the operator can't learn which keys exist in other tenants. + +## Before you start + +Your IdP must put the tenant ID in each operator's access token, in a string claim at the top level of the token. Iris can't read a claim inside another claim, a list, or a number. + +| IdP | Claim to use for the tenant | +| --- | --- | +| Auth0 | `org_id`. The application selects the organization at sign-in, with `organization=` on `/authorize`. | +| Zitadel | `urn:zitadel:iam:user:resourceowner:id` | +| Okta, with a custom authorization server | A custom claim from the user profile, for example `tenant_id`. | +| Keycloak | A user attribute mapper, for example `tenant_id`. The built-in Organizations claim is a map, which Iris can't read. | +| Amazon Cognito | A claim that a pre token generation trigger, V2 or V3, adds to the access token. | +| Microsoft Entra ID | A custom claim. Don't use `tid`: it's the Entra directory, not your tenant. | +| PingFederate, PingOne, AD FS | An attribute that you map to the access token. | + +The IdP decides each operator's tenant, so a wrong mapping in the IdP moves an operator to another tenant. Let only a small group change the mappings. + +## Set up several tenants + +1. Configure the Identity Service release for more than one tenant. Leave `singleTenantID` empty, read the tenant from the claim, and make the tenant administrators `product_admin` for Agent Memory. Also set the Agent Memory runtime credential and grant validator, as in [Deploy Agent Memory](/content/operate/iris/agent-memory/self-managed/deploy.md#2-create-secrets): + + ```yaml + runtime: + apiKeyTenant: + singleTenantID: "" + controlAuth: + oidc: + enabled: true + issuer: https:/// + jwks_uri: https:/// + audience: [api://redis-iris] + tenant_id_claim: tenant_id + roleBindings: + - {claim_value: memory-admins, scope: product, product: memory, role: product_admin} + ``` + +1. Configure the Agent Memory release. Leave `tenantId` empty, and give the control plane the same IdP settings and claim: + + ```yaml + tenantId: "" + controlplane: + configData: + auth: + oidc: + enabled: true + issuer: https:/// + jwks_uri: https:/// + audience: [api://redis-iris] + tenant_id_claim: tenant_id + role_claims: [groups] + role_mappings: + memory-admins: product_admin + ``` + + Keep the data plane on agent keys. A `suite_admin` from the IdP stays in its own tenant on the Agent Memory control plane, as in the Identity Service. Only the Agent Memory admin token can see and change the stores of every tenant. + +1. A tenant administrator signs in with the IdP, and creates the tenant's stores through the Agent Memory control plane with the access token. +1. The tenant administrator creates the agent keys at the Identity Service, `POST /v1/api-keys`, with the same token. Don't put `tenant` in the grants: the Identity Service uses the token's tenant. +1. The agents use the keys on the Agent Memory data plane, as in [Give an agent access with an agent key](/content/operate/iris/identity-service/self-managed/operations.md#give-an-agent-access-with-an-agent-key). + +## Check the isolation + +Use two tenants, A and B, and create a store and a key in each. Then confirm these results: + +- Tenant A's key gets `403` on tenant B's store. +- A tenant administrator of A doesn't see B's stores in the store list. +- A tenant administrator of A can't create a key for a store of B. The Identity Service returns `400`. + +## Provider operators + +- The Identity Service control token can act in every tenant. It must name the tenant in each grant. +- A `deployment_admin` can list, read, and revoke keys in every tenant, but can't create, update, or rotate keys. +- The Agent Memory admin token can see and change the stores of every tenant. Don't create stores with it: it puts them in one tenant, `"1"`. + +## Limits + +- Every tenant shares every database in the Agent Memory `databases` setting. The stores are separate, but a tenant can create stores in each database, so one tenant can slow down the others. +- Agent Playbooks, Lens, Context Gateway, and Intent Router serve one tenant for each release. +- To move an installation from one tenant to several, use the old tenant ID as the claim value of the first tenant. Its stores and keys keep that ID. Run the [isolation check](#check-the-isolation) after the change. diff --git a/content/operate/iris/identity-service/self-managed/operations.md b/content/operate/iris/identity-service/self-managed/operations.md new file mode 100644 index 0000000000..0ef197fe41 --- /dev/null +++ b/content/operate/iris/identity-service/self-managed/operations.md @@ -0,0 +1,254 @@ +--- +Title: Operate the Identity Service +alwaysopen: false +categories: +- docs +- operate +- iris +description: Create, rotate, and revoke agent keys, add services, rotate credentials, audit changes, upgrade, back up, and troubleshoot the Identity Service. +linkTitle: Operations +weight: 90 +hideListLinks: true +aliases: +- /operate/iris/identity-service/operations/ +--- + +This page covers the daily work on the Identity Service of a `redis-iris` release: managing agent keys, adding services, rotating credentials, and finding who changed what. It also covers upgrades, backups, and troubleshooting. + +The examples use a port-forward to the Identity Service: + +```bash +kubectl -n port-forward svc/iris-identity-service 9200:9200 +``` + +```bash +export IRIS_IDENTITY_URL='http://localhost:9200' +export IRIS_IDENTITY_TOKEN='' +``` + +When operator sign-in is on, use your own access token instead of the control token, so the audit log names you. Each call that changes a grant goes to the service's control plane first. If a call fails with `424`, check that control plane. + +The `iris` command-line interface (CLI) reads `IRIS_IDENTITY_URL` and `IRIS_IDENTITY_TOKEN`. To set it up, see [Set up on a self-managed deployment](/content/develop/ai/context-engine/sdk-and-cli.md#set-up-on-a-self-managed-deployment). + +## Give an agent access with an agent key + +This example gives an agent read and write access to one Agent Memory store. + +1. Find the store ID in the Agent Memory control plane. +1. Create the key: + + ```bash + curl -sS -X POST "$IRIS_IDENTITY_URL/v1/api-keys" \ + -H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "support-agent", + "grants": [{ + "product": "memory", + "resourceType": "mem-store", + "resourceId": "", + "actions": ["read", "write"] + }] + }' + ``` + + The response has `keyId` and `token`. The `token` is the agent key, and it's shown only once. Give it to the agent through your secret store, and record only the `keyId`. + +1. The agent sends the key to the Agent Memory data plane in the `Authorization: Bearer` header, or in `X-Api-Key`. + +Optional fields: + +- `expiresAt`: when the key stops working, in Unix seconds. Without it, the key doesn't expire. +- `userId`: the user that owns the key. It needs `identityService.apiKeys.userBinding.enabled: true`. + +For the other services, change the grant: + +| Service | Grant | +| --- | --- | +| Agent Playbooks | `{"product": "playbook", "resourceType": "playbook", "resourceId": "", "actions": ["read"]}` | +| Lens, send turns | `{"product": "lens", "resourceType": "lens-instance", "resourceId": "default", "actions": ["ingest"]}` | +| Lens, read turns | The same, with `"actions": ["read"]` | +| Context Gateway | `{"product": "context-gateway", "resourceType": "gateway", "resourceId": "", "actions": ["use"]}` | +| Intent Router | `{"product": "intent", "resourceType": "intent-router", "resourceId": "", "actions": ["read"]}` | + +With the CLI, each service's own command creates its keys, because it knows the grant the service needs. For example, `iris intent-router service key` creates a key for a Router. + +### Create keys in the Iris console + +The Iris console has an **API Keys** page at `/api-keys`. It calls the Identity Service through the console gateway. + +- With IdP sign-in (`ui.sso.enabled: true`, the default), the page uses the operator's access token. Only a `suite_admin`, or a `product_admin` of the key's services, can manage the key. +- In admin token mode (`ui.sso.enabled: false`), the page asks for the control token. Use this mode for recovery only. + +On the page, you can create, list, and delete keys. The page shows the token once, when you create the key. It's shown only to users with access to Agent Memory, Agent Playbooks, Context Gateway, or Intent Router. A Lens administrator uses the control API. + +## Change, rotate, and revoke a key + +```bash +# List every key, or the keys of one resource +curl -sS -H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" \ + "$IRIS_IDENTITY_URL/v1/api-keys?product=memory&resourceId=" +``` + +```bash +# Show one key, without its token +curl -sS -H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" \ + "$IRIS_IDENTITY_URL/v1/api-keys/" +``` + +```bash +# Change the name, the grants, or expiresAt. The token stays the same. +curl -sS -X PATCH "$IRIS_IDENTITY_URL/v1/api-keys/" \ + -H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" \ + -H 'Content-Type: application/json' \ + -d '{"grants": [{"product": "memory", "resourceType": "mem-store", "resourceId": "", "actions": ["read"]}]}' +``` + +```bash +# Rotate: get a new token. The old token works for graceSeconds more. +curl -sS -X POST "$IRIS_IDENTITY_URL/v1/api-keys//rotate?graceSeconds=3600" \ + -H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" +``` + +```bash +# Revoke: delete the key. +curl -sS -X DELETE "$IRIS_IDENTITY_URL/v1/api-keys/" \ + -H "Authorization: Bearer $IRIS_IDENTITY_TOKEN" +``` + +With the CLI: + +```bash +iris identity keys list --product memory --resource-id +iris identity keys show +iris identity keys rotate --grace 1h --write .env --env +iris identity keys revoke +``` + +`rotate` writes the new key to the dotenv file with mode `0600` and never prints it. `revoke` asks for confirmation unless you pass `--yes`. + +The list returns every key in one response, without pages. + +| Operation | Use it when | What happens to the token | What the agent sees | +| --- | --- | --- | --- | +| Update | The agent needs other resources or actions, a new name, or a new expiry. | It doesn't change. | The new grants, after the data plane's cache refreshes, within `runtime.cache.hardTtlSeconds` (300 seconds by default). | +| Rotate | Someone else might know the token, or your rotation schedule is due. | You get a new key with a new `keyId`, a new token, and the same grants. The old token works for `graceSeconds` more (default 3600), but never after its own `expiresAt`. The response's `oldExpiresAt` says when it stops. | It must switch to the new token before `oldExpiresAt`. | +| Revoke | Someone else has the token, or the agent no longer needs access. | The key is deleted. A second revoke returns `404`. | It loses access within `runtime.cache.hardTtlSeconds`. | +| Set `expiresAt` | The access is for a limited time. | It stops at that time. | It loses access at that time. | + +## Add or remove a service + +`` is `memory`, `playbook`, `lens`, `contextGateway`, or `intent`. + +- **Add a service.** Set `global.iris.products..enabled: true`, add the service's values, and run `helm upgrade`. The chart creates the service's credentials and its Identity Service entries. Check that `GET /v1/products` lists the new service. +- **Turn a service off.** Set `global.iris.products..enabled: false` and run `helm upgrade`. The chart removes the service's Identity Service entries, and keeps its credentials and data, so the service continues where it stopped when you turn it on again. +- **Remove a service permanently.** Turn it off, then delete its Secrets and its data. + +The agent keys of a service that's off stay in the Identity Service. Revoke them if you don't need them. + +## Rotate credentials + +The Identity Service reads the control token, the runtime credentials, and the internal tokens from their mounted files on each request. When you change one of these Secrets in place, the new value works after Kubernetes updates the mounted file, in about a minute, without a restart. Some calls can fail while the files update. + +| Credential | How to rotate | +| --- | --- | +| Control token | Change the Secret. For a generated token, delete the Secret, then run `helm upgrade`. | +| Runtime credential | Change the Secret that the Identity Service and the data plane share. | +| Internal token | Change the Secret that the Identity Service and the control plane share. | +| Agent key | [Rotate it](#change-rotate-and-revoke-a-key) through the control API. | +| Policy overlays | The Identity Service reads them only at startup. Change `identityService.secrets.checksum`, or restart the pods. | +| Console session key | Change the Secret, then restart the console's sign-in service with `kubectl -n rollout restart deployment/iris-ui-auth`. Every user must sign in again. | +| Metadata Redis credential | Follow [Rotate the metadata Redis credential](#rotate-the-metadata-redis-credential). | + +Make every new value unique. The Identity Service checks for duplicate values only when it starts, so a duplicate you set in place stops the next pod from starting. `helm upgrade` and `helm rollback` keep the rotated values. + +### Rotate the metadata Redis credential + +The Identity Service and the services read the metadata Redis connection only when they start. + +1. In Redis, add a new user, and keep the old user. +1. Update each Secret that holds the connection. +1. Restart the Identity Service, then the service control planes, then the data planes and the workers, then the console gateway and the console's sign-in service. If you use the Agent Memory queue monitor, restart it too. +1. Run `helm test iris --namespace --logs`. +1. Keep the old user. Each Agent Memory store keeps the database credential it was created with, so deleting the old user breaks the old stores. + +## Find who did what + +The Identity Service writes one log line with the message `audit event` for each control operation. It has no audit database, so collect the pod logs with your log system. To search the logs Kubernetes still keeps, across every Identity Service pod: + +```bash +kubectl -n logs -l app.kubernetes.io/name=iris-identity-service --tail=-1 \ + | grep '"audit event"' | grep '' +``` + +| Field | Meaning | +| --- | --- | +| `action` | The operation, for example `api_key.create` or `api_key.revoke`. | +| `result` | `allowed`, `denied`, or `failed`. | +| `subject`, `user_id`, `issuer` | The caller. | +| `auth_type` | `admin_token` for the control token. | +| `authorized_by` | The role that allowed the operation, for example `suite_admin`. | +| `key_id` | The key that the operation changed. | + +The log never contains tokens. A request refused before it reaches the service, such as one with a malformed body, has no audit line. + +## Upgrade and roll back + +- **Upgrade.** Pin the chart version, upgrade the one release, and then run `helm test`. Helm upgrades every component at the same time. Upgrade within one minor version, or from the last patch of the previous minor version to the new minor version. Don't skip a minor version. Read the release notes of each version first. +- **Roll back.** `helm rollback` restores the workloads and the values. It doesn't change the generated Secrets, or the keys and grants in the metadata Redis. The same version rules apply. Keep the chart of each version you install, with `helm pull redis-ai/redis-iris --version `. + +## Back up and restore + +The metadata Redis holds every agent key and grant. Back up database 0 of that Redis on a schedule, with persistence turned on. Also export the Secrets of the release namespace, and store the export as you store database passwords. + +A restore brings back the keys and grants as they were at backup time: + +- Keys created after the backup don't work. Create them again and give them to their agents. +- Keys revoked after the backup work again. Revoke them again. + +If you lose the release namespace, restore the exported Secrets before you reinstall the release with the same values. Existing agent keys keep working after the restore. + +### Move the metadata Redis + +Moving the metadata Redis causes an outage, so plan a maintenance window. While the Identity Service is stopped, agents lose access after at most `runtime.cache.hardTtlSeconds`, and you can't manage keys. If the services use the same Redis, plan for them too. + +1. Set `identityService.replicaCount` to `0` and run `helm upgrade`. +1. Copy the data to the new Redis. +1. Put the new host in `metadata.urls` in the metadata Secret. +1. Set `identityService.replicaCount` to its earlier value and run `helm upgrade`. + +Don't let the Identity Service run after the copy. If it does, keys you revoke after the copy work again, and the new Redis lacks the keys you create after the copy. + +### Planned outage of the metadata Redis + +While the metadata Redis is down, the Identity Service turns unready, and no agent key can be checked, created, or changed. Kubernetes doesn't restart the pods. When Redis returns, readiness recovers without a restart. + +## Troubleshooting + +The Identity Service returns errors as RFC 7807 problem documents. The `type` field names the error. + +{{< table-scrollable >}} +| Symptom | Possible cause | What to do | +| --- | --- | --- | +| The install stops and names a field | A value differs from `global.iris`, or a required value is missing. | Remove or set the field the message names. | +| Readiness returns `503` | The Identity Service can't reach the metadata Redis. | Check the URL, user, password, and Transport Layer Security (TLS) settings in the metadata Secret. | +| The pod doesn't start | Two credentials share a value, or a value isn't valid. | Read the first error line in the pod log. It names the field. | +| `401` on the control API | Wrong control token, or the operator token has the wrong issuer, audience, or signature, or expired. | Compare the token's `iss` and `aud` claims with `global.iris.auth.oidc`. | +| `503` on the control API | The Identity Service can't read its token file or reach the JSON Web Key Set (JWKS) URI. | Check the Secret mount and the network path to your identity provider (IdP). | +| `403` on the control API | The caller has no `suite_admin` or `product_admin` role for this service. | Check `global.iris.auth.roleBindings` and the groups in the token. | +| `404` for a key that exists | The key is in another tenant. | Check the caller's tenant. | +| `400` "must be the caller's tenant" or "tenant is not accepted by this installation" | The grant names another tenant. | Remove `tenant` from the grant. | +| `400` from grant validation | The resource doesn't exist, or an action isn't valid. | Check the resource ID in the service's control plane. | +| `424` | The Identity Service can't use the grant validator: the control plane is down, or the internal token is wrong. | Check the control-plane pods and logs. | +| Agents get `503` "Identity Service Unavailable", and the data-plane log shows `service-credential-rejected` | The data plane's runtime credential is wrong. | Compare the data plane's credential Secret with the Secret the Identity Service mounts. | +| A revoked key still works | The data plane's key cache. | Wait up to `runtime.cache.hardTtlSeconds`. | +{{< /table-scrollable >}} + +## Limits + +- **One tenant for each release.** For more than one tenant, install one release for each tenant. Several tenants in one Agent Memory installation is a [preview](/content/operate/iris/identity-service/self-managed/multi-tenant.md). +- **One IdP for each release.** Every operator signs in through the IdP in `global.iris.auth.oidc`. A tenant can't bring its own IdP. +- **Subject grants have no effect** in a `redis-iris` installation. +- **No FIPS.** The suite chart doesn't support the Federal Information Processing Standards (FIPS) profile. +- **The key list has no pages.** It returns every key. +- **No NetworkPolicy.** You must supply it. diff --git a/content/operate/iris/identity-service/self-managed/prerequisites.md b/content/operate/iris/identity-service/self-managed/prerequisites.md new file mode 100644 index 0000000000..024ee4de3a --- /dev/null +++ b/content/operate/iris/identity-service/self-managed/prerequisites.md @@ -0,0 +1,70 @@ +--- +Title: Self-managed Identity Service prerequisites +alwaysopen: false +categories: +- docs +- operate +- iris +description: Check the metadata Redis, Secrets, network access, and values you need before you install the Identity Service with the redis-iris suite chart. +linkTitle: Prerequisites +weight: 20 +hideListLinks: true +--- + +The Identity Service depends on a metadata Redis and Secrets that you set up first. This page lists what the Identity Service needs, in addition to the [suite prerequisites](/content/operate/iris/self-managed/_index.md#prerequisites), before you [install it](/content/operate/iris/identity-service/self-managed/deploy.md). + +## What you need + +| Requirement | Detail | +| --- | --- | +| Suite chart | `redis-iris` chart `1.0` in the Redis AI Helm repository, `https://helm.redis.io/ai`. The suite prerequisites list the Kubernetes, Helm, and Metrics API versions. | +| Namespace | A namespace that holds only this release. The examples use ``. | +| Metadata Redis | A Redis database for the Identity Service, as described in [Metadata Redis](#metadata-redis). | +| `kubectl`, `curl` | For the install and administration examples. | +| Identity provider (IdP) | Optional. An OpenID Connect (OIDC) IdP for operator sign-in. See [Operator sign-in](/content/operate/iris/identity-service/self-managed/authentication.md#operator-sign-in-with-your-identity-provider). | + +`redis-iris` `1.0` is a new installation. There is no in-place move from the standalone product charts. See [Move from a preview or standalone install](/content/operate/iris/self-managed/_index.md#move-from-a-preview-or-standalone-install). + +## Metadata Redis + +The metadata Redis holds every agent key and grant. If you lose it, every agent loses access. The chart never deploys it. + +- Turn on persistence, with an append-only file (AOF) or RDB snapshots, and keep the data directory on durable storage. Without persistence, a restart of that Redis deletes every key and grant. +- Back it up on a schedule. See [Back up and restore](/content/operate/iris/identity-service/self-managed/operations.md#back-up-and-restore). +- Protect access to it. A caller that can write to it can create an agent key with any grant. Give the Identity Service its own Redis user, and give no other user access to the Identity Service keys: `iris:apikey:*`, `iris:subject-grants:*`, and `iris:subject-grant-sets:*`. +- The Identity Service always uses database 0, and ignores a `/` index in the URL. + +## Network access + +| From | To | Why | +| --- | --- | --- | +| Operators, the Iris console, and automation | Identity Service `9200` | Manage agent keys. | +| Service data planes | Identity Service `9200` | Check agent keys. | +| Identity Service | Service control planes | Validate grants. | +| Identity Service | Metadata Redis | Storage. | +| Identity Service | Your IdP's JSON Web Key Set (JWKS) URI | Only when you turn on operator sign-in. | + +The chart renders no NetworkPolicy and no Ingress for the Identity Service. Let only the services, the console, and your operators reach port `9200`. + +## Secrets + +Create the metadata Secret before you install. [Deploy](/content/operate/iris/identity-service/self-managed/deploy.md#2-create-the-metadata-secret) shows the command. + +| Secret | Key | Holds | +| --- | --- | --- | +| `ids-metadata` | `metadata.yaml` | The metadata Redis connection. | + +By default, the chart generates every other Identity Service credential: the control token, and a runtime credential and an internal token for each enabled service. To supply them yourself, see [Supply your own credentials](/content/operate/iris/identity-service/self-managed/deploy.md#supply-your-own-credentials). + +The suite also needs the shared Secrets that the [suite install](/content/operate/iris/self-managed/_index.md#2-create-the-shared-secrets) lists, such as the license and the CA bundle. + +## Helm values that stop an install + +The suite derives the Identity Service's name, port, tenant, service registration, and operator sign-in from `global.iris`. Rendering fails and names the field when: + +- `identityService.metadata.existingSecret` is missing. +- A value under `identityService` differs from the value the suite derives from `global.iris`. Remove the field the message names. +- `global.security.profile` is `fips`. The suite chart doesn't support the Federal Information Processing Standards (FIPS) profile. +- In GitOps mode, the two `global.iris.secrets.*` Secrets have the same name. + +The Identity Service also refuses to start when two of its credentials have the same value: the control token, the runtime credentials, and the internal tokens. The first error line of the pod log names the field. diff --git a/content/operate/iris/self-managed/_index.md b/content/operate/iris/self-managed/_index.md index 642272629e..0c48ed8d62 100644 --- a/content/operate/iris/self-managed/_index.md +++ b/content/operate/iris/self-managed/_index.md @@ -61,7 +61,7 @@ Create these Secrets in the release namespace. Each service guide lists that ser | Secret | Key | Holds | | --- | --- | --- | | `iris-license` | `license.key` | The suite license. | -| `ids-metadata` | `metadata.yaml` | The Identity Service metadata Redis connection. See [Create the metadata Secret](/content/operate/iris/identity-service/deploy.md#2-create-the-metadata-secret). | +| `ids-metadata` | `metadata.yaml` | The Identity Service metadata Redis connection. See [Create the metadata Secret](/content/operate/iris/identity-service/self-managed/deploy.md#2-create-the-metadata-secret). | | `cluster-ca` | `ca.crt` | The certificate authority (CA) bundle every component trusts: the cluster CA from the `kube-root-ca.crt` ConfigMap, plus any private CA your Redis or IdP uses. | | `iris-ui-oidc` | `client-secret` | The console's client secret at your IdP. Register the redirect URI `/auth/callback`. | | `ui-session-redis` | `urls` | The Redis URL for console sessions. |