Enable HTTPS by default in development environment

This commit enables HTTPS in the development environment using self-signed
certificates to further improve dev-prod parity and catch SSL-related issues
early.

Changes made:

1. Created self-signed certificate for localhost
   - File: infrastructure/kubernetes/overlays/dev/dev-certificate.yaml
   - Type: Self-signed via cert-manager
   - Validity: 90 days (auto-renewed)
   - Valid for: localhost, bakery-ia.local, *.bakery-ia.local, 127.0.0.1
   - Issuer: selfsigned-issuer ClusterIssuer

2. Updated dev ingress to enable HTTPS
   - File: infrastructure/kubernetes/overlays/dev/dev-ingress.yaml
   - Enabled SSL redirect: ssl-redirect: false → true
   - Added TLS configuration with certificate
   - Updated CORS origins to prefer HTTPS (HTTPS URLs first, HTTP fallback)
   - Access: https://localhost (instead of http://localhost)

3. Added cert-manager resources to dev overlay
   - File: infrastructure/kubernetes/overlays/dev/kustomization.yaml
   - Added dev-certificate.yaml
   - Added selfsigned-issuer ClusterIssuer

4. Created comprehensive HTTPS setup guide
   - File: docs/DEV-HTTPS-SETUP.md
   - Includes certificate trust instructions for macOS, Linux, Windows
   - Testing procedures with curl and browsers
   - Troubleshooting guide
   - FAQ section

5. Updated dev-prod parity documentation
   - File: docs/DEV-PROD-PARITY-CHANGES.md
   - Added HTTPS as 4th improvement
   - Updated "What Stays Different" table (SSL/TLS → Certificates)
   - Added HTTPS benefits section

Benefits:
✓ Matches production HTTPS-only behavior
✓ Tests SSL/TLS configurations in development
✓ Catches mixed content warnings early
✓ Tests secure cookie handling (Secure, SameSite attributes)
✓ Validates cert-manager integration
✓ Tests certificate auto-renewal
✓ Better security testing capabilities

Impact:
- Browser will show certificate warning (self-signed)
- Users can trust certificate or click "Proceed"
- No additional resource usage
- Access via https://localhost (was http://localhost)

Certificate details:
- Type: Self-signed
- Algorithm: RSA 2048-bit
- Validity: 90 days
- Auto-renewal: 15 days before expiration
- Common Name: localhost
- DNS Names: localhost, bakery-ia.local, *.bakery-ia.local
- IP Addresses: 127.0.0.1, ::1

Setup required:
- Optional: Trust certificate in system/browser (see DEV-HTTPS-SETUP.md)
- Required: cert-manager must be installed in cluster
- Access at: https://localhost

What stays different from production:
- Certificate type: Self-signed (dev) vs Let's Encrypt (prod)
- Trust: Manual (dev) vs Automatic (prod)
- Domain: localhost (dev) vs real domain (prod)

This completes the dev-prod parity improvements, bringing development
environment much closer to production with:
1. 2 replicas for critical services ✓
2. Rate limiting enabled ✓
3. Specific CORS origins ✓
4. HTTPS enabled ✓

See docs/DEV-HTTPS-SETUP.md for complete setup and testing instructions.
This commit is contained in:
Claude
2026-01-02 19:25:45 +00:00
parent efa8984dad
commit 2ee4aa51e4
5 changed files with 464 additions and 4 deletions

337
docs/DEV-HTTPS-SETUP.md Normal file
View File

