Cybersecurity-Projects/PROJECTS/intermediate/credential-rotation-enforcer
CarterPerez-dev 91199476e1 feat: comprehensive audit-driven hardening pass
Closes 36 findings from the project audit. The headline win is correctness:
the daemon now actually does what the README claims, all three audit-log
integrity layers verify, and the rotation control loop terminates.

Critical fixes:
- Orchestrator bumps Credential.last_rotated_at on success and writes a
  sealed credential_versions row, so the policy evaluator stops re-firing
  the same rotation every tick
- Envelope encryption (AES-256-GCM, KEK-wrapped DEK, AAD-bound) is now
  wired into the rotation success path; the credential_versions table is
  no longer dead schema
- cre audit verify checks all three layers (hash chain + HMAC ratchet +
  Merkle batches) instead of only the chain
- BatchSealerScheduler runs every 300s and on shutdown so audit_batches
  actually fills with signed Merkle roots
- Compliance bundle exports the real audit_batches list and covers
  public_key.pem under the manifest's checksum
- cre run/watch hard-fail without CRE_HMAC_KEY_HEX + CRE_KEK_HEX rather
  than silently using zero defaults

Quality fixes:
- HTTP::Client wrapper with connect/read timeouts + bounded retry-with-
  jitter on 408/429/5xx; Telegram error logs redact the bot token
- EventBus dispatch isolates Block subscribers behind select+timeout so
  one stuck subscriber can't pin the bus
- RotationWorker checks rotations.in_flight before dispatching to dedupe
  duplicate schedules; PG enforces it at the DB via partial unique index
- Commit-step failures transition rotations to Inconsistent (a terminal
  state) and raise a critical alert instead of pretending success
- env_file rotator uses per-PID pending paths plus an exclusive flock on
  a sibling .lock file so two daemons can't race on the rename
- Versioned migration runner replaces the soup of CREATE IF NOT EXISTS;
  SQLite gains BEFORE UPDATE / BEFORE DELETE triggers on audit_events
- AuditSubscriber publishes a critical alert + panics by default when
  log writes fail, instead of silently dropping
- Engine.stop drains via an ack channel with a 2s deadline rather than
  the previous magic 50ms sleep
- Policy DSL moved into CRE::Policy::Dsl module (consumers `include` it);
  multiple matching policies raise PolicyConflictError
- Evaluator uses credentials.overdue() per-max_age group instead of
  loading every credential each tick
- AuditRepo gains each_in_range streaming and all_batches enumeration;
  bundle export streams instead of buffering
- New cre verify-bundle <zip> CLI re-runs every check the bundle README
  documents; new cre tui-demo for synthetic event preview

Tests: 179 unit (up from 159), all passing. New rotation_worker_spec,
new SigV4 regression vector, new orchestrator success-path coverage
(envelope write + last_rotated_at bump + Inconsistent state).

