up
Some checks failed
Build Test Deploy / docs (push) Has been cancelled
Build Test Deploy / deploy (push) Has been cancelled
Build Test Deploy / build-test (push) Has been cancelled
Build Test Deploy / authority-container (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled

This commit is contained in:
2025-10-12 20:37:18 +03:00
parent b97fc7685a
commit 607e72e2a1
306 changed files with 21409 additions and 4449 deletions

View File

@@ -0,0 +1,47 @@
# StellaOps Authority Audit Events
StellaOps Authority emits structured audit records for every credential flow and bootstrap operation. The goal is to provide deterministic, privacy-aware telemetry that can be persisted offline and replayed for incident response without leaking credentials.
## Contract
Audit events share the `StellaOps.Cryptography.Audit.AuthEventRecord` contract. Key fields:
- `EventType` — canonical identifier such as `authority.password.grant`, `authority.client_credentials.grant`, or `authority.bootstrap.user`.
- `OccurredAt` — UTC timestamp captured at emission time.
- `CorrelationId` — stable identifier propagated across logs and persistence.
- `Outcome``Success`, `Failure`, `LockedOut`, `RateLimited`, or `Error`.
- `Reason` — optional failure or policy message.
- `Subject``AuthEventSubject` carrying subject identifier, username, display name, and optional realm metadata. All subject fields are tagged as PII.
- `Client``AuthEventClient` with client identifier, display name, and originating provider/plugin.
- `Scopes` — granted or requested OAuth scopes (sorted before emission).
- `Network``AuthEventNetwork` with remote address, forwarded headers, and user agent string (all treated as PII).
- `Properties` — additional `AuthEventProperty` entries for context-specific details (lockout durations, policy decisions, retries, etc.).
## Data Classifications
Every string value uses `ClassifiedString` to assign a data classification:
- `None` — public or operational metadata (event type, outcome).
- `Personal` — personally identifiable information (PII) such as subject identifiers, usernames, remote IP addresses, and user agents.
- `Sensitive` — secrets or derived credentials (client secrets, retry tokens). Avoid storing raw credentials; emit only hashed or summarised data when the classification is `Sensitive`.
Downstream log sinks and persistence layers can inspect classifications to redact or separate PII before export.
## Event Naming
Event names follow dotted notation:
- `authority.password.grant` — password grant handled by OpenIddict.
- `authority.client_credentials.grant` — client credential grant handling.
- `authority.bootstrap.user` and `authority.bootstrap.client` — bootstrap API operations.
- Future additions should preserve the `authority.<surface>.<action>` pattern to keep filtering deterministic.
## Persistence
The Authority host converts audit records into `AuthorityLoginAttemptDocument` rows for MongoDB persistence. Documents must:
- Preserve `CorrelationId`, `SubjectId`, `ClientId`, `Plugin`, `Outcome`, `Reason`, and `OccurredAt`.
- Store remote address in `remoteAddress` only after classification as PII.
- Include summary booleans such as `Successful` to accelerate lockout policy checks.
When exporting to external SIEMs, honour the `ClassifiedString.Classification` tag to avoid shipping PII into restricted environments.

View File

@@ -0,0 +1,106 @@
# Authority Threat Model (STRIDE)
> Prepared by Security Guild — 2025-10-12. Scope covers Authority host, Standard plug-in, CLI, bootstrap workflow, and offline revocation distribution.
## 1. Scope & Method
- Methodology: STRIDE applied to primary Authority surfaces (token issuance, bootstrap, revocation, operator tooling, plug-in extensibility).
- Assets in scope: identity credentials, OAuth tokens (access/refresh), bootstrap invites, revocation manifests, signing keys, audit telemetry.
- Out of scope: Third-party IdPs federated via OpenIddict (tracked separately in SEC6 backlog).
## 2. Assets & Entry Points
| Asset / Surface | Description | Primary Actors |
|-----------------|-------------|----------------|
| Token issuance APIs (`/token`, `/authorize`) | OAuth/OIDC endpoints mediated by OpenIddict | CLI, UI, automation agents |
| Bootstrap channel | Initial admin invite + bootstrap CLI workflow | Platform operators |
| Revocation bundle | Offline JSON + detached JWS consumed by agents | Feedser, Agents, Zastava |
| Plug-in manifests | Standard plug-in configuration and password policy overrides | Operators, DevOps |
| Signing keys | ES256 signing keys backing tokens and revocation manifests | Security Guild, HSM/KeyOps |
| Audit telemetry | Structured login/audit stream persisted to Mongo/observability stack | SOC, SecOps |
## 3. Trust Boundaries
| Boundary | Rationale | Controls |
|----------|-----------|----------|
| TB1 — Public network ↔️ Authority ingress | Internet/extranet exposure for `/token`, `/authorize`, `/bootstrap` | TLS 1.3, reverse proxy ACLs, rate limiting (SEC3.A / CORE8.RL) |
| TB2 — Authority host ↔️ Mongo storage | Credential store, revocation state, audit log persistence | Authenticated Mongo, network segmentation, deterministic serializers |
| TB3 — Authority host ↔️ Plug-in sandbox | Plug-ins may override password policy and bootstrap flows | Code signing, manifest validation, restart-time loading only |
| TB4 — Operator workstation ↔️ CLI | CLI holds bootstrap secrets and revocation bundles | OS keychain storage, MFA on workstations, offline kit checksum |
| TB5 — Authority ↔️ Downstream agents | Revocation bundle consumption, token validation | Mutual TLS (planned), detached JWS signatures, bundle freshness checks |
## 4. Data Flow Diagrams
### 4.1 Runtime token issuance
```mermaid
flowchart LR
subgraph Client Tier
CLI[StellaOps CLI]
UI[UI / Automation]
end
subgraph Perimeter
RP[Reverse Proxy / WAF]
end
subgraph Authority
AUTH[Authority Host]
PLGIN[Standard Plug-in]
STORE[(Mongo Credential Store)]
end
CLI -->|OAuth password / client creds| RP --> AUTH
UI -->|OAuth flows| RP
AUTH -->|PasswordHashOptions + Secrets| PLGIN
AUTH -->|Verify / Persist hashes| STORE
STORE -->|Rehash needed| AUTH
AUTH -->|Access / refresh token| RP --> Client Tier
```
### 4.2 Bootstrap & revocation
```mermaid
flowchart LR
subgraph Operator
OPS[Operator Workstation]
end
subgraph Authority
AUTH[Authority Host]
STORE[(Mongo)]
end
subgraph Distribution
OFFKIT[Offline Kit Bundle]
AGENT[Authorized Agent / Feedser]
end
OPS -->|Bootstrap CLI (`stellaops auth bootstrap`)| AUTH
AUTH -->|One-time invite + Argon2 hash| STORE
AUTH -->|Revocation export (`stellaops auth revoke export`)| OFFKIT
OFFKIT -->|Signed JSON + .jws| AGENT
AGENT -->|Revocation ACK / telemetry| AUTH
```
## 5. STRIDE Analysis
| Threat | STRIDE Vector | Surface | Risk (L×I) | Existing Controls | Gaps / Actions | Owner |
|--------|---------------|---------|------------|-------------------|----------------|-------|
| Spoofed revocation bundle | Spoofing | TB5 — Authority ↔️ Agents | Med×High | Detached JWS signature (planned), offline kit checksums | Finalise signing key registry & verification script (SEC4.B/SEC4.HOST); add bundle freshness requirement | Security Guild (follow-up: **SEC5.B**) |
| Parameter tampering on `/token` | Tampering | TB1 — Public ingress | Med×High | ASP.NET model validation, OpenIddict, rate limiter (CORE8.RL) | Add audit coverage for tampered inputs, align correlation IDs with SOC (SEC2.A/SEC2.B) | Security Guild + Authority Core (follow-up: **SEC5.C**) |
| Bootstrap invite replay | Repudiation | TB4 — Operator CLI ↔️ Authority | Low×High | One-time bootstrap tokens, Argon2id hashing on creation | Enforce invite expiration + audit trail for unused invites | Security Guild (follow-up: **SEC5.D**) |
| Token replay by stolen agent | Information Disclosure | TB5 | Med×High | Planned revocation bundles, optional mTLS | Require agent binding (device fingerprint) and enforce revocation grace window alerts | Security Guild + Zastava (follow-up: **SEC5.E**) |
| Privilege escalation via plug-in override | Elevation of Privilege | TB3 — Plug-in sandbox | Med×High | Signed plug-ins, restart-only loading, configuration validation | Add static analysis on manifest overrides + runtime warning when policy weaker than host | Security Guild + DevOps (follow-up: **SEC5.F**) |
| Offline bundle tampering | Tampering | Distribution | Low×High | SHA256 manifest, signed bundles (planned) | Add supply-chain attestation for Offline Kit, publish verification CLI in docs | Security Guild + Ops (follow-up: **SEC5.G**) |
| Failure to log denied tokens | Repudiation | TB2 — Authority ↔️ Mongo | Med×Med | Serilog structured events (partial), Mongo persistence path (planned) | Finalise audit schema (SEC2.A) and ensure `/token` denies include subject/client/IP fields | Security Guild + Authority Core (follow-up: **SEC5.H**) |
Risk scoring uses qualitative scale (Low/Med/High) for likelihood × impact; mitigation priority follows High > Med > Low.
## 6. Follow-up Backlog Hooks
| Backlog ID | Linked Threat | Summary | Target Owners |
|------------|---------------|---------|---------------|
| SEC5.B | Spoofed revocation bundle | Complete libsodium/Core signing integration and ship revocation verification script. | Security Guild + Authority Core |
| SEC5.C | Parameter tampering on `/token` | Finalise audit contract (`SEC2.A`) and add request tamper logging. | Security Guild + Authority Core |
| SEC5.D | Bootstrap invite replay | Implement expiry enforcement + audit coverage for unused bootstrap invites. | Security Guild |
| SEC5.E | Token replay by stolen agent | Document device binding requirements and create detector for stale revocation acknowledgements. | Security Guild + Zastava |
| SEC5.F | Plug-in override escalation | Static analysis of plug-in manifests; warn on weaker password policy overrides. | Security Guild + DevOps |
| SEC5.G | Offline bundle tampering | Extend Offline Kit build to include attested manifest + verification CLI sample. | Security Guild + Ops |
| SEC5.H | Failure to log denied tokens | Ensure audit persistence for all `/token` denials with correlation IDs. | Security Guild + Authority Core |
Update `src/StellaOps.Cryptography/TASKS.md` (Security Guild board) with the above backlog entries to satisfy SEC5.A exit criteria.

View File

@@ -0,0 +1,76 @@
# Authority Password Hashing Guidance
> **Status:** Drafted 2025-10-11 alongside SEC1.A / SEC1.PLG rollout. Argon2id is now the default hashing algorithm for the Standard plug-in and recommended for all Authority identity providers.
## 1. Overview
StellaOps Authority issues and verifies credentials through the shared `StellaOps.Cryptography` provider abstraction. As of October 2025:
- **Default algorithm:** Argon2id (PHC format `$argon2id$v=19$m=<mem>,t=<time>,p=<parallelism>$<salt>$<hash>`).
- **Legacy support:** PBKDF2-SHA256 hashes (`PBKDF2.<iterations>.<payload>`) continue to verify, but successful logins are transparently rehashed to Argon2id.
- **Configuration path:** `authority.security.passwordHashing` in the primary Authority configuration controls system-wide defaults. Individual plug-ins may override via `passwordHashing` in their manifests.
## 2. Recommended Parameters
| Environment | memorySizeInKib | iterations | parallelism | Notes |
|-------------|-----------------|------------|-------------|-------|
| Production (default) | 19456 | 2 | 1 | Balances CPU with 19MiB memory cost; ~175ms on 4 vCPU host. |
| High-security enclave | 32768 | 3 | 1 | Increases memory pressure; confirm capacity on shared hosts. |
| Resource-constrained lab | 8192 | 2 | 1 | Use only for bootstrap/testing; increase once hardware upgraded. |
| PBKDF2 fallback | — | ≥210000 | — | Set `algorithm: Pbkdf2` only when Argon2 hardware support unavailable. |
> ⚠️ Lowering parameters below these baselines should be a temporary measure. Document any deviations in runbooks and schedule follow-up work to restore defaults.
## 3. Configuring Authority Defaults
`authority.yaml` (or equivalent) accepts the following block:
```yaml
security:
passwordHashing:
algorithm: Argon2id # or Pbkdf2
memorySizeInKib: 19456 # ~19 MiB
iterations: 2
parallelism: 1
```
These values propagate to plug-ins that do not provide explicit overrides. Runtime validation ensures all numbers are > 0 and the algorithm is recognised.
## 4. Plug-in Overrides
The Standard plug-in inherits the host defaults but can fine-tune parameters per installation:
```yaml
passwordHashing:
algorithm: Argon2id
memorySizeInKib: 8192
iterations: 2
parallelism: 1
```
- When the plug-in configuration omits `passwordHashing`, the Authority defaults apply.
- Setting `algorithm: Pbkdf2` keeps PBKDF2 active but still upgrades credentials when the host default switches back to Argon2id.
- Invalid overrides (e.g., `memorySizeInKib: 0`) cause startup to fail with a descriptive validation error.
## 5. Observability & Migration
- Successful PBKDF2 verification logs a **rehash-needed** event and immediately persists an Argon2id hash.
- Metrics emitted: `auth.plugins.standard.password_rehash_total{algorithm="pbkdf2"}` (add dashboards to monitor upgrade progress).
- During migration, expect a gradual decline in PBKDF2 hashes as users authenticate. Use operator scripts to query `authority_users_*` collections for lingering `PBKDF2.` prefixes if you need to track completion.
## 6. Operational Checklist
1. Update Authority configuration with desired defaults; restart the host.
2. Regenerate plug-in manifests (if overrides required) and redeploy.
3. Monitor `password_rehash_total` and login success rates; investigate any spike in failures (likely due to mis-sized limits).
4. Review hardware utilisation; Argon2id increases memory pressure compared to PBKDF2.
5. Archive this document with the change request and notify SOC of the new baseline.
For additional context on tuning trade-offs, consult OWASP Password Storage Cheat Sheet and the StellaOps Security Guild guidance (to be published in `docs/security/rate-limits.md`).
## 7. Native Argon2 Preview Build Flag
- Set `dotnet build -p:StellaOpsCryptoSodium=true` (or define the MSBuild property in your CI) to enable the `STELLAOPS_CRYPTO_SODIUM` compilation symbol.
- The symbol switches `StellaOps.Cryptography` to use the native-oriented build pipeline so we can wire libsodium/Core bindings without affecting the managed default.
- Until the native implementation lands (SEC1.B follow-up), the flag falls back to the managed Konscious implementation while still validating the alternate compilation path.
- Document any production usage of the flag in your change log so future upgrades can align with the Security Guild rollout plan.

View File

@@ -0,0 +1,56 @@
{
"$schema": "../../etc/authority/revocation_bundle.schema.json",
"schemaVersion": "1.0.0",
"issuer": "https://auth.stella-ops.example",
"bundleId": "6f9d08bfa0c24a0a9f7f59e6c17d2f8e8bca2ef34215c3d3ba5a9a1f0fbe2d10",
"issuedAt": "2025-10-12T15:00:00Z",
"validFrom": "2025-10-12T15:00:00Z",
"sequence": 42,
"signingKeyId": "authority-signing-20251012",
"revocations": [
{
"id": "7ad4f3d2c21b461d9b3420e1151be9c4",
"category": "token",
"tokenType": "access_token",
"clientId": "feedser-cli",
"subjectId": "user:ops-admin",
"reason": "compromised",
"reasonDescription": "Access token reported by SOC automation run R-2045.",
"revokedAt": "2025-10-12T14:32:05Z",
"scopes": [
"feedser:export",
"feedser:jobs"
],
"fingerprint": "AD35E719C12204D7E7C92ED3F6DEBF0A44642D41AAF94233F9A47E183F4C5F18",
"metadata": {
"reportId": "R-2045",
"source": "soc-automation"
}
},
{
"id": "user:departed-vendor",
"category": "subject",
"subjectId": "user:departed-vendor",
"reason": "lifecycle",
"revokedAt": "2025-10-10T18:15:00Z",
"metadata": {
"ticket": "HR-8821"
}
},
{
"id": "ci-runner-legacy",
"category": "client",
"clientId": "ci-runner-legacy",
"reason": "rotation",
"revokedAt": "2025-10-09T11:00:00Z",
"expiresAt": "2025-11-01T00:00:00Z",
"metadata": {
"replacement": "ci-runner-2025"
}
}
],
"metadata": {
"generator": "stellaops-authority@1.4.0",
"jobId": "revocation-export-20251012T1500Z"
}
}

View File

@@ -0,0 +1,70 @@
# Authority Revocation Bundle
The Authority service exports revocation information as an offline-friendly JSON document plus a detached JWS signature. Operators can mirror the bundle alongside Feedser exports to ensure air-gapped scanners receive the latest token, subject, and client revocations.
## File layout
| Artefact | Description |
| --- | --- |
| `revocation-bundle.json` | Canonical JSON document describing revoked entities. Validates against [`etc/authority/revocation_bundle.schema.json`](../../etc/authority/revocation_bundle.schema.json). |
| `revocation-bundle.json.jws` | Detached JWS signature covering the exact UTF-8 bytes of `revocation-bundle.json`. |
| `revocation-bundle.json.sha256` | Hex-encoded SHA-256 digest used by mirror automation (optional but recommended). |
All hashes and signatures are generated after applying the deterministic formatting rules below.
## Deterministic formatting rules
- JSON is serialised with UTF-8 encoding, 2-space indentation, and lexicographically sorted object keys.
- Arrays are sorted by deterministic keys:
- Top-level `revocations` sorted by (`category`, `id`, `revokedAt`).
- Nested arrays (`scopes`) sorted ascending, unique enforced.
- Numeric values (`sequence`) are emitted without leading zeros.
- Timestamps use UTC ISO-8601 format with `Z` suffix.
Consumers MUST treat the combination of `schemaVersion` and `sequence` as a monotonic feed. Bundles with older `sequence` values are ignored unless `bundleId` differs and `issuedAt` is newer (supporting replay detection).
## Revocation entry categories
| Category | Description | Required fields |
| --- | --- | --- |
| `token` | A single OAuth token (access, refresh, device, authorization code). | `tokenType`, `clientId`, `revokedAt`, optional `subjectId` |
| `subject` | All credentials issued to a subject (user/service account). | `subjectId`, `revokedAt` |
| `client` | Entire OAuth client registration is revoked. | `clientId`, `revokedAt` |
| `key` | Signing/encryption key material revoked. | `id`, `revokedAt` |
`reason` is a machine-friendly code (`compromised`, `rotation`, `policy`, `lifecycle`, etc). `reasonDescription` may include a short operator note.
## Detached JWS workflow
1. Serialise `revocation-bundle.json` using the deterministic rules.
2. Compute SHA-256 digest; write to `revocation-bundle.json.sha256`.
3. Sign using ES256 (default) with the configured Authority signing key. The JWS header uses:
```json
{
"alg": "ES256",
"kid": "{signingKeyId}",
"typ": "application/vnd.stellaops.revocation-bundle+jws",
"b64": false,
"crit": ["b64"]
}
```
4. Persist the detached signature payload to `revocation-bundle.json.jws` (per RFC 7797).
Verification steps:
1. Validate `revocation-bundle.json` against the schema.
2. Re-compute SHA-256 and compare with `.sha256` (if present).
3. Resolve the signing key from JWKS (`/.well-known/jwks.json`) or the offline key bundle.
4. Verify the detached JWS using the stored signing key (example tooling coming with `stella auth revoke verify`).
## Example
The repository contains an [example bundle](revocation-bundle-example.json) demonstrating a mixed export of token, subject, and client revocations. Use it as a reference for integration tests and tooling.
## Operations Quick Reference
- `stella auth revoke export` emits a canonical JSON bundle, `.sha256` digest, and detached JWS signature in one command. Use `--output` to write into your mirror staging directory.
- `stella auth revoke verify` validates a bundle using cached JWKS or an offline PEM key and reports digest mismatches before distribution.
- `POST /internal/revocations/export` provides the same payload for orchestrators that already talk to the bootstrap API.
- `POST /internal/signing/rotate` rotates JWKS material without downtime; always export a fresh bundle afterward so downstream mirrors receive signatures from the new `kid`.
- Offline Kit automation should mirror `revocation-bundle.json*` alongside Feedser exports so agents ingest revocations during the same sync pass.