A microservices platform demonstrating Security by Design principles for connected aviation telemetry systems.
This project started as a way to consolidate security patterns I've used across different organizations. Instead of writing another "how to secure your API" article, I wanted a working codebase where every control can be tested and verified. The aviation telemetry scenario is fictional, but the security implementation is production-grade.
What this proves: End-to-end Security Engineering, from threat model to signed container in production-ready Kubernetes, with full observability and audit trail.
Evaluate in 15 minutes:
- Threat Model → docs/THREAT_MODEL.md (STRIDE, 30+ threats, mitigations)
- CI/CD Pipeline → .github/workflows/ci.yml (SAST → DAST → SBOM → Cosign)
- K8s Policies → kubernetes/skylink/templates/networkpolicy.yaml (zero-trust)
Verify controls work (after make up):
- RBAC denial →
curl -H "Authorization: Bearer $TOKEN" /admin/→ 403 + audit event - Idempotency → same event twice → 201 then 200
- Rate limit → 61 requests/min → 429 +
rate_limit_exceeded_totalincrements
Hiring relevance: Security Engineering Lead · Platform Security · DevSecOps Director
|
Authentication & Authorization
|
DevSecOps Pipeline
|
|
Privacy & Data Protection
|
Kubernetes Production-Ready
|
I built this to show how Security by Design actually works in practice. Not just documentation, but working code with real security controls you can run and test.
Who is this for?
| Audience | Value |
|---|---|
| Security Engineers | Reference architecture for threat modeling and security controls |
| Architects | Template for secure microservices design |
| DevOps/Platform Teams | Secure CI/CD pipeline with SAST, SCA, DAST, SBOM, and image signing |
What makes it different?
- It runs. Docker Compose + Kubernetes Helm chart, not just diagrams
- Security controls have tests. 478 of them, including OWASP Top 10 scenarios
- Covers the full lifecycle: threat model through signed container
SkyLink simulates a connected aircraft telemetry platform where:
- Aircraft send real-time telemetry data (GPS position, speed, altitude)
- Crew members access weather forecasts and contact information
- Ground systems receive and process telemetry for flight monitoring
This aviation context justifies strict security requirements:
| Requirement | Justification |
|---|---|
| Strong Authentication | Only authorized aircraft can transmit data |
| Role-Based Access Control | 5 roles with least-privilege permissions |
| Data Integrity | Telemetry must be tamper-proof (idempotency, checksums) |
| Privacy Protection | GPS coordinates rounded, PII minimized in logs |
| Audit Trail | All security events logged for compliance |
| High Availability | Rate limiting prevents DoS, circuit breakers for resilience |
Note: This is a fictional scenario for educational purposes. The security controls demonstrated are applicable to any API-based microservices architecture.
SkyLink is a demonstration platform for connected aircraft services, built with security as a foundational principle. This project showcases practical Security by Design implementations:
- Multi-layer authentication (JWT RS256 + mTLS)
- Role-Based Access Control (5 roles, 7 permissions, principle of least privilege)
- Defense in depth (rate limiting, payload limits, strict validation)
- Privacy by Design (PII minimization, structured logging without sensitive data)
- Secure CI/CD pipeline (SAST, SCA, DAST, SBOM, image signing)
Internet
│
┌────────────────────────────────┴────────────────────────────────┐
│ API GATEWAY (:8000) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Security │ │ Rate │ │ JWT RS256 │ │
│ │ Headers │ │ Limiting │ │ Authentication │ │
│ │ (OWASP) │ │ (slowapi) │ │ + mTLS Validation │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Payload │ │ Structured │ │ Prometheus │ │
│ │ Limit (64KB) │ │ JSON Logging │ │ Metrics │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
└─────────────┬──────────────┬──────────────┬─────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ TELEMETRY │ │ WEATHER │ │ CONTACTS │
│ :8001 │ │ :8002 │ │ :8003 │
│ │ │ │ │ │
│ • Idempotent│ │ • Demo mode │ │ • OAuth 2.0 │
│ • GPS round │ │ • Fixtures │ │ • PostgreSQL│
│ • 201/200/ │ │ │ │ • Encrypted │
│ 409 │ │ │ │ tokens │
└─────────────┘ └─────────────┘ └──────┬──────┘
│
▼
┌─────────────┐
│ PostgreSQL │
│ :5432 │
└─────────────┘
| Layer | Mechanism | Implementation |
|---|---|---|
| Transport | mTLS (Mutual TLS) | X.509 client certificates, CA validation |
| Application | JWT RS256 | 2048-bit RSA keys, 15-min expiry, audience validation |
| Cross-Validation | CN ↔ JWT sub | Certificate CN must match JWT subject |
| Authorization | RBAC | 5 roles, 7 permissions, principle of least privilege |
Implementation: skylink/auth.py, skylink/mtls.py, skylink/rbac.py
| Control | Description | Implementation |
|---|---|---|
| Rate Limiting | Per-identity throttling | 60 req/min per aircraft_id (skylink/rate_limit.py) |
| Payload Limits | DoS protection | 64 KB max request size |
| Input Validation | Strict schema enforcement | Pydantic extra="forbid", OpenAPI additionalProperties: false |
| Idempotency | Replay attack mitigation | Unique (aircraft_id, event_id) constraint |
Implementation: skylink/middlewares.py
| Data | Protection | Details |
|---|---|---|
| GPS Coordinates | Rounding | 4 decimals (~11m accuracy) |
| Logs | Sanitization | No PII, only trace_id for correlation |
| OAuth Tokens | Encryption | AES-GCM encryption at rest |
All responses include security headers (see skylink/middlewares.py):
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Referrer-Policy: no-referrer
Permissions-Policy: geolocation=(), microphone=(), camera=()| Feature | Description | Documentation |
|---|---|---|
| Structured JSON Logging | W3C trace correlation (X-Trace-Id) |
middlewares.py |
| Prometheus Metrics | Counters, histograms, gauges | /metrics endpoint |
| Grafana Dashboards | Pre-configured security dashboard | MONITORING.md |
| Audit Logging | Security-relevant event tracking | AUDIT_LOGGING.md |
| Alert Rules | 14 security alerts (auth, rate limit, errors) | security.yml |
# Start monitoring stack
docker compose --profile monitoring up -d
# Access dashboards
# Grafana: http://localhost:3000 (admin/admin)
# Prometheus: http://localhost:9090Secure cryptographic key management with rotation scripts:
| Key Type | Algorithm | Rotation Script |
|---|---|---|
| JWT Signing | RS256 (2048-bit) | scripts/rotate_jwt_keys.sh |
| Token Encryption | AES-256-GCM | scripts/rotate_encryption_key.sh |
| mTLS Certificates | X.509 | scripts/renew_certificates.sh |
See KEY_MANAGEMENT.md for rotation procedures and compliance.
CI/CD pipeline with security gates at every stage:
- GitHub Actions: .github/workflows/ci.yml — See setup guide
- GitLab CI: .gitlab-ci.yml — See setup guide
┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────────────┐ ┌───────┐
│ LINT │──▶│ TEST │──▶│ BUILD │──▶│ SCAN │──▶│ SBOM │──▶│ SECURITY-SCAN │──▶│ SIGN │
└───────┘ └───────┘ └───────┘ └───────┘ └───────┘ └───────────────┘ └───────┘
| Tool | Purpose | Stage |
|---|---|---|
| Ruff | Python linting | lint |
| Black | Code formatting | lint |
| Bandit | SAST (security linting) | lint |
| pytest | Unit tests (478 tests, 81% coverage) | test |
| Trivy | Container vulnerability scanning | scan |
| pip-audit | Python dependency SCA | scan |
| Gitleaks | Secret detection | scan |
| OpenAPI Generator | OpenAPI spec validation | scan |
| CycloneDX | SBOM generation | sbom |
| OWASP ZAP | DAST baseline scan | security-scan |
| Cosign | Image signing & SBOM attestation | sign |
Images are signed using Sigstore Cosign with keyless signing (OIDC) and SBOM attestation:
# Verify image signature (keyless)
cosign verify \
--certificate-identity-regexp="https://github.com/laugiov/security-by-design" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
ghcr.io/laugiov/security-by-design:latest
# Verify SBOM attestation
cosign verify-attestation \
--certificate-identity-regexp="https://github.com/laugiov/security-by-design" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
--type cyclonedx \
ghcr.io/laugiov/security-by-design:latestProduction-ready Helm chart with security best practices:
# Deploy to Kubernetes
helm install skylink ./kubernetes/skylink \
--namespace skylink --create-namespace \
-f kubernetes/skylink/values-prod.yaml| Security Feature | Implementation |
|---|---|
| Pod Security | Restricted profile (non-root, read-only fs, drop ALL capabilities) |
| Network Policies | Zero-trust default deny, explicit allow rules |
| Secrets | External Secrets Operator integration |
| Availability | HPA (auto-scaling), PDB (disruption budget) |
| Observability | ServiceMonitor for Prometheus Operator |
See docs/KUBERNETES.md for complete deployment guide.
- Docker & Docker Compose
- OpenSSL (for key generation)
- curl, jq (optional, for testing)
git clone <repo-url> skylink
cd skylink
# Copy environment template
cp .env.example .env
# Generate RSA keys for JWT signing
openssl genrsa -out /tmp/private.pem 2048
openssl rsa -in /tmp/private.pem -pubout -out /tmp/public.pem
# Add keys to .env
echo "PRIVATE_KEY_PEM=\"$(cat /tmp/private.pem)\"" >> .env
echo "PUBLIC_KEY_PEM=\"$(cat /tmp/public.pem)\"" >> .envmake build && make up
# Verify health
make health# Get a JWT token
AIRCRAFT_ID=$(uuidgen)
TOKEN=$(curl -s -X POST http://localhost:8000/auth/token \
-H "Content-Type: application/json" \
-d "{\"aircraft_id\": \"$AIRCRAFT_ID\"}" | jq -r '.access_token')
echo "Token: ${TOKEN:0:50}..."EVENT_ID=$(uuidgen)
# First request: 201 Created
curl -s -X POST http://localhost:8000/telemetry/ingest \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"aircraft_id\": \"$AIRCRAFT_ID\",
\"event_id\": \"$EVENT_ID\",
\"ts\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",
\"metrics\": {\"speed\": 450.5, \"gps\": {\"lat\": 48.8566, \"lon\": 2.3522}}
}"
# Response: {"status": "created", "event_id": "..."}
# Same request again: 200 OK (idempotent duplicate)
# Same event_id with different data: 409 Conflict| Method | Endpoint | Description | Auth |
|---|---|---|---|
GET |
/health |
Health check | No |
GET |
/metrics |
Prometheus metrics | No |
POST |
/auth/token |
Obtain JWT token | No |
POST |
/telemetry/ingest |
Ingest telemetry data | JWT + RBAC (telemetry:write) |
GET |
/weather/current |
Current weather | JWT + RBAC (weather:read) |
GET |
/contacts/ |
List contacts | JWT + RBAC (contacts:read) |
| Code | Meaning |
|---|---|
200 |
Success / Idempotent duplicate |
201 |
Created |
400 |
Validation error |
401 |
Unauthorized (missing/invalid JWT) |
403 |
Forbidden (mTLS CN ≠ JWT sub, or RBAC permission denied) |
409 |
Conflict (idempotency violation) |
413 |
Payload too large |
429 |
Rate limit exceeded |
skylink/
├── openapi/ # OpenAPI specifications (Contract-First)
├── skylink/ # API Gateway (port 8000)
│ ├── main.py # FastAPI application
│ ├── auth.py # JWT RS256 authentication
│ ├── mtls.py # mTLS configuration
│ ├── middlewares.py # Security headers, logging, payload limit
│ ├── rate_limit.py # Rate limiting (slowapi)
│ ├── rbac.py # Role-Based Access Control
│ ├── rbac_roles.py # Role and permission definitions
│ ├── config.py # Configuration management
│ └── routers/ # API endpoints
├── telemetry/ # Telemetry service (port 8001)
├── weather/ # Weather service (port 8002)
├── contacts/ # Contacts service (port 8003)
├── scripts/ # PKI & utility scripts
├── tests/ # Test suite
├── kubernetes/ # Kubernetes Helm chart
│ └── skylink/ # Helm chart with security policies
├── docs/ # Documentation
│ ├── DEMO.md # Demo guide
│ ├── KUBERNETES.md # Kubernetes deployment guide
│ ├── TECHNICAL_DOCUMENTATION.md # Technical documentation
│ ├── GITHUB_CI_SETUP.md # GitHub Actions setup guide
│ └── GITLAB_CI_SETUP.md # GitLab CI/CD setup guide
├── Dockerfile.* # Multi-stage Dockerfiles (non-root user)
├── docker-compose.yml # Orchestration
├── .gitlab-ci.yml # GitLab CI/CD pipeline
└── .github/workflows/ci.yml # GitHub Actions pipeline
| Document | Description |
|---|---|
| docs/THREAT_MODEL.md | STRIDE-based threat analysis and risk assessment |
| docs/SECURITY_ARCHITECTURE.md | Data flow diagrams, trust boundaries, security controls |
| docs/MONITORING.md | Security monitoring with Prometheus and Grafana |
| docs/KEY_MANAGEMENT.md | Cryptographic key management, rotation procedures, compliance |
| docs/AUDIT_LOGGING.md | Audit event logging, security event tracking, compliance |
| docs/AUTHORIZATION.md | Role-Based Access Control (RBAC), permissions, role matrix |
| docs/KUBERNETES.md | Kubernetes deployment with Helm, security policies, operations |
| docs/DEMO.md | Step-by-step demonstration walkthrough |
| docs/TECHNICAL_DOCUMENTATION.md | Complete technical documentation (architecture, security, RRA) |
| docs/GITHUB_CI_SETUP.md | GitHub Actions CI/CD setup guide (secrets, variables, workflow) |
| docs/GITLAB_CI_SETUP.md | GitLab CI/CD setup guide (variables, registry, pipeline) |
| Component | Technology | Version |
|---|---|---|
| Language | Python | 3.12 |
| Framework | FastAPI | ^0.120 |
| ASGI Server | Uvicorn | ^0.27 |
| Authentication | PyJWT | ^2.8 |
| Validation | Pydantic | ^2.10 |
| Rate Limiting | slowapi | ^0.1.9 |
| Metrics | prometheus-fastapi-instrumentator | ^7.0 |
| Database | PostgreSQL | 16 |
| ORM | SQLAlchemy | ^2.0 |
| Containers | Docker | 24+ |
# Run all tests
make test
# Or with poetry
poetry run pytest478 tests with 81% coverage — covering authentication, RBAC authorization, rate limiting, input validation, idempotency, OWASP Top 10 security tests, security headers, error handling, and service integration.
- Threat Modeling — STRIDE analysis in docs/THREAT_MODEL.md
- Strict Input Validation — Pydantic
extra="forbid", reject unknown fields - JWT RS256 Authentication — Short TTL (15 min), audience validation
- RBAC Authorization — 5 roles, 7 permissions, least privilege principle
- mTLS Cross-Validation — Certificate CN must match JWT subject
- Rate Limiting — Per-identity throttling with Prometheus counter
- OWASP Top 10 Security Tests — 97 tests covering injection, XSS, access control, etc.
- Security Headers — OWASP recommended set
- Structured Logging — JSON format, no PII, trace_id correlation
- SAST — Bandit security linting
- SCA — pip-audit dependency scanning
- Container Scanning — Trivy (fail on HIGH/CRITICAL)
- Secret Detection — Gitleaks
- DAST — OWASP ZAP baseline scan
- SBOM Generation — CycloneDX format
- Image Signing — Cosign with SBOM attestation
- Non-root Containers — User
skylink:1000 - Secrets Management — Environment variables, never in code
- Kubernetes Security — Pod Security Restricted, NetworkPolicies, External Secrets
| Category | Status | Evidence |
|---|---|---|
| Threat Modeling | ✅ | THREAT_MODEL.md — STRIDE, 30+ threats |
| Security Architecture | ✅ | SECURITY_ARCHITECTURE.md — DFD, trust boundaries |
| Authentication | ✅ | test_auth*.py, test_mtls*.py — 45+ tests |
| Authorization | ✅ | AUTHORIZATION.md — 5 roles, 7 permissions |
| Monitoring & Alerting | ✅ | MONITORING.md — 14 alert rules |
| Audit Logging | ✅ | AUDIT_LOGGING.md — 20 event types |
| Key Management | ✅ | KEY_MANAGEMENT.md — rotation scripts |
| Supply Chain Security | ✅ | CI pipeline — SBOM, Cosign, Trivy |
| Kubernetes Security | ✅ | KUBERNETES.md — Pod Security Restricted |
| Control | OWASP ASVS | NIST SSDF | SLSA | Zero Trust |
|---|---|---|---|---|
| Threat Modeling (STRIDE) | V1.1 | PO.1 | — | — |
| JWT RS256 + mTLS | V3.5, V9.1 | PS.1 | — | Identity verification |
| RBAC (least privilege) | V4.1 | PS.1 | — | Explicit access |
| Input validation | V5.1 | PW.5 | — | Never trust input |
| SAST/DAST/SCA | V14.2 | PW.7, PW.8 | L1 | — |
| SBOM + signing | V14.2 | PS.3 | L2 | — |
| Container hardening | V14.1 | PO.5 | — | Assume breach |
| NetworkPolicies | — | PO.5 | — | Micro-segmentation |
| Audit logging | V7.1 | PW.9 | — | Continuous monitoring |
The aviation scenario is just a context. The patterns work for any API-based system:
| Domain | Relevant Controls |
|---|---|
| SaaS / API Platform | JWT auth, RBAC, rate limiting, audit trail |
| Fintech / Regulated | Threat model, key rotation, encryption, compliance logging |
| Multi-tenant | NetworkPolicies for isolation, per-identity rate limiting |
New to this project? Follow this recommended learning path:
1. UNDERSTAND THE RISKS
└── Read docs/THREAT_MODEL.md
└── STRIDE analysis, threat scenarios
2. EXPLORE THE ARCHITECTURE
└── Read docs/SECURITY_ARCHITECTURE.md
└── Data flow diagrams, trust boundaries
3. HANDS-ON DEMO
└── Follow docs/DEMO.md step by step
└── JWT auth, rate limiting, idempotency
4. DEEP DIVE INTO CODE
└── Explore skylink/ source code
└── Security comments explain each control
5. REVIEW OPERATIONAL SECURITY
└── docs/MONITORING.md → Prometheus/Grafana
└── docs/AUDIT_LOGGING.md → Security events
└── docs/KEY_MANAGEMENT.md → Key rotation
See CONTRIBUTING.md for guidelines.
Laurent Giovannoni, 20+ years scaling SaaS platforms as CTO/VP Engineering
This reflects how I approach security in practice: CI/CD gates that don't block developers, RBAC that scales, observable systems that auditors can actually verify. I've done this work across multiple organizations. The patterns here come from real production experience, not just theory.
Security issues? See SECURITY.md. Please use GitHub Security Advisories, not LinkedIn.
MIT License. See LICENSE for details.