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 likely 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:
- Is at least 8 characters long.
- Contains both uppercase and lowercase letters.
- Contains digits or special characters.
- 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.jsoninstead 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 is the recommended backend for self-hosted deployments)
How to Fix
Docker Compose
Use Docker secrets or reference an external secrets manager:
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:
dotnet user-secrets set "Jwt:SigningKey" "<your-secret>"
Bare Metal / systemd
Configure a secrets provider in appsettings.json:
{
"Secrets": {
"Provider": "vault",
"VaultUrl": "https://vault.internal:8200",
"UseSecretManager": true
}
}
Store secrets in the provider instead of config files:
# 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:
kubectl create secret generic stellaops-secrets \
--from-literal=jwt-signing-key="<key>" \
--from-literal=smtp-password="<password>"
Reference in Helm values:
secrets:
provider: "kubernetes"
existingSecret: "stellaops-secrets"
Or use an external secrets operator (e.g., External Secrets Operator with Vault):
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 securitycheck.security.encryption— encryption key managementcheck.security.apikey— API key security practices
