聊聊部署在K8S的项目如何获取客户端真实IP

这篇具有很好参考价值的文章主要介绍了聊聊部署在K8S的项目如何获取客户端真实IP。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

最近部门有个需求,需要对一些客户端IP做白名单,在白名单范围内,才能做一些业务操作。按我们的部门的一贯做法,我们会封装一个client包,提供给业务方使用。(注: 我们的项目是运行在K8S上)本以为这是一个不是很难的功能,部门的小伙伴不到一天,就把功能实现了,他通过本地调试,可以获取到正确的客户端IP,但是发布到测试环境,发现获取到的客户端IP一直是节点的IP,后面那个小伙伴排查了很久,一直没头绪,就找到我帮忙一直排查一下。今天文章主要就是来复盘这个过程

排查过程

首先先排查了一下他获取客户端IP的实现逻辑

public class IpUtils {
    private static Logger logger = LoggerFactory.getLogger(IpUtils.class);
    private static final String IP_UTILS_FLAG = ",";
    private static final String UNKNOWN = "unknown";
    private static final String LOCALHOST_IP = "0:0:0:0:0:0:0:1";
    private static final String LOCALHOST_IP1 = "127.0.0.1";

    /**
     * 获取IP地址
     *
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = null;
        try {
            //以下两个获取在k8s中,将真实的客户端IP,放到了x-Original-Forwarded-For。而将WAF的回源地址放到了 x-Forwarded-For了。
            ip = request.getHeader("X-Original-Forwarded-For");
            System.out.println("X-Original-Forwarded-For:" + ip);
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("X-Forwarded-For");
            }
            //获取nginx等代理的ip
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("x-forwarded-for");
                System.out.println("x-forwarded-for:" + ip);
            }
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (StringUtils.isEmpty(ip) || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            //兼容k8s集群获取ip
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
                System.out.println("getRemoteAddr:" + ip);
                if (LOCALHOST_IP1.equalsIgnoreCase(ip) || LOCALHOST_IP.equalsIgnoreCase(ip)) {
                    //根据网卡取本机配置的IP
                    InetAddress iNet = null;
                    try {
                        iNet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        logger.error("getClientIp error: {}", e);
                    }
                    ip = iNet.getHostAddress();
                }
            }
        } catch (Exception e) {
            logger.error("IPUtils ERROR ", e);
        }
        //使用代理,则获取第一个IP地址
        if (!StringUtils.isEmpty(ip) && ip.indexOf(IP_UTILS_FLAG) > 0) {
            ip = ip.substring(0, ip.indexOf(IP_UTILS_FLAG));

        }

        return ip;
    }

}

这逻辑看着貌似没问题,因为本地调试可以获取到正确的客户端IP,而测试环境获取不到,大概率是环境有问题。于是就把方向转为定位环境的差异性

环境定位

测试环境

我们测试环境的访问流程为客户端–> k8s service nodeport—>pod

通过搜索在https://kubernetes.io/zh-cn/docs/tutorials/services/source-ip/
在这篇文章找到答案。

Kubernetes Service 转发场景下,无论使用 iptbales 或 ipvs 的负载均衡转发模式,转发时都会对数据包做 SNAT,即不会保留客户端真实源 IP

整体流程

聊聊部署在K8S的项目如何获取客户端真实IP

上文的链接也贴了解法

聊聊部署在K8S的项目如何获取客户端真实IP
具体步骤就是

1、步骤一:业务pod的配置调度到指定节点

示例

spec:
   nodeName: node1    #指定pod节点配置
   containers:
      - name: pod-name

2、步骤二:将业务的service yaml 默认配置的externalTrafficPolicy: Cluster改为 externalTrafficPolicy: Local

示例

spec:
  type: NodePort
  externalTrafficPolicy: Local

3、步骤三:通过指定在pod上的node节点 + nodeport进行访问

示例

http://node1:nodeport

假设部署了node1和node2节点,只能通过node1:nodeport才能访问到具体业务,如果通过node2:nodeport,则请求的数据包会被抛弃

聊聊部署在K8S的项目如何获取客户端真实IP
通过上述的方案,解决了在测试环境通过service nodeport获取不到正确客户端ip的问题

uat环境

当测试环境没问题后,将项目发布到UAT环境,然后不出意外的话,又出意外了。

uat的访问流程为 客户端- -> nginx+keepalive --> ingress --> pod

因为访问方式不一样,因此解法又有差异。通过搜索了解到*用户ip的传递依靠的是X-Forwarded-参数。但是默认情况下,ingress是没有开启的 因此我们需要开启。开启需要如下参数

聊聊部署在K8S的项目如何获取客户端真实IP

  • use-forwarded-headers: 如果设置为True时,则将设定的X-Forwarded-* Header传递给后端,
    当Ingress在L7 代理/负载均衡器之后使用此选项。如果设置为 false 时,则会忽略传入的X-Forwarded-*Header,
    当 Ingress 直接暴露在互联网或者 L3/数据包的负载均衡器后面,并且不会更改数据包中的源 IP请使用此选项。
  • forwarded-for-header: 设置用于标识客户端的原始 IP 地址的 Header字段。默认值X-Forwarded-For。如果想修改为自定义的字段名,则可以在configmap的data配置块下添加:forwarded-for-header: “THE_NAME_YOU_WANT”
  • compute-full-forwarded-for: 将 remote address 附加到 X-Forwarded-For
    Header而不是替换它。当启用此选项后端应用程序负责根据自己的受信任代理列表排除并提取客户端 IP。

详细的介绍可以查看官网

https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#use-forwarded-headers​

我们在Ingress Nginx Controller 的 Configmap添加如下内容

apiVersion: v1
kind: ConfigMap
......
data:
  compute-full-forwarded-for: "true"
  use-forwarded-headers: "true"
  forwarded-for-header:"X-Forwarded-For"

配置后,发现没鸟用,没有效果。后面查了很多资料,发现网上都是那么配的,后面就觉得是不是nginx - keepalive 这一环节出了啥问题,于是就问了一下运维,看他nginx那边是否有配置X-Forwarded-For,他说没有,那我就问他能否配置一下,他的回答是因为nginx那边启用了 ssl_preread 模块无法使用X-Forwarded-For
聊聊部署在K8S的项目如何获取客户端真实IP
后面就问他能否改下,他回答说是后面公司要采用F5了,到时候在配置一下就好。而他目前事情比较多,没时间帮我弄这个。

由于业务比较赶,运维又没空搞,于是就和业务那边沟通,采取了折中方案,就是通过自定义请求头,我们在client包配置了一个属性,那个属性用来让业务将白名单ip填进去,示例

lybgeek:
  whilte-ips: 192.168.1.1,192.168.2.1

在业务项目启动的时候,client包会自动将配置的白名单塞入请求头

   header("x-custom-forwarded-for",whilteIps)

服务端那边获取客户端ip做如下改造

@Slf4j
public final class IPHelper {

    private IPHelper(){}

    private static final String IP_UTILS_FLAG = ",";
    private static final String UNKNOWN = "unknown";
    private static final String LOCALHOST_IP = "0:0:0:0:0:0:0:1";
    private static final String LOCALHOST_IP1 = "127.0.0.1";


   private static final String[] headersToTry = {
           //在k8s中,将真实的客户端IP,放到了x-Original-Forwarded-For。而将WAF的回源地址放到了 x-Forwarded-For了。
            "X-Original-Forwarded-For",
            "X-Forwarded-For",
            "Proxy-Client-IP",
            "WL-Proxy-Client-IP",
            "HTTP_X_FORWARDED_FOR",
            "HTTP_X_FORWARDED",
            "HTTP_X_CLUSTER_CLIENT_IP",
            "HTTP_CLIENT_IP",
            "HTTP_FORWARDED_FOR",
            "HTTP_FORWARDED",
            "HTTP_VIA",
            "REMOTE_ADDR",
            // 自定义请求头
            "X-Custom-Forwarded-For",
    };

        /**
         * 获取用户的真正IP地址
         *
         * @param request request对象
         * @return 返回用户的IP地址
         */
         @SneakyThrows
         public static String getIpAddr(HttpServletRequest request) {
             String ip = null;
            for (String header : headersToTry) {
                ip = request.getHeader(header);
                if (StringUtils.hasText(ip) && !UNKNOWN.equalsIgnoreCase(ip)){
                    log.info("hit the target client ip -> 【{}】 by header --> 【{}】",ip,header);
                    return ip;
                }
            }
             //兼容k8s集群获取ip
             if (org.springframework.util.StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                 ip = request.getRemoteAddr();
                 if (LOCALHOST_IP1.equalsIgnoreCase(ip) || LOCALHOST_IP.equalsIgnoreCase(ip)) {
                     //根据网卡取本机配置的IP
                     InetAddress iNet = null;
                     try {
                         iNet = InetAddress.getLocalHost();
                     } catch (UnknownHostException e) {
                         log.error("getIpAddr error: {}", e);
                     }
                     ip = iNet.getHostAddress();
                 }
                 log.info("hit the target client ip -> 【{}】 by method 【getRemoteAddr】 ",ip);
             }

             //使用代理,则获取第一个IP地址
             if (!org.springframework.util.StringUtils.isEmpty(ip) && ip.indexOf(IP_UTILS_FLAG) > 0) {
                 ip = ip.substring(0, ip.indexOf(IP_UTILS_FLAG));
             }
             return ip;
        }





}

