mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-14 07:42:11 +02:00
feat(iam): allow bootstrap mode and token to be sourced from env vars
Adds an environment-variable fallback for the iam-svc bootstrap configuration so the token can be injected from a Kubernetes Secret (or any equivalent secret store) without ever appearing in the processor-group YAML — which is typically version-controlled. Resolution order is fixed and per-setting: bootstrap_mode = params["bootstrap_mode"] or $IAM_BOOTSTRAP_MODE bootstrap_token = params["bootstrap_token"] or $IAM_BOOTSTRAP_TOKEN If neither source supplies a value, the service refuses to start with a clear message naming both options. The two settings are resolved independently, which lets operators commit the mode in YAML (it is not a secret) while pulling the token from a Secret-backed ``IAM_BOOTSTRAP_TOKEN`` env var. Validation invariants are unchanged: * mode must be 'token' or 'bootstrap' * mode='token' requires a token (from any source) * mode='bootstrap' must NOT have a token (ambiguous intent) There is no permissive fallback — the service fails closed in every branch where configuration is incomplete. docs/tech-specs/iam-protocol.md gains a 'Configuration sources' subsection under 'Bootstrap modes' that documents the precedence table and the K8s injection pattern. The 'Bootstrap-token lifecycle' step about removing the token after rotation now applies to whichever source was used (Secret, env var, or YAML field).
This commit is contained in:
parent
67b2fc448f
commit
7e3fe28e7f
2 changed files with 53 additions and 10 deletions
|
|
@ -273,6 +273,25 @@ cannot distinguish:
|
||||||
This matches the general IAM error-policy stance (see `iam.md`) and
|
This matches the general IAM error-policy stance (see `iam.md`) and
|
||||||
prevents externally enumerating IAM's state.
|
prevents externally enumerating IAM's state.
|
||||||
|
|
||||||
|
### Configuration sources
|
||||||
|
|
||||||
|
The mode and token can be supplied two ways. Resolution order is
|
||||||
|
fixed; there is no permissive fallback.
|
||||||
|
|
||||||
|
| Source | Field |
|
||||||
|
|---|---|
|
||||||
|
| Processor-group YAML / CLI argument | `bootstrap_mode`, `bootstrap_token` |
|
||||||
|
| Environment variable | `IAM_BOOTSTRAP_MODE`, `IAM_BOOTSTRAP_TOKEN` |
|
||||||
|
|
||||||
|
For each setting the service uses the explicit param value if
|
||||||
|
present; otherwise the environment variable; otherwise the service
|
||||||
|
refuses to start. The env-var path is intended for the K8s
|
||||||
|
deployment pattern where the token is injected from a `Secret` via
|
||||||
|
`secretKeyRef`, so the plaintext never has to live in YAML or git.
|
||||||
|
A typical production manifest holds `bootstrap_mode: "token"` in
|
||||||
|
the YAML and pulls `IAM_BOOTSTRAP_TOKEN` from the Secret; the YAML
|
||||||
|
is then safe to version-control.
|
||||||
|
|
||||||
### Bootstrap-token lifecycle
|
### Bootstrap-token lifecycle
|
||||||
|
|
||||||
The bootstrap token — whether operator-supplied (`token` mode) or
|
The bootstrap token — whether operator-supplied (`token` mode) or
|
||||||
|
|
@ -283,7 +302,8 @@ operator's first admin action after bootstrap should be:
|
||||||
1. Create a durable admin user and API key (or issue a durable API
|
1. Create a durable admin user and API key (or issue a durable API
|
||||||
key to the bootstrap admin).
|
key to the bootstrap admin).
|
||||||
2. Revoke the bootstrap key via `revoke-api-key`.
|
2. Revoke the bootstrap key via `revoke-api-key`.
|
||||||
3. Remove the bootstrap token from any deployment configuration.
|
3. Remove the bootstrap token from any deployment configuration
|
||||||
|
(Secret, env var, or YAML field — wherever it was sourced).
|
||||||
|
|
||||||
The `name="bootstrap"` marker makes bootstrap keys easy to detect in
|
The `name="bootstrap"` marker makes bootstrap keys easy to detect in
|
||||||
tooling (e.g. a `tg-list-api-keys` filter).
|
tooling (e.g. a `tg-list-api-keys` filter).
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ Shape mirrors trustgraph.config.service.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
from trustgraph.schema import Error
|
from trustgraph.schema import Error
|
||||||
from trustgraph.schema import IamRequest, IamResponse
|
from trustgraph.schema import IamRequest, IamResponse
|
||||||
|
|
@ -27,6 +28,13 @@ default_ident = "iam-svc"
|
||||||
default_iam_request_queue = iam_request_queue
|
default_iam_request_queue = iam_request_queue
|
||||||
default_iam_response_queue = iam_response_queue
|
default_iam_response_queue = iam_response_queue
|
||||||
|
|
||||||
|
# Environment variables consulted as a fallback when the
|
||||||
|
# corresponding params field is not set in the processor-group YAML
|
||||||
|
# or via CLI. Intended for K8s Secret / env-var injection so the
|
||||||
|
# bootstrap token never has to live in the YAML (and thus in git).
|
||||||
|
ENV_BOOTSTRAP_MODE = "IAM_BOOTSTRAP_MODE"
|
||||||
|
ENV_BOOTSTRAP_TOKEN = "IAM_BOOTSTRAP_TOKEN"
|
||||||
|
|
||||||
|
|
||||||
class Processor(AsyncProcessor):
|
class Processor(AsyncProcessor):
|
||||||
|
|
||||||
|
|
@ -39,26 +47,41 @@ class Processor(AsyncProcessor):
|
||||||
"iam_response_queue", default_iam_response_queue,
|
"iam_response_queue", default_iam_response_queue,
|
||||||
)
|
)
|
||||||
|
|
||||||
bootstrap_mode = params.get("bootstrap_mode")
|
# Resolve bootstrap mode + token. Precedence: explicit
|
||||||
bootstrap_token = params.get("bootstrap_token")
|
# params (CLI / processor-group YAML) → environment variable
|
||||||
|
# → unset (fail-closed). The env-var path is the K8s-native
|
||||||
|
# injection point: an `IAM_BOOTSTRAP_TOKEN` from a Secret
|
||||||
|
# never has to land in the YAML, and therefore never enters
|
||||||
|
# git history.
|
||||||
|
bootstrap_mode = (
|
||||||
|
params.get("bootstrap_mode")
|
||||||
|
or os.environ.get(ENV_BOOTSTRAP_MODE)
|
||||||
|
)
|
||||||
|
bootstrap_token = (
|
||||||
|
params.get("bootstrap_token")
|
||||||
|
or os.environ.get(ENV_BOOTSTRAP_TOKEN)
|
||||||
|
)
|
||||||
|
|
||||||
if bootstrap_mode not in ("token", "bootstrap"):
|
if bootstrap_mode not in ("token", "bootstrap"):
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"iam-svc: --bootstrap-mode is required. Set to 'token' "
|
"iam-svc: bootstrap-mode is required. Set to 'token' "
|
||||||
"(with --bootstrap-token) for production, or 'bootstrap' "
|
"(with bootstrap-token) for production, or 'bootstrap' "
|
||||||
"to enable the explicit bootstrap operation over the "
|
"to enable the explicit bootstrap operation over the "
|
||||||
"pub/sub bus (dev / quick-start only, not safe under "
|
"pub/sub bus (dev / quick-start only, not safe under "
|
||||||
"public exposure). Refusing to start."
|
"public exposure). Configurable via processor-group "
|
||||||
|
f"params or the {ENV_BOOTSTRAP_MODE} environment "
|
||||||
|
"variable. Refusing to start."
|
||||||
)
|
)
|
||||||
if bootstrap_mode == "token" and not bootstrap_token:
|
if bootstrap_mode == "token" and not bootstrap_token:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"iam-svc: --bootstrap-mode=token requires "
|
"iam-svc: bootstrap-mode=token requires bootstrap-token "
|
||||||
"--bootstrap-token. Refusing to start."
|
f"(or the {ENV_BOOTSTRAP_TOKEN} environment "
|
||||||
|
"variable). Refusing to start."
|
||||||
)
|
)
|
||||||
if bootstrap_mode == "bootstrap" and bootstrap_token:
|
if bootstrap_mode == "bootstrap" and bootstrap_token:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"iam-svc: --bootstrap-token is not accepted when "
|
"iam-svc: bootstrap-token is not accepted when "
|
||||||
"--bootstrap-mode=bootstrap. Ambiguous intent. "
|
"bootstrap-mode=bootstrap. Ambiguous intent. "
|
||||||
"Refusing to start."
|
"Refusing to start."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue