Kubernetes Egress Gateway with Squid Proxy

Kubernetes Egress Gateway with Squid Proxy
Page content

Squid is a mature, full-featured HTTP proxy and caching server that can be deployed on Kubernetes for egress control. This post explores using traditional proxy technology for modern Kubernetes egress requirements with access control, caching, and comprehensive logging.

Egress Gateway Series

This series covers Kubernetes egress gateway solutions:

✓ All parts complete!

Why Squid Proxy?

Squid has been the industry-standard HTTP proxy for over 25 years, providing:

  • Mature and stable - Battle-tested in production environments
  • Access Control Lists (ACLs) - Fine-grained traffic control
  • Caching - Reduce bandwidth and improve response times
  • Comprehensive logging - Audit and compliance support
  • SSL/TLS inspection - HTTPS traffic visibility
  • Authentication - Integration with LDAP, AD, RADIUS
  • Low resource usage - Efficient proxy operations

Architecture Overview

┌─────────────────────────────────────────────────────────────────┐
│                    Kubernetes Cluster                           │
│                                                                 │
│   ┌──────────┐    ┌──────────┐    ┌──────────┐                 │
│   │  Pod A   │    │  Pod B   │    │  Pod C   │                 │
│   └────┬─────┘    └────┬─────┘    └────┬─────┘                 │
│        │               │               │                        │
│        └───────────────┼───────────────┘                        │
│                        │                                        │
│                        ▼                                        │
│            ┌───────────────────────┐                            │
│            │  Squid Egress Proxy   │<─── Squid Config           │
│            └───────────┬───────────┘                            │
│                        │                                        │
│           ┌────────────┴────────────┐                           │
│           │                         │                           │
│           ▼                         ▼                           │
│   ┌──────────────────┐    ┌──────────────────┐                 │
│   │ External HTTP/   │    │  Cache Storage   │                 │
│   │ HTTPS            │    │                  │                 │
│   └──────────────────┘    └──────────────────┘                 │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                        │
                        │ Access Logs
                        ▼
              ┌──────────────────┐
              │   ELK / Splunk   │
              └──────────────────┘

              ┌──────────────────┐
              │   Auth Server    │───┐
              └──────────────────┘   │
                                     │ (Authentication)
                                     └───────────────┘

Traffic Flow

  1. Pods configured with HTTP_PROXY/HTTPS_PROXY environment variables
  2. HTTP/HTTPS traffic routed to Squid proxy service
  3. Squid evaluates ACLs for allow/deny decisions
  4. Cache lookup for frequently accessed content
  5. SSL inspection (optional) for HTTPS traffic
  6. Request forwarded to external destination
  7. Response cached and logged

Prerequisites

Component Version Notes
Kubernetes 1.25+ Any distribution
Squid 5.x+ Latest stable
kubectl Latest Configured with cluster access
Storage PV/PVC For cache and logs

Installation

Step 1: Create Squid Namespace

kubectl create namespace squid-proxy

Step 2: Create Squid Configuration

Create squid-config.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: squid-config
  namespace: squid-proxy
data:
  squid.conf: |
    # Basic configuration
    http_port 3128
    visible_hostname squid-proxy
    
    # Access Control Lists
    acl localnet src 10.0.0.0/8
    acl localnet src 172.16.0.0/12
    acl localnet src 192.168.0.0/16
    
    # SSL ports
    acl SSL_ports port 443
    acl Safe_ports port 80
    acl Safe_ports port 443
    acl Safe_ports port 21
    acl Safe_ports port 70
    acl Safe_ports port 210
    acl Safe_ports port 1025-65535
    
    # HTTP methods
    acl CONNECT method CONNECT
    acl GET method GET
    acl POST method POST
    
    # Allowed domains
    acl allowed_domains dstdomain .github.com
    acl allowed_domains dstdomain .docker.io
    acl allowed_domains dstdomain .quay.io
    acl allowed_domains dstdomain .maven.org
    acl allowed_domains dstdomain .npmjs.com
    acl allowed_domains dstdomain .pypi.org
    
    # Access rules
    http_access deny !Safe_ports
    http_access deny CONNECT !SSL_ports
    http_access allow localhost
    http_access allow allowed_domains
    http_access allow localnet
    http_access deny all
    
    # Caching configuration
    cache_dir ufs /var/spool/squid 1000 16 256
    cache_mem 256 MB
    maximum_object_size_in_memory 4 MB
    maximum_object_size 100 MB
    cache_swap_low 98
    cache_swap_high 99
    
    # Logging
    access_log /var/log/squid/access.log squid
    cache_log /var/log/squid/cache.log
    cache_store_log /var/log/squid/store.log
    
    # Performance tuning
    cache_mem_high_watermark 95
    read_ahead_gap 128 KB
    positive_dns_ttl 6 hours
    negative_dns_ttl 1 minute
    
    # Refresh patterns (cache control)
    refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
    refresh_pattern . 0 20% 4320

