Files
notifier/docs/CORS_KUBERNETES_GUIDE.md

358 lines
8.6 KiB
Markdown

# CORS Configuration for Kubernetes Deployments
## Important: CORS Only Affects REST API, Not gRPC
**Key Point:** CORS (Cross-Origin Resource Sharing) is a **browser security mechanism** that only applies to HTTP/REST APIs accessed from web browsers. It does **NOT** affect gRPC communication.
## Service-to-Service Communication (gRPC)
### ✅ No CORS Configuration Needed
For services running in the same Kubernetes cluster communicating via gRPC:
**You don't need to configure CORS at all!**
Here's why:
1. **gRPC uses HTTP/2** - CORS is not enforced
2. **Server-to-Server** - CORS only applies to browser-based requests
3. **No Origin header** - Backend services don't send Origin headers
### gRPC Client Connection Example
```go
// Service A connecting to notifier service via gRPC
conn, err := grpc.Dial("notifier-grpc.default.svc.cluster.local:50051",
grpc.WithInsecure())
if err != nil {
log.Fatalf("Failed to connect: %v", err)
}
defer conn.Close()
client := pb.NewNotifierServiceClient(conn)
```
**No CORS configuration required**
The gRPC service runs on port `50051` (as defined in your k8s/service.yaml) and is accessible at:
- From same namespace: `notifier-grpc:50051`
- From other namespace: `notifier-grpc.default.svc.cluster.local:50051`
- Full DNS: `notifier-grpc.default.svc.cluster.local:50051`
## When You DO Need CORS Configuration
CORS configuration is **only** needed when:
1. **Web browsers** access the REST API (port 8080)
2. The web app is served from a **different origin** than the API
3. The request goes through the **REST API**, not gRPC
### Common Scenarios
| Scenario | Needs CORS? | Reason |
|----------|-------------|---------|
| Backend service → gRPC API | ❌ No | Not a browser, uses gRPC |
| Backend service → REST API | ❌ No | Not a browser |
| Web app → gRPC (grpc-web) | ✅ Yes | Browser-based, needs CORS |
| Web app → REST API | ✅ Yes | Browser-based, needs CORS |
| CLI tool → REST API | ❌ No | Not a browser |
| Postman/curl → REST API | ❌ No | Not a browser |
## CORS Configuration for Web Applications
If you have a web frontend that calls the REST API, you need to configure CORS.
### Scenario 1: Frontend in Same Cluster
**Example:** Frontend at `https://app.example.com`, API at `https://notifier.example.com`
Update your ConfigMap (k8s/configmap.yaml):
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: notifier-config
data:
config.yaml: |
# ... existing config ...
cors:
allowed_origins:
- "https://app.example.com" # Your frontend domain
allowed_methods:
- "GET"
- "POST"
- "OPTIONS"
- "DELETE"
allowed_headers:
- "Content-Type"
- "Authorization"
allow_credentials: true # If your frontend sends auth tokens
max_age: 3600
```
### Scenario 2: Multiple Frontends
```yaml
cors:
allowed_origins:
- "https://app.example.com" # Main app
- "https://dashboard.example.com" # Admin dashboard
- "https://mobile.example.com" # Mobile web app
allowed_methods:
- "GET"
- "POST"
- "OPTIONS"
- "DELETE"
allowed_headers:
- "Content-Type"
- "Authorization"
allow_credentials: true
max_age: 3600
```
### Scenario 3: Development Environment
For local development (frontend at http://localhost:3000):
```yaml
cors:
allowed_origins:
- "http://localhost:3000" # React/Next.js dev server
- "http://localhost:8080" # Vue/Angular dev server
- "http://localhost:5173" # Vite dev server
- "https://app.example.com" # Production frontend
allowed_methods:
- "GET"
- "POST"
- "OPTIONS"
- "DELETE"
allowed_headers:
- "Content-Type"
- "Authorization"
allow_credentials: false
max_age: 3600
```
### Scenario 4: No Web Frontend (Backend Services Only)
If you only have backend services using gRPC:
```yaml
cors:
# Empty or minimal config - no origins needed
allowed_origins: [] # No browser clients
allowed_methods:
- "GET"
- "POST"
allowed_headers:
- "Content-Type"
```
The REST API will still work for non-browser clients (like curl, Postman, or backend HTTP clients), but browsers will be blocked unless their origin is whitelisted.
## Complete Kubernetes Configuration Example
Here's your updated k8s/configmap.yaml with CORS:
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: notifier-config
labels:
app: notifier
data:
config.yaml: |
server:
grpc_port: 50051
rest_port: 8080
host: "0.0.0.0"
mode: "both"
queue:
type: "local"
max_size: 10000
worker_count: 10
retry_attempts: 3
retry_backoff: "exponential"
local:
buffer_size: 1000
persist_to_disk: false
notifiers:
stdout: true
logging:
level: "info"
format: "json"
output_path: "stdout"
metrics:
enabled: true
port: 9090
path: "/metrics"
prometheus_enabled: true
health_check:
enabled: true
port: 8081
path: "/health"
interval: 30
# CORS configuration for REST API
# Only needed if web browsers will access the REST API
cors:
# Add your frontend domains here
# For backend-only services, leave empty
allowed_origins: []
# Or if you have a web frontend:
# allowed_origins:
# - "https://app.example.com"
allowed_methods:
- "GET"
- "POST"
- "OPTIONS"
- "DELETE"
allowed_headers:
- "Content-Type"
- "Authorization"
allow_credentials: false
max_age: 3600
```
## Environment-Specific Configuration
You can also use environment variables for different environments:
### Development ConfigMap
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: notifier-config-dev
namespace: development
data:
NOTIFIER_CORS_ALLOWED_ORIGINS: "http://localhost:3000,http://localhost:8080"
NOTIFIER_CORS_ALLOW_CREDENTIALS: "false"
```
### Production ConfigMap
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: notifier-config-prod
namespace: production
data:
NOTIFIER_CORS_ALLOWED_ORIGINS: "https://app.example.com,https://dashboard.example.com"
NOTIFIER_CORS_ALLOW_CREDENTIALS: "true"
```
## Troubleshooting
### "CORS error" in Browser Console
**Symptom:**
```
Access to fetch at 'https://notifier.example.com/api/v1/notifications'
from origin 'https://app.example.com' has been blocked by CORS policy
```
**Solution:**
Add `https://app.example.com` to `allowed_origins` in your ConfigMap.
### Backend Service Can't Connect
**Symptom:**
```go
// Service in cluster trying to connect
conn, err := grpc.Dial("notifier-grpc:50051", grpc.WithInsecure())
// Error: connection refused
```
**Solution:**
This is NOT a CORS issue. Check:
1. Service name is correct (`notifier-grpc`)
2. Port is correct (`50051`)
3. Service is running: `kubectl get pods -l app=notifier`
4. Service endpoints: `kubectl get endpoints notifier-grpc`
### REST API Returns 200 but No CORS Headers
**Symptom:**
Browser blocks the response even though the API returns 200 OK.
**Solution:**
The origin is not in the whitelist. Check:
1. Origin is exactly matching (including protocol and port)
2. ConfigMap has been updated
3. Pod has been restarted to pick up new config:
```bash
kubectl rollout restart deployment notifier
```
## Quick Reference
### For Backend Services Only (Recommended)
```yaml
cors:
allowed_origins: [] # Empty - no browser access needed
```
### For Web Frontend + Backend Services
```yaml
cors:
allowed_origins:
- "https://your-frontend-domain.com"
allow_credentials: true # If using auth tokens
```
### Applying Changes
After updating the ConfigMap:
```bash
# Update ConfigMap
kubectl apply -f k8s/configmap.yaml
# Restart pods to pick up new config
kubectl rollout restart deployment notifier
# Verify pods are running
kubectl get pods -l app=notifier
# Check logs for CORS configuration
kubectl logs -l app=notifier | grep CORS
```
You should see:
```
CORS enabled for origins: [https://app.example.com]
```
Or if no origins configured:
```
CORS has no allowed origins configured - all cross-origin requests will be blocked
```
## Summary
**For your use case (services in Kubernetes using gRPC):**
**You don't need to configure CORS at all!**
CORS only applies to web browsers accessing the REST API. Your backend services communicating via gRPC are completely unaffected by CORS configuration.
**Only add CORS configuration if:**
- You have a web frontend (React, Vue, Angular, etc.)
- That web frontend calls the REST API (not gRPC)
- The frontend is served from a different domain than the API
For purely backend service-to-service communication in Kubernetes, CORS is irrelevant.