Github Repo Link for traefik-real-ip Plugin
For an insightful deep dive into how the X-Forwarded-For header impacts rate limiting and why setting the real client IP is essential, consider reading this blog. Understanding these concepts is pivotal in ensuring robust and secure application deployments.
Traefik is a modern, dynamic reverse proxy and load balancer designed to simplify the deployment and management of applications. It's especially powerful in a Kubernetes environment, where it acts as an ingress controller, managing external access to the services running within the cluster.
- Automatic Service Discovery: Traefik dynamically discovers services in your infrastructure and automatically reconfigures itself.
- Load Balancing: Distributes incoming traffic across multiple service instances to ensure availability and reliability.
- SSL Termination: Automatically handles SSL certificates, allowing for secure communication.
- Middleware Support: Easily extend Traefik’s capabilities with middlewares for authentication, rate limiting, etc.
A reverse proxy serves as an intermediary for requests from clients seeking resources from servers. Traefik, when configured as a reverse proxy, forwards client requests to the appropriate backend services based on the defined rules.
In Kubernetes, an Ingress Controller like Traefik manages the ingress traffic by defining rules for routing external traffic to internal services. This is crucial for enabling access to the applications running inside the cluster from the outside world.
One of the standout features of Traefik is its support for custom plugins. This capability allows users to extend Traefik’s functionality according to their specific needs. Plugins can be integrated as part of Traefik's pipeline, handling requests before they reach the backend services.
To deploy a custom local plugin, we use an init container to download and set up the plugin source code in the correct directory. This approach is especially useful when you need to host the plugin locally within the Kubernetes cluster.
Here’s an example of how you can set up a custom local plugin with Traefik:
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app: ingress-traefik-controller
name: ingress-traefik-controller
namespace: traefik
spec:
selector:
matchLabels:
app: ingress-traefik-controller
template:
metadata:
labels:
app: ingress-traefik-controller
spec:
automountServiceAccountToken: true
containers:
- args:
- --global.checknewversion
- --global.sendanonymoususage
- --entryPoints.metrics.address=:9100/tcp
- --entryPoints.traefik.address=:9000/tcp
- --entryPoints.web.proxyProtocol.insecure
- --entryPoints.web.forwardedHeaders.insecure
- --entryPoints.web.address=:8000/tcp
- --entryPoints.websecure.address=:8443/tcp
- --entryPoints.websecure.proxyProtocol.insecure
- --entryPoints.websecure.forwardedHeaders.insecure
- --api.dashboard=true
- --ping=true
- --metrics.prometheus=true
- --metrics.prometheus.entrypoint=metrics
- --providers.kubernetescrd
- --providers.kubernetesingress
- --entryPoints.websecure.http.tls=true
- --log.level=DEBUG
- --experimental.localPlugins.traefik-real-ip.modulename=github.com/r3d-shadow/traefik-real-ip
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
image: docker.io/traefik:v3.0.2
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /ping
port: 9000
scheme: HTTP
initialDelaySeconds: 2
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 2
name: ingress-traefik-controller
ports:
- containerPort: 9100
name: metrics
protocol: TCP
- containerPort: 9000
name: traefik
protocol: TCP
- containerPort: 8000
name: web
protocol: TCP
- containerPort: 8443
name: websecure
protocol: TCP
readinessProbe:
failureThreshold: 1
httpGet:
path: /ping
port: 9000
scheme: HTTP
initialDelaySeconds: 2
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 2
resources:
limits:
cpu: 200m
memory: 100Mi
requests:
cpu: 100m
memory: 50Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsGroup: 65532
runAsNonRoot: true
runAsUser: 65532
volumeMounts:
- mountPath: /data
name: data
- mountPath: /tmp
name: tmp
- mountPath: /plugins-storage
name: plugins-storage
- mountPath: /plugins-local
name: plugins-local
initContainers:
- args:
- |
apk update && apk add git && git clone https://github.com/r3d-shadow/traefik-real-ip /plugins-local/src/github.com/r3d-shadow/traefik-real-ip --depth 1 --single-branch --branch main
command:
- /bin/sh
- -c
image: alpine:3
imagePullPolicy: IfNotPresent
name: install-plugin
resources: {}
securityContext:
runAsUser: 0
volumeMounts:
- mountPath: /plugins-local
name: plugins-local
restartPolicy: Always
securityContext: {}
serviceAccountName: ingress-traefik-controller
terminationGracePeriodSeconds: 60
volumes:
- emptyDir: {}
name: data
- emptyDir: {}
name: tmp
- emptyDir: {}
name: plugins-storage
- emptyDir: {}
name: plugins-local
updateStrategy:
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
type: RollingUpdate
In the example above:
- Init Container:
install-plugin
is an init container that runs before the main Traefik container starts. It installsgit
and clones the plugin repository into the/plugins-local
directory. - Volume Mounts: The cloned plugin code is mounted into the main Traefik container at the
/plugins-local
path.
The traefik_real_ip
plugin for Traefik enhances the ability to accurately identify and set the real client IP address when Traefik is deployed behind multiple layers of proxies or load balancers.
In scenarios where Traefik sits behind a series of proxies (e.g., CDN, load balancers), the client’s original IP address can be obscured. Correctly identifying the client’s real IP address is crucial for:
- Security: Accurate IP logging helps in security audits and incident response.
- Rate Limiting and Geo-blocking: Applying policies based on the client’s real IP address.
- Custom Application Logic: Some applications might rely on the client’s IP for personalized content delivery or access control.
The plugin examines the X-Forwarded-For
header, which carries a list of IP addresses representing the client’s route through proxies. By configuring the forwardedForDepth
parameter, you specify how far back in the list to look to determine the real client IP.
forwardedForDepth
: Determines the position in theX-Forwarded-For
header to use as the real IP. A value of1
(default) means the last IP in the list is used. Increasing this value allows you to step back through the proxies to find the actual client IP.
To prevent IP spoofing attacks, it’s important to configure the forwardedForDepth
correctly. For example, with forwardedForDepth: 2
, the plugin will pick the second last IP in the X-Forwarded-For
header chain as the real IP, mitigating attempts to spoof the client's IP address.
Let's illustrate the usage of the traefik_real_ip
plugin with a practical example involving an X-Forwarded-For
header:
Consider a scenario where a request is made with the following X-Forwarded-For
header:
X-Forwarded-For: SPOOF_IP, REAL_IP, PROXY1, PROXY0
To simulate this scenario using curl:
curl -X POST https://example.com/whoami -H "X-Forwarded-For: SPOOF_IP"
In this setup:
- X-Forwarded-For Header: The header includes a chain of IP addresses, potentially including spoofed IPs.
- Plugin Behavior: With
forwardedForDepth: 2
configured in thetraefik_real_ip
plugin, it will set theX-Real-Ip
header toREAL_IP
, which is the second IP in theX-Forwarded-For
chain after the spoofed IP. - X-Real-Ip Header: After processing, the X-Real-Ip will accurately reflect the real client IP as follows:
X-Real-Ip: REAL_IP
This ensures that the application receives the correct client IP information despite the presence of spoofed IPs in the X-Forwarded-For
header, thereby enhancing security and trustworthiness in identifying client origins.
Here’s a configuration setup for deploying the traefik_real_ip
plugin in Kubernetes with Traefik:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: whoami
name: whoami
spec:
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- image: containous/whoami
imagePullPolicy: Always
name: whoami
ports:
- containerPort: 80
name: web
protocol: TCP
resources: {}
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: whoami
spec:
ports:
- name: web
port: 80
protocol: TCP
targetPort: web
selector:
app: whoami
type: ClusterIP
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: whoami
spec:
entryPoints:
- web
- websecure
routes:
- match: Host(`example.com`) && PathPrefix(`/whoami`)
kind: Rule
services:
- name: whoami
port: 80
middlewares:
- name: traefik-real-ip
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: traefik-real-ip
spec:
plugin:
traefik-real-ip:
forwardedForDepth: 2
---
For further details on using plugins with Traefik, consult the official Traefik documentation.