Part 12 — Projects
Four end-to-end projects that combine everything. Each has a brief, a deliverable, a "definition of done", and a grading rubric so you can judge your own work. Do them in order — each builds on the last, and all four target SecureShop (Chapter 4). Budget a weekend each, or a week at a relaxed pace.
Set up once:
~/sec-lab/
ca/ your PKI (Ch 29)
keys/ key material (Ch 22)
tools/ release-sign (Ch 22/26), tls-audit (Ch 32), monitors
reports/ one markdown report per Part -- you have 7 already
projects/
p1-ca-mtls/ p2-harden/ p3-breakfix/ p4-pq-audit/
Everything runs locally in containers or VMs you own (Chapter 0.6).
Chapter 53 — Project 1: your own CA, plus mutual TLS
Brief
Build the certificate authority that issues every certificate SecureShop uses — public-facing server certs, internal service certs, and client certs for mTLS — with a proper offline root, an online issuing intermediate, sane profiles, a revocation mechanism, and automated renewal. Then wire three services to use it and prove the trust decisions are correct.
Architecture
SecureShop Dev Root CA (offline: key encrypted,
ed25519, 10-year, CA:TRUE never on a networked box)
| signs ONE thing: the intermediate
v
SecureShop Issuing CA (online: does day-to-day
ecdsa P-256, 3-year, issuance; pathlen:0)
CA:TRUE, pathlen:0
/ | \
server profile client profile internal-service profile
EKU serverAuth EKU clientAuth EKU serverAuth+clientAuth
90-day, SANs 30-day, CN=identity 7-day, SPIFFE-style URI SAN
| | |
shop.securesh.op orders-api-client orders-api <-> postgres
www.securesh.op admin-console (both present + verify certs)
Tasks
1. ROOT
- ed25519 key, encrypted at rest (openssl pkey -aes-256-cbc).
- Self-signed cert, basicConstraints critical CA:TRUE,
keyUsage critical keyCertSign,cRLSign. 10 years.
- Store the passphrase in your secrets manager (Ch 49), not a file.
2. INTERMEDIATE
- ECDSA P-256 key. CSR -> signed by root with pathlen:0.
- This is the only key the issuing scripts touch.
3. PROFILES (an openssl.cnf or a small `issue` script per type)
- server: SAN(s) required; EKU serverAuth; 90d; CA:FALSE.
- client: CN = a stable identity; EKU clientAuth; 30d.
- service: URI SAN spiffe://securesh.op/ns/default/sa/<name>;
EKU serverAuth,clientAuth; 7d.
- Reject requests that don't match the profile (e.g. server cert
with no SAN -> refuse).
4. REVOCATION
- Maintain a CRL (openssl ca -gencrl) OR run a tiny OCSP responder
(openssl ocsp -index ...). Revoke a test cert; prove clients
reject it.
5. WIRE IT UP
- nginx/Caddy for shop.securesh.op with the server cert; HTTP->HTTPS
redirect; TLS 1.3+1.2, AEAD only; HSTS.
- /admin location: require a client cert (verify against the
intermediate); no cert -> 403.
- orders-api <-> postgres: mutual TLS, each verifies the other's
service cert against your CA (Postgres: ssl=on, clientcert=verify-full).
6. RENEWAL
- A cron/systemd-timer script that reissues any cert < 1/3 of its
lifetime remaining, reloads the service, and alerts on failure.
- Bonus: stand up `step-ca` or Vault PKI and drive it via ACME so
renewal is fully hands-off.
7. VERIFY (write results into the report)
- openssl verify -show_chain for each leaf.
- curl --cacert (works), curl (fails, cert error), curl -k (works
but note the danger).
- testssl.sh / sslyze against shop.securesh.op -> A-equivalent.
- mTLS: connection with/without a valid client cert.
- Revoke -> client rejects. Expire (issue with -days 1, wait) ->
client rejects.
- Break the chain (omit intermediate) -> curl fails; explain why a
browser might not.
Deliverable
~/sec-lab/projects/p1-ca-mtls/ containing: the CA scripts/config, the service
configs, the renewal script, and report.md with every verification above
(command + output + one-line explanation), plus a diagram of your PKI and the
trust decisions.
Definition of done
- Root is offline and its key is encrypted; you can state exactly where the passphrase lives.
- Every leaf verifies to the root through the intermediate.
- Each profile enforces its constraints (a wrong-shaped request is refused).
-
shop.securesh.opscores A-equivalent on testssl.sh/sslyze. -
/adminis reachable only with a valid client cert. -
orders-api↔Postgres is mutually authenticated. - Revocation works (CRL or OCSP), demonstrated.
- Renewal is automated and alerts on failure.
Rubric (score yourself /100)
PKI structure & key protection ........... 20
Correct, enforced cert profiles ......... 15
Server TLS hardening (A-grade) .......... 15
mTLS on /admin and service-to-service ... 20
Revocation implemented & proven ......... 10
Automated renewal + alerting ........... 10
Report quality (evidence + reasoning) .. 10
Further reading
- Book (free): OpenSSL Cookbook, Ivan Ristić — the CA and CSR chapters.
- Docs: smallstep
step-ca"Run your own private CA" tutorial; HashiCorp Vault PKI secrets engine;mkcert(for the "just works locally" contrast). - Article: "Everything you should know about certificates and PKI" — Mike Malone (smallstep).
- Spec: SPIFFE/SPIRE docs — the URI-SAN service-identity model.
- Guidance: CA/Browser Forum Baseline Requirements (skim the profile sections to see what "real" cert profiles constrain).