Doctor plugin checks: implement health check classes and documentation

Implement remediation-aware health checks across all Doctor plugin modules
(Agent, Attestor, Auth, BinaryAnalysis, Compliance, Crypto, Environment,
EvidenceLocker, Notify, Observability, Operations, Policy, Postgres, Release,
Scanner, Storage, Vex) and their backing library counterparts (AI, Attestation,
Authority, Core, Cryptography, Database, Docker, Integration, Notify,
Observability, Security, ServiceGraph, Sources, Verification).

Each check now emits structured remediation metadata (severity, category,
runbook links, and fix suggestions) consumed by the Doctor dashboard
remediation panel.

Also adds:
- docs/doctor/articles/ knowledge base for check explanations
- Advisory AI search seed and allowlist updates for doctor content
- Sprint plan for doctor checks documentation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
master
2026-03-27 12:28:00 +02:00
parent fbd24e71de
commit c58a236d70
326 changed files with 18500 additions and 463 deletions

View File

@@ -0,0 +1,138 @@
---
checkId: check.security.secrets
plugin: stellaops.doctor.security
severity: fail
tags: [security, secrets, configuration]
---
# Secrets Configuration
## What It Checks
Validates that secrets are properly managed and not exposed as plain text in configuration. The check scans the following configuration keys for potential plain-text secrets:
| Key | What it protects |
|---|---|
| `Jwt:SigningKey` | JWT token signing |
| `Jwt:Secret` | JWT secret (alternative key) |
| `ApiKey` | API authentication key |
| `ApiSecret` | API secret |
| `S3:SecretKey` | Object storage credentials |
| `Smtp:Password` | Email server credentials |
| `Ldap:Password` | Directory service credentials |
| `Redis:Password` | Cache/message broker credentials |
| `Valkey:Password` | Cache/message broker credentials |
A value is considered a plain-text secret if it:
1. Is at least 8 characters long.
2. Contains both uppercase and lowercase letters.
3. Contains digits or special characters.
4. Does NOT start with a secrets provider prefix: `vault:`, `azurekv:`, `aws:`, `gcp:`, `${`, or `@Microsoft.KeyVault`.
The check also examines whether a secrets management provider is configured (`Secrets:Provider`, `KeyVault:Provider`, `Secrets:VaultUrl`, `KeyVault:Url`, `Vault:Address`). A missing secrets manager is only flagged if plain-text secrets are also found.
Note: Connection strings are intentionally excluded from this check as they are DSNs (host/port/db) and are expected in configuration.
## Why It Matters
Plain-text secrets in configuration files are a critical security risk. Configuration files are often committed to version control, stored in CI artifacts, or readable by anyone with filesystem access. Leaked secrets enable:
- Token forgery (JWT signing keys).
- Unauthorized API access (API keys).
- Data access via backend services (database, SMTP, LDAP passwords).
- Lateral movement within the infrastructure.
## Common Causes
- Secrets stored directly in `appsettings.json` instead of using a secrets provider
- Environment variables containing secrets not sourced from a secrets manager
- Development secrets left in production configuration
- No secrets management provider configured (HashiCorp Vault, Azure Key Vault, etc.)
## How to Fix
### Docker Compose
Use Docker secrets or reference an external secrets manager:
```yaml
services:
platform:
environment:
Jwt__SigningKey: "vault:secret/data/stellaops/jwt#signing_key"
Secrets__Provider: "vault"
Secrets__VaultUrl: "http://vault:8200"
secrets:
- jwt_signing_key
secrets:
jwt_signing_key:
file: ./secrets/jwt_signing_key.txt
```
Or use `dotnet user-secrets` for development:
```bash
dotnet user-secrets set "Jwt:SigningKey" "<your-secret>"
```
### Bare Metal / systemd
Configure a secrets provider in `appsettings.json`:
```json
{
"Secrets": {
"Provider": "vault",
"VaultUrl": "https://vault.internal:8200",
"UseSecretManager": true
}
}
```
Store secrets in the provider instead of config files:
```bash
# HashiCorp Vault
vault kv put secret/stellaops/jwt signing_key="<key>"
# dotnet user-secrets (development)
dotnet user-secrets set "Jwt:SigningKey" "<key>"
```
### Kubernetes / Helm
Store secrets as Kubernetes Secrets:
```bash
kubectl create secret generic stellaops-secrets \
--from-literal=jwt-signing-key="<key>" \
--from-literal=smtp-password="<password>"
```
Reference in Helm values:
```yaml
secrets:
provider: "kubernetes"
existingSecret: "stellaops-secrets"
```
Or use an external secrets operator (e.g., External Secrets Operator with Vault):
```yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: stellaops-secrets
spec:
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: stellaops-secrets
data:
- secretKey: jwt-signing-key
remoteRef:
key: secret/stellaops/jwt
property: signing_key
```
## Verification
```
stella doctor run --check check.security.secrets
```
## Related Checks
- `check.security.jwt.config` — JWT signing key security
- `check.security.encryption` — encryption key management
- `check.security.apikey` — API key security practices