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:

KeyWhat it protects
Jwt:SigningKeyJWT token signing
Jwt:SecretJWT secret (alternative key)
ApiKeyAPI authentication key
ApiSecretAPI secret
S3:SecretKeyObject storage credentials
Smtp:PasswordEmail server credentials
Ldap:PasswordDirectory service credentials
Redis:PasswordCache/message broker credentials
Valkey:PasswordCache/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:

Common Causes

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