@@ -0,0 +1,337 @@
# HTTPS in Development Environment
## Overview
Development environment now uses HTTPS by default to match production behavior and catch SSL-related issues early.
**Benefits:**
- ✅ Matches production HTTPS behavior
- ✅ Tests SSL/TLS configurations
- ✅ Catches mixed content warnings
- ✅ Tests secure cookie handling
- ✅ Better dev-prod parity
---
## Quick Start
### 1. Deploy with HTTPS Enabled
```bash
# Start development environment
skaffold dev --profile=dev
# Wait for certificate to be issued
kubectl get certificate -n bakery-ia
# You should see:
# NAME READY SECRET AGE
# bakery-dev-tls-cert True bakery-dev-tls-cert 1m
```
### 2. Access Your Application
```bash
# Access via HTTPS (will show certificate warning in browser)
open https://localhost
# Or via curl (use -k to skip certificate verification)
curl -k https://localhost/api/health
```
---
## Trust the Self-Signed Certificate
To avoid browser certificate warnings, you need to trust the self-signed certificate.
### Option 1: Accept Browser Warning (Quick & Easy)
When you visit `https://localhost`:
1. Browser shows "Your connection is not private" or similar
2. Click "Advanced" or "Show details"
3. Click "Proceed to localhost" or "Accept the risk"
4. Certificate warning will appear on first visit only per browser session
### Option 2: Trust Certificate in System (Recommended)
#### On macOS:
```bash
# 1. Export the certificate from Kubernetes
kubectl get secret bakery-dev-tls-cert -n bakery-ia -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/bakery-dev-cert.crt
# 2. Add to Keychain
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /tmp/bakery-dev-cert.crt
# 3. Verify
security find-certificate -c localhost -a
# 4. Cleanup
rm /tmp/bakery-dev-cert.crt
```
**Alternative (GUI):**
1. Export certificate: `kubectl get secret bakery-dev-tls-cert -n bakery-ia -o jsonpath='{.data.tls\.crt}' | base64 -d > bakery-dev-cert.crt`
2. Double-click the `.crt` file to open Keychain Access
3. Find "localhost" certificate
4. Double-click → Trust → "Always Trust"
5. Close and enter your password
#### On Linux:
```bash
# 1. Export the certificate
kubectl get secret bakery-dev-tls-cert -n bakery-ia -o jsonpath='{.data.tls\.crt}' | base64 -d | sudo tee /usr/local/share/ca-certificates/bakery-dev.crt
# 2. Update CA certificates
sudo update-ca-certificates
# 3. For browsers (Chromium/Chrome)
mkdir -p $HOME/.pki/nssdb
certutil -d sql:$HOME/.pki/nssdb -A -t "P,," -n "Bakery Dev" -i /usr/local/share/ca-certificates/bakery-dev.crt
```
#### On Windows:
```powershell
# 1. Export the certificate
kubectl get secret bakery-dev-tls-cert -n bakery-ia -o jsonpath='{.data.tls.crt}' | Out-File -Encoding ASCII bakery-dev-cert.crt
# 2. Import to Trusted Root
Import-Certificate -FilePath .\bakery-dev-cert.crt -CertStoreLocation Cert:\LocalMachine\Root
# Or use GUI:
# - Double-click bakery-dev-cert.crt
# - Install Certificate
# - Store Location: Local Machine
# - Place in: Trusted Root Certification Authorities
```
---
## Testing HTTPS
### Test with curl
```bash
# Without certificate verification (quick test)
curl -k https://localhost/api/health
# With certificate verification (after trusting cert)
curl https://localhost/api/health
# Check certificate details
curl -vI https://localhost/api/health 2>&1 | grep -A 10 "Server certificate"
# Test CORS with HTTPS
curl -H "Origin: https://localhost:3000" \
-H "Access-Control-Request-Method: POST" \
-X OPTIONS https://localhost/api/health
```
### Test with Browser
1. Open `https://localhost`
2. Check for SSL/TLS padlock in address bar
3. Click padlock → View certificate
4. Verify:
- Issued to: localhost
- Issued by: localhost (self-signed)
- Valid for: 90 days
### Test Frontend
```bash
# Update your frontend .env to use HTTPS
echo "VITE_API_URL=https://localhost/api" > frontend/.env.local
# Frontend should now make HTTPS requests
```
---
## Certificate Details
### Certificate Specifications
- **Type**: Self-signed (for development)
- **Algorithm**: RSA 2048-bit
- **Validity**: 90 days (auto-renews 15 days before expiration)
- **Common Name**: localhost
- **DNS Names**:
- localhost
- bakery-ia.local
- api.bakery-ia.local
- *.bakery-ia.local
- **IP Addresses**: 127.0.0.1, ::1
### Certificate Issuer
- **Issuer**: `selfsigned-issuer` (cert-manager ClusterIssuer)
- **Auto-renewal**: Managed by cert-manager
- **Secret Name**: `bakery-dev-tls-cert`
---
## Troubleshooting
### Certificate Not Issued
```bash
# Check certificate status
kubectl describe certificate bakery-dev-tls-cert -n bakery-ia
# Check cert-manager logs
kubectl logs -n cert-manager deployment/cert-manager
# Check if cert-manager is installed
kubectl get pods -n cert-manager
# If cert-manager is not installed:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.2/cert-manager.yaml
```
### Certificate Warning in Browser
**Normal for self-signed certificates!** Choose one:
1. Click "Proceed" (quick, temporary)
2. Trust the certificate in your system (permanent)
### Mixed Content Warnings
If you see "mixed content" errors:
- Ensure all API calls use HTTPS
- Check for hardcoded HTTP URLs
- Update `VITE_API_URL` to use HTTPS
### Certificate Expired
```bash
# Check expiration
kubectl get certificate bakery-dev-tls-cert -n bakery-ia -o jsonpath='{.status.notAfter}'
# Force renewal
kubectl delete certificate bakery-dev-tls-cert -n bakery-ia
kubectl apply -k infrastructure/kubernetes/overlays/dev
# cert-manager will automatically recreate it
```
### Browser Shows "NET::ERR_CERT_AUTHORITY_INVALID"
This is expected for self-signed certificates. Options:
1. Click "Advanced" → "Proceed to localhost"
2. Trust the certificate (see instructions above)
3. Use curl with `-k` flag for testing
---
## Disable HTTPS (Not Recommended)
If you need to temporarily disable HTTPS:
```bash
# Edit dev-ingress.yaml
vim infrastructure/kubernetes/overlays/dev/dev-ingress.yaml
# Change:
# nginx.ingress.kubernetes.io/ssl-redirect: "true" → "false"
# nginx.ingress.kubernetes.io/force-ssl-redirect: "true" → "false"
# Comment out the tls section:
# tls:
# - hosts:
# - localhost
# secretName: bakery-dev-tls-cert
# Redeploy
skaffold dev --profile=dev
```
---
## Differences from Production
| Aspect | Development | Production |
|--------|-------------|------------|
| Certificate Type | Self-signed | Let's Encrypt |
| Validity | 90 days | 90 days |
| Auto-renewal | cert-manager | cert-manager |
| Trust | Manual trust needed | Automatically trusted |
| Domains | localhost | Real domains |
| Browser Warning | Yes (self-signed) | No (CA-signed) |
---
## FAQ
### Q: Why am I seeing certificate warnings?
**A:** Self-signed certificates aren't trusted by browsers by default. Trust the certificate or click "Proceed."
### Q: Do I need to trust the certificate?
**A:** No, but it makes development easier. You can click "Proceed" on each browser session.
### Q: Will this affect my frontend development?
**A:** Slightly. Update `VITE_API_URL` to use `https://`. Otherwise works the same.
### Q: Can I use HTTP instead?
**A:** Yes, but not recommended. It reduces dev-prod parity and won't catch HTTPS issues.
### Q: How often do I need to re-trust the certificate?
**A:** Only when the certificate is recreated (every 90 days or when you delete the cluster).
### Q: Does this work with bakery-ia.local?
**A:** Yes! The certificate is valid for both `localhost` and `bakery-ia.local`.
---
## Additional Security Testing
With HTTPS enabled, you can now test:
### 1. Secure Cookies
```javascript
// In your frontend
document.cookie = "session=test; Secure; SameSite=Strict";
```
### 2. Mixed Content Detection
```javascript
// This will show warning in dev (good - catches prod issues!)
fetch('http://api.example.com/data') // ❌ Mixed content
fetch('https://api.example.com/data') // ✅ Secure
```
### 3. HSTS (HTTP Strict Transport Security)
```bash
# Check HSTS headers
curl -I https://localhost/api/health | grep -i strict
```
### 4. TLS Version Testing
```bash
# Test TLS 1.2
curl --tlsv1.2 https://localhost/api/health
# Test TLS 1.3
curl --tlsv1.3 https://localhost/api/health
```
---
## Summary
**Enabled**: HTTPS in development by default
**Certificate**: Self-signed, auto-renewed
**Access**: `https://localhost`
**Trust**: Optional but recommended
**Benefit**: Better dev-prod parity
**Next Steps:**
1. Deploy: `skaffold dev --profile=dev`
2. Access: `https://localhost`
3. Trust: Follow instructions above (optional)
4. Test: Verify HTTPS works
For issues, see Troubleshooting section or check cert-manager logs.