Docs: README + learn/00..04 aligned with the post-audit behavior;
removed /snooze (was a stub) and the AwsIamKey/Database CredentialKind
variants that had no rotators. Added required-env-var documentation.
2026-04-29 03:35:10 -04:00
..
.github/workflows feat(cre): source tree skeleton with empty module dirs and CI workflow 2026-04-28 23:54:59 -04:00
config feat(demo): Tier 1 zero-deps demo + Tier 2 docker-compose scaffold 2026-04-29 01:16:08 -04:00
docker chore: replace Makefile with justfile to match repo convention 2026-04-29 01:28:37 -04:00
learn feat: comprehensive audit-driven hardening pass 2026-04-29 03:35:10 -04:00
scripts docs: install.sh + learn/ walkthrough series + full README 2026-04-29 01:21:17 -04:00
spec feat: comprehensive audit-driven hardening pass 2026-04-29 03:35:10 -04:00
src feat: comprehensive audit-driven hardening pass 2026-04-29 03:35:10 -04:00
.editorconfig feat(cre): bootstrap Crystal project shell 2026-04-28 18:05:36 -04:00
.gitignore feat(cre): bootstrap Crystal project shell 2026-04-28 18:05:36 -04:00
CONFIGURATION.md feat: wire RotationWorker + Telegram in cre run + CONFIGURATION.md guide 2026-04-29 01:42:45 -04:00
LICENSE docs(README): match repo project pattern (#27 intermediate) 2026-04-29 01:50:30 -04:00
README.md feat: comprehensive audit-driven hardening pass 2026-04-29 03:35:10 -04:00
justfile feat(demo): add tui-demo command + fix demo-full recipe 2026-04-29 01:34:25 -04:00
shard.lock feat(cre): bootstrap Crystal project shell 2026-04-28 18:05:36 -04:00
shard.yml feat(cre): bootstrap Crystal project shell 2026-04-28 18:05:36 -04:00

README.md

 ██████╗██████╗ ███████╗
██╔════╝██╔══██╗██╔════╝
██║     ██████╔╝█████╗
██║     ██╔══██╗██╔══╝
╚██████╗██║  ██║███████╗
 ╚═════╝╚═╝  ╚═╝╚══════╝

Cybersecurity Projects Crystal License: AGPLv3 PostgreSQL

Credential rotation enforcer written in Crystal. Tracks credentials, evaluates compile-time-checked policies, and executes the four-step rotation contract against AWS Secrets Manager, HashiCorp Vault, GitHub fine-grained PATs, and local .env files. Single binary, live TUI, bidirectional Telegram bot, tamper-evident audit log, signed compliance evidence export.

This is a quick overview — security theory, architecture, and full walkthroughs are in the learn modules. Operator setup lives in CONFIGURATION.md.

What It Does

  • Compile-time-checked policy DSL (typo'd action symbols, missing fields, bad credential property references all fail crystal build)
  • Bus + plugin architecture — typed events fan out across Crystal channels; subscribers (audit, TUI, Telegram, log) react independently; rotators register at compile time via register_as :kind macro
  • Four-step rotation contract (generate → apply → verify → commit) borrowed from AWS Secrets Manager's rotation Lambda template, dual-version safe under concurrent reads
  • Three-layer tamper-evident audit log: SHA-256 hash chain + ratcheting HMAC-SHA256 + Ed25519-signed Merkle batches
  • AEAD envelope encryption (AES-256-GCM, per-row DEKs wrapped by KEK, AAD-bound to credential identity, reserved algorithm_id byte for crypto agility)
  • Hand-rolled live TUI (no external TUI framework — stdlib ANSI escapes only) with event-driven repaints coalesced to a tick interval
  • Bidirectional Telegram bot — viewer tier (/status, /queue, /history, /alerts) + operator tier (/rotate)
  • Compliance evidence export bundle (signed ZIP with audit log, Merkle batches, control mapping for SOC 2 / PCI-DSS / ISO 27001 / HIPAA)

Quick Start

git clone https://github.com/CarterPerez-dev/Cybersecurity-Projects.git
cd Cybersecurity-Projects/PROJECTS/intermediate/credential-rotation-enforcer
shards install && shards build cre --release
./bin/cre demo

Or use the install script:

curl -fsSL https://raw.githubusercontent.com/CarterPerez-dev/Cybersecurity-Projects/main/PROJECTS/intermediate/credential-rotation-enforcer/scripts/install.sh | bash

[!TIP] This project uses just as a command runner. Type just to see all available recipes.

Install: curl -sSf https://just.systems/install.sh | bash -s -- --to ~/.local/bin

Demo tiers

just demo               # Tier 1 — zero-deps SQLite + .env rotator (under 30s)
just tui-demo           # Live TUI preview with synthetic events (8s, no setup)
just demo-full          # Tier 2 — Docker Compose: PG + LocalStack + Vault + fake-GH
just demo-full-down     # tear down the stack

Daemon usage

cre run and cre watch require two 32-byte secrets — the seed key for the audit-log HMAC ratchet and the KEK that wraps per-row data keys. Generate fresh values once and store them somewhere durable (KMS, password manager, sealed env file):

export CRE_HMAC_KEY_HEX=$(openssl rand -hex 32)
export CRE_KEK_HEX=$(openssl rand -hex 32)
export CRE_SIGNING_KEY_HEX=$(openssl rand -hex 32)   # optional: enables Merkle-batch sealing

Then:

cre run --db=sqlite:cre.db                       # headless daemon
cre watch --db=sqlite:cre.db                     # daemon + live TUI
cre check --db=sqlite:cre.db --output=json       # one-shot CI gate (no key required)
cre rotate <credential-id>                       # manual rotation (uses same env-driven rotators as run)
cre policy list                                  # inspect compiled policies
cre audit verify                                 # hash chain + HMAC ratchet (+ Merkle if --public-key given)
cre export --framework=soc2 --out=evidence.zip   # signed compliance bundle
cre verify-bundle evidence.zip                   # offline re-verify a bundle

cre check exits 1 when any credential violates its policy — drop into any CI pipeline.

Flagship Rotators

Rotator What it talks to Auth
AWS Secrets Manager secretsmanager.<region>.amazonaws.com SigV4 (rolled from scratch in src/cre/aws/signer.cr)
HashiCorp Vault vault read database/creds/<role> + lease revoke X-Vault-Token
GitHub fine-grained PATs POST/DELETE /user/personal-access-tokens Bearer ghp_...
Local .env file atomic temp+rename n/a

Adding a fifth rotator means dropping a single file in src/cre/rotators/ — the register_as :kind macro hooks it into the registry at compile time. Zero changes to the orchestrator, scheduler, or any subscriber.

Architecture

                       ┌──────────────────────────────────────┐
                       │       cre  (single Crystal binary)   │
                       │                                      │
   ┌────────────┐      │  ┌──────────────────────────────┐    │
   │ Scheduler  │─────►│  │       Typed Event Bus        │    │
   │ (fiber)    │      │  └──┬─────┬─────┬─────┬─────┬───┘    │
   └────────────┘      │     │     │     │     │     │        │
                       │  ┌──▼──┐ ┌▼────┐ ┌▼──┐ ┌▼──┐ ┌▼───┐  │
                       │  │Rot. │ │Audit│ │TUI│ │Tg │ │Pol.│  │
                       │  │Wrkr │ │Sub  │ │Sub│ │Bot│ │Eval│  │
                       │  └──┬──┘ └──┬──┘ └───┘ └───┘ └────┘  │
                       │     │       │                        │
                       │     ▼       ▼                        │
                       │  ┌──────────────────────────────┐    │
                       │  │  Persistence (PG / SQLite)   │    │
                       │  │  + 3-layer audit integrity   │    │
                       │  └──────────────────────────────┘    │
                       └──────────────────────────────────────┘

All long-lived components are fibers in one OS process. The bus is in-process (Crystal channels are nanosecond-scale) so the architectural overhead is essentially free. Per-subscriber overflow policy: Block for audit (compliance — never drop), Drop for TUI / metrics / Telegram (best-effort).

The Three-Layer Audit Log

   Layer 3 ─ Ed25519-signed Merkle batches  →  auditor verifies with public key only
   Layer 2 ─ HMAC ratchet (key zeroized per rotation)  →  past entries unforgeable
   Layer 1 ─ SHA-256 hash chain  →  any single-row tampering breaks forward chain
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   Postgres ─ append-only via TRIGGER + role grants (INSERT-only)

cre audit verify walks all three layers and reports which (if any) is broken.

Stack

Language: Crystal 1.20+

Dependencies: crystal-db (DB abstraction), crystal-pg (PostgreSQL), crystal-sqlite3 (SQLite), tourmaline (Telegram framework, used minimally), webmock.cr (test HTTP mocks)

Direct LibCrypto FFI for AES-256-GCM AEAD (Crystal stdlib OpenSSL::Cipher lacks GCM auth_data/auth_tag) and Ed25519 signing (stdlib lacks high-level wrapper). Bindings live in src/cre/crypto/aead.cr and src/cre/audit/signing.cr.

Testing: stdlib Spec runner, 179+ unit tests + integration tests against real PostgreSQL via Docker.

Configuration

Setup is fully env-var driven — no config file required. See CONFIGURATION.md for the operator guide:

  • Required env vars (CRE_KEK_HEX, CRE_HMAC_KEY_HEX, DATABASE_URL)
  • Per-rotator setup (AWS IAM policy, Vault token policy, GitHub admin PAT)
  • Telegram bot creation + chat-ID discovery
  • systemd service unit with hardening directives
  • Production security checklist

Learn

This project includes step-by-step learning materials covering security theory, architecture, and implementation.

Module Topic
00 - Overview Prerequisites, quick start, three-tier demo path
01 - Concepts Rotation theory, real breaches, NIST/SOC2/PCI/ISO/HIPAA framework controls
02 - Architecture Bus + plugin design, persistence layers, three-layer audit integrity, AEAD envelope
03 - Implementation Code-level walkthrough; where to look in source for each concept
04 - Challenges 10 ranked extension challenges (PG ALTER USER, Slack, ML-KEM, OpenTimestamps, SPIFFE, JIT broker, etc.)

License

AGPL 3.0