Step 3: Create Squid Deployment

Create squid-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: squid-proxy
  namespace: squid-proxy
  labels:
    app: squid-proxy
spec:
  replicas: 2  # High availability
  selector:
    matchLabels:
      app: squid-proxy
  template:
    metadata:
      labels:
        app: squid-proxy
    spec:
      containers:
        - name: squid
          image: ubuntu/squid:5.2-22.04_beta
          ports:
            - containerPort: 3128
              name: http
          volumeMounts:
            - name: squid-config
              mountPath: /etc/squid/squid.conf
              subPath: squid.conf
              readOnly: true
            - name: squid-cache
              mountPath: /var/spool/squid
            - name: squid-logs
              mountPath: /var/log/squid
          resources:
            requests:
              cpu: 250m
              memory: 512Mi
            limits:
              cpu: 1000m
              memory: 2Gi
          livenessProbe:
            tcpSocket:
              port: 3128
            initialDelaySeconds: 30
            periodSeconds: 30
          readinessProbe:
            tcpSocket:
              port: 3128
            initialDelaySeconds: 10
            periodSeconds: 10
      volumes:
        - name: squid-config
          configMap:
            name: squid-config
        - name: squid-cache
          persistentVolumeClaim:
            claimName: squid-cache-pvc
        - name: squid-logs
          emptyDir: {}
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchLabels:
                    app: squid-proxy
                topologyKey: kubernetes.io/hostname

Step 4: Create Persistent Volume Claim

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: squid-cache-pvc
  namespace: squid-proxy
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: standard

Step 5: Create Service

apiVersion: v1
kind: Service
metadata:
  name: squid-proxy
  namespace: squid-proxy
  labels:
    app: squid-proxy
spec:
  selector:
    app: squid-proxy
  ports:
    - name: http
      port: 3128
      targetPort: 3128
      protocol: TCP
  type: ClusterIP

Step 6: Apply Configuration

kubectl apply -f squid-config.yaml
kubectl apply -f squid-deployment.yaml
kubectl apply -f squid-pvc.yaml
kubectl apply -f squid-service.yaml

Step 7: Verify Deployment

# Check pods are running
kubectl get pods -n squid-proxy

# Expected output:
# NAME                            READY   STATUS
# squid-proxy-xxxxxxxxxx-xxxxx    1/1     Running
# squid-proxy-yyyyyyyyyy-yyyyy    1/1     Running

# Check service
kubectl get svc -n squid-proxy

# Test proxy connectivity
kubectl run test-pod --image=curlimages/curl -it --rm --restart=Never \
  -- curl -x http://squid-proxy.squid-proxy.svc:3128 \
  https://www.google.com

Advanced Configuration

Authentication with Basic Auth

# Add to squid.conf
data:
  squid.conf: |
    # Basic authentication
    auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/passwd
    auth_param basic children 5
    auth_param basic realm "Squid Proxy"
    auth_param basic credentialsttl 2 hours
    
    acl authenticated proxy_auth REQUIRED
    http_access allow authenticated allowed_domains
    http_access deny all
# Create password file
kubectl create secret generic squid-auth \
  --from-literal=users=$(htpasswd -cb - user1 password123) \
  --namespace squid-proxy