View File

@@ -79,6 +79,57 @@ nginx.ingress.kubernetes.io/cors-allow-origin: "http://localhost,http://localhos
---
### 4. **Enabled HTTPS with Self-Signed Certificates**
**Files**:
- `infrastructure/kubernetes/overlays/dev/dev-ingress.yaml`
- `infrastructure/kubernetes/overlays/dev/dev-certificate.yaml`
- `infrastructure/kubernetes/overlays/dev/kustomization.yaml`
Changed:
```yaml
# Ingress
nginx.ingress.kubernetes.io/ssl-redirect: "false" → "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "false" → "true"
# Added TLS configuration
tls:
- hosts:
- localhost
- bakery-ia.local
secretName: bakery-dev-tls-cert
# Updated CORS to prefer HTTPS
cors-allow-origin: "https://localhost,https://localhost:3000,..." (HTTPS first)
```
**Why**:
- Matches production HTTPS-only behavior
- Tests SSL/TLS configurations in development
- Catches mixed content warnings early
- Tests secure cookie handling
- Validates certificate management
**Benefits**:
- SSL-related issues caught in development
- Tests cert-manager integration
- Secure cookie testing
- Mixed content detection
- Better security testing
**Certificate Details**:
- Type: Self-signed (via cert-manager)
- Validity: 90 days (auto-renewed)
- Common Name: localhost
- Also valid for: bakery-ia.local, *.bakery-ia.local
- Issuer: selfsigned-issuer
**Setup Required**:
- Trust certificate in browser/system (optional but recommended)
- See `docs/DEV-HTTPS-SETUP.md` for full instructions
---
## Resource Impact
### Before Option 1
@@ -107,7 +158,7 @@ These settings intentionally remain different from production:
| DEBUG | true | false | Need verbose debugging |
| LOG_LEVEL | DEBUG | INFO | Need detailed logs |
| PROFILING_ENABLED | true | false | Performance analysis |
| SSL/TLS | HTTP | HTTPS | Simpler local dev |
| Certificates | Self-signed | Let's Encrypt | Local CA for dev |
| Image Pull Policy | Never | Always | Faster iteration |
| Most replicas | 1 | 2-3 | Resource efficiency |
| Monitoring | Disabled | Enabled | Save resources |
@@ -132,6 +183,13 @@ These settings intentionally remain different from production:
- Middleware tested
- High limits prevent friction
### ✅ HTTPS/SSL Testing
- Matches production HTTPS-only behavior
- Tests certificate management
- Catches mixed content warnings
- Validates secure cookie handling
- Tests TLS configurations
### ✅ Resource Efficiency
- Only +30% resource usage
- Maximum benefit for minimal cost

