diff --git a/docs/server/security/authentication/assets/certificates-list.png b/docs/server/security/authentication/assets/certificates-list.png new file mode 100644 index 0000000000..ad504427a5 Binary files /dev/null and b/docs/server/security/authentication/assets/certificates-list.png differ diff --git a/docs/server/security/authentication/assets/certificates-manage-dropdown.png b/docs/server/security/authentication/assets/certificates-manage-dropdown.png new file mode 100644 index 0000000000..8c5c54298a Binary files /dev/null and b/docs/server/security/authentication/assets/certificates-manage-dropdown.png differ diff --git a/docs/server/security/authentication/assets/register-sso-server-modal.png b/docs/server/security/authentication/assets/register-sso-server-modal.png new file mode 100644 index 0000000000..d3041f1ef1 Binary files /dev/null and b/docs/server/security/authentication/assets/register-sso-server-modal.png differ diff --git a/docs/server/security/authentication/assets/register-sso-user-modal.png b/docs/server/security/authentication/assets/register-sso-user-modal.png new file mode 100644 index 0000000000..9f6fdf4f3b Binary files /dev/null and b/docs/server/security/authentication/assets/register-sso-user-modal.png differ diff --git a/docs/server/security/authentication/sso-certificates.mdx b/docs/server/security/authentication/sso-certificates.mdx new file mode 100644 index 0000000000..94269c338c --- /dev/null +++ b/docs/server/security/authentication/sso-certificates.mdx @@ -0,0 +1,200 @@ +--- +title: "Authentication: SSO Certificates and Users" +sidebar_label: SSO Certificates & Users +description: "Register an SSO server certificate and SSO user entries in RavenDB, and keep SSO authentication working across certificate renewals by reusing the same key." +sidebar_position: 6 +--- + +import Admonition from '@theme/Admonition'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; + +# Authentication: SSO Certificates and Users + +RavenDB can authenticate users who arrive through an [SSO reverse-proxy application](../sso/overview.mdx) +— GitHub, Google, Microsoft/Entra ID, or Windows/Kerberos — instead of issuing one X.509 client certificate +per user. The cluster needs to know two things to do this: + +- **Which SSO servers to trust** — registered as certificates with `Usage = SsoServer`. +- **How to map an SSO identity to RavenDB permissions** — stored as **SSO user entries** with + `Usage = SsoClient`. + +Both are managed from **Manage Server → Certificates** in the Studio, and live in the same certificate +list as your regular server and client certificates. + + +SSO is a commercial feature gated by the RavenDB license. Registering an SSO server certificate requires +**Cluster Admin** clearance; SSO user entries can be managed by **Operator** clearance and above. + + +For end-to-end deployment of the SSO application itself, see the +[SSO section](../sso/overview.mdx). This page focuses on the certificate-management side of the workflow. + +## Register an SSO server certificate + +The SSO server presents its public certificate to clients on `https://`. Registering that +certificate in RavenDB tells the cluster "trust SSO identities forwarded by this server." Identification +is by **public-key pinning hash**, not by the certificate file itself. + +### Studio + +1. Open **Manage Server → Certificates**. +2. Click **Register SSO server**. +3. Either upload the certificate (PEM/PFX) or paste the SSO URL — the cluster will fetch the certificate + from `https:///api/certificate`, validate it, and show the parsed details for confirmation. +4. Give it a name and save. + +Open the **Manage user access** dropdown to find the SSO actions: + +![Manage user access dropdown with SSO actions](./assets/certificates-manage-dropdown.png) + +**Upload SSO certificate** opens the registration modal — either fetch the certificate from the SSO URL or +upload a file directly: + +![Register SSO server certificate modal](./assets/register-sso-server-modal.png) + +Once registered, the SSO server certificate appears under a dedicated **SSO** section of the certificates +list, alongside any existing SSO user entries: + +![Certificates list with SSO entries](./assets/certificates-list.png) + +### HTTP API + +```http +PUT /admin/certificates +Content-Type: application/json + +{ + "Name": "production-sso", + "Certificate": "", + "Usage": "SsoServer", + "SecurityClearance": "ValidUser", + "Permissions": {} +} +``` + +To let the cluster fetch the certificate for you: + +```http +GET /admin/certificates/sso/server/fetch?url=https://sso.example.com +``` + +This downloads `https://sso.example.com/api/certificate`, validates it, and returns the base64 bytes — +hand them to `PUT /admin/certificates` above. The fetch is limited to **1 MB** and times out after +**10 seconds**. + + +SSO server certificates cannot be generated by RavenDB — they belong to the SSO deployment and must be +imported. `POST /admin/certificates` with `Usage = SsoServer` is rejected. + + +## Register an SSO user entry + +An SSO user entry is a mapping from one or more **SSO identities** to a RavenDB security clearance and +database permissions. Each identity is a `(Provider, Domain, Identifier)` tuple: + +| Field | Type | Example | +|---|---|---| +| `Provider` | `Github` | `Google` | `Microsoft` | `Windows` | `Windows` | +| `Domain` | string | Kerberos realm — only used when `Provider = Windows`; leave empty otherwise. | +| `Identifier` | string | `alice@example.com`, GitHub username, etc. | + +Each entry must trust the SSO servers it accepts identities from. You can either: + +- list one or more `SsoServerPublicKeyPinningHashes` from registered SSO servers, **or** +- set `AllowAnySsoServer = true` to accept the identity from any SSO server registered in the cluster. + +Listing multiple pinning hashes is how you give one user access through several SSO deployments +(production + staging, for example) without duplicating the entry. + +### Studio + +1. **Manage Server → Certificates → Register SSO user**. +2. Enter a **Name**, choose a **Security Clearance**, and pick the **Permissions** per database. +3. Add one or more SSO identifiers (provider + identifier, plus a domain when the provider is + Windows — the Kerberos realm). +4. Pick the trust model — either tick **Allow any SSO server**, or select the SSO servers you registered + above. +5. Save. + +The modal collects all the entry's fields on one screen — display name, identifiers, security clearance, +trust scope (per-server pinning or any SSO server), and per-database permissions: + +![Add SSO user modal](./assets/register-sso-user-modal.png) + +### HTTP API + +```http +PUT /admin/certificates/sso/user +Content-Type: application/json + +{ + "Name": "alice", + "Usage": "SsoClient", + "SsoIdentifiers": [ + { "Provider": "Google", "Domain": "", "Identifier": "alice@example.com" } + ], + "SsoServerPublicKeyPinningHashes": [""], + "AllowAnySsoServer": false, + "SecurityClearance": "ValidUser", + "Permissions": { "Northwind": "ReadWrite" } +} +``` + +Validation rules enforced by the endpoint: + +- `Name` and at least one entry in `SsoIdentifiers` are required. +- Either `AllowAnySsoServer = true` **or** at least one pinning hash. +- Every pinning hash must reference a previously registered SSO server. Otherwise the request fails with + a message pointing you back to the SSO server registration step. +- Each `(Provider, Domain, Identifier)` tuple is **unique cluster-wide** — edit the existing entry instead + of creating a second one for the same identity. + +## Renewing the SSO server certificate + + +RavenDB identifies trusted SSO servers by their **public key pinning hash**, not by the certificate file. +Every SSO user entry that doesn't have `AllowAnySsoServer = true` is pinned to one or more specific +hashes. **If you renew the SSO server certificate with a new key pair, the hash changes and every +non-`AllowAnySsoServer` entry stops accepting logins through that server until you re-register the new +certificate and update each user entry's pinning list.** + +To keep SSO authentication working continuously across renewals, always renew the SSO server certificate +with **the same private key** (the same Certificate Signing Request). The public key — and therefore the +pinning hash — stays identical, and every existing SSO user entry keeps working without any change in +RavenDB. + + +This is the same mechanism RavenDB uses for [implicit trust between server and client certificates](./certificate-renewal-and-rotation.mdx#implicit-trust-by-public-key-pinning-hash) — see that section +for the underlying details. + +Practical guidance: + +- **Let's Encrypt via the bundled SSO `certbot`** — already pins `--key-type rsa` and `--reuse-key`, + so renewals automatically keep the public key stable. No action is needed on the RavenDB side. +- **Manually managed certificates** — when generating a renewal, re-use the existing CSR or private key + rather than creating a fresh key pair. Most ACME clients and CAs accept a CSR-based renewal explicitly + for this reason. +- **If you must rotate the key** — register the new SSO server certificate as a second `SsoServer` entry + before retiring the old one. Update every SSO user entry to include the new pinning hash alongside the + old one. Once all clients are on the new certificate, you can remove the old `SsoServer` entry and the + old hash from the user entries. + +## Supported providers + +| `SsoProvider` | Typical `Identifier` | When to set `Domain` | +|---|---|---| +| `Github` | GitHub username | Not used — leave empty. | +| `Google` | Email address | Not used — leave empty. | +| `Microsoft` | Email or object id | Not used — leave empty. | +| `Windows` | `user@DOMAIN` | **Required** — set to the Kerberos realm. | + +`Domain` is only consulted when `Provider = Windows`. For the OAuth providers it is ignored — leave it +empty (or omit it from the JSON payload). + +## Related reading + +- [SSO Overview](../sso/overview.mdx) — what the SSO application does and how the auth flow works. +- [Deploying the SSO application](../sso/deploying-sso-app.mdx) — installer and Docker Compose setup. +- [Certificate Renewal & Rotation](./certificate-renewal-and-rotation.mdx#implicit-trust-by-public-key-pinning-hash) — the public-key pinning hash mechanism that underpins SSO server trust. diff --git a/docs/server/security/overview.mdx b/docs/server/security/overview.mdx index 8f3c0d6489..3d35841c12 100644 --- a/docs/server/security/overview.mdx +++ b/docs/server/security/overview.mdx @@ -47,6 +47,21 @@ In Studio, administrators can use the [Certificates View](../../server/security/ +## Single Sign-On (SSO) + +RavenDB can accept users authenticated by GitHub, Google, Microsoft/Entra ID, or Windows/Kerberos through a +dedicated SSO reverse-proxy application. The cluster maps each SSO identity to a security clearance and per-database +permissions, without distributing X.509 client certificates per user. + +**Read more:** + +- [SSO Overview](../../server/security/sso/overview.mdx) +- [Deploying the SSO Application](../../server/security/sso/deploying-sso-app.mdx) +- [SSO Configuration Reference](../../server/security/sso/sso-configuration.mdx) +- [Configuring RavenDB for SSO](../../server/security/sso/configuring-ravendb-for-sso.mdx) + + + ## Authorization Authorization in RavenDB is based on the same X.509 certificates. diff --git a/docs/server/security/sso/_category_.json b/docs/server/security/sso/_category_.json new file mode 100644 index 0000000000..24d142addc --- /dev/null +++ b/docs/server/security/sso/_category_.json @@ -0,0 +1,4 @@ +{ + "position": 5, + "label": "SSO" +} diff --git a/docs/server/security/sso/assets/clusters-page-multiple.png b/docs/server/security/sso/assets/clusters-page-multiple.png new file mode 100644 index 0000000000..67f789d71b Binary files /dev/null and b/docs/server/security/sso/assets/clusters-page-multiple.png differ diff --git a/docs/server/security/sso/assets/login-page-all-providers.png b/docs/server/security/sso/assets/login-page-all-providers.png new file mode 100644 index 0000000000..ed3d6eb41a Binary files /dev/null and b/docs/server/security/sso/assets/login-page-all-providers.png differ diff --git a/docs/server/security/sso/configuring-ravendb-for-sso.mdx b/docs/server/security/sso/configuring-ravendb-for-sso.mdx new file mode 100644 index 0000000000..d3f7dc31a5 --- /dev/null +++ b/docs/server/security/sso/configuring-ravendb-for-sso.mdx @@ -0,0 +1,44 @@ +--- +title: "SSO: Configuring RavenDB" +sidebar_label: Configuring RavenDB +description: "Where to find the cluster-side configuration steps for SSO — registering the SSO server certificate and creating SSO user entries." +sidebar_position: 4 +--- + +import Admonition from '@theme/Admonition'; + +# SSO: Configuring RavenDB + +Once the [SSO application](./deploying-sso-app.mdx) is deployed, a RavenDB cluster needs two things to +accept users coming through it: + +1. The **SSO server's public certificate**, registered with `Usage = SsoServer`. +2. One or more **SSO user entries** (`Usage = SsoClient`) that map an upstream identity to a security + clearance and database permissions. + +Both are managed from **Manage Server → Certificates** in the Studio (or through the admin certificates +HTTP API), alongside the cluster's regular server and client certificates. The full procedure — Studio +flow, HTTP payloads, validation rules, supported providers, and the certificate-renewal caveat — is +documented on a single page: + + +[Authentication: SSO Certificates and Users](../authentication/sso-certificates.mdx) + + +## End-to-end checklist + +1. Deploy the SSO application (see [Deploying the SSO Application](./deploying-sso-app.mdx)). +2. As a **Cluster Admin**, [register the SSO server certificate](../authentication/sso-certificates.mdx#register-an-sso-server-certificate) — + either by fetching it from the SSO URL or by uploading it directly. +3. As an **Operator** or higher, [create SSO user entries](../authentication/sso-certificates.mdx#register-an-sso-user-entry) + for each user or group, choosing the right provider, identifier, and database permissions. +4. Users now log in at `https://./studio/`. They authenticate through the + provider; the SSO server issues a short-lived client certificate carrying their SSO identity; RavenDB + matches that identity against the registered SSO user entry and applies its permissions. + + +SSO server trust is pinned by **public key**. Renewing the SSO certificate with a new key pair changes +the pinning hash and breaks every SSO user entry pinned to the old hash. See +[Renewing the SSO server certificate](../authentication/sso-certificates.mdx#renewing-the-sso-server-certificate) +for the rationale and the key-rotation workaround. + diff --git a/docs/server/security/sso/deploying-sso-app.mdx b/docs/server/security/sso/deploying-sso-app.mdx new file mode 100644 index 0000000000..f15d102958 --- /dev/null +++ b/docs/server/security/sso/deploying-sso-app.mdx @@ -0,0 +1,375 @@ +--- +title: "SSO: Deploying the SSO Application" +sidebar_label: Deploying the SSO Application +description: "Deploy the RavenDB SSO reverse proxy with Docker — DNS, TLS via Let's Encrypt, OAuth2 provider registration, and Kerberos/AD setup." +sidebar_position: 2 +--- + +import Admonition from '@theme/Admonition'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; + +# SSO: Deploying the SSO Application + +The SSO application is distributed as a single Docker image (`ravendb/sso`) that bundles Nginx, the +authentication sidecar, and `oauth2-proxy`. The [ravendb/sso](https://github.com/ravendb/sso) repository +ships with an **interactive installer** (`install.sh` for Linux/macOS, `install.ps1` for Windows) that +asks all the questions, pre-flights DNS and host permissions, generates a complete deployment directory, +and optionally starts the stack. This is the recommended way to set up a new SSO server. + + +Unless you have a specific reason to assemble the deployment by hand (for example because you're +integrating into existing automation), run `install.sh` / `install.ps1`. It produces the same artifacts +that the manual setup below describes, but with secrets generated for you, file permissions set, and DNS +sanity-checked. + + +## Prerequisites + +- Docker (Compose v2). +- A public domain you control, with the ability to create DNS records. +- At least one of: + - OAuth credentials for **GitHub**, **Google**, or **Microsoft/Entra ID**, or + - A Kerberos keytab for an Active Directory service principal. +- For the bundled Let's Encrypt option: an **AWS account** with **Route53** hosting the SSO domain and an + IAM user authorized to create TXT records (DNS-01 challenge). The installer can also wire up a + manually-supplied certificate instead. + +## DNS setup + +The SSO server is reached at a base domain (e.g. `sso.example.com`). Each RavenDB cluster behind it is +exposed at `.` — for example `my-cluster.sso.example.com`. You therefore need +both an apex record and a wildcard for sub-aliases: + +| Type | Name | Value | +|---|---|---| +| `A` | `sso.example.com` | your server's public IP | +| `CNAME` | `*.sso.example.com` | `sso.example.com` | + +The installer probes both records before continuing and will warn (but not block) if they are missing. + +## Recommended: interactive installer + +The installer is a single self-contained script — every template it writes (including `nlog.config` and +the `docker-compose.yml`) is embedded inline, so you can download and run it directly without cloning the +repository. + + + + +Download and run: + +```bash +curl -fsSL https://raw.githubusercontent.com/ravendb/sso/main/install.sh -o install.sh +chmod +x install.sh +./install.sh +``` + +Or pipe directly into Bash: + +```bash +bash <(curl -fsSL https://raw.githubusercontent.com/ravendb/sso/main/install.sh) +``` + + + + +Download and run: + +```powershell +Invoke-WebRequest -Uri https://raw.githubusercontent.com/ravendb/sso/main/install.ps1 -OutFile install.ps1 +.\install.ps1 +``` + +Or run directly from the URL: + +```powershell +iex (irm https://raw.githubusercontent.com/ravendb/sso/main/install.ps1) +``` + + + + +If you'd rather have the rest of the repository (for the manual `example/` deployment, source, or tests), +clone it and run the script from there instead: + +```bash +git clone https://github.com/ravendb/sso.git && cd sso && ./install.sh +``` + +The script walks through the following steps: + +1. **Output directory** — defaults to `./sso-deploy`. Must be empty. +2. **Host user check** *(Linux only)* — verifies that UID `10000` exists; offers to create the + `ravendb-sso` system user (the SSO container runs as `10000:10000`). +3. **SSO URL** — your public `https://sso.example.com`. +4. **DNS sanity check** — resolves the apex `A` record and probes a random subdomain to confirm the + wildcard `CNAME` is in place. +5. **TLS certificate strategy** — pick `certbot-route53` (automated Let's Encrypt via Route53 DNS-01) or + `manual` (you supply `fullchain.pem` and `privkey.pem`). +6. **Authentication providers** — multi-select between GitHub, Google, Microsoft/Entra ID, and Kerberos. + Each selected provider expands into its own credential prompts. +7. **Kerberos files** *(if Kerberos was chosen)* — paths to `krb5.conf` and `krb5.keytab`. On Linux the + script verifies that UID `10000` can read both files and offers to apply an ACL (`setfacl -m u:10000:r`) + if not. +8. **Clusters** — for each RavenDB cluster, an alias (lowercase letters, digits, hyphens) and one or more + node URLs (HTTPS only). +9. **Summary and confirmation**, then file generation. + +### What the installer produces + +``` +sso-deploy/ + sso.env # SSO secrets — mode 600 + certbot.env # AWS creds + LE email (only if cert mode = certbot) — mode 600 + docker-compose.yml # Two services if certbot, one if manual certs + config/ + settings.json # Generated cluster topology + nlog.config # Copied from the example + certs/ # Pre-created, owned by UID 10000 (populated by certbot or pre-staged) + logs/ # Pre-created, owned by UID 10000 + README-deploy.md # Per-deployment quick reference +``` + +`OAUTH2_PROXY_COOKIE_SECRET` is generated automatically (32 hex chars). `config/`, `certs/`, and `logs/` +are chowned to `10000:10000` so the container can write to them. After the script finishes, start the +stack from the generated directory: + +```bash +cd sso-deploy +docker compose up -d +``` + +The script also offers to run that command for you as its final step. + +## Manual deployment with Docker Compose + +If you can't or don't want to run the installer, the example under `Raven.Sso/example/` in the +[ravendb/sso](https://github.com/ravendb/sso) repository is the same layout the installer generates, just +filled in by hand. It runs two containers: + +- **`certbot`** — `certbot/dns-route53`. Obtains the wildcard certificate on first start and renews every 12 hours. +- **`sso`** — `ravendb/sso:latest`. Starts once `certbot` has produced a valid certificate, and reloads Nginx + every 6 hours to pick up renewals. + +### 1. Copy and fill the environment files + +The example splits environment variables into two files so AWS credentials aren't exposed to the SSO container: + +```bash +cp sso.env.example sso.env +cp certbot.env.example certbot.env +``` + +Edit each file with your domain, OAuth credentials, and AWS keys. The complete list of variables is on the +[SSO application configuration](./sso-configuration.mdx) page. + +### 2. (Optional) Configure clusters via `settings.json` + +The list of RavenDB clusters the SSO server should proxy can live in `config/settings.json`: + +```json +{ + "Clusters": [ + { + "ClusterUrls": ["https://a.my-cluster.example.com"], + "ClusterAlias": "my-cluster" + } + ] +} +``` + +Alternatively, set `RAVENDBSSO_Clusters` in `sso.env` to the same JSON array — environment variables override +file values. + +### 3. Start the stack + +```bash +docker compose up -d +``` + +On first run, `certbot` requests the wildcard certificate from Let's Encrypt; this can take 1–2 minutes +while the DNS TXT record propagates. The SSO container's `depends_on` waits for `certbot` to report healthy +before starting Nginx. + + +The example's `certbot` entrypoint pins `--key-type rsa`. RavenDB's certificate utilities only support RSA; +do not switch this to ECDSA. + + +### 4. Volumes and on-host layout + +``` +example/ + sso.env # SSO env vars + certbot.env # certbot env vars + docker-compose.yml + config/ + settings.json # optional — clusters can come from env vars instead + nlog.config # optional — logging configuration (autoReload) + certs/ # populated by certbot + live// + fullchain.pem + privkey.pem + logs/ # SSO + Nginx logs +``` + +## Quick start (single container) + +For local testing without Let's Encrypt, build the image directly and pass credentials on the command line: + +```bash +docker build -t auth-proxy . + +docker run --rm -it \ + -p 8080:8080 \ + -e OAUTH2_PROXY_COOKIE_SECRET=$(openssl rand -base64 32) \ + -e GITHUB_CLIENT_ID=... \ + -e GITHUB_CLIENT_SECRET=... \ + -e GOOGLE_CLIENT_ID=... \ + -e GOOGLE_CLIENT_SECRET=... \ + -e MICROSOFT_CLIENT_ID=... \ + -e MICROSOFT_CLIENT_SECRET=... \ + -e MICROSOFT_TENANT=common \ + -v /path/to/app.keytab:/etc/nginx/app.keytab:ro \ + -v /path/to/krb5.conf:/etc/krb5.conf:ro \ + auth-proxy +``` + +Any combination of providers can run concurrently. Volumes for the keytab and `krb5.conf` are only needed +when using Kerberos. + +After login, the SSO portal lists the clusters the user can reach — each cluster is exposed under its own +sub-alias of the SSO domain: + +![SSO cluster picker](./assets/clusters-page-multiple.png) + +## Registering OAuth providers + +For every OAuth provider you enable, the redirect URI is +`https:///oauth2//callback`. + +### GitHub + +1. Open [GitHub Settings → Developer settings → OAuth Apps](https://github.com/settings/developers). +2. **New OAuth App**. +3. Set **Homepage URL** to your SSO URL and **Authorization callback URL** to + `https:///oauth2/github/callback`. +4. Generate a client secret. Set `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET`. + +### Google + +1. In [Google Cloud Console](https://console.cloud.google.com/) → **APIs & Services → Credentials**, create + an **OAuth 2.0 Client ID** of type **Web application**. +2. Add `https:///oauth2/google/callback` to **Authorized redirect URIs**. +3. Set `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`. + +### Microsoft / Entra ID + +The SSO image uses oauth2-proxy's `entra-id` provider, which supports both single-tenant and multi-tenant +apps. + +1. In [Azure Portal → App registrations](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade), + click **New registration**. +2. Pick **Supported account types** — single-tenant, multi-tenant, or multi-tenant + personal accounts. + This must match the `MICROSOFT_TENANT` value you choose. +3. Set **Redirect URI** (Web) to `https:///oauth2/microsoft/callback`. +4. Under **Token configuration**, add `email` as an **optional claim** for the ID token — organizational + accounts don't emit it by default, and the SSO server uses it as the username. +5. Under **Certificates & secrets**, create a client secret. +6. Set `MICROSOFT_CLIENT_ID`, `MICROSOFT_CLIENT_SECRET`, and `MICROSOFT_TENANT`: + - **Single-tenant**: your tenant GUID. + - **Multi-tenant (orgs only)**: `organizations`. + - **Multi-tenant + personal accounts**: `common`. +7. For multi-tenant apps, scope access by setting `MICROSOFT_ALLOWED_TENANTS` (comma-separated tenant + GUIDs) and / or `MICROSOFT_ALLOWED_EMAIL_DOMAINS`. Without these allowlists a multi-tenant app accepts + **any** organization's users. + +### Cookie secret + +`oauth2-proxy` requires an encryption key for its session cookie. + +```bash +# Single-container quick-start (32-byte base64): +openssl rand -base64 32 + +# docker-compose example (16/24/32-char hex): +openssl rand -hex 16 +``` + +Set the result as `OAUTH2_PROXY_COOKIE_SECRET`. + +## Kerberos / Active Directory + +On a domain controller (or with delegated rights): + +```powershell +# 1. Create a service account, e.g. svc_docker, then map an SPN to it: +setspn -A HTTP/app.domain.com svc_docker + +# 2. Export a keytab: +ktpass /princ HTTP/app.domain.com@DOMAIN.COM ` + /mapuser DOMAIN\svc_docker ` + /pass YourPassword123 ` + /out C:\temp\app.keytab ` + /crypto All ` + /ptype KRB5_NT_PRINCIPAL +``` + +Mount the keytab at `/etc/nginx/app.keytab` and supply a Kerberos client config at `/etc/krb5.conf`: + +```ini +[libdefaults] + default_realm = DOMAIN.COM + dns_lookup_kdc = true + dns_lookup_realm = false + +[realms] + DOMAIN.COM = { + kdc = dc01.domain.com + admin_server = dc01.domain.com + } + +[domain_realm] + .domain.com = DOMAIN.COM + domain.com = DOMAIN.COM +``` + +If Kerberos auth fails or isn't available, Nginx falls back to OAuth automatically. + +## Logs + +All logs are written under `/app/logs/` inside the container — mount the directory to keep them on the host: + +| File | Source | Content | +|---|---|---| +| `app.log` | NLog (rotating, 7-day retention) | Startup, certificate generation, cluster polling, errors | +| `audit.log` | NLog (rotating, 30-day retention) | Login success/failure, access denied, logout — with user and client IP | +| `app.out.log` | stdout | Raw .NET process stdout | +| `app.err.log` | stderr | Raw .NET process stderr | +| `nginx-access.log` | Nginx | Standard combined access log | +| `nginx-error.log` | Nginx | Errors and warnings | +| `nginx-audit.log` | Nginx | Per-request audit log — real client IP, authenticated user, target host, status, response time | + +`nlog.config` is loaded from `/app/config/nlog.config` at startup with `autoReload="true"`, so log levels +can be changed without restarting the container. + +## Troubleshooting + +| Symptom | Likely cause | +|---|---| +| `certbot` fails with a Route53 error | AWS credentials or the DNS zone isn't actually in Route53. | +| `sso` container exits immediately | `RAVENDBSSO_Url` not set in `sso.env`. | +| `502 Bad Gateway` shortly after startup | The .NET sidecar on `127.0.0.1:3000` is still warming up — retry. | +| Login loops back to the provider | Wrong callback URL registered, or `OAUTH2_PROXY_COOKIE_SECRET` length is invalid (must be 16, 24, or 32 chars). | + +```bash +docker logs ravendb-sso # nginx + .NET app +docker logs certbot # certificate issues +``` + +## Next steps + +Once the SSO server is up, register its certificate and create SSO user entries in your RavenDB cluster +following [Configuring RavenDB for SSO](./configuring-ravendb-for-sso.mdx). diff --git a/docs/server/security/sso/overview.mdx b/docs/server/security/sso/overview.mdx new file mode 100644 index 0000000000..c122199dbb --- /dev/null +++ b/docs/server/security/sso/overview.mdx @@ -0,0 +1,71 @@ +--- +title: "SSO: Overview" +sidebar_label: Overview +description: "Authenticate RavenDB users through GitHub, Google, Microsoft/Entra ID, or Kerberos/AD via the RavenDB SSO reverse proxy." +sidebar_position: 1 +--- + +import Admonition from '@theme/Admonition'; + +# SSO: Overview + +RavenDB SSO lets users access a cluster using their existing identity provider — GitHub, Google, +Microsoft/Entra ID, or Windows/Kerberos — instead of distributing X.509 client certificates per user. +Authentication is handled by a dedicated **SSO application** (an authenticating reverse proxy) that sits +in front of one or more RavenDB clusters. RavenDB itself only needs to know which SSO servers it trusts +and how to map an SSO identity to a security clearance and database permissions. + +![SSO login page](./assets/login-page-all-providers.png) + +## Components + +| Component | Role | +|---|---| +| **SSO application** | Nginx + .NET 10 sidecar packaged as `ravendb/sso`. Terminates TLS, runs OAuth2 / Kerberos, issues a per-user ECDSA client certificate, and proxies to RavenDB over mTLS. | +| **`oauth2-proxy`** | Bundled inside the SSO image. Handles the OAuth2 dance for GitHub, Google, and Microsoft. | +| **RavenDB cluster** | Trusts the SSO server's certificate and resolves the SSO identity (embedded in the per-user cert) to a registered SSO user entry. | + +## Authentication flow + +1. The browser hits the SSO URL — for example `https://my-cluster.sso.example.com/studio/`. +2. Nginx checks for a valid JWT cookie. If none is present, it redirects to upstream auth: + - `/auth` — Kerberos/AD (`auth_gss`), or + - `/oauth` — `oauth2-proxy` for GitHub, Google, or Microsoft. +3. After upstream auth succeeds, Nginx forwards the authenticated username to the .NET sidecar at `/auth` + via the `X-Auth-User` header. +4. The sidecar generates a unique ECDSA client certificate for that user (cached by thumbprint) and issues + a JWT cookie containing the certificate and key paths. +5. Nginx validates the JWT and uses the referenced certificate to open an mTLS connection to RavenDB. +6. RavenDB sees a client certificate whose subject carries an **SSO identity extension** + (`Provider`, `Domain`, `Identifier` — `Domain` is only populated when `Provider = Windows`, where it + carries the Kerberos realm). It looks up a matching **SSO user entry** in the cluster and applies that + entry's security clearance and database permissions. + +## Trust model + +Two new certificate kinds are stored in the cluster: + +- **`Usage = SsoServer`** — the public certificate of a trusted SSO deployment. Registered once per SSO server. +- **`Usage = SsoClient`** — an **SSO user entry**: one or more `SsoIdentifier` tuples + (`Provider` + `Domain` + `Identifier`, where `Domain` is only used for Windows/Kerberos) plus the + RavenDB permissions to grant. Each entry either: + - pins to specific SSO servers via `SsoServerPublicKeyPinningHashes`, or + - sets `AllowAnySsoServer = true` to accept the identity from any trusted SSO server. + +This means a single SSO user entry can grant the same RavenDB permissions to a user who logs in through +multiple SSO deployments (production and staging, for example) by listing several pinning hashes. + +## Licensing + + +SSO is gated by the RavenDB license. Both registering an SSO server certificate and creating an SSO user +entry will fail with a license error if your license does not permit SSO. Registering an SSO server +certificate also requires the **Cluster Admin** security clearance — creating SSO user entries only requires +**Operator** clearance. + + +## Next steps + +- [Deploying the SSO application](./deploying-sso-app.mdx) — interactive `install.sh` / `install.ps1` installer (recommended), plus the equivalent manual Docker Compose setup. +- [SSO application configuration](./sso-configuration.mdx) — full reference of `settings.json` keys and `RAVENDBSSO_*` environment variables. +- [Configuring RavenDB for SSO](./configuring-ravendb-for-sso.mdx) — registering SSO server certificates and SSO user entries in your cluster. diff --git a/docs/server/security/sso/sso-configuration.mdx b/docs/server/security/sso/sso-configuration.mdx new file mode 100644 index 0000000000..65759be073 --- /dev/null +++ b/docs/server/security/sso/sso-configuration.mdx @@ -0,0 +1,149 @@ +--- +title: "SSO: Configuration Reference" +sidebar_label: Configuration Reference +description: "Full reference of settings.json keys and RAVENDBSSO_* environment variables consumed by the RavenDB SSO application." +sidebar_position: 3 +--- + +import Admonition from '@theme/Admonition'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# SSO: Configuration Reference + +The SSO application reads its configuration from two sources, layered in this order (later sources override +earlier ones): + +1. `config/settings.json` (optional). +2. Environment variables prefixed with `RAVENDBSSO_` (or `RAVENDBSSO.`). + +In addition, the bundled `oauth2-proxy` and `certbot` containers read their own environment variables. + +## Environment-variable convention + +Every SSO setting can be expressed as a `RAVENDBSSO_*` environment variable. The mapping rules are: + +- Strip the `RAVENDBSSO_` (or `RAVENDBSSO.`) prefix. +- Each remaining underscore becomes a `:` to denote a nested configuration key. +- If the value parses as a JSON object or array, it is automatically flattened into nested keys. + +For example, `RAVENDBSSO_Clusters` containing a JSON array is equivalent to a top-level `Clusters` entry in +`settings.json`. + +## SSO application settings + +These map to properties on `SsoSettings`: + +| Setting | Environment variable | Default | Required | Description | +|---|---|---|---|---| +| `Url` | `RAVENDBSSO_Url` | — | **Yes** | Public SSO URL, e.g. `https://sso.example.com`. Used as the CORS origin and as the basis for the host log messages. | +| `CertificatePath` | `RAVENDBSSO_CertificatePath` | _falls back to Let's Encrypt certs in `./certs`_ | No | Path to the TLS certificate (PEM, full chain). | +| `CertificateKeyPath` | `RAVENDBSSO_CertificateKeyPath` | _falls back to Let's Encrypt certs in `./certs`_ | No | Path to the TLS private key. | +| `CertificateFlags` | `RAVENDBSSO_CertificateFlags` | — | No | `X509KeyStorageFlags` to use when loading the certificate. | +| `Clusters` | `RAVENDBSSO_Clusters` | `[]` | **At least one** | List of clusters to proxy. Each entry: `{ ClusterUrls: ["https://a.host", ...], ClusterAlias: "alias" }`. Each cluster is exposed at `.`. | +| `UserCertsDir` | `RAVENDBSSO_UserCertsDir` | `/app/users-certs` | No | Directory where per-user ECDSA client certificates are written. | +| `JwtKeyPath` | `RAVENDBSSO_JwtKeyPath` | `/app/certs/jwt/jwt.key` | No | Path to the JWT signing (private) key. | +| `JwtPubPath` | `RAVENDBSSO_JwtPubPath` | `/app/certs/jwt/jwt.pub` | No | Path to the JWT verification (public) key. | +| `InternalAuthSecret` | `RAVENDBSSO_InternalAuthSecret` | _injected by `start.sh`_ | No | Shared secret that Nginx attaches as the `X-Internal-Auth` header on internal mint/validate endpoints. When set, the sidecar rejects requests that don't present it; left empty in local/dev/test runs without Nginx, in which case the check is skipped. Generated automatically inside the container — you don't normally set this yourself. | + +### Defining clusters + + + + +```bash +# single cluster +RAVENDBSSO_Clusters=[{"ClusterUrls":["https://a.my-cluster.example.com"],"ClusterAlias":"my-cluster"}] + +# multiple clusters +RAVENDBSSO_Clusters=[{"ClusterUrls":["https://a.prod.example.com"],"ClusterAlias":"prod"},{"ClusterUrls":["https://a.dev.example.com"],"ClusterAlias":"dev"}] +``` + + + + +```json +{ + "Clusters": [ + { + "ClusterUrls": ["https://a.my-cluster.example.com"], + "ClusterAlias": "my-cluster" + } + ] +} +``` + + + + + +On Linux the SSO process rejects `JwtKeyPath`, `JwtPubPath`, `UserCertsDir`, `CertificatePath`, and +`CertificateKeyPath` if they resolve outside `/app` (the SSO image's working tree) or `/etc/nginx`. This +prevents environment-variable path injection — keep your customized paths under those directories. + + +### CLI flags + +| Flag | Purpose | +|---|---| +| `--generate-keys` | Generate the JWT keypair at `JwtKeyPath` / `JwtPubPath` and exit. Used once during initial setup or when rotating keys. | + +## OAuth2 / `oauth2-proxy` environment variables + +| Variable | Required | Description | +|---|---|---| +| `OAUTH2_PROXY_COOKIE_SECRET` | **Yes** | Cookie-encryption secret. **16, 24, or 32 characters** (or a 32-byte base64 value for the single-container quick-start). Generate with `openssl rand -hex 16` or `openssl rand -base64 32`. | +| `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` | One of three providers | GitHub OAuth app credentials. | +| `GITHUB_ALLOWED_ORGANIZATIONS` | No | Comma-separated GitHub org slugs (e.g. `my-org,another-org`). Users must be public org members, or the OAuth app must be approved via GitHub's *OAuth App Access Policy*. Empty = allow all. | +| `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` | One of three providers | Google OAuth credentials. | +| `GOOGLE_ALLOWED_EMAIL_DOMAINS` | No | Comma-separated permitted email domains (e.g. `example.com,corp.example.com`). Empty = allow all. | +| `MICROSOFT_CLIENT_ID` / `MICROSOFT_CLIENT_SECRET` | One of three providers | Azure AD / Entra ID app credentials. The SSO image uses oauth2-proxy's `entra-id` provider. | +| `MICROSOFT_TENANT` | One of three providers | A tenant GUID for **single-tenant** apps, or `common` / `organizations` / `consumers` for **multi-tenant**. `common` accepts any organization plus personal Microsoft accounts; `organizations` accepts any org but excludes personal accounts. Multi-tenant values automatically disable OIDC issuer verification (tokens come from many issuers). The Azure app registration's *Supported account types* must match, and `email` must be added as an optional ID-token claim so organizational accounts emit it (it becomes the username). | +| `MICROSOFT_ALLOWED_TENANTS` | No | Optional tenant allowlist for multi-tenant apps — comma-separated tenant GUIDs. Empty = allow all. The personal-accounts tenant id is `9188040d-6c67-4c5b-b112-36a304b66dad`. | +| `MICROSOFT_ALLOWED_EMAIL_DOMAINS` | No | Comma-separated permitted email domains for Microsoft sign-in. With a multi-tenant `MICROSOFT_TENANT` and an empty allowlist, **any organization's users can log in** — combine this with `MICROSOFT_ALLOWED_TENANTS` to scope access. | + +At least one OAuth provider must be configured unless you exclusively use Kerberos (see +[Deploying the SSO Application](./deploying-sso-app.mdx#kerberos--active-directory)). + +## Certbot environment variables (Let's Encrypt example) + +These are read by the bundled `certbot/dns-route53` container in `Raven.Sso/example/docker-compose.yml`. + +| Variable | Required | Description | +|---|---|---| +| `SSO_DOMAIN` | **Yes** | Must resolve to the same host as `RAVENDBSSO_Url`. | +| `CERT_EMAIL` | **Yes** | Email registered with Let's Encrypt. | +| `AWS_ACCESS_KEY_ID` | **Yes** | AWS access key for the Route53 DNS-01 challenge. | +| `AWS_SECRET_ACCESS_KEY` | **Yes** | Matching secret key. | +| `AWS_SESSION_TOKEN` | No | Only for temporary AWS credentials. | +| `AWS_REGION` | No | Default `us-east-1`. | +| `LETSENCRYPT_STAGING` | No | Set to `true` to use Let's Encrypt's staging issuer for testing (avoids production rate limits). | + +## Putting it together + +A minimal `sso.env` for a production deployment using Google and Microsoft sign-in: + +```bash +RAVENDBSSO_Url=https://sso.example.com +RAVENDBSSO_Clusters=[{"ClusterUrls":["https://a.my-cluster.example.com"],"ClusterAlias":"my-cluster"}] + +OAUTH2_PROXY_COOKIE_SECRET=$(openssl rand -hex 16) + +GOOGLE_CLIENT_ID=... +GOOGLE_CLIENT_SECRET=... +GOOGLE_ALLOWED_EMAIL_DOMAINS=example.com + +MICROSOFT_CLIENT_ID=... +MICROSOFT_CLIENT_SECRET=... +MICROSOFT_TENANT=common +MICROSOFT_ALLOWED_EMAIL_DOMAINS=example.com +``` + +And the matching `certbot.env`: + +```bash +SSO_DOMAIN=https://sso.example.com +CERT_EMAIL=ops@example.com +AWS_ACCESS_KEY_ID=... +AWS_SECRET_ACCESS_KEY=... +```