LDAP/Active Directory Authentication

# Add to squid.conf
data:
  squid.conf: |
    # LDAP authentication
    auth_param ldap program /usr/lib/squid/ldap_auth \
      -b "dc=example,dc=com" \
      -D "cn=admin,dc=example,dc=com" \
      -w "admin_password" \
      -f "(sAMAccountName=%s)" \
      -H ldap://ad.example.com
    
    auth_param ldap children 5
    auth_param ldap realm "Squid Proxy"
    auth_param ldap credentialsttl 2 hours
    
    acl ldap_users proxy_auth REQUIRED
    http_access allow ldap_users allowed_domains

SSL/TLS Bump (HTTPS Inspection)

# Add to squid.conf
data:
  squid.conf: |
    # SSL Bump configuration
    https_port 3129 intercept ssl-bump \
      cert=/etc/squid/ssl/squid.crt \
      key=/etc/squid/ssl/squid.key
    
    ssl_bump peek all
    ssl_bump splice allowed_domains
    ssl_bump terminate all
    
    # ACLs for SSL bump
    acl step1 at_step SslBump1
    acl step2 at_step SslBump2
    acl step3 at_step SslBump3
    
    ssl_bump peek step1
    ssl_bump peek step2
    ssl_bump splice step3
# Generate CA certificate for SSL bump
openssl req -new -newkey rsa:2048 -sha256 -days 3650 -nodes -x509 \
  -subj "/C=US/ST=State/L=City/O=Organization/CN=Squid Proxy CA" \
  -keyout squid-ca.key -out squid-ca.crt

# Create secret
kubectl create secret tls squid-ssl-cert \
  --cert=squid-ca.crt \
  --key=squid-ca.key \
  --namespace squid-proxy

Content Filtering

# Add to squid.conf
data:
  squid.conf: |
    # Block specific categories
    acl blocked_sites dstdomain .facebook.com
    acl blocked_sites dstdomain .twitter.com
    acl blocked_sites dstdomain .youtube.com
    
    # Block file types
    acl blocked_files urlpath_regex -i \.(exe|zip|rar|mp4|avi)$
    
    # Block keywords
    acl blocked_keywords urlpath_regex -i (gambling|casino|porn)
    
    # Apply blocks
    http_access deny blocked_sites
    http_access deny blocked_files
    http_access deny blocked_keywords

Rate Limiting

# Add to squid.conf
data:
  squid.conf: |
    # Rate limiting by IP
    acl too_many_ips maxconn 10
    http_access deny too_many_ips
    
    # Rate limiting by bandwidth
    delay_pools 1
    delay_class 1 2
    delay_parameters 1 1000000/1000000 50000/50000
    delay_access 1 allow all
    
    # Connection limits
    conn_limit_max 100
    conn_limit_threshold 80

Routing Traffic to Squid Proxy

Method 1: Environment Variables

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-server
  template:
    metadata:
      labels:
        app: api-server
    spec:
      containers:
        - name: api
          image: myapp/api:v1.0
          env:
            - name: HTTP_PROXY
              value: "http://squid-proxy.squid-proxy.svc:3128"
            - name: HTTPS_PROXY
              value: "http://squid-proxy.squid-proxy.svc:3128"
            - name: NO_PROXY
              value: "localhost,.svc.cluster.local,.cluster.local,10.0.0.0/8"

Method 2: Init Container Configuration

spec:
  initContainers:
    - name: set-proxy
      image: busybox
      command: ['sh', '-c', 'echo "export HTTP_PROXY=http://squid-proxy:3128" >> /etc/profile.d/proxy.sh']
      volumeMounts:
        - name: proxy-config
          mountPath: /etc/profile.d
  containers:
    - name: app
      image: myapp/api:v1.0
  volumes:
    - name: proxy-config
      emptyDir: {}

Method 3: NetworkPolicy (CNI-dependent)

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: force-squid-proxy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-server
  policyTypes:
    - Egress
  egress:
    # Allow DNS
    - to:
        - namespaceSelector: {}
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
    
    # Force all HTTP/HTTPS through Squid
    - to:
        - namespaceSelector:
            matchLabels:
              name: squid-proxy
          podSelector:
            matchLabels:
              app: squid-proxy
      ports:
        - protocol: TCP
          port: 3128