View File

@@ -0,0 +1,51 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: bakery-dev-tls-cert
namespace: bakery-ia
spec:
# Self-signed certificate for local development
secretName: bakery-dev-tls-cert
# Certificate duration
duration: 2160h # 90 days
renewBefore: 360h # 15 days
# Subject configuration
subject:
organizations:
- Bakery IA Development
# Common name
commonName: localhost
# DNS names this certificate is valid for
dnsNames:
- localhost
- bakery-ia.local
- api.bakery-ia.local
- "*.bakery-ia.local"
# IP addresses (for localhost)
ipAddresses:
- 127.0.0.1
- ::1
# Use self-signed issuer for development
issuerRef:
name: selfsigned-issuer
kind: ClusterIssuer
group: cert-manager.io
# Private key configuration
privateKey:
algorithm: RSA
encoding: PKCS1
size: 2048
# Usages
usages:
- server auth
- client auth
- digital signature
- key encipherment

View File

@@ -4,16 +4,21 @@ metadata:
name: bakery-ingress
namespace: bakery-ia
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
# Dev-Prod Parity: Enable HTTPS by default
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
# Dev-Prod Parity: Use specific origins instead of wildcard to catch CORS issues early
nginx.ingress.kubernetes.io/cors-allow-origin: "http://localhost,http://localhost:3000,http://localhost:3001,http://127.0.0.1,http://127.0.0.1:3000,http://127.0.0.1:3001,http://bakery-ia.local,https://localhost,https://127.0.0.1"
# HTTPS origins first (preferred), with HTTP fallback for development flexibility
nginx.ingress.kubernetes.io/cors-allow-origin: "https://localhost,https://localhost:3000,https://localhost:3001,https://127.0.0.1,https://127.0.0.1:3000,https://127.0.0.1:3001,https://bakery-ia.local,http://localhost,http://localhost:3000,http://localhost:3001,http://127.0.0.1,http://127.0.0.1:3000"
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS, PATCH"
nginx.ingress.kubernetes.io/cors-allow-headers: "Content-Type, Authorization, X-Requested-With, Accept, Origin, Cache-Control"
nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
nginx.ingress.kubernetes.io/enable-cors: "true"
# Prevent nginx from redirecting to add trailing slashes
nginx.ingress.kubernetes.io/use-regex: "true"
# Development, SSE and WebSocket annotations
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
@@ -22,10 +27,16 @@ metadata:
nginx.ingress.kubernetes.io/proxy-buffering: "off"
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
nginx.ingress.kubernetes.io/upstream-keepalive-timeout: "3600"
# WebSocket upgrade support
nginx.ingress.kubernetes.io/websocket-services: "gateway-service"
spec:
ingressClassName: nginx
tls:
- hosts:
- localhost
- bakery-ia.local
secretName: bakery-dev-tls-cert
rules:
- host: localhost
http:

View File

@@ -12,6 +12,9 @@ resources:
# Monitoring disabled for dev to save resources
# - ../../base/components/monitoring
- dev-ingress.yaml
# Dev-Prod Parity: Enable HTTPS with self-signed certificates
- dev-certificate.yaml
- ../../base/components/cert-manager/cluster-issuer-staging.yaml
# Exclude nominatim from dev to save resources
# Using scale to 0 for StatefulSet to prevent pod creation