diff --git a/src/pages/docs/api.astro b/src/pages/docs/api.astro index aa437cf..8acd03b 100644 --- a/src/pages/docs/api.astro +++ b/src/pages/docs/api.astro @@ -15,10 +15,13 @@ import DocsLayout from '@/layouts/DocsLayout.astro'; no GraphQL endpoint.
- Every /v1 route except GET /v1/health checks
- OM_API_SECRET. Send it as Authorization: Bearer … or
- X-API-Key. The console HTML does not. Treat the API as an internal service.
- Details are on Security.
+ Machine clients send that admin's personal API token as Authorization: Bearer … or
+ X-API-Key. A console session cookie is the other accepted credential. The examples
+ below use $OM_API_TOKEN for the value the console showed once.
+ GET /v1/health, GET /v1/auth/session, and
+ POST /v1/auth/logout are open. Admin management requires the session.
+ Treat the API as an internal service. Details are on Security
+ and Console.
GET /v1/health{`{"status":"ok","service":"opensourceom-api"}`}. Does not check the API secret. Helm probes this path.{`{"status":"ok","service":"opensourceom-api"}`}. Does not check a token. Helm probes this path.GET /v1/graph/stats{`curl -s http://localhost:8080/v1/health
-curl -s -H "Authorization: Bearer $OM_API_SECRET" \\
+curl -s -H "Authorization: Bearer $OM_API_TOKEN" \\
http://localhost:8080/v1/graph/stats
-curl -s -H "Authorization: Bearer $OM_API_SECRET" \\
+curl -s -H "Authorization: Bearer $OM_API_TOKEN" \\
-H 'Content-Type: application/json' \\
-d '{"nodes":[],"edges":[]}' \\
http://localhost:8080/v1/ingest`}
@@ -151,9 +154,9 @@ curl -s -H "Authorization: Bearer $OM_API_SECRET" \\
GET /v1/identity/blast-radius returns the identity, reachable
(at most 200 nodes), max_depth (6), and summary. Pass
identity_id or name. A name that matches no identity is 400.
- This route requires the API secret, like the other /v1 reads.
+ This route requires a personal API token or a console session, like the other /v1 reads.
- {`curl -s -X POST -H "Authorization: Bearer $OM_API_SECRET" \\
+ {`curl -s -X POST -H "Authorization: Bearer $OM_API_TOKEN" \\
-H 'Content-Type: application/json' \\
-d '{"webhook":"https://hooks.slack.com/services/..."}' \\
http://localhost:8080/v1/export/slack`}
diff --git a/src/pages/docs/architecture.astro b/src/pages/docs/architecture.astro
index 083184f..959744a 100644
--- a/src/pages/docs/architecture.astro
+++ b/src/pages/docs/architecture.astro
@@ -104,7 +104,7 @@ import DocsLayout from '@/layouts/DocsLayout.astro';
The CLI opens Postgres and writes batches, findings, and export calls itself. The API opens
the same database. POST /v1/ingest is the HTTP equivalent of an upsert.
om scan does not call the API. That split is why scans work when you only start
- the Postgres container, and why the console API key is irrelevant to the CLI.
+ the Postgres container, and why a console session is irrelevant to the CLI.
Deployment shapes
diff --git a/src/pages/docs/cli.astro b/src/pages/docs/cli.astro
index 5f913f9..92a4675 100644
--- a/src/pages/docs/cli.astro
+++ b/src/pages/docs/cli.astro
@@ -11,7 +11,7 @@ import DocsLayout from '@/layouts/DocsLayout.astro';
OpenSourceOM CLI
The om binary talks to Postgres directly. Scans, rules, queries, and exports do
- not go through the HTTP API, so they do not send OM_API_SECRET. Build it from
+ not go through the HTTP API, so they do not send a personal API token. Build it from
the core repo with go build -o om ./cmd/om, or use a release archive.
diff --git a/src/pages/docs/configuration.astro b/src/pages/docs/configuration.astro
index 0a90d7e..a872ace 100644
--- a/src/pages/docs/configuration.astro
+++ b/src/pages/docs/configuration.astro
@@ -6,7 +6,7 @@ import DocsLayout from '@/layouts/DocsLayout.astro';
Configure OpenSourceOM
@@ -45,7 +45,7 @@ import DocsLayout from '@/layouts/DocsLayout.astro';
OM_PUBLIC_URL
http://localhost:8080
- Public URL hint. Requests use the listen address.
+ Browser origin for the console and optional OAuth callbacks. No trailing path.
OM_API_PORT
@@ -53,9 +53,29 @@ import DocsLayout from '@/layouts/DocsLayout.astro';
om serve
- OM_API_SECRET
- change-me-in-production
- Shared secret for every /v1 route except GET /v1/health. See Security.
+ OM_ADMIN_EMAIL
+ empty
+ When set, only this address can become the first admin.
+
+
+ OM_SMTP_HOST / OM_SMTP_PORT / OM_SMTP_FROM
+ empty / 587 / empty
+ Optional. When set, adding an admin also emails the temporary password.
+
+
+ OM_SMTP_USERNAME / OM_SMTP_PASSWORD
+ empty
+ Optional SMTP authentication. Set both when the server requires a login.
+
+
+ OM_GOOGLE_CLIENT_ID / OM_GOOGLE_CLIENT_SECRET
+ empty
+ Google sign-in. Both are required. Callback {'{OM_PUBLIC_URL}'}/auth/google/callback.
+
+
+ OM_GITHUB_CLIENT_ID / OM_GITHUB_CLIENT_SECRET
+ empty
+ GitHub sign-in. Both are required. Callback {'{OM_PUBLIC_URL}'}/auth/github/callback.
+ The console signs people in with an email and password. There is no API-key field.
+ While no admin exists, the page is a setup form. Keep the API port private until you
+ finish it, or run om admin create --email you@example.com on the machine
+ first. Set OM_ADMIN_EMAIL when that first address should be fixed ahead of
+ time. See Configuration.
+
+ An admin adds the next person from the Admins panel. The temporary + password is shown once. If SMTP is configured, it is emailed too. That person chooses + their own password at the next sign-in. Every signed-in account is an admin, and the last + admin cannot be removed. +
- The Run CSPM rules button calls POST /v1/rules/run. That route
- requires the API secret. Enter it in the API key field and choose
- Save API key. The page stores it in localStorage.om_api_key and
- sends X-API-Key on /v1 calls. The key is not written into the graph.
- Clearing the field and saving removes it. The HTML page loads without a key. Stats, findings,
- and the graph need the secret. GET /v1/health does not.
+ Google and GitHub buttons appear only when that provider is configured. Register
+ {'{OM_PUBLIC_URL}'}/auth/google/callback or
+ {'{OM_PUBLIC_URL}'}/auth/github/callback. A verified email from the provider
+ must already be an admin. The Security panel turns on optional
+ authenticator-app two-factor authentication.
- Use the value of OM_API_SECRET from the environment the API process actually
- has. Running om rules run on the CLI does the same work without the browser key,
- because the CLI uses Postgres directly.
+ The browser keeps an httpOnly session cookie for 7 days. Creating an admin also creates a
+ personal API token. The console shows it once. Scripts send that token as
+ Authorization: Bearer or X-API-Key. The token cannot add or
+ remove admins. Rotate it from the Admins panel when you need a new one.
+
+ The Run CSPM rules button calls POST /v1/rules/run with the
+ session cookie. Running om rules run on the CLI does the same work without a
+ browser session, because the CLI uses Postgres directly.
opensourceom
unless you override POSTGRES_USER, POSTGRES_PASSWORD, and
POSTGRES_DB. The API listens on OM_API_PORT (default 8080) and
- receives OM_API_SECRET from .env. A volume named
+ receives OM_ADMIN_EMAIL and the optional Google, GitHub, and SMTP settings from
+ .env. Open the console and create the admin, or run
+ om admin create --email on the host first. Keep port 8080 private until an
+ admin exists. A volume named
postgres-data keeps the database across restarts.
diff --git a/src/pages/docs/exports.astro b/src/pages/docs/exports.astro
index 73d8d0c..a6a1a4d 100644
--- a/src/pages/docs/exports.astro
+++ b/src/pages/docs/exports.astro
@@ -67,7 +67,7 @@ import DocsLayout from '@/layouts/DocsLayout.astro';
The API posts the same digest:
-{`curl -s -X POST -H "Authorization: Bearer $OM_API_SECRET" \\
+ {`curl -s -X POST -H "Authorization: Bearer $OM_API_TOKEN" \\
-H 'Content-Type: application/json' \\
-d '{"webhook":"https://hooks.slack.com/services/..."}' \\
http://localhost:8080/v1/export/slack`}
diff --git a/src/pages/docs/index.astro b/src/pages/docs/index.astro
index 1c47e8f..a737507 100644
--- a/src/pages/docs/index.astro
+++ b/src/pages/docs/index.astro
@@ -78,7 +78,7 @@ import DocsLayout from '@/layouts/DocsLayout.astro';
Architecture — components and data flow
Docker Compose — local Postgres and API
Kubernetes — Helm chart
- Security — API secret, read-only collectors, vulnerability reports
+ Security — personal API tokens, read-only collectors, vulnerability reports
Open source scope — what stays in Core versus planned commercial work
diff --git a/src/pages/docs/kubernetes.astro b/src/pages/docs/kubernetes.astro
index 2a6f1e1..93e46c0 100644
--- a/src/pages/docs/kubernetes.astro
+++ b/src/pages/docs/kubernetes.astro
@@ -23,14 +23,13 @@ import DocsLayout from '@/layouts/DocsLayout.astro';
Install
- api.secret and postgres.password are required. The image repository
+ postgres.password is required. The image repository
defaults to ghcr.io/opensourceom/core. An empty image.tag falls
back to the chart appVersion (0.3.0). Set the tag when you are not
running that release.
{`docker build -t ghcr.io/opensourceom/core:0.3.0 .
helm install om deploy/helm/opensourceom \\
- --set api.secret='change-me' \\
--set postgres.password='change-me' \\
--set image.tag=0.3.0`}
@@ -78,9 +77,29 @@ helm install om deploy/helm/opensourceom \\
Written to OM_ENV.
- api.secret
- empty, required
- OM_API_SECRET.
+ api.smtp.host / api.smtp.port / api.smtp.from
+ empty / 587 / empty
+ Optional SMTP for temporary admin passwords. Setting the host requires from. A password stays in the chart Secret.
+
+
+ api.publicURL
+ empty
+ OM_PUBLIC_URL. Set this to the browser origin before optional Google or GitHub login works.
+
+
+ api.adminEmail
+ empty
+ OM_ADMIN_EMAIL. When set, only this address can become the first admin.
+
+
+ api.google.clientId / api.google.clientSecret
+ empty
+ Google sign-in. Setting the id requires the secret. The secret stays in the chart Secret.
+
+
+ api.github.clientId / api.github.clientSecret
+ empty
+ GitHub sign-in. Same pattern as Google.
postgres.enabled
@@ -144,6 +163,7 @@ helm install om deploy/helm/opensourceom \\
Keep the Service off the public internet, or put your own gateway in front of it. See
- Security for how OM_API_SECRET applies.
+ Security for personal API tokens and console login. Set
+ api.publicURL to that gateway’s origin when you enable Google or GitHub.
diff --git a/src/pages/docs/rules.astro b/src/pages/docs/rules.astro
index dc62074..0145b51 100644
--- a/src/pages/docs/rules.astro
+++ b/src/pages/docs/rules.astro
@@ -94,14 +94,15 @@ import DocsLayout from '@/layouts/DocsLayout.astro';
HTTP
- {`curl -s http://localhost:8080/v1/rules
-curl -s -X POST -H "Authorization: Bearer $OM_API_SECRET" \\
+ {`curl -s -H "Authorization: Bearer $OM_API_TOKEN" \\
+ http://localhost:8080/v1/rules
+curl -s -X POST -H "Authorization: Bearer $OM_API_TOKEN" \\
http://localhost:8080/v1/rules/run
-curl -s -X POST -H "Authorization: Bearer $OM_API_SECRET" \\
+curl -s -X POST -H "Authorization: Bearer $OM_API_TOKEN" \\
'http://localhost:8080/v1/rules/run?id=cspm-internet-workload'`}
- The console button does the same POST. See Console for the
- browser API key.
+ The console button does the same POST with the signed-in session. See
+ Console.
Adding a check
diff --git a/src/pages/docs/security.astro b/src/pages/docs/security.astro
index d5bd773..7e3bad1 100644
--- a/src/pages/docs/security.astro
+++ b/src/pages/docs/security.astro
@@ -6,7 +6,7 @@ import DocsLayout from '@/layouts/DocsLayout.astro';
OpenSourceOM security model
@@ -14,27 +14,50 @@ import DocsLayout from '@/layouts/DocsLayout.astro';
IAM relationships, and findings. Protect Postgres and the API the way you protect that data.
- API secret
+ Personal API tokens
- OM_API_SECRET defaults to change-me-in-production when the variable
- is unset or empty. Every /v1 route except GET /v1/health requires
- Authorization: Bearer <secret> or X-API-Key: <secret>.
- That includes graph reads, findings, the rules list, blast radius, ingest, rules run, and
- Slack export. A missing or wrong secret is 401.
+ Each admin has a personal API token. The console shows the raw value once, after the account
+ is created or after a rotation. Graph and ingest routes require
+ Authorization: Bearer <token>, X-API-Key: <token>, or
+ a console session cookie. That includes graph reads, findings, the rules list, blast radius,
+ ingest, rules run, and Slack export. A missing or wrong credential is 401. There is no shared
+ secret and no open mode.
- GET /v1/health does not check the secret, so probes can call it. The console
- HTML at / and /ui/ is also unauthenticated. The page sends the key
- from local storage on API calls. See Console.
+ GET /v1/health, GET /v1/auth/session,
+ POST /v1/auth/logout, POST /v1/auth/setup,
+ POST /v1/auth/login, and POST /v1/auth/totp do not require a session.
+ GET /v1/admins, POST /v1/admins,
+ POST /v1/admins/token, and
+ DELETE /v1/admins/{'{id}'} require a console session. A personal token cannot
+ add or remove admins. The console HTML at / and /login loads
+ without a session so the sign-in page can render. See Console.
- Change the secret before anyone else can reach the port. Helm refuses to install until
- api.secret is set. The CLI never sends this header. Anyone who can open a local
- shell as a user with database credentials can scan and export without it.
+ Keep the API port private until the first admin exists. Whoever can open it first can become
+ that admin. Create the account with om admin create before you publish the
+ port when you can. The CLI never sends this header. Anyone who can open a local shell as a
+ user with database credentials can scan and export without a token.
- The secret is one shared key, not a user login. Do not publish the Service to the internet.
- Put it on a private network or behind a gateway you operate.
+ People use an email and password. Scripts use a personal token. Do not publish the Service
+ to the internet. Put it on a private network or behind a gateway you operate.
+
+
+ Console admins
+
+ People sign in with an email and password. Every account is an admin. The first account is
+ created from the setup form, or with om admin create before the port is
+ reachable. OM_ADMIN_EMAIL pins that first address when you set it. Later admins
+ receive a temporary password and choose their own at the next sign-in. Passwords are stored
+ as Argon2id hashes. Optional authenticator-app two-factor authentication is per admin.
+ Sessions are httpOnly cookies. The cookie is marked Secure when
+ OM_PUBLIC_URL uses https.
+
+
+ Google and GitHub are optional. A verified provider email must match an existing admin.
+ OAuth client secrets and the SMTP password are deployment secrets. Password login works
+ without them, including on an air-gapped console.
Collectors
@@ -54,8 +77,7 @@ import DocsLayout from '@/layouts/DocsLayout.astro';
Console script
The graph view loads vis-network from /ui/vis-network.min.js on the API. It is
- not loaded from a CDN. The API key saved in the console stays in browser local storage and
- is not written into the graph.
+ not loaded from a CDN. The session cookie is httpOnly, so page script cannot read it.
Reporting a vulnerability