proxy-detection/IP_DETECTION_K8S_SETUP.md

9.3 KiB

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
# 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.

# 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.

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

// 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.

// 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.

// 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

# 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

# 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