其实做的事情,就将原来的工具类稍微重构了一下,并加入自定义请求头X-Custom-Forwarded-For

总结

这次的复盘总结就是很多东西没那么想当然,有些简单的东西,里面可能也有有坑。当遇到跨部门合作时,如果遇到一些不可抗力因素,我们除了向上反馈之外,还要有兜底方案,不然会非常被动文章来源地址https://www.toymoban.com/news/detail-500780.html

到了这里,关于聊聊部署在K8S的项目如何获取客户端真实IP的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 第13关 解决K8s中Ingress Nginx控制器无法获取真实客户端IP的问题

    ------ 课程视频同步分享在今日头条和B站 大家好,我是博哥爱运维。 这节课带大家探索并分享最全面的解决在使用Kubernetes(K8s)和Ingress-Nginx-Controller中无法获取客户端真实IP问题的视频教程,帮助你快速理解并解决这一问题。 如果我们按下面网络架构图,暴露我们服务到公

    2024年02月03日
    浏览(50)
  • nginx 多层代理 + k8s ingress 后端服务获取客户真实ip 配置

    1.nginx http 七层代理 修改命令空间: namespace: nginx-ingress : configmap:nginx-configuration 添加如上配置 compute-full-forwarded-for: “true” forwarded-for-header: X-Forwarded-For use-forwarded-headers: “true” 即可; 2. nginx stream 4层代理: nginx 需要编译增加–with-stream_realip_module模块,然后对应的server块

    2024年02月08日
    浏览(45)
  • Kubernetes(k8s)实战:Kubernetes(k8s)部署Springboot项目

    wordpress是用于快速搭建博客系统。 该yaml文件创建一个mysql,并且生成一个service,service对外暴露的端口是3306 我们发现,搭建成功了,用浏览器访问192.168.56.101:30493,发现访问成功了! 在集群中,pod之间可以通过service 的name进行访问,不仅仅是ip,这就意味着,service中不仅帮

    2024年02月12日
    浏览(98)
  • 【k8s完整实战教程4】使用kubesphere部署项目到k8s

    系列文章:这个系列已完结,如对您有帮助,求点赞收藏评论。 读者寄语: 再小的帆,也能远航! 【k8s完整实战教程0】前言 【k8s完整实战教程1】源码管理-Coding 【k8s完整实战教程2】腾讯云搭建k8s托管集群 【k8s完整实战教程3】k8s集群部署kubesphere 【k8s完整实战教程4】使用

    2023年04月18日
    浏览(63)
  • k8s 部署Jenkins项目

    要求:当前集群配置了storageClass,并已指定默认的storageClass,一般情况下,创建的storageClass即为默认类 指定默认storageClass的方式 1.1 部署helm 1.2 部署jenkins 1.3 检查 jenkins 1.4 配置访问 3.1 准备ruoyi数据 3.2 准备k8s证书 3.3 准备maven配置文件 3.4 配置钉钉插件 在系统管理的下方有未

    2024年01月21日
    浏览(49)
  • K8s部署PHP项目

            前端时间PHP项目部署升级需要 ,需要把Laravel开发的项目部署K8s上,下面以laravel项目为例,讲解采用yaml文件方式部署项目。 一、部署步骤 Dockerfile是一个用来构建镜像的文本文件,在容器运行时,需要把项目文件和项目运行所必须的组件安装其中。 Kubernetes使用命名

    2024年02月05日
    浏览(33)
  • k8s部署微服务项目

    之前用docker-compose部署微服务项目,但是只能单节点的(那你用微服务架构干啥?),所以想搞一下k8s集群,网上找了下资料没有视频专门讲这一块,自己找了很多资料,搞了蛮长时间的,所以记录一下 1.安装k8s和管理界面kuboard 高版本的k8s已经抛弃了docker,容器使用的话要安装1

    2023年04月22日
    浏览(29)
  • k8s集群部署springboot项目

    本篇,我们将基于k8s集群,模拟一个比较接近实际业务的使用场景,使用k8s集群部署一个springboot的项目,我们的需求是: 部署SpringBoot项目到阿里云服务器 ; 基于容器打包,推送私有镜像仓库 ; 采用K8S集群部署,对外暴露服务,pod副本扩容,公网可以访问 ; 基于阿里云服

    2024年02月02日
    浏览(46)
  • k8s的jenkins部署java项目到k8s集群cicd持续集成

    k8s1.16.0-k8s的jenkins部署java项目到k8s集群cicd(ci成,cd手动部署的) 注意: 本文档只是实现了ci,cd是通过ci生成的镜像,再手工再k8s-master执行的部署(只因pod部署的jenkins连接k8s的认证不知怎么操作,若jenkins是单独部署在k8s-master机器上,能直接在master执行kubectl命令就没这个问题了

    2024年02月03日
    浏览(63)
  • 【Go】K8s 管理系统项目[Jenkins Pipeline K8s环境–应用部署]

    考虑到实际工作中前后端可能是不同的同学完成,一般Api部分完成后改动会比较小,web部分改动会比较频繁.于是将api和web分了2个pipeline实现 docker目录存放镜像构建相关文件 k8s-plantform-api 存放api部分代码 Jenkinsfile用作pipeline配置 yaml用作生成k8s下k8s-plantform-api相关资源 1.1.1 docker目

    2023年04月08日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包