314 lines
No EOL
9.3 KiB
Markdown
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 |