现有一套Java开发的应用,需要能获取到用户访问的真实IP地址,以此来过滤到一些不安全的因素。而实际部署的场景中Java服务提供给用户访问需要经过多次代理,默认情况下是无法获取到客户端真实IP地址的,因此要实现该需求,就得将客户端真实IP地址透传到后端。
# haproxy.cfg
defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
log 127.0.0.1 local3
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 5m
timeout server 5m
timeout http-keep-alive 10s
timeout check 10s
unique-id-format %{+X}o\ %ci%cp%fi%fp%Ts%rt%pid
frontend https_link_ha
bind *:443 ssl crt /usr/local/etc/haproxy/cert/crt/ ca-file /usr/local/etc/haproxy/cert/ca/ca.pem verify optional
#log 127.0.0.1 local3
mode http
log-format "%ID %ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r"
option accept-invalid-http-request
# 配置请求头
http-request set-header x-request-id %[unique-id]
http-request set-header x-request-time %[date()]
http-request set-header X-Real-IP %[src]
default_backend pre
backend pre
server 1 192.168.100.100:8080 check inter 1500 rise 3 fall 3 weight 3
server 2 192.168.100.101:9000 check inter 1500 rise 3 fall 3 weight 4
server 3 192.168.100.102:8090 check inter 1500 rise 3 fall 3 weight 5
其中主要是两个配置:
option forwardfor except 127.0.0.0/8
在由Haproxy
发往后端的请求中加上XFF
首部,其值是前个客户端的IP。
http-request set-header X-Real-IP %[src]
在X-Real-IP
中设置客户端IP。
# nginx.conf 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" "$http_x_forwarded_for" ';
# location 反向代理的配置
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
更多参考资料:https://www.cnblogs.com/yanzi2020/p/17471481.html
默认情况下,Ingress
没有开启XFF。
在
Ingress
上要使用XFF
,需要使用到以下三个参数:
use-forwarded-headers
:是否开启XFF
头传递,默认是false
。
forwarded-for-header
:XFF
的真实header名,默认是X-Forwarded-For
。
compute-full-forwarded-for
:列出客户端访问所经过的代理IP,默认情况下,XFF
是从remote_addr
中获取的值。
# 在Nginx Ingress的ConfigMap里增加以下两个配置
use-forwarded-headers: 'true'
compute-full-forwarded-for: 'true'
注意:不是所有的场景都能通过XFF
获取到用户的真实IP,比如当SLB前面还有CDN的情况下,获取的可能就是CDN的来源IP
官方文档:Traefik EntryPoints Documentation - Traefik
命令行方式
--entryPoints.web.address=:80
--entryPoints.web.forwardedHeaders.insecure
# k8s yaml文件示例,仅做参考
---
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
meta.helm.sh/release-name: traefik
meta.helm.sh/release-namespace: default
labels:
app.kubernetes.io/instance: traefik
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: traefik
helm.sh/chart: traefik-9.11.0
name: traefik
namespace: default
resourceVersion: '505763774'
spec:
progressDeadlineSeconds: 600
replicas: 6
revisionHistoryLimit: 10
selector:
matchLabels:
app.kubernetes.io/instance: traefik
app.kubernetes.io/name: traefik
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app.kubernetes.io/instance: traefik
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: traefik
helm.sh/chart: traefik-9.11.0
spec:
containers:
- args:
- '--global.checknewversion'
- '--global.sendanonymoususage'
- '--entryPoints.traefik.address=:9000/tcp'
- '--entryPoints.web.address=:8000/tcp'
- '--entryPoints.websecure.address=:8443/tcp'
- '--api.dashboard=true'
- '--ping=true'
- '--providers.kubernetescrd'
- '--providers.kubernetesingress'
- '--entrypoints.web.forwardedHeaders.insecure'
- '--entrypoints.websecure.forwardedHeaders.insecure'
image: 'traefik:2.3.3'
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /ping
port: 9000
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 2
name: traefik
ports:
- 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: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 2
resources: {}
securityContext:
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsGroup: 65532
runAsNonRoot: true
runAsUser: 65532
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /data
name: data
- mountPath: /tmp
name: tmp
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext:
fsGroup: 65532
serviceAccount: traefik
serviceAccountName: traefik
terminationGracePeriodSeconds: 60
volumes:
- emptyDir: {}
name: data
- emptyDir: {}
name: tmp
status:
availableReplicas: 6
conditions:
- lastTransitionTime: '2022-10-10T07:58:50Z'
lastUpdateTime: '2022-10-10T07:58:50Z'
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: 'True'
type: Available
- lastTransitionTime: '2020-11-25T22:53:59Z'
lastUpdateTime: '2022-11-17T10:44:40Z'
message: ReplicaSet "traefik-54bf67c74d" has successfully progressed.
reason: NewReplicaSetAvailable
status: 'True'
type: Progressing
observedGeneration: 13
readyReplicas: 6
replicas: 6
updatedReplicas: 6
---
apiVersion: v1
kind: Service
metadata:
annotations:
meta.helm.sh/release-name: traefik
meta.helm.sh/release-namespace: default
labels:
app.kubernetes.io/instance: traefik
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: traefik
helm.sh/chart: traefik-9.11.0
name: traefik
namespace: default
resourceVersion: '505762379'
spec:
clusterIP: 10.96.252.109
externalTrafficPolicy: Cluster
ports:
- name: web
nodePort: 30079
port: 80
protocol: TCP
targetPort: web
selector:
app.kubernetes.io/instance: traefik
app.kubernetes.io/name: traefik
sessionAffinity: None
type: NodePort
status:
loadBalancer: {}
在deployment部署中的traefik启动参数中添加
- '--entrypoints.web.forwardedHeaders.insecure'
和
- '--entrypoints.websecure.forwardedHeaders.insecure'
启动参数
本文仅做记录和参考,在实际使用中需要充分测试。