Monitoring and Logging

Access Logs

# View access logs
kubectl logs -n squid-proxy squid-proxy-xxxxx -- squid

# Stream logs
kubectl logs -n squid-proxy squid-proxy-xxxxx -f -- squid

# Export logs
kubectl logs -n squid-proxy squid-proxy-xxxxx -- squid > squid-access.log

Log Format Customization

# Add to squid.conf
data:
  squid.conf: |
    # Custom log format
    logformat squidextended %ts.%03tu %6tr %>a %Ss/%03>Hs %<st %rm %ru %un %Sh/%<A %mt
    
    access_log /var/log/squid/access.log squidextended

Prometheus Metrics

# Deploy squid-exporter
apiVersion: apps/v1
kind: Deployment
metadata:
  name: squid-exporter
  namespace: squid-proxy
spec:
  replicas: 1
  selector:
    matchLabels:
    app: squid-exporter
  template:
    metadata:
      labels:
        app: squid-exporter
    spec:
      containers:
        - name: exporter
          image: boynux/squid-exporter:latest
          ports:
            - containerPort: 9301
          args:
            - "-squid.host=squid-proxy.squid-proxy.svc"
            - "-squid.port=3128"

---
apiVersion: v1
kind: Service
metadata:
  name: squid-exporter
  namespace: squid-proxy
spec:
  selector:
    app: squid-exporter
  ports:
    - port: 9301
      targetPort: 9301

Grafana Dashboard

apiVersion: v1
kind: ConfigMap
metadata:
  name: squid-proxy-dashboard
  namespace: monitoring
data:
  squid-proxy.json: |
    {
      "dashboard": {
        "title": "Squid Proxy",
        "panels": [
          {
            "title": "Request Rate",
            "targets": [
              {
                "expr": "rate(squid_requests_total[5m])"
              }
            ]
          },
          {
            "title": "Cache Hit Ratio",
            "targets": [
              {
                "expr": "rate(squid_cache_hits_total[5m]) / rate(squid_requests_total[5m])"
              }
            ]
          },
          {
            "title": "Bandwidth Usage",
            "targets": [
              {
                "expr": "rate(squid_kbytes_read_total[5m]) * 1024"
              }
            ]
          },
          {
            "title": "Active Connections",
            "targets": [
              {
                "expr": "squid_current_connections"
              }
            ]
          },
          {
            "title": "Denied Requests",
            "targets": [
              {
                "expr": "rate(squid_acl_denied_total[5m])"
              }
            ]
          }
        ]
      }
    }

Security Best Practices

1. Restrict Allowed Destinations

# Add to squid.conf
data:
  squid.conf: |
    # Whitelist specific domains
    acl allowed_api dstdomain .api.stripe.com
    acl allowed_api dstdomain .api.github.com
    acl allowed_api dstdomain .registry.npmjs.org
    
    # Whitelist IP ranges
    acl allowed_ips dst 52.0.0.0/8
    acl allowed_ips dst 35.0.0.0/8
    
    # Deny everything else
    http_access allow allowed_api
    http_access allow allowed_ips
    http_access deny all

2. Enable Request Body Logging

# Add to squid.conf
data:
  squid.conf: |
    # Log request headers
    logformat detailed %ts.%03tu %6tr %>a %Ss/%03>Hs %<st %rm %ru %un %Sh/%<A %mt %{Referer}>h %{User-Agent}>h
    
    access_log /var/log/squid/detailed.log detailed

3. Implement SSL Bump for Inspection

# See SSL/TLS Bump section above
# Important: Distribute CA cert to all pods for trust

4. Regular Configuration Audits

# Validate Squid configuration
kubectl exec -n squid-proxy squid-proxy-xxxxx -- squid -k check

# Check configuration syntax
kubectl exec -n squid-proxy squid-proxy-xxxxx -- squid -k parse

Troubleshooting

