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
2 changes: 1 addition & 1 deletion .github/workflows/check-compose-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
Comment thread
twk3 marked this conversation as resolved.

- name: Install yq
uses: mikefarah/yq@v4
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/smoke-test-services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ jobs:
contents: read # Required for checkout
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
Comment thread
twk3 marked this conversation as resolved.

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::761136292957:role/GitHubActions-CurrentsDevDocker-ECRPull
aws-region: us-east-1
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/validate-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
Comment thread
twk3 marked this conversation as resolved.

- name: Print versions
run: |
Expand Down Expand Up @@ -64,14 +64,15 @@ jobs:
options: --privileged
steps:
- name: Install dependencies
timeout-minutes: 5
run: |
dnf install -y podman git curl
# Download pre-built docker-compose binary (avoids pip compilation issues)
curl -L "https://github.com/docker/compose/releases/download/v2.32.4/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Print versions
run: |
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The Docker Compose configuration is modular, allowing you to choose which data s
- [🚀 Quickstart Guide](./quickstart.md)
- [Container Image Access](./container-images.md)
- [Configuration Reference](./configuration.md)
- [Enable SAML SSO](./sso-saml.md)
- [Logging Configuration](./logging.md)
- [Backup and Restore](./backup-restore.md)
- [Upgrading Currents On-Prem](./upgrading.md)
Expand Down
22 changes: 22 additions & 0 deletions docs/backup-restore.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Currents stores data in several locations:
| ClickHouse | `data/clickhouse` | Analytics and reporting data |
| Redis | `data/redis` | Cache and session data (optional to backup) |
| RustFS | `data/rustfs` | Artifacts, screenshots, videos (if using the provided rustfs) |
| SAML SSO | `data/sso` | IdP metadata XML + optional SP cert/key (only if SSO is enabled) |

## Before You Begin

Expand Down Expand Up @@ -124,6 +125,16 @@ tar -czvf redis-backup-$(date +%Y%m%d).tar.gz data/redis/
docker compose start
```

### SAML SSO Files (Optional)

If SAML SSO is enabled, `data/sso` holds your IdP metadata XML (and, if you sign AuthnRequests, the SP certificate/key PEMs). It is included in the full `data/` backup above; to back up just this folder:

```bash
tar -czvf sso-backup-$(date +%Y%m%d).tar.gz data/sso/
```

> **Important:** If you configured an SP private key (`sp-key.pem`), this folder contains a secret — store the archive securely. The IdP metadata itself can always be re-downloaded from your identity provider, so this backup is a convenience rather than a hard requirement.

## Restore Procedures

### Prerequisites
Expand Down Expand Up @@ -220,3 +231,14 @@ tar -xzvf rustfs-backup-YYYYMMDD.tar.gz
docker compose up -d
```

### SAML SSO Restore

```bash
docker compose down
rm -rf data/sso/
tar -xzvf sso-backup-YYYYMMDD.tar.gz
docker compose up -d
```

