Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 1 addition & 98 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ The service includes comprehensive user data collection capabilities for various
* [Control model/provider overrides via authorization](#control-modelprovider-overrides-via-authorization)
* [Safety Shields](#safety-shields)
* [Authentication](#authentication)
* [K8s based authentication](#k8s-based-authentication)
* [JSON Web Keyset based authentication](#json-web-keyset-based-authentication)
* [No-op authentication](#no-op-authentication)
* [CORS](#cors)
* [Default values](#default-values)
* [Allow credentials](#allow-credentials)
Expand Down Expand Up @@ -397,101 +394,7 @@ utilized:

## Authentication

Currently supported authentication modules are:
* `k8s` Kubernetes based authentication
* `jwk-token` JSON Web Keyset based authentication
* `noop` No operation authentication (default)
* `noop-with-token` No operation authentication with token

### K8s based authentication

K8s based authentication is suitable for running the Lightspeed Stack in Kubernetes environments.
The user accessing the service must have a valid Kubernetes token and the appropriate RBAC permissions to access the service.
The user must have `get` permission on the Kubernetes RBAC non-resource URL `/ls-access`.
Here is an example of granting `get` on `/ls-access` via a ClusterRole’s nonResourceURLs rule.
Example:
```yaml
# Allow GET on non-resource URL /ls-access
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: lightspeed-access
rules:
- nonResourceURLs: ["/ls-access"]
verbs: ["get"]
---
# Bind to a user, group, or service account
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: lightspeed-access-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: lightspeed-access
subjects:
- kind: User # or ServiceAccount, Group
name: SOME_USER_OR_SA
apiGroup: rbac.authorization.k8s.io
```

Configuring K8s based authentication requires the following steps:
1. Enable K8s authentication module
```yaml
authentication:
module: "k8s"
```
2. Configure the Kubernetes authentication settings.
When deploying Lightspeed Stack in a Kubernetes cluster, it is not required to specify cluster connection details.
It automatically picks up the in-cluster configuration or through a kubeconfig file.
This step is not neccessary.
When running outside a kubernetes cluster or connecting to external Kubernetes clusters, Lightspeed Stack requires the cluster connection details in the configuration file:
- `k8s_cluster_api` Kubernetes Cluster API URL. The URL of the K8S/OCP API server where tokens are validated.
- `k8s_ca_cert_path` Path to the CA certificate file for clusters with self-signed certificates.
- `skip_tls_verification` Whether to skip TLS verification.
```yaml
authentication:
module: "k8s"
skip_tls_verification: false
k8s_cluster_api: "https://your-k8s-api-server:6443"
k8s_ca_cert_path: "/path/to/ca.crt"
```

### JSON Web Keyset based authentication

JWK (JSON Web Keyset) based authentication is suitable for scenarios where you need to authenticate users based on tokens. This method is commonly used in web applications and APIs.

To configure JWK based authentication, you need to specify the following settings in the configuration file:
- `module` must be set to `jwk-token`
- `jwk_config` JWK configuration settings must set at least `url` field:
- `url`: The URL of the JWK endpoint.
- `jwt_configuration`: JWT configuration settings.
- `user_id_claim`: The key of the user ID in JWT claim.
- `username_claim`: The key of the username in JWT claim.

```yaml
authentication:
module: "jwk-token"
jwk_config:
url: "https://your-jwk-url"
jwt_configuration:
user_id_claim: user_id
username_claim: username
```

### No-op authentication

Lightspeed Stack provides 2 authentication module to bypass the authentication and authorization checks:
- `noop` No operation authentication (default)
- `noop-with-token` No operation authentication accepting a bearer token

If authentication module is not specified, Lightspeed Stack will use `noop` by default.
To activate `noop-with-token`, you need to specify it in the configuration file:

```yaml
authentication:
module: "noop-with-token"
```
See [authentication and authorization](docs/auth.md).

## CORS

Expand Down
260 changes: 260 additions & 0 deletions docs/auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
# Authentication and Authorization

Lightspeed Core Stack implements a modular authentication and authorization
system with multiple authentication methods. Authorization is configurable
through role-based access control.

## Authentication configuration

The authentication system is configured via the `authentication` section in
the configuration file.

## Authentication Modules

Authentication is handled through selectable modules configured via the
`module` field in the authentication configuration.

### No-op (`noop`)

Development-only authentication that bypasses security checks.

**Configuration:**
```yaml
authentication:
module: noop
```

**Behavior:**
- Accepts any request without token validation
- Extracts `user_id` from query parameters (defaults to `00000000-0000-0000-0000-000`)
- Uses fixed username `lightspeed-user`

### No-op with Token (`noop-with-token`)

Development authentication that requires tokens but doesn't validate them.

**Configuration:**
```yaml
authentication:
module: noop-with-token
```

**Behavior:**
- Extracts bearer token from the `Authorization` header
- Same user ID and username handling as `noop`
- Token is passed through unvalidated for downstream use

### Kubernetes (`k8s`)

K8s based authentication is suitable for running the Lightspeed Stack in
Kubernetes environments. The user accessing the service must have a valid
Kubernetes token and the appropriate RBAC permissions to access the service.
The user must have the `get` permission on the Kubernetes RBAC non-resource URL
`/ls-access`. Here is an example of granting `get` on `/ls-access` via a
ClusterRole’s nonResourceURLs rule:

```yaml
# Allow GET on non-resource URL /ls-access
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: lightspeed-access
rules:
- nonResourceURLs: ["/ls-access"]
verbs: ["get"]
---
# Bind to a user, group, or service account
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: lightspeed-access-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: lightspeed-access
subjects:
- kind: User # or ServiceAccount, Group
name: SOME_USER_OR_SA
apiGroup: rbac.authorization.k8s.io
```

**Configuration:**

When deploying Lightspeed Stack in a Kubernetes cluster, it is not required to
specify cluster connection details, it automatically picks up the in-cluster
configuration or through a kubeconfig file.

When running outside a kubernetes cluster or connecting to external Kubernetes
clusters, Lightspeed Stack requires the cluster connection details in the
configuration file:

- `k8s_cluster_api` Kubernetes Cluster API URL. The URL of the k8s/OCP API server where tokens are validated.
- `k8s_ca_cert_path` Path to the CA certificate file for clusters with self-signed certificates.
- `skip_tls_verification` Whether to skip TLS verification.

For example:

```yaml
authentication:
module: k8s
k8s_cluster_api: https://kubernetes.default.svc # optional, will be auto-detected
k8s_ca_cert_path: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt # optional, will be auto-detected
skip_tls_verification: false # optional, insecure
```
Comment on lines +101 to +103
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix misleading TLS verification note.

The sample sets skip_tls_verification: false but labels that setting “insecure.” false is the safe option (TLS verification on); only true is insecure. Please adjust the comment so readers don’t disable verification by mistake.

-  skip_tls_verification: false  # optional, insecure
+  skip_tls_verification: false  # optional; set to true only if you must skip TLS verification (insecure)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
k8s_ca_cert_path: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt # optional, will be auto-detected
skip_tls_verification: false # optional, insecure
```
k8s_ca_cert_path: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt # optional, will be auto-detected
skip_tls_verification: false # optional; set to true only if you must skip TLS verification (insecure)
🤖 Prompt for AI Agents
In docs/auth.md around lines 101 to 103, the inline comment mislabels the TLS
verification setting: it marks `skip_tls_verification: false` as "insecure" even
though `false` is the safe option (verification enabled); update the comment so
it correctly states that `false` is secure (TLS verification on) and that only
`true` is insecure (disables verification), e.g., change the phrase to something
like "optional, secure when false; set to true to disable TLS verification
(insecure)" so readers aren't misled.


**Behavior:**
- Validates bearer tokens via the Kubernetes TokenReview API
- Performs authorization checks using SubjectAccessReview (SAR)
- Checks access to configured virtual path (default: `/ls-access`) with `get` verb
- Extracts user ID and username from token claims
- Special handling for the `kube:admin` user (uses cluster ID as user ID)

**Requirements:**
- Valid Kubernetes service account token in the `Authorization` header
- RBAC rules granting access to the virtual path
- Cluster access or kubeconfig file

### JWK Token (`jwk-token`)

JWK (JSON Web Keyset) based authentication is suitable for scenarios where you
need to authenticate users based on tokens. This method is commonly used in web
applications and APIs.

Users provide a JWT (JSON Web Token) in the `Authorization` header of their
requests. This JWT is validated against the JWK set fetched from the configured
URL.

**Configuration:**
```yaml
authentication:
module: jwk-token
jwk_config:
url: https://auth.example.com/.well-known/jwks.json
jwt_configuration:
user_id_claim: sub # optional, defaults to 'sub'
username_claim: name # optional, defaults to 'preferred_username'
role_rules: [] # optional role extraction rules. See Authorization section for details.
```

**Behavior:**
- Fetches JWK set from configured URL (cached for 1 hour)
- Validates JWT signature against JWK set
- Extracts user ID and username from configurable JWT claims
- Returns default credentials (guest-like) if no `Authorization` header present (guest access)

## Authorization System

Authorization is controlled through role-based access control using two resolver types.

### Role Resolution

Determines user roles based on authentication method:

**No-op/K8s Authentication:**
- Uses a no-op role resolver
- All users get the special `*` (everyone) role only
- To be expanded in the future

**JWK Token Authentication:**
- Uses JWT claims to determine user roles through JSONPath expressions
- Falls back to a no-op resolver if no role rules are configured

#### JWT Role Rules

Extract roles from JWT claims using JSONPath expressions, for example:

```yaml
authentication:
module: jwk-token
jwk_config:
jwt_configuration:
role_rules:
- jsonpath: "$.realm_access.roles[*]"
operator: contains
value: "manager"
roles: ["manager"]
- jsonpath: "$.org_id"
operator: "equals"
value: [["dummy_corp"]]
roles: ["dummy_employee"]
- jsonpath: "$.groups[*]"
operator: in
value: ["developers", "qa"]
roles: ["developer"]
negate: false
```

**Fields:**
- `jsonpath`: JSONPath expression to extract values from JWT claims.
- `operator`: Comparison operator (see below)
- `value`: Value(s) to evaluate the extracted values and operator against
- `roles`: List of roles to assign if the rule matches
- `negate`: If true, inverts the rule match result (optional, defaults to false)

Note that the JSONPath expression always yields a list of values, even for
single-value expressions, so comparisons should be done accordingly.

**Operators:**
- `equals`: Exact match
- `contains`: Value contains the specified string
- `in`: Value is in the specified list
- `match`: Regex pattern match (uses pre-compiled patterns)

### Access Resolution

Various operations inside lightspeed require authorization checks. Those
operations are associated with actions (e.g. `query`, `info`, `admin`).

Once user roles are determined, checking whether a user is allowed to perform
an action is done through access resolvers.

**No-op resolver:**

A resolver which uses a no-op access resolver that grants all users access to
all actions, used when no access rules are configured no-op authentication is
configured, or at-least currently when k8s authentication is configured.

**Rule-based Access:**

A resolver which does the obvious thing of checking whether any of the user's
roles is allowed to perform the requested action based on the access rules in
the authorization configuration. It also grants all users which have the `admin`
action unrestricted access to all other actions.

#### Access Rules

Define which roles can perform which actions:

```yaml
authorization:
access_rules:
# `*` is a special role that is given to all users
- role: "*"
actions: ["query", "info"]
- role: "manager"
# admin is a special *action* that grants unrestricted access to all actions.
# Note that only the `admin` *action* is special, there is no special `admin` role.
actions: ["admin"]
- role: "dummy_employee"
actions: ["list_conversations"]
- role: "developer"
actions: ["query", "get_config", "list_conversations"]
```

**Available Actions:**
- `admin` - If a user has this action, they automatically can perform all other actions
- `query` - Access query endpoints
- `query_other_conversations` - Query conversations not owned by the user
- `streaming_query` - Access streaming query endpoints
- `info` - Access the `/` endpoint, `/info` endpoint, `/readiness` endpoint, and `/liveness` endpoint
- `get_config` - Access the `/config` endpoint
- `get_models` - Access the `/models` endpoint
- `get_metrics` - Access the `/metrics` endpoint
- `list_conversations` - Access the `/conversations` endpoint
- `list_other_conversations` - Access conversations not owned by the user
- `get_conversation` - `GET` conversations from `/conversations/{conversation_id}` endpoint
- `read_other_conversations` - Read conversations not owned by the user
- `delete_conversation` - `DELETE` conversations from `/conversations/{conversation_id}` endpoint
- `delete_other_conversations` - Delete conversations not owned by the user
- `feedback` - Access the `/feedback` endpoint
- `model_override` - Allow user to choose the model when querying
Loading