K3S使用Traefik Ingress后端无法获取到客户端真实IP地址

Viewed 9

K3S自带的Traefik Ingress转发,后端API接口获取到的是K3S Node的IP,而不是客户端正确IP,我的Traefik没修改过配置文件,Service配置的ClusterIP

我的后端获取IP的逻辑:

public static String getClientIP(HttpServletRequest request, String... otherHeaderNames) {
        String[] headers = new String[]{"X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"};
        if (ArrayUtil.isNotEmpty(otherHeaderNames)) {
            headers = (String[])ArrayUtil.addAll(new String[][]{headers, otherHeaderNames});
        }

        return getClientIPByHeader(request, headers);
    }

public static String getClientIPByHeader(HttpServletRequest request, String... headerNames) {
        for(String header : headerNames) {
            String ip = request.getHeader(header);
            if (!NetUtil.isUnknown(ip)) {
                return NetUtil.getMultistageReverseProxyIp(ip);
            }
        }

        String ip = request.getRemoteAddr();
        return NetUtil.getMultistageReverseProxyIp(ip);
    }

项目直接部署的话,IP获取的是正确的,但在k8s上就有这个问题

1 Answers

先上解决方案:

  1. 修改Traefik的Service配置文件:
    externalTrafficPolicy: Local
  2. 配置Traefik的Forwarded Headers
spec:
  containers:
    - args:
        - '--entrypoints.web.forwardedHeaders.insecure' // 添加这个
  1. 重启Traefik Pod,问题解决。

原理:

默认情况下(externalTrafficPolicy: Cluster),当外部请求到达Service时:

  1. 请求先到达节点的某个端口
  2. 然后被kube-proxy转发到随机选择的Pod
  3. 这个过程中,源IP会被替换为节点IP(SNAT)

设置externalTrafficPolicy: Local后:

  1. k8s会保留原始客户端IP
  2. 请求会直接路由到接收请求的节点上运行的Pod
  3. 不会跨节点转发流量,避免SNAT操作
  4. 节点的kube-proxy会把请求直接转发给本地的Pod

但是要注意的问题是:

  1. 要求Traefik Pod必须运行在接收流量的节点上
  2. 可能导致负载不均衡(流量只会到有Pod的节点)
  3. 需要确保Traefik Pod有足够的副本分布在各个节点