> **Podman users:** the API reads this folder as uid 1000 — after extracting, run `podman unshare chown -R 1000:1000 data/sso` (rootless) or `sudo chown -R 1000:1000 data/sso` (rootful). See the [quickstart troubleshooting](./quickstart.md#podman-permission-denied-errors) section.

16 changes: 16 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,21 @@ Less commonly changed settings with sensible defaults.
| `INVITE_EXPIRATION_DAYS` | int | `14` | Number of days before invitations expire |
| `EMAIL_LINKS_BASE_URL` | string | `${APP_BASE_URL}` | Base URL for links in emails (derived from APP_BASE_URL) |

### SAML SSO (Optional)

Enable SAML single sign-on by setting the two required variables and mounting your IdP metadata file into `./data/sso`. See [Enable SAML SSO](sso-saml.md) for the full walkthrough. SSO turns on automatically once both required variables are set — there is no separate enable flag.

| Variable | Type | Default | Description |
|----------|------|---------|-------------|
| `SSO_SAML_IDP_METADATA_FILE` | string | _(empty)_ | Path to the mounted IdP metadata XML (e.g. `/etc/currents/sso/idp-metadata.xml`). **Required to enable SSO.** |
| `SSO_SAML_ISSUER` | string | _(empty)_ | SP entityID / audience — a stable opaque identifier (e.g. `currents-onprem:your-org`). Must match the Audience / SP Entity ID configured in your IdP. **Required to enable SSO.** |
| `SSO_SAML_PROVIDER_ID` | string | `onprem-saml` | Provider id; recommend your IdP name (e.g. `okta`). Becomes the last path segment of the ACS callback URL. |
| `SSO_SAML_DEFAULT_ROLE` | string | `member` | Role granted to auto-provisioned SSO users |
| `SSO_ALLOWED_DOMAINS` | string | _(empty)_ | Comma-separated email domains allowed to sign in via SSO |
| `SSO_SAML_AUTHN_REQUESTS_SIGNED` | bool | `false` | Sign outbound AuthnRequests (requires the SP key/cert below) |
| `SSO_SAML_SP_CERT_FILE` | string | _(empty)_ | Path to the mounted SP certificate PEM (only when signing requests) |
| `SSO_SAML_SP_KEY_FILE` | string | _(empty)_ | Path to the mounted SP private key PEM (only when signing requests) |

### Docker Compose Configuration

These variables configure Docker Compose behavior only (not passed to containers). All are optional with sensible defaults.
Expand Down Expand Up @@ -107,6 +122,7 @@ These variables configure Docker Compose behavior only (not passed to containers
| `DC_CLICKHOUSE_VOLUME` | string | `./data/clickhouse` | ClickHouse data volume path |
| `DC_RUSTFS_VOLUME` | string | `./data/rustfs` | RustFS data volume path |
| `DC_SCHEDULER_STARTUP_VOLUME` | string | `./data/startup` | Scheduler startup data volume |
| `DC_SSO_VOLUME` | string | `./data/sso` | SAML SSO files directory (IdP metadata + optional SP PEMs), mounted read-only to `/etc/currents/sso` |

#### Traefik Configuration

Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Docker Compose configuration for self-hosted Currents deployment.

- [Quickstart Guide](quickstart.md) — Get up and running with Docker Compose
- [Configuration Reference](configuration.md) — All environment variables and settings
- [Enable SAML SSO](sso-saml.md) — Delegate sign-in to your SAML identity provider
- [Logging Configuration](logging.md) — Configure container logging for production
- [Backup and Restore](backup-restore.md) — Backup and restore procedures
- [Upgrading Currents On-Prem](upgrading.md) — Upgrade workflows and version management
Expand Down
21 changes: 20 additions & 1 deletion docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,17 @@ Common SMTP configurations:
| Mailgun | `smtp.mailgun.org` | 587 | false |
| Gmail | `smtp.gmail.com` | 587 | false |

### SAML SSO (optional)

To let users sign in through your SAML identity provider (Okta, Entra ID, etc.), create a SAML app in your IdP, mount its metadata XML into `./data/sso`, and set two variables:

```bash
SSO_SAML_IDP_METADATA_FILE=/etc/currents/sso/idp-metadata.xml
SSO_SAML_ISSUER=currents-onprem:your-org
```

See [Enable SAML SSO](./sso-saml.md) for the full walkthrough.

See [Configuration Reference](./configuration.md) for all available options.

## Step 4: Start Services
Expand Down Expand Up @@ -322,7 +333,7 @@ This is due to Podman's rootless mode and UID mapping. Follow these steps:
Create the data directories manually before starting services:

```bash
mkdir -p data/mongodb data/redis data/clickhouse data/rustfs data/startup data/traefik/certs data/traefik/config
mkdir -p data/mongodb data/redis data/clickhouse data/rustfs data/startup data/sso data/traefik/certs data/traefik/config
```

#### Step 2: Set Permissions
Expand All @@ -346,6 +357,9 @@ podman unshare chown -R 10001:10001 data/rustfs
# Scheduler runs as uid 1000
podman unshare chown -R 1000:1000 data/startup

# API runs as uid 1000 (only needed if using SAML SSO)
podman unshare chown -R 1000:1000 data/sso

# Traefik runs as root (uid 0) - no chown needed, just create dirs
```

Expand All @@ -366,6 +380,9 @@ sudo chown -R 10001:10001 data/rustfs
# Scheduler runs as uid 1000
sudo chown -R 1000:1000 data/startup

# API runs as uid 1000 (only needed if using SAML SSO)
sudo chown -R 1000:1000 data/sso

# Traefik runs as root (uid 0) - no chown needed
```

Expand All @@ -387,6 +404,7 @@ sudo chcon -Rt svirt_sandbox_file_t data/redis
sudo chcon -Rt svirt_sandbox_file_t data/clickhouse
sudo chcon -Rt svirt_sandbox_file_t data/rustfs
sudo chcon -Rt svirt_sandbox_file_t data/startup
sudo chcon -Rt svirt_sandbox_file_t data/sso
sudo chcon -Rt svirt_sandbox_file_t data/traefik
```

Expand Down Expand Up @@ -515,6 +533,7 @@ DC_CLICKHOUSE_VOLUME=clickhouse-data
| `DC_CLICKHOUSE_VOLUME` | `./data/clickhouse` | ClickHouse data storage |
| `DC_RUSTFS_VOLUME` | `./data/rustfs` | RustFS object storage |
| `DC_SCHEDULER_STARTUP_VOLUME` | `./data/startup` | Scheduler startup state |
| `DC_SSO_VOLUME` | `./data/sso` | SAML SSO files, mounted read-only |

> **Tip:** Named Docker volumes are useful when you need encryption, network-attached storage, or custom volume drivers that aren't possible with bind mounts.

Expand Down
106 changes: 106 additions & 0 deletions docs/sso-saml.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Enable SAML SSO

Currents on-prem can delegate sign-in to your SAML 2.0 identity provider (Okta, Microsoft Entra ID, Google Workspace, etc.). Once enabled, users sign in **email-first**: they enter their email, are redirected to your IdP to authenticate, and on return are automatically provisioned into your organization.

SSO turns on automatically once the two required variables are set and the IdP metadata file is mounted.

## Prerequisites

- A running on-prem deployment (see the [Quickstart Guide](./quickstart.md)).
- **SMTP configured** — email is used for account provisioning and invitations. See the [SMTP Configuration](./quickstart.md#smtp-configuration) section.
- Admin access to your identity provider to create a SAML application.

## Step 1 — Choose two identifiers

You'll reuse these in both your IdP and your `.env`:

- **Provider id** (`SSO_SAML_PROVIDER_ID`) — set it to your IdP's service name, e.g. `okta`. It becomes the last path segment of the callback URL.
- **Issuer** (`SSO_SAML_ISSUER`) — a **stable, opaque** identifier that names this deployment as a SAML Service Provider. Use the form `currents-onprem:<your-org>`, e.g. `currents-onprem:acme`.

> ⚠️ **The issuer is not a URL.** Use a stable string like `currents-onprem:acme`. It only has to match the "Audience / SP Entity ID" you enter in your IdP — it does not need to resolve, and using your deployment URL is discouraged because that URL can change.

## Step 2 — Create the SAML app in your IdP

Create a new **SAML 2.0** application. Using Okta as the example, set:

| IdP field | Value |
|-----------|-------|
| **Single Sign-On URL** (a.k.a. ACS / Assertion Consumer Service URL) | `<APP_BASE_URL>/api/auth/sso/saml2/callback/okta` |
| **Audience URI** (a.k.a. SP Entity ID) | `currents-onprem:acme` |
| **Name ID format** | `Persistent` |
| **Application username** | Email |

Replace `<APP_BASE_URL>` with your deployment's base URL (the value of `APP_BASE_URL` in your `.env`, e.g. `https://currents.acme.com`), `okta` with your `SSO_SAML_PROVIDER_ID`, and `currents-onprem:acme` with your `SSO_SAML_ISSUER`.

Then **download the application's IdP metadata XML** (in Okta: the app's *Metadata URL*, or *SAML Setup Instructions → Identity Provider metadata*). This one file contains the IdP sign-on URL, entity ID, and signing certificate — Currents reads all of them from it.

## Step 3 — Provide the metadata file

Drop the metadata XML into `./data/sso` (bind-mounted read-only into the API container at `/etc/currents/sso`):

```bash
mkdir -p ./data/sso
cp ~/Downloads/okta-metadata.xml ./data/sso/idp-metadata.xml
```

## Step 4 — Configure and restart

Add the following to your `.env`:

```bash
# Required
SSO_SAML_IDP_METADATA_FILE=/etc/currents/sso/idp-metadata.xml
SSO_SAML_ISSUER=currents-onprem:acme

# Recommended
SSO_SAML_PROVIDER_ID=okta
```

Restart the stack to apply:

```bash
docker compose up -d
```

## Step 5 — Sign in

Go to your dashboard and enter your email on the sign-in screen. If your email is not the root admin's local password account, you'll be redirected to your IdP; after authenticating you'll be returned and provisioned into the organization.

> New SSO users are added to your single on-prem organization automatically, with the role set by `SSO_SAML_DEFAULT_ROLE` (default `member`). The root admin account continues to sign in with its email + password.

## Optional configuration

| Variable | Purpose |
|----------|---------|
| `SSO_SAML_DEFAULT_ROLE` | Role for auto-provisioned SSO users. Default `member`. |
| `SSO_ALLOWED_DOMAINS` | Comma-separated allow-list of email domains, e.g. `acme.com,acme.io`. Defense-in-depth on top of your IdP. |
| `SSO_SAML_AUTHN_REQUESTS_SIGNED` | Set `true` to sign outbound AuthnRequests. Requires an SP key/cert (below). |
| `SSO_SAML_SP_CERT_FILE` / `SSO_SAML_SP_KEY_FILE` | Paths to your SP certificate/private key PEMs (place them in `./data/sso` too). Only needed for signed requests. |

### Signing AuthnRequests (advanced)

If your IdP requires signed requests, generate a self-signed SP keypair, drop it in `./data/sso`, and point the vars at it:

```bash
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout ./data/sso/sp-key.pem -out ./data/sso/sp-cert.pem -days 825 \
-subj "/CN=currents-onprem"
```

```bash
SSO_SAML_AUTHN_REQUESTS_SIGNED=true
SSO_SAML_SP_CERT_FILE=/etc/currents/sso/sp-cert.pem
SSO_SAML_SP_KEY_FILE=/etc/currents/sso/sp-key.pem
```

## Rotating the IdP certificate

The metadata file is the single source for the IdP sign-on URL, entity ID, and signing certificate. To rotate the IdP's signing certificate, download the refreshed metadata XML, replace `./data/sso/idp-metadata.xml`, and restart the API — no other configuration changes are needed.

## Troubleshooting

- **Sign-in isn't redirecting to the IdP** — confirm both `SSO_SAML_IDP_METADATA_FILE` and `SSO_SAML_ISSUER` are set and the file exists at the mounted path; SSO is off until both are present. Check the API logs at startup for SSO config validation errors.
- **IdP rejects the request / "audience mismatch"** — the **Audience / SP Entity ID** in your IdP must exactly equal `SSO_SAML_ISSUER`.
- **Assertion signature invalid** — the metadata XML is stale or from the wrong app; re-download it from the IdP and replace the file.

See the [Configuration Reference](./configuration.md#saml-sso-optional) for the full list of SSO variables.
31 changes: 31 additions & 0 deletions on-prem/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,36 @@ INVITE_EXPIRATION_DAYS=
# CORALOGIX_API_KEY=
# CORALOGIX_APP_NAME="customer-name"

# =============================================================================
# SAML SSO (Optional)
# =============================================================================
# Let users sign in through your SAML identity provider (Okta, Entra ID, etc.).
# SSO turns on automatically once the two required vars below are set and the IdP
# metadata file is mounted — there is no separate enable flag. Full walkthrough:
# docs/sso-saml.md.
#
# Setup summary:
# 1. Create a SAML app in your IdP with:
# ACS / Single Sign-On URL: <APP_BASE_URL>/api/auth/sso/saml2/callback/okta
# Audience / SP Entity ID: currents-onprem:your-org
# 2. mkdir -p ./data/sso, then download the app's metadata XML to
# ./data/sso/idp-metadata.xml (mounted read-only to /etc/currents/sso).
# 3. Set the required vars below and restart.
#
# Required to enable SSO:
# SSO_SAML_IDP_METADATA_FILE=/etc/currents/sso/idp-metadata.xml
# SSO_SAML_ISSUER=currents-onprem:your-org # stable opaque ID (NOT a URL); must match the IdP Audience
#
# Recommended:
# SSO_SAML_PROVIDER_ID=okta # your IdP name; last segment of the ACS URL (default: onprem-saml)
#
# Optional:
# SSO_SAML_DEFAULT_ROLE=member # role for auto-provisioned SSO users (default: member)
# SSO_ALLOWED_DOMAINS=acme.com,acme.io # restrict SSO to these email domains
# SSO_SAML_AUTHN_REQUESTS_SIGNED=false # sign AuthnRequests (requires the SP key/cert below)
# SSO_SAML_SP_CERT_FILE=/etc/currents/sso/sp-cert.pem
# SSO_SAML_SP_KEY_FILE=/etc/currents/sso/sp-key.pem

# =============================================================================
# Docker Compose Configuration
# =============================================================================
Expand Down Expand Up @@ -156,6 +186,7 @@ INVITE_EXPIRATION_DAYS=
# DC_CLICKHOUSE_VOLUME=./data/clickhouse
# DC_RUSTFS_VOLUME=./data/rustfs
# DC_SCHEDULER_STARTUP_VOLUME=./data/startup
# DC_SSO_VOLUME=./data/sso # SAML SSO files (IdP metadata + optional SP PEMs)

# =============================================================================
# Traefik TLS Termination (Optional Profile)
Expand Down
4 changes: 3 additions & 1 deletion on-prem/docker-compose.cache.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Currents On-Prem Docker Compose
# Version: staging
# Version: 2026-01-26-001
# Profile: cache - Cache (redis)
#
# Generated by: ./scripts/generate-compose.sh
Expand Down Expand Up @@ -47,6 +47,8 @@ services:
BETTER_AUTH_ENABLED: "true"
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_CURRENTS_PASSWORD}
EMAIL_TRANSPORTER: smtp
volumes:
- ${DC_SSO_VOLUME:-./data/sso}:/etc/currents/sso:ro
depends_on:
redis:
condition: service_started
Expand Down
4 changes: 3 additions & 1 deletion on-prem/docker-compose.database.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Currents On-Prem Docker Compose
# Version: staging
# Version: 2026-01-26-001
# Profile: database - Database services (redis, mongodb, clickhouse)
#
# Generated by: ./scripts/generate-compose.sh
Expand Down Expand Up @@ -50,6 +50,8 @@ services:
BETTER_AUTH_ENABLED: "true"
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_CURRENTS_PASSWORD}
EMAIL_TRANSPORTER: smtp
volumes:
- ${DC_SSO_VOLUME:-./data/sso}:/etc/currents/sso:ro
depends_on:
mongodb:
condition: service_healthy
Expand Down
4 changes: 3 additions & 1 deletion on-prem/docker-compose.full.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Currents On-Prem Docker Compose
# Version: staging
# Version: 2026-01-26-001
# Profile: full - All services (redis, mongodb, clickhouse, rustfs)
#
# Generated by: ./scripts/generate-compose.sh
Expand Down Expand Up @@ -52,6 +52,8 @@ services:
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_CURRENTS_PASSWORD}
EMAIL_TRANSPORTER: smtp
FILE_STORAGE_FORCE_PATH_STYLE: "true"
volumes:
- ${DC_SSO_VOLUME:-./data/sso}:/etc/currents/sso:ro
depends_on:
mongodb:
condition: service_healthy
Expand Down
Loading