# Credential and Secret Management Policy
# Cold Bore Ballistics LLC — Internal Reference
# Classification: Confidential

Subordinate to: `CBB_Information_Security_Policy.md` §9

---

## 1. Purpose and Scope

This policy governs how CBB creates, stores, rotates, and retires credentials, secrets, API keys, certificates, and any other sensitive authentication material. It applies to all CBB systems, all individuals with access to CBB credentials, and all automated or AI-assisted tooling operating under CBB credentials.

---

## 2. Mandatory Storage: Azure Key Vault

All secrets must be stored in Azure Key Vault. No exceptions.

CBB operates two Key Vaults:

| Vault | Purpose | Scope |
|---|---|---|
| `kv-cbb-shared-prod` | Cross-cutting infrastructure secrets (VPN PSK, shared infra credentials) | CBB-wide |
| `kv-cbb-m365-prod` | M365 and identity-related secrets | M365 / Entra ID scope |

Secrets may not be stored in:

- Plain text files (`.env`, `.txt`, `.md`, `.json`, or any other format)
- Version control repositories (any branch, any commit, including private repos)
- Slack messages, email, or any communication channel
- Documentation, runbooks, or architecture references
- Local shell history, scripts, or configuration files checked into git

If a secret is discovered in any of the above locations, it must be treated as compromised and rotated immediately. See §6 (Incident: Secret Exposure).

---

## 3. Secret Types and Requirements

### 3.1 App Registration Client Secrets

Client secrets for Entra ID app registrations are a last resort. Certificates are preferred wherever the target service supports them.

When a client secret is unavoidable:

- It must be stored in Key Vault immediately upon creation — never written down or copied elsewhere
- It must be scoped to the minimum expiry that is operationally practical (maximum 12 months)
- It must not be shared across registrations
- Its Key Vault secret name must follow the pattern: `appreg-<app-name>-secret`

Public client app registrations (device-code / interactive flows) must not have client secrets at all. See `Entra_App_Registration_Policy.md` §6.

### 3.2 API Keys

API keys for third-party services (Twilio, SendGrid, Atlassian, Namecheap, etc.) must be stored in Key Vault with a descriptive name indicating the service and scope (e.g., `twilio-auth-token`, `sendgrid-api-key-transactional`).

API keys must be scoped to the minimum required permissions within the issuing service. Where the service supports multiple API key scopes or roles, use the narrowest scope that fulfills the integration's requirements.

### 3.3 Certificates

Certificates issued by the CBB internal CA (`mkcert` CA at `~/.local/share/mkcert/`) are used for internal HTTPS services. The CA private key must be stored securely and backed up offline. Loss of the CA private key requires reissuing all dependent certificates.

Certificates for external services must use a trusted public CA appropriate to the use case. Let's Encrypt is not used at CBB; CBB's internal CA is the chosen approach for internal services.

### 3.4 VPN Pre-Shared Keys

The IPsec VPN PSK is stored in Key Vault under `kv-cbb-shared-prod/ipsec-psk`. It must not appear in Terraform state files, IaC variable files, or any other text-readable format outside Key Vault.

### 3.5 SSH Keys and Device Credentials

SSH keys used for server access must be generated per-operator and per-host where practical. Private keys must never be committed to version control or stored in shared locations. Public keys are not secret but must be managed and revoked upon offboarding.

---

## 4. Rotation

| Secret type | Rotation trigger | Maximum age |
|---|---|---|
| App registration client secret | Expiry, suspected compromise, or offboarding of anyone with access | 12 months |
| API keys | Suspected compromise or offboarding of anyone with access | No maximum; rotate annually as a best practice |
| VPN PSK | Suspected compromise or offboarding of anyone with access | No maximum; rotate annually as a best practice |
| Internal CA certificate | CA compromise or key loss | Per certificate validity period |
| SSH keys | Offboarding of the key holder | No maximum |
| M365 account passwords | Suspected compromise | No forced rotation schedule; rely on MFA and conditional access |

Rotation must be completed before the previous secret is revoked, to avoid service interruption. After confirming the new secret is functioning, revoke the old one immediately.

---

## 5. Prohibition on Secret Sharing

Secrets must never be shared between:

- Two different app registrations or service accounts
- Two different human operators
- Human operators and automated tooling

Each integration, each operator, and each automated process receives its own credential. This ensures that a compromise or offboarding can be scoped and contained without rotating credentials that affect unrelated systems.

---

## 6. Incident: Secret Exposure

A secret is considered exposed if it appears in any location not authorized by §2, or if there is reason to believe an unauthorized party has accessed it.

Upon discovery of a suspected secret exposure:

1. Treat the secret as compromised immediately — do not wait to confirm.
2. Rotate the secret and store the new value in Key Vault.
3. Revoke the old secret in the issuing service.
4. If the secret was committed to a git repository: rotate first, then purge the commit history. Rotation takes priority over cleanup.
5. Assess the blast radius: what systems or integrations used this secret? Were any of them accessed by an unauthorized party?
6. Report the incident per `Incident_Response_Policy.md`.

---

## Document Control

| Field | Value |
|---|---|
| **Document owner** | `jschwefel@coldboreballisticsllc.com` |
| **Classification** | Confidential (03_-_Confidential/) |
| **Location** | `OneDrive/CBB_Corporate/03_-_Confidential/Security/Credential_and_Secret_Management_Policy.md` |
| **Cross-references** | `CBB_Information_Security_Policy.md`; `Entra_App_Registration_Policy.md`; `Azure_Key_Vault_Architecture_Reference.md`; `Incident_Response_Policy.md` |
| **Created** | 2026-06-07 |
| **Last updated** | 2026-06-09 |
| **Next full review due** | 2027-06-07 |

---

## Revision Control Block

| Rev | Date | Changed By | Reviewed By | Approved By | Summary |
|-----|------|------------|-------------|-------------|---------|
| 0.9 | 2026-06-07 | jschwefel | — | — | Initial release |
| DRAFT | 2026-06-09 | Claude Code | — | — | Removed non-standard `# Last Updated` header line (per TECHNICAL_DOCUMENTATION_GUIDE §7). No content changes. |
