fix: remove committed private keys and add runtime cert generation (closes #12)
Some checks failed
CI/CD Pipeline / Code Format (push) Successful in 3s
CI/CD Pipeline / Clippy Lints (push) Successful in 43s
CI/CD Pipeline / All Unit Tests (push) Successful in 1m12s
CI/CD Pipeline / Security Audit (push) Successful in 5s
CI/CD Pipeline / Enrollment Tests (push) Successful in 1m12s
CI/CD Pipeline / Build Debian Package (Ubuntu 22.04) (push) Failing after 4s
CI/CD Pipeline / Verify Enrollment CLI Flag (push) Successful in 57s
CI/CD Pipeline / Build Debian Package (push) Failing after 4s
CI/CD Pipeline / Build RPM Package (push) Successful in 2m12s
CI/CD Pipeline / Build Arch Package (push) Successful in 2m18s
CI/CD Pipeline / Build Alpine Package (push) Failing after 3m7s
Some checks failed
CI/CD Pipeline / Code Format (push) Successful in 3s
CI/CD Pipeline / Clippy Lints (push) Successful in 43s
CI/CD Pipeline / All Unit Tests (push) Successful in 1m12s
CI/CD Pipeline / Security Audit (push) Successful in 5s
CI/CD Pipeline / Enrollment Tests (push) Successful in 1m12s
CI/CD Pipeline / Build Debian Package (Ubuntu 22.04) (push) Failing after 4s
CI/CD Pipeline / Verify Enrollment CLI Flag (push) Successful in 57s
CI/CD Pipeline / Build Debian Package (push) Failing after 4s
CI/CD Pipeline / Build RPM Package (push) Successful in 2m12s
CI/CD Pipeline / Build Arch Package (push) Successful in 2m18s
CI/CD Pipeline / Build Alpine Package (push) Failing after 3m7s
- Remove all private key files from git tracking (git rm --cached) - configs/certs/ca.key.pem, server.key.pem, client001.key.pem - tests/e2e/certs/client.key - Also remove public certs from configs/certs/ (generated at runtime) - Add .gitignore patterns for *.key, *.key.pem, configs/certs/*.pem, *.srl - Add scripts/generate-dev-certs.sh for runtime test cert generation - Update Python e2e test to generate certs on demand (ensure_certs()) - Update test_wrong_cert_connection to generate wrong-CA certs at runtime - Add gitleaks secret scanning job to CI workflow - Update SECURITY_FINDINGS_REPORT.md with critical finding for Issue #12 - Update SECURITY_CONTROLS_MATRIX.md evidence references - Add README.md to configs/certs/ and tests/e2e/certs/ Private keys were dev/test only - no production key rotation needed. Git history purge with filter-repo will follow after PR merge. Co-authored-by: git-echo <git-echo@moon-dragon.us>
This commit is contained in:
committed by
GitHub
parent
d0c0790cbf
commit
efaac33c47
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@ -61,6 +61,18 @@ jobs:
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: cargo install cargo-audit && cargo audit --ignore RUSTSEC-2025-0134
|
||||
|
||||
gitleaks:
|
||||
name: Secret scanning
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Gitleaks
|
||||
uses: gitleaks/gitleaks-action@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
enrollment-tests:
|
||||
name: Enrollment Tests
|
||||
needs: [fmt, clippy]
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@ -14,5 +14,12 @@ debian/linux-patch-api.substvars
|
||||
*.buildinfo
|
||||
*.changes
|
||||
|
||||
# Private key material - NEVER commit
|
||||
*.key
|
||||
*.key.pem
|
||||
configs/certs/*.pem
|
||||
configs/certs/*.srl
|
||||
tests/e2e/certs/*.key
|
||||
|
||||
# Agent Zero project data
|
||||
.a0proj/
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
| **SPEC.md Reference** | Lines 132-138 |
|
||||
| **Requirement** | Internal self-hosted CA for certificate issuance |
|
||||
| **Implementation** | OpenSSL CA infrastructure with 4096-bit RSA keys |
|
||||
| **Evidence** | `configs/CA_SETUP.md`, `configs/certs/ca.pem`, `configs/certs/ca.key.pem` |
|
||||
| **Evidence** | `configs/CA_SETUP.md`, `scripts/generate-dev-certs.sh` (private keys generated at runtime, not committed) |
|
||||
| **Test Result** | ✅ PASS - CA properly signs server and client certificates |
|
||||
| **Compliance Status** | ✅ COMPLIANT |
|
||||
|
||||
@ -52,7 +52,7 @@
|
||||
| **SPEC.md Reference** | Line 136 |
|
||||
| **Requirement** | Unique certificate per client (no shared certs) |
|
||||
| **Implementation** | Per-client certificate generation with unique CN |
|
||||
| **Evidence** | `configs/certs/client001.pem`, `SECURITY.md` line 65 |
|
||||
| **Evidence** | `scripts/generate-dev-certs.sh` (certificates generated at runtime, not committed) |
|
||||
| **Test Result** | ✅ PASS - Each client has distinct certificate |
|
||||
| **Compliance Status** | ✅ COMPLIANT |
|
||||
|
||||
@ -63,7 +63,7 @@
|
||||
| **SPEC.md Reference** | Line 135 |
|
||||
| **Requirement** | 1 year standard certificate expiration |
|
||||
| **Implementation** | Certificates generated with `-days 365` parameter |
|
||||
| **Evidence** | `configs/certs/` certificate files, `openssl x509 -in cert.pem -noout -dates` |
|
||||
| **Evidence** | `scripts/generate-dev-certs.sh` (certificates generated at runtime, not committed) |
|
||||
| **Test Result** | ✅ PASS - Expired certificates properly rejected (FUZZ_TEST_REPORT.md Test 3.2) |
|
||||
| **Compliance Status** | ✅ COMPLIANT |
|
||||
|
||||
@ -137,7 +137,7 @@
|
||||
| **SPEC.md Reference** | Lines 86-89 |
|
||||
| **Requirement** | Private key permissions 600 (owner read/write only) |
|
||||
| **Implementation** | File permissions set during certificate deployment |
|
||||
| **Evidence** | `configs/certs/*.key.pem` (chmod 600), `DEPLOYMENT_SECURITY_GUIDE.md` Section 1 |
|
||||
| **Evidence** | Private keys generated at runtime with `chmod 600` by `scripts/generate-dev-certs.sh`, not committed to repository |
|
||||
| **Test Result** | ✅ PASS - Key files properly protected |
|
||||
| **Compliance Status** | ✅ COMPLIANT |
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
| **Total Tests** | 16 |
|
||||
| **Passed** | 16 |
|
||||
| **Failed** | 0 |
|
||||
| **Critical Findings** | 0 (Previously 1 - RESOLVED) |
|
||||
| **Critical Findings** | 1 (Issue #12 - Committed Private Keys - RESOLVED) |
|
||||
| **High Findings** | 0 (Previously 2 - RESOLVED) |
|
||||
| **Medium Findings** | 3 (Unchanged) |
|
||||
| **Low Findings** | 4 (Unchanged) |
|
||||
@ -150,6 +150,36 @@ Consider storing CA key on separate, more secure host.
|
||||
|
||||
---
|
||||
|
||||
### 🔴 CRITICAL: Committed Private Key Material (Issue #12)
|
||||
|
||||
**Description:**
|
||||
Private key files (`*.key`, `*.key.pem`) were committed to version control in:
|
||||
- `configs/certs/ca.key.pem` — CA private key
|
||||
- `configs/certs/server.key.pem` — Server private key
|
||||
- `configs/certs/client001.key.pem` — Client private key
|
||||
- `tests/e2e/certs/client.key` — E2E test client private key
|
||||
|
||||
Committed private keys are a critical security risk: anyone with repository access
|
||||
(even read-only) can impersonate the server or clients, decrypt captured TLS traffic,
|
||||
or forge certificates signed by the CA.
|
||||
|
||||
**Status:** ✅ RESOLVED
|
||||
|
||||
**Remediation Applied:**
|
||||
1. Removed all private key files from git tracking (`git rm --cached`)
|
||||
2. Added `*.key`, `*.key.pem`, `configs/certs/`, and `tests/e2e/certs/*.key` to `.gitignore`
|
||||
3. Created `scripts/generate-dev-certs.sh` to generate test certificates at runtime
|
||||
4. Updated e2e tests to generate certificates on demand instead of loading from disk
|
||||
5. Added `gitleaks` secret scanning to CI pipeline
|
||||
6. Git history will be purged with `git filter-repo` after PR merge
|
||||
|
||||
**Key Rotation:**
|
||||
These keys were used for development/testing only. No production key rotation is needed.
|
||||
All committed keys should be considered compromised and must not be used in any
|
||||
production environment.
|
||||
|
||||
---
|
||||
|
||||
### 🟢 LOW: No Automated Security Scanning
|
||||
|
||||
**Description:**
|
||||
|
||||
33
configs/certs/README.md
Normal file
33
configs/certs/README.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Development Certificates
|
||||
|
||||
**⚠️ Private keys are NOT committed to version control.**
|
||||
|
||||
This directory is used for local development certificates only. Private key
|
||||
files (`*.key`, `*.key.pem`) are excluded from git via `.gitignore`.
|
||||
|
||||
## Generating Development Certificates
|
||||
|
||||
Run the generation script from the repository root:
|
||||
|
||||
```bash
|
||||
./scripts/generate-dev-certs.sh
|
||||
```
|
||||
|
||||
This creates:
|
||||
- `ca.pem` / `ca.key.pem` — Internal CA certificate and key
|
||||
- `server.pem` / `server.key.pem` — Server certificate and key
|
||||
- `client001.pem` / `client001.key.pem` — Client certificate and key
|
||||
- `tests/e2e/certs/` — E2E test certificates
|
||||
|
||||
## Production Deployments
|
||||
|
||||
Production deployments should use certificates issued by the organisation's
|
||||
internal CA. The `install.sh` script and systemd unit handle production
|
||||
certificate paths at `/etc/linux_patch_api/certs/`.
|
||||
|
||||
## Security
|
||||
|
||||
- **Never commit private keys** (`*.key`, `*.key.pem`) to version control
|
||||
- Private keys must have `0600` permissions in production
|
||||
- The `gitleaks` CI check scans for accidentally committed secrets
|
||||
- See `SECURITY_FINDINGS_REPORT.md` and `SECURITY.md` for full details
|
||||
@ -1,5 +0,0 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg46Ewu04V/qVbFIaW
|
||||
ll6hUNA1ocfdND68cRv6GiOBikyhRANCAARORR0UUR6G6ndxeefpKai+82eH58ud
|
||||
sW5qox3Ed4I0WF12RcSwioAPrt5WNB+ptw0wvzx78wH8CdkqjyUb7Koc
|
||||
-----END PRIVATE KEY-----
|
||||
@ -1,12 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBsTCCAVegAwIBAgIQVxQmz3/uqfSgf+8ukKa6GTAKBggqhkjOPQQDAjA4MR4w
|
||||
HAYDVQQDDBVQYXRjaCBNYW5hZ2VyIFJvb3QgQ0ExFjAUBgNVBAoMDVBhdGNoIE1h
|
||||
bmFnZXIwHhcNMjYwNTE4MTU1MjUxWhcNMzYwNTE1MTU1MjUxWjA4MR4wHAYDVQQD
|
||||
DBVQYXRjaCBNYW5hZ2VyIFJvb3QgQ0ExFjAUBgNVBAoMDVBhdGNoIE1hbmFnZXIw
|
||||
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARORR0UUR6G6ndxeefpKai+82eH58ud
|
||||
sW5qox3Ed4I0WF12RcSwioAPrt5WNB+ptw0wvzx78wH8CdkqjyUb7Koco0MwQTAP
|
||||
BgNVHQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBTcLRFILwfBbjqUm3fT8AzIAN5mQDAP
|
||||
BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIQDMHR7n6plBEz7tP9Si
|
||||
Cs6Rk8m2gt9CL6qHlkeWiDJmtgIgVXrj2Lmqn1dEuKbVu9LaxPyvXU4/t2etWHgJ
|
||||
lfK+SS8=
|
||||
-----END CERTIFICATE-----
|
||||
@ -1 +0,0 @@
|
||||
790CDB9FA2002BF59B3EE88AF326CB060353D113
|
||||
@ -1,8 +0,0 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIH7MIGhAgEAMD8xHTAbBgNVBAMMFHBhdGNoLW1hbmFnZXItY2xpZW50MREwDwYD
|
||||
VQQKDAhJbnRlcm5hbDELMAkGA1UEBhMCVVMwWTATBgcqhkjOPQIBBggqhkjOPQMB
|
||||
BwNCAAQauACwaR4SyVoHEPviQgV0I4fbyFuGoHiQExzpYf9Ta025dy88T/a6qG6G
|
||||
TYJMtbRSjP/piLWfZ/2ze2AdbmczoAAwCgYIKoZIzj0EAwIDSQAwRgIhAJ/BBYsB
|
||||
aIhKjdwRr0vTqtYPKeeyO2rzHuyRnSvKKkdOAiEA94zCvG0FzkFiqGKT1oHGCVf9
|
||||
qZdkjkodRAUk6/4S2AU=
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
@ -1,5 +0,0 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg0iNlJbfLqO8Y5sOh
|
||||
1xRe2bPq8fF9M1ybEOnqmbSGpdGhRANCAAQauACwaR4SyVoHEPviQgV0I4fbyFuG
|
||||
oHiQExzpYf9Ta025dy88T/a6qG6GTYJMtbRSjP/piLWfZ/2ze2Adbmcz
|
||||
-----END PRIVATE KEY-----
|
||||
@ -1,12 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBuzCCAWGgAwIBAgIUeQzbn6IAK/WbPuiK8ybLBgNT0RMwCgYIKoZIzj0EAwIw
|
||||
ODEeMBwGA1UEAwwVUGF0Y2ggTWFuYWdlciBSb290IENBMRYwFAYDVQQKDA1QYXRj
|
||||
aCBNYW5hZ2VyMB4XDTI2MDUxODE2MDAwNloXDTI3MDUxODE2MDAwNlowPzEdMBsG
|
||||
A1UEAwwUcGF0Y2gtbWFuYWdlci1jbGllbnQxETAPBgNVBAoMCEludGVybmFsMQsw
|
||||
CQYDVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBq4ALBpHhLJWgcQ
|
||||
++JCBXQjh9vIW4ageJATHOlh/1NrTbl3LzxP9rqoboZNgky1tFKM/+mItZ9n/bN7
|
||||
YB1uZzOjQjBAMB0GA1UdDgQWBBQhTcmoHT0HqIuEUkL891TKMlWWjjAfBgNVHSME
|
||||
GDAWgBTcLRFILwfBbjqUm3fT8AzIAN5mQDAKBggqhkjOPQQDAgNIADBFAiApQ6N8
|
||||
qQR1vWLU3QNrcIwLxK8g2shV5ggypS/CKkfTgwIhAJdZd0silwqEpPo5ng0I5SJ9
|
||||
MOd4Kx0dps2kY/wqgMSI
|
||||
-----END CERTIFICATE-----
|
||||
@ -1,8 +0,0 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIH1MIGcAgEAMDoxGDAWBgNVBAMMD2xpbnV4LXBhdGNoLWFwaTERMA8GA1UECgwI
|
||||
SW50ZXJuYWwxCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
|
||||
C32xU18H3OljGW+wQUesT1qSB+bp5cCkNW9rfpv7wjr79eHriZkQ8EgrdVAK9Zw0
|
||||
fZJNdd4LyekDGXiQU/qAJ6AAMAoGCCqGSM49BAMCA0gAMEUCIQDf7FSy4YiZvWkj
|
||||
G9BgdSPTcIq8VYSGm7nnXprD8u1ZTwIgO6/5jH72reiCaaMm62X1Vrpc+8SDMVtO
|
||||
+dlP4dZ+BM8=
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
@ -1,5 +0,0 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKWrGjaMdvANVPz/d
|
||||
LQPtDS4FmU8H0gg8zix2AvxaQp2hRANCAAQLfbFTXwfc6WMZb7BBR6xPWpIH5unl
|
||||
wKQ1b2t+m/vCOvv14euJmRDwSCt1UAr1nDR9kk113gvJ6QMZeJBT+oAn
|
||||
-----END PRIVATE KEY-----
|
||||
@ -1,12 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBtjCCAVygAwIBAgIUeQzbn6IAK/WbPuiK8ybLBgNT0RIwCgYIKoZIzj0EAwIw
|
||||
ODEeMBwGA1UEAwwVUGF0Y2ggTWFuYWdlciBSb290IENBMRYwFAYDVQQKDA1QYXRj
|
||||
aCBNYW5hZ2VyMB4XDTI2MDUxODE2MDAwNloXDTI3MDUxODE2MDAwNlowOjEYMBYG
|
||||
A1UEAwwPbGludXgtcGF0Y2gtYXBpMREwDwYDVQQKDAhJbnRlcm5hbDELMAkGA1UE
|
||||
BhMCVVMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQLfbFTXwfc6WMZb7BBR6xP
|
||||
WpIH5unlwKQ1b2t+m/vCOvv14euJmRDwSCt1UAr1nDR9kk113gvJ6QMZeJBT+oAn
|
||||
o0IwQDAdBgNVHQ4EFgQUDTnKCjj1BJ0MdwJHPUGf0raJ6/kwHwYDVR0jBBgwFoAU
|
||||
3C0RSC8HwW46lJt30/AMyADeZkAwCgYIKoZIzj0EAwIDSAAwRQIhAJ4jy8W2hbqK
|
||||
kiTI9aYS+xwMJlxH6cFJaKplrA+a5Ay8AiANPJdJN9ucgCsq/N3Ai6kO89rcXy8Z
|
||||
60kvNNc3Zg/Oog==
|
||||
-----END CERTIFICATE-----
|
||||
82
scripts/generate-dev-certs.sh
Executable file
82
scripts/generate-dev-certs.sh
Executable file
@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env bash
|
||||
# Generate development/test certificates for Linux Patch API.
|
||||
#
|
||||
# This script creates a self-signed CA, server certificate, and client
|
||||
# certificate suitable for local development and testing. It is NOT
|
||||
# intended for production use.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/generate-dev-certs.sh [OUTPUT_DIR]
|
||||
#
|
||||
# If OUTPUT_DIR is omitted, certificates are written to configs/certs/
|
||||
# relative to the repository root. The e2e Python test certs are also
|
||||
# regenerated under tests/e2e/certs/.
|
||||
#
|
||||
# Private keys (*.key, *.key.pem) are excluded from git via .gitignore
|
||||
# and must NEVER be committed to version control.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
OUTPUT_DIR="${1:-$REPO_ROOT/configs/certs}"
|
||||
E2E_DIR="$REPO_ROOT/tests/e2e/certs"
|
||||
|
||||
DAYS_CA=3650
|
||||
DAYS_CERT=365
|
||||
|
||||
echo "Generating development certificates..."
|
||||
echo " Output dir: $OUTPUT_DIR"
|
||||
echo " E2E dir: $E2E_DIR"
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
mkdir -p "$E2E_DIR"
|
||||
|
||||
# CA
|
||||
echo "[1/6] Generating CA key and certificate..."
|
||||
openssl genrsa -out "$OUTPUT_DIR/ca.key.pem" 4096 2>/dev/null
|
||||
chmod 600 "$OUTPUT_DIR/ca.key.pem"
|
||||
openssl req -x509 -new -nodes -key "$OUTPUT_DIR/ca.key.pem" -sha256 -days "$DAYS_CA" -out "$OUTPUT_DIR/ca.pem" -subj "/CN=LinuxPatchAPI Dev CA/O=Internal/C=US"
|
||||
|
||||
# Server certificate
|
||||
echo "[2/6] Generating server key and certificate..."
|
||||
openssl genrsa -out "$OUTPUT_DIR/server.key.pem" 2048 2>/dev/null
|
||||
chmod 600 "$OUTPUT_DIR/server.key.pem"
|
||||
openssl req -new -key "$OUTPUT_DIR/server.key.pem" -out "$OUTPUT_DIR/server.csr.pem" -subj "/CN=localhost/O=Internal/C=US"
|
||||
openssl x509 -req -in "$OUTPUT_DIR/server.csr.pem" -CA "$OUTPUT_DIR/ca.pem" -CAkey "$OUTPUT_DIR/ca.key.pem" -CAcreateserial -out "$OUTPUT_DIR/server.pem" -days "$DAYS_CERT" -sha256
|
||||
|
||||
# Client certificate
|
||||
echo "[3/6] Generating client key and certificate..."
|
||||
openssl genrsa -out "$OUTPUT_DIR/client001.key.pem" 2048 2>/dev/null
|
||||
chmod 600 "$OUTPUT_DIR/client001.key.pem"
|
||||
openssl req -new -key "$OUTPUT_DIR/client001.key.pem" -out "$OUTPUT_DIR/client001.csr.pem" -subj "/CN=client001/O=Internal/C=US"
|
||||
openssl x509 -req -in "$OUTPUT_DIR/client001.csr.pem" -CA "$OUTPUT_DIR/ca.pem" -CAkey "$OUTPUT_DIR/ca.key.pem" -CAcreateserial -out "$OUTPUT_DIR/client001.pem" -days "$DAYS_CERT" -sha256
|
||||
|
||||
# E2E test certificates
|
||||
echo "[4/6] Generating e2e test CA certificate..."
|
||||
cp "$OUTPUT_DIR/ca.pem" "$E2E_DIR/ca.crt"
|
||||
|
||||
echo "[5/6] Generating e2e test client certificate..."
|
||||
openssl genrsa -out "$E2E_DIR/client.key" 2048 2>/dev/null
|
||||
chmod 600 "$E2E_DIR/client.key"
|
||||
openssl req -new -key "$E2E_DIR/client.key" -out "$E2E_DIR/client.csr" -subj "/CN=e2e-test-client/O=Internal/C=US"
|
||||
openssl x509 -req -in "$E2E_DIR/client.csr" -CA "$OUTPUT_DIR/ca.pem" -CAkey "$OUTPUT_DIR/ca.key.pem" -CAcreateserial -out "$E2E_DIR/client.crt" -days "$DAYS_CERT" -sha256
|
||||
|
||||
# Cleanup CSR files
|
||||
echo "[6/6] Cleaning up CSR files..."
|
||||
rm -f "$OUTPUT_DIR/server.csr.pem" "$OUTPUT_DIR/client001.csr.pem" "$E2E_DIR/client.csr"
|
||||
|
||||
echo
|
||||
echo "Development certificates generated successfully."
|
||||
echo " CA cert: $OUTPUT_DIR/ca.pem"
|
||||
echo " Server cert: $OUTPUT_DIR/server.pem"
|
||||
echo " Server key: $OUTPUT_DIR/server.key.pem"
|
||||
echo " Client cert: $OUTPUT_DIR/client001.pem"
|
||||
echo " Client key: $OUTPUT_DIR/client001.key.pem"
|
||||
echo " E2E CA cert: $E2E_DIR/ca.crt"
|
||||
echo " E2E client cert: $E2E_DIR/client.crt"
|
||||
echo " E2E client key: $E2E_DIR/client.key"
|
||||
echo
|
||||
echo "⚠ WARNING: These are development-only certificates. Do NOT use in production."
|
||||
echo "⚠ Private keys (*.key, *.key.pem) are excluded from git via .gitignore."
|
||||
24
tests/e2e/certs/README.md
Normal file
24
tests/e2e/certs/README.md
Normal file
@ -0,0 +1,24 @@
|
||||
# E2E Test Certificates
|
||||
|
||||
**⚠️ Private keys are NOT committed to version control.**
|
||||
|
||||
This directory holds mTLS certificates used by the Python E2E test suite.
|
||||
The `client.key` private key is excluded from git via `.gitignore`.
|
||||
|
||||
## Generating Test Certificates
|
||||
|
||||
Certificates are generated automatically by the E2E test runner, or manually:
|
||||
|
||||
```bash
|
||||
./scripts/generate-dev-certs.sh
|
||||
```
|
||||
|
||||
This script creates `ca.crt`, `client.crt`, and `client.key` in this directory.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Committed | Description |
|
||||
|------|-----------|-------------|
|
||||
| `ca.crt` | ✅ Yes | CA certificate (public) |
|
||||
| `client.crt` | ✅ Yes | Client certificate (public) |
|
||||
| `client.key` | ❌ No | Client private key (gitignored) |
|
||||
@ -1,5 +0,0 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg5dkQDY44tZkcnQ6M
|
||||
lGDNFyFrEvcOlnDoKfA/uTvBCtehRANCAAT8X1WUWE52l/i2I3MmlSiPgrESEJ2R
|
||||
I6CJvV2hHKirY+wJbanH39b1ebW8b+W3fuhEHPaFFcpPFEnPriA+xWvT
|
||||
-----END PRIVATE KEY-----
|
||||
@ -16,6 +16,7 @@ Usage:
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from dataclasses import dataclass, field
|
||||
@ -37,6 +38,19 @@ CA_CERT = CERTS_DIR / "ca.crt"
|
||||
CLIENT_CERT = CERTS_DIR / "client.crt"
|
||||
CLIENT_KEY = CERTS_DIR / "client.key"
|
||||
|
||||
|
||||
def ensure_certs() -> None:
|
||||
"""Generate e2e test certificates at runtime if they do not exist."""
|
||||
if CLIENT_KEY.exists() and CLIENT_CERT.exists() and CA_CERT.exists():
|
||||
return
|
||||
script = Path(__file__).resolve().parent.parent.parent / "scripts" / "generate-dev-certs.sh"
|
||||
if not script.exists():
|
||||
raise FileNotFoundError(
|
||||
f"Certificate generation script not found: {script}. "
|
||||
"Run ./scripts/generate-dev-certs.sh manually."
|
||||
)
|
||||
subprocess.check_call([str(script)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
TARGETS = {
|
||||
"dev": {
|
||||
"name": "linux-patch-manager-dev",
|
||||
@ -537,14 +551,51 @@ def test_wrong_cert_connection(client: PatchAPIClient) -> str:
|
||||
"""Verify that connections with wrong cert are rejected.
|
||||
|
||||
Per spec: invalid/expired certificates should be silently dropped.
|
||||
Uses project test certs (different CA) which should be rejected.
|
||||
Generates a separate "wrong CA" certificate at runtime to test that
|
||||
certificates signed by an untrusted CA are rejected.
|
||||
"""
|
||||
project_ca = "/a0/usr/projects/linux_patch_api/configs/certs/ca.pem"
|
||||
project_cert = "/a0/usr/projects/linux_patch_api/configs/certs/client001.pem"
|
||||
project_key = "/a0/usr/projects/linux_patch_api/configs/certs/client001.key.pem"
|
||||
import tempfile
|
||||
wrong_ca_dir = Path(tempfile.mkdtemp(prefix="lpa-wrong-ca-"))
|
||||
try:
|
||||
# Generate a completely separate CA + client cert (wrong CA)
|
||||
wrong_ca_key = wrong_ca_dir / "ca.key"
|
||||
wrong_ca_cert = wrong_ca_dir / "ca.crt"
|
||||
wrong_client_key = wrong_ca_dir / "client.key"
|
||||
wrong_client_csr = wrong_ca_dir / "client.csr"
|
||||
wrong_client_cert = wrong_ca_dir / "client.crt"
|
||||
|
||||
if not Path(project_ca).exists():
|
||||
return "SKIPPED: Project test certs not available"
|
||||
subprocess.run(
|
||||
["openssl", "genrsa", "-out", str(wrong_ca_key), "2048"],
|
||||
check=True, capture_output=True,
|
||||
)
|
||||
subprocess.run(
|
||||
["openssl", "req", "-x509", "-new", "-nodes", "-key", str(wrong_ca_key),
|
||||
"-sha256", "-days", "1", "-out", str(wrong_ca_cert),
|
||||
"-subj", "/CN=Wrong CA/O=Attacker/C=US"],
|
||||
check=True, capture_output=True,
|
||||
)
|
||||
subprocess.run(
|
||||
["openssl", "genrsa", "-out", str(wrong_client_key), "2048"],
|
||||
check=True, capture_output=True,
|
||||
)
|
||||
subprocess.run(
|
||||
["openssl", "req", "-new", "-key", str(wrong_client_key),
|
||||
"-out", str(wrong_client_csr),
|
||||
"-subj", "/CN=wrong-client/O=Attacker/C=US"],
|
||||
check=True, capture_output=True,
|
||||
)
|
||||
subprocess.run(
|
||||
["openssl", "x509", "-req", "-in", str(wrong_client_csr),
|
||||
"-CA", str(wrong_ca_cert), "-CAkey", str(wrong_ca_key),
|
||||
"-CAcreateserial", "-out", str(wrong_client_cert),
|
||||
"-days", "1", "-sha256"],
|
||||
check=True, capture_output=True,
|
||||
)
|
||||
|
||||
project_cert = str(wrong_client_cert)
|
||||
project_key = str(wrong_client_key)
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
return "SKIPPED: Could not generate wrong-CA test certificates"
|
||||
|
||||
session = requests.Session()
|
||||
session.cert = (project_cert, project_key)
|
||||
@ -559,6 +610,8 @@ def test_wrong_cert_connection(client: PatchAPIClient) -> str:
|
||||
return "Correctly rejected connection with untrusted client certificate"
|
||||
finally:
|
||||
session.close()
|
||||
import shutil
|
||||
shutil.rmtree(wrong_ca_dir, ignore_errors=True)
|
||||
|
||||
|
||||
def test_job_lifecycle(client: PatchAPIClient) -> str:
|
||||
@ -776,7 +829,10 @@ def main():
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Verify certs exist
|
||||
# Generate certs at runtime if missing (private keys are not committed)
|
||||
ensure_certs()
|
||||
|
||||
# Verify certs exist after generation attempt
|
||||
if not CA_CERT.exists():
|
||||
print(f"ERROR: CA cert not found: {CA_CERT}")
|
||||
sys.exit(1)
|
||||
|
||||
Reference in New Issue
Block a user