test-certs.sh 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. #!/usr/bin/env bash
  2. # test-certs.sh — Generate throwaway test certs for unit tests in
  3. # internal/auth/mtls_test.go. NOT for production. Creates a temp
  4. # self-signed CA + a few leaf certs (valid, expired, wrong-CN, untrusted)
  5. # in scripts/cert-manager/testdata/ so tests can read them with
  6. # crypto/x509.ReadFile.
  7. #
  8. # Usage:
  9. # scripts/cert-manager/test-certs.sh
  10. # scripts/cert-manager/test-certs.sh clean # remove testdata
  11. #
  12. # Idempotent: regenerates the dir each run.
  13. set -euo pipefail
  14. TESTDATA="$(dirname "$0")/testdata"
  15. case "${1:-}" in
  16. clean)
  17. rm -rf "$TESTDATA"
  18. echo "removed $TESTDATA"
  19. exit 0
  20. ;;
  21. "")
  22. ;;
  23. *)
  24. echo "usage: $0 [clean]" >&2
  25. exit 1
  26. ;;
  27. esac
  28. rm -rf "$TESTDATA"
  29. mkdir -p "$TESTDATA"
  30. # --- Test CA (self-signed) -------------------------------------------------
  31. TEST_CA_KEY="$TESTDATA/test-ca.key"
  32. TEST_CA_CRT="$TESTDATA/test-ca.crt"
  33. openssl genrsa -out "$TEST_CA_KEY" 2048 2>/dev/null
  34. openssl req -new -x509 -key "$TEST_CA_KEY" -sha256 -days 365 \
  35. -subj "/CN=broad-announce Test CA/O=test/OU=test" \
  36. -addext "basicConstraints=critical,CA:TRUE" \
  37. -out "$TEST_CA_CRT"
  38. # Helper: generate a leaf signed by the test CA
  39. gen_leaf() {
  40. local name="$1" cn="$2" san="$3" days="$4"
  41. local key="$TESTDATA/${name}.key"
  42. local csr="$TESTDATA/${name}.csr"
  43. local crt="$TESTDATA/${name}.crt"
  44. openssl genrsa -out "$key" 2048 2>/dev/null
  45. if [[ -n "$san" ]]; then
  46. openssl req -new -key "$key" -sha256 \
  47. -subj "/CN=$cn/O=test/OU=test" \
  48. -addext "subjectAltName=$san" \
  49. -out "$csr"
  50. else
  51. openssl req -new -key "$key" -sha256 \
  52. -subj "/CN=$cn/O=test/OU=test" \
  53. -out "$csr"
  54. fi
  55. # Build extfile: include subjectAltName only if non-empty
  56. local extfile
  57. if [[ -n "$san" ]]; then
  58. extfile=$(mktemp)
  59. cat > "$extfile" <<EOF
  60. basicConstraints=CA:FALSE
  61. keyUsage=critical,digitalSignature,keyEncipherment
  62. extendedKeyUsage=clientAuth
  63. subjectAltName=$san
  64. EOF
  65. else
  66. extfile=$(mktemp)
  67. cat > "$extfile" <<'EOF'
  68. basicConstraints=CA:FALSE
  69. keyUsage=critical,digitalSignature,keyEncipherment
  70. extendedKeyUsage=clientAuth
  71. EOF
  72. fi
  73. openssl x509 -req -in "$csr" -CA "$TEST_CA_CRT" -CAkey "$TEST_CA_KEY" \
  74. -CAcreateserial -sha256 -days "$days" \
  75. -extfile "$extfile" \
  76. -out "$crt"
  77. rm -f "$extfile"
  78. }
  79. # --- Test fixtures ---------------------------------------------------------
  80. # source_cn format: source:<source_id>.<company_slug>
  81. gen_leaf "valid" "source:src-123.acme-001" "DNS:source.src-123.acme-001" 30
  82. gen_leaf "wrong-cn" "source:src-999.acme-001" "DNS:source.src-999.acme-001" 30
  83. gen_leaf "no-san" "source:src-789.acme-001" "" 30
  84. # Note: "expired" is generated in-memory in internal/auth/mtls_test.go
  85. # (template.NotAfter in the past), not as a file fixture, because openssl
  86. # refuses to sign certs with end-before-start dates.
  87. # An UNTRUSTED leaf — signed by a different CA
  88. UNTRUSTED_KEY="$TESTDATA/untrusted-ca.key"
  89. UNTRUSTED_CRT="$TESTDATA/untrusted-ca.crt"
  90. UNTRUSTED_LEAF_KEY="$TESTDATA/untrusted.key"
  91. UNTRUSTED_LEAF_CRT="$TESTDATA/untrusted.crt"
  92. openssl genrsa -out "$UNTRUSTED_KEY" 2048 2>/dev/null
  93. openssl req -new -x509 -key "$UNTRUSTED_KEY" -sha256 -days 30 \
  94. -subj "/CN=untrusted-attacker/O=evil/OU=evil" \
  95. -addext "basicConstraints=critical,CA:TRUE" \
  96. -out "$UNTRUSTED_CRT"
  97. openssl genrsa -out "$UNTRUSTED_LEAF_KEY" 2048 2>/dev/null
  98. openssl req -new -key "$UNTRUSTED_LEAF_KEY" -sha256 \
  99. -subj "/CN=source:src-123.acme-001/O=evil/OU=evil" \
  100. -addext "subjectAltName=DNS:source.src-123.acme-001" \
  101. -out "$TESTDATA/untrusted.csr"
  102. openssl x509 -req -in "$TESTDATA/untrusted.csr" -CA "$UNTRUSTED_CRT" -CAkey "$UNTRUSTED_KEY" \
  103. -CAcreateserial -sha256 -days 30 \
  104. -extfile <(echo "extendedKeyUsage=clientAuth") \
  105. -out "$UNTRUSTED_LEAF_CRT"
  106. # --- Write a manifest for the tests ----------------------------------------
  107. cat > "$TESTDATA/MANIFEST.txt" <<EOF
  108. test-ca.crt — self-signed test CA (the trust anchor for tests)
  109. test-ca.key — test CA private key (DO NOT use in prod)
  110. valid.{key,crt} — leaf with correct CN/SAN, valid 30d
  111. wrong-cn.{key,crt} — leaf with correct format but wrong source_id in CN
  112. no-san.{key,crt} — leaf with correct CN but no SAN
  113. untrusted.{key,crt} — leaf signed by an UNTRUSTED CA (simulates attacker)
  114. untrusted-ca.{key,crt} — the attacker's CA
  115. Note: "expired" fixture is generated in-memory in
  116. internal/auth/mtls_test.go (template.NotAfter in the past), not as a
  117. file fixture, because openssl refuses to sign certs with
  118. end-before-start dates.
  119. Production uses scripts/cert-manager/ca-init.sh to generate the real PKI.
  120. This dir is .gitignored.
  121. EOF
  122. echo "test certs generated in $TESTDATA"
  123. ls "$TESTDATA" | head