Issue: Pods Can’t Access External Services

# Check Squid pods are running
kubectl get pods -n squid-proxy

# Verify Squid configuration
kubectl exec -n squid-proxy squid-proxy-xxxxx -- squid -k check

# Test from inside Squid pod
kubectl exec -n squid-proxy squid-proxy-xxxxx -it -- \
  curl -x localhost:3128 https://www.google.com

# Check pod proxy configuration
kubectl get pod api-server-xxxxx -n production -o yaml | grep -i proxy

Issue: Cache Not Working

# Check cache directory
kubectl exec -n squid-proxy squid-proxy-xxxxx -- ls -la /var/spool/squid

# View cache stats
kubectl exec -n squid-proxy squid-proxy-xxxxx -- squidclient mgr:info

# Check cache hit ratio
kubectl exec -n squid-proxy squid-proxy-xxxxx -- squidclient mgr:info | grep -i "hit ratio"

Issue: SSL Bump Not Working

# Verify SSL certificate
kubectl exec -n squid-proxy squid-proxy-xxxxx -- openssl x509 -in /etc/squid/ssl/squid.crt -text

# Check SSL port configuration
kubectl exec -n squid-proxy squid-proxy-xxxxx -- netstat -tlnp | grep 3129

# Test SSL bump
kubectl exec -n squid-proxy squid-proxy-xxxxx -it -- \
  curl -x localhost:3129 -k https://www.google.com

Common Problems and Solutions

Problem Cause Solution
Connection refused Squid not listening Check port configuration
403 Forbidden ACL blocking traffic Review http_access rules
503 Service Unavailable Cache directory full Increase cache size
SSL errors Certificate not trusted Distribute CA to clients
High memory usage Cache too large Reduce cache_mem setting

Comparison with Other Solutions

Feature Squid Envoy Istio Cilium
Protocol HTTP/HTTPS HTTP/1,2,3, TCP HTTP/1,2,3, TCP L3/L4
Caching ✅ Full ❌ No ❌ No ❌ No
ACLs ✅ Advanced ✅ Advanced ✅ Advanced ⚠️ Limited
SSL Inspection ✅ Full ✅ Full ✅ Full ❌ No
Authentication ✅ LDAP/AD/RADIUS ⚠️ Manual ✅ mTLS ❌ No
Logging ✅ Comprehensive ✅ Comprehensive ✅ Comprehensive ✅ Hubble
Maturity 25+ years 8 years 7 years 8 years
Resource Usage Low Low High Low

When to Choose Squid

Choose Squid when:

  • ✅ Need HTTP caching to reduce bandwidth
  • ✅ Require mature, stable proxy solution
  • ✅ Need LDAP/AD authentication
  • ✅ Comprehensive access logging required
  • ✅ Content filtering needed
  • ✅ Team has proxy administration experience

Consider alternatives when:

  • 📋 Need TCP/UDP proxying (choose Envoy)
  • 📋 Want automatic mTLS (choose Istio)
  • 📋 Need eBPF performance (choose Cilium)
  • 📋 gRPC/HTTP2 primary protocols (choose Envoy)

Next Steps

In the next post of this series:

  • Cloud NAT Solutions - GCP Cloud NAT, Azure Firewall
  • Managed services comparison
  • Cost analysis

Conclusion

Squid Proxy provides:

Advantages:

  • ✅ Mature, battle-tested technology (25+ years)
  • ✅ HTTP caching reduces bandwidth costs
  • ✅ Advanced ACLs for fine-grained control
  • ✅ Comprehensive access logging
  • ✅ LDAP/AD/RADIUS authentication
  • ✅ Content filtering capabilities
  • ✅ Low resource usage
  • ✅ SSL/TLS inspection support

Considerations:

  • 📋 HTTP/HTTPS focused (not TCP/UDP)
  • 📋 Self-managed (operations overhead)
  • 📋 Manual mTLS configuration
  • 📋 Less suited for gRPC/HTTP2

For organizations needing a mature HTTP proxy with caching, authentication, and comprehensive logging, Squid remains an excellent choice that has stood the test of time.