proxy-detection/IP_DETECTION_K8S_SETUP.md

314 lines
No EOL
9.3 KiB
Markdown

# Real IP Detection in Kubernetes - Complete Setup Guide
This document summarizes all the steps taken to successfully configure real IP detection for the proxy detection API in a Kubernetes cluster with MetalLB LoadBalancer.
## Problem Statement
Initially, the API was only receiving internal Kubernetes cluster IPs instead of real client IPs when deployed in Kubernetes. The application needed to detect the actual client IP addresses through various proxy headers while running behind:
- MetalLB LoadBalancer
- Nginx Ingress Controller
- Kubernetes Service mesh
## Solution Overview
We implemented a multi-layered approach to ensure proper IP detection:
1. **Kubernetes Infrastructure Configuration**
2. **Application-Level IP Detection**
3. **Ingress and LoadBalancer Optimization**
---
## 1. Kubernetes Infrastructure Setup
### MetalLB LoadBalancer Configuration
**Issue**: The MetalLB LoadBalancer was not properly preserving client IPs.
**Solution**:
- Identified LoadBalancer external IP: `192.95.29.118`
- Configured `externalTrafficPolicy: Local` to preserve source IPs
- Corrected namespace from `ingress-nginx` to `ingress`
```yaml
# k8s-service.yaml
apiVersion: v1
kind: Service
metadata:
name: proxy-detection-service
namespace: default
spec:
type: LoadBalancer
externalTrafficPolicy: Local # Critical for IP preservation
ports:
- port: 80
targetPort: 2424
protocol: TCP
selector:
app: proxy-detection
```
### Ingress Controller Configuration
**Issue**: Nginx Ingress was not configured to pass real client IPs.
**Solution**: Enhanced ingress configuration with custom annotations and ConfigMap.
```yaml
# k8s-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: proxy-detection-ingress
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/use-real-ip: "true"
nginx.ingress.kubernetes.io/real-ip-header: "X-Forwarded-For"
nginx.ingress.kubernetes.io/compute-full-forwarded-for: "true"
nginx.ingress.kubernetes.io/forwarded-for-header: "X-Forwarded-For"
# Additional IP preservation headers
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Real-IP $remote_addr";
more_set_headers "X-Forwarded-For $proxy_add_x_forwarded_for";
spec:
rules:
- host: proxy-detection.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: proxy-detection-service
port:
number: 80
```
### ConfigMap for Custom Headers
**Created**: Custom ConfigMap to configure Nginx for proper header handling.
```bash
kubectl create configmap nginx-configuration \
--from-literal=use-forwarded-headers=true \
--from-literal=compute-full-forwarded-for=true \
--from-literal=use-proxy-protocol=false \
-n ingress
```
---
## 2. Application-Level IP Detection
### Comprehensive IP Header Detection
**Issue**: The application wasn't checking all possible IP headers from various sources.
**Solution**: Implemented comprehensive IP detection supporting 30+ header sources.
#### Enhanced `getClientIP()` Function
```typescript
// src/utils.ts
export function getClientIP(request: FastifyRequest): string {
const headers = request.headers;
const ipSources = [
// CDN and Cloud Provider Headers
headers['cf-connecting-ip']?.toString(), // Cloudflare
headers['cf-pseudo-ipv4']?.toString(), // Cloudflare IPv4
headers['true-client-ip']?.toString(), // Akamai/CDNs
headers['x-akamai-edgescape']?.toString().split(',')[0]?.trim(),
headers['fastly-client-ip']?.toString(), // Fastly CDN
headers['x-azure-clientip']?.toString(), // Azure
// Standard Proxy Headers
headers['x-forwarded-for']?.toString().split(',')[0]?.trim(), // Most common
headers['x-original-forwarded-for']?.toString().split(',')[0]?.trim(),
headers['x-client-ip']?.toString(), // Apache
headers['x-real-ip']?.toString(), // Nginx
headers['x-originating-ip']?.toString(), // IIS
// Load Balancer Headers
headers['x-cluster-client-ip']?.toString(), // Kubernetes
headers['forwarded']?.toString().match(/for=([^;,\s]+)/)?.[1], // RFC 7239
headers['x-appengine-remote-addr']?.toString(), // Google App Engine
// Security and Firewall Headers
headers['x-sucuri-clientip']?.toString(), // Sucuri WAF
headers['incap-client-ip']?.toString(), // Incapsula
// Fastify defaults
request.ip,
request.socket.remoteAddress
];
return ipSources.find(ip => ip && ip !== 'unknown' && ip.trim() !== '') || 'unknown';
}
```
### Trust Proxy Configuration
**Issue**: Fastify wasn't configured to trust the Kubernetes network ranges.
**Solution**: Configured comprehensive trust proxy settings.
```typescript
// src/config.ts
export const config = {
server: {
trustProxy: [
'10.0.0.0/8', // Private networks
'172.16.0.0/12', // Docker/K8s networks
'192.168.0.0/16', // Local networks
'127.0.0.1', // Localhost
'::1' // IPv6 localhost
]
}
};
```
---
## 3. Debugging and Verification
### Debug Endpoints
**Created**: Comprehensive debug endpoints to troubleshoot IP detection.
```typescript
// Detailed debug endpoint showing all possible IP sources
fastify.get('/ip-debug-detailed', async (request) => {
return {
allSources: {
'cf-connecting-ip': headers['cf-connecting-ip'],
'x-forwarded-for': headers['x-forwarded-for'],
'x-real-ip': headers['x-real-ip'],
'x-cluster-client-ip': headers['x-cluster-client-ip'],
// ... all 30+ headers
},
detectedClientIP: getClientIP(request),
fastifyIPs: request.ips || [],
geolocation: { /* geo info */ },
security: { /* security headers */ }
};
});
```
### Testing Process
1. **Local Testing**: Verified basic functionality
2. **Kubernetes Deployment**: Deployed with debug endpoints
3. **IP Verification**: Used `/ip-debug-detailed` to verify real IPs
4. **Load Testing**: Confirmed consistent IP detection
---
## 4. Key Configuration Commands
### Kubernetes Deployment Commands
```bash
# Apply all configurations
kubectl apply -f k8s-deployment.yaml
kubectl apply -f k8s-service.yaml
kubectl apply -f k8s-ingress.yaml
kubectl apply -f k8s-secret.yaml
# Create nginx configuration
kubectl create configmap nginx-configuration \
--from-literal=use-forwarded-headers=true \
--from-literal=compute-full-forwarded-for=true \
-n ingress
# Verify LoadBalancer
kubectl get svc proxy-detection-service
# Should show EXTERNAL-IP: 192.95.29.118
# Check ingress status
kubectl get ingress proxy-detection-ingress
```
### Verification Commands
```bash
# Test real IP detection
curl -H "X-Forwarded-For: 203.0.113.1" http://192.95.29.118/ip-debug-detailed
# Check pods
kubectl get pods -l app=proxy-detection
# View logs
kubectl logs -l app=proxy-detection
```
---
## 5. Critical Success Factors
### What Made It Work
1. **Correct Namespace**: Changed from `ingress-nginx` to `ingress`
2. **externalTrafficPolicy: Local**: Preserved source IPs at LoadBalancer level
3. **Comprehensive Header Detection**: Supported all major proxy/CDN headers
4. **Trust Proxy Configuration**: Properly configured Fastify to trust K8s networks
5. **Ingress Annotations**: Used correct nginx annotations for IP forwarding
### Common Pitfalls Avoided
- ❌ Using wrong namespace for ingress controller
- ❌ Missing `externalTrafficPolicy: Local`
- ❌ Not configuring trust proxy in application
- ❌ Limited header detection (only checking X-Forwarded-For)
- ❌ Missing nginx ingress annotations
---
## 6. Final Architecture
```
Internet Client (Real IP: 203.0.113.1)
MetalLB LoadBalancer (192.95.29.118)
↓ [preserves source IP with externalTrafficPolicy: Local]
Nginx Ingress Controller
↓ [adds X-Forwarded-For, X-Real-IP headers]
Kubernetes Service
↓ [routes to pod]
Proxy Detection API Pod
↓ [getClientIP() checks 30+ headers]
Returns: Real Client IP (203.0.113.1)
```
## 7. Results
**Success**: Real client IPs are now properly detected
**Reliability**: Consistent IP detection across all request types
**Scalability**: Supports multiple CDNs, proxies, and security services
**Debugging**: Comprehensive debug endpoints for troubleshooting
The API now successfully detects real client IPs in the Kubernetes environment with MetalLB LoadBalancer, providing accurate proxy detection capabilities for production use.
COMMANDS
# Configure NGINX Ingress Controller (in 'ingress' namespace)
kubectl patch configmap ingress-nginx-controller -n ingress --patch '
data:
use-forwarded-headers: "true"
compute-full-forwarded-for: "true"
real-ip-header: "X-Forwarded-For"
set-real-ip-from: "192.95.29.118/32"
enable-real-ip: "true"
proxy-real-ip-cidr: "192.95.29.118/32"
'
# Set external traffic policy to Local for source IP preservation
kubectl patch service ingress-ingress-nginx-controller -n ingress --patch '
spec:
externalTrafficPolicy: Local
'
kubectl rollout restart deployment ingress-nginx-controller -n ingress