当前位置: 代码网 > it编程>编程语言>Java > SpringBoot Admin升级boot等组件版本后无法监控微服务问题

SpringBoot Admin升级boot等组件版本后无法监控微服务问题

2024年08月14日 Java 我要评论
最近,安全中心报告了spring-boot-admin服务由于集成security版本过低导致了安全问题,spring security regexrequestmatcher 认证绕过漏洞(cve-

最近,安全中心报告了spring-boot-admin服务由于集成security版本过低导致了安全问题,spring security regexrequestmatcher 认证绕过漏洞(cve-2022-22978),建议升级security版本,

  • 5.5.x 版本使用者建议升级至5.5.7及其以上
  • 5.6.x 版本使用者建议升级至5.6.4及其以上。

排查服务发现当前服务集成security版本为5.3.9,于是决定动手升级。

一、升级版本

由于项目通过集成

<dependency>
   <groupid>org.springframework.boot</groupid>
   <artifactid>spring-boot-starter-security</artifactid>
</dependency>

接入security,因此security版本由springboot统一管理。

工程中各依赖版本如下:

<spring-boot.version>2.3.12.release</spring-boot.version>
<spring-cloud.version>hoxton.sr12</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.6.release</spring-cloud-alibaba.version>

于是为了满足上述升级需求,我们对该服务进行版本调整。

<spring-boot.version>2.6.8</spring-boot.version>
<spring-cloud.version>2021.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2021.0.1.0</spring-cloud-alibaba.version>

springboot升级到2.4+之后,不再支持bootstrap版本,因此服务此时需要调整配置由bootstrap.yml转变为application.yml。

spring:
  application:
    name: xxx
  cloud:
    nacos:
      config:
        username: xxx
        password: xxx
        server-addr: xxx:8848
        namespace: xxx
        group: default_group
        refresh-enabled: false
  config:
    import:
      - optional:nacos:xxx.properties?group=${spring.cloud.nacos.config.group}&refreshenabled=false
      - optional:nacos:xxx.properties?group=${spring.cloud.nacos.config.group}&refreshenabled=false

工程可以正常启动,但访问监控页面,监控服务健康检查处于down状态,且无元数据。

二、排查原因

排查源码发现spring-boot-admin-server-cloud源码包中

instancediscoverylistener通过监听event事件动态更新监控服务状态。

服务启动后通过监听事件调用

instancediscoverylistener.discover()方法。

protected void discover() {
		log.debug("discovering new instances from discoveryclient");
		flux.fromiterable(discoveryclient.getservices()).filter(this::shouldregisterservice)
				.flatmapiterable(discoveryclient::getinstances).filter(this::shouldregisterinstancebasedonmetadata)
				.flatmap(this::registerinstance).collect(collectors.toset()).flatmap(this::removestaleinstances)
				.subscribe((v) -> {
				}, (ex) -> log.error("unexpected error.", ex));
	}

方法首先会从注册中心获取注册服务,之后屏蔽不需要监控的服务,再进行bean转换。

此处需要注意discoveryclient::getinstances方法,即bean转换方法。

由于服务注册中心为consul,因此getinstance会调用

consuldiscoveryclient.getinstances()。

 public list<serviceinstance> getinstances(final string serviceid, final queryparams queryparams) {
        list<serviceinstance> instances = new arraylist();
        this.addinstancestolist(instances, serviceid, queryparams);
        return instances;
    }

    private void addinstancestolist(list<serviceinstance> instances, string serviceid, queryparams queryparams) {
        builder requestbuilder = healthservicesrequest.newbuilder().setpassing(this.properties.isquerypassing()).setqueryparams(queryparams).settoken(this.properties.getacltoken());
        string[] querytags = this.properties.getquerytagsforservice(serviceid);
        if (querytags != null) {
            requestbuilder.settags(querytags);
        }

        healthservicesrequest request = requestbuilder.build();
        response<list<healthservice>> services = this.client.gethealthservices(serviceid, request);
        iterator var8 = ((list)services.getvalue()).iterator();

        while(var8.hasnext()) {
            healthservice service = (healthservice)var8.next();
            instances.add(new consulserviceinstance(service, serviceid));
        }

    }

方法中真正的bean转换在于new consulserviceinstance(service, serviceid)。

该构造器方法如下:

   public consulserviceinstance(healthservice healthservice, string serviceid) {
        this(healthservice.getservice().getid(), serviceid, consulserverutils.findhost(healthservice), healthservice.getservice().getport(), getsecure(healthservice), getmetadata(healthservice), healthservice.getservice().gettags());
        this.healthservice = healthservice;
    }

可见metadata数据通过getmetadata()方法获取。

    private static map<string, string> getmetadata(healthservice healthservice) {
        map<string, string> metadata = healthservice.getservice().getmeta();
        if (metadata == null) {
            metadata = new linkedhashmap();
        }

        return (map)metadata;
    }

从代码看出,metadata数据从service实例的meta属性中获取。而由于监控服务统一注册tag标签而非meta到注册中心,因此此处数据获取为空,从而导致spring-boot-admin 元数据及监控路径拼接异常,进而服务监控失败,状态down。

那么为什么之前可以正常监控,升级版本之后便监控异常呢?

通过回退之前版本我们发现,老版本中meta数据获取方法并非当前这样。

private void addinstancestolist(list<serviceinstance> instances, string serviceid, queryparams queryparams) {
        builder requestbuilder = healthservicesrequest.newbuilder().setpassing(this.properties.isquerypassing()).setqueryparams(queryparams).settoken(this.properties.getacltoken());
        string querytag = this.properties.getquerytagforservice(serviceid);
        if (querytag != null) {
            requestbuilder.settag(querytag);
        }

        healthservicesrequest request = requestbuilder.build();
        response<list<healthservice>> services = this.client.gethealthservices(serviceid, request);

        healthservice service;
        string host;
        map metadata;
        boolean secure;
        for(iterator var8 = ((list)services.getvalue()).iterator(); var8.hasnext(); instances.add(new defaultserviceinstance(service.getservice().getid(), serviceid, host, service.getservice().getport(), secure, metadata))) {
            service = (healthservice)var8.next();
            host = consulserverutils.findhost(service);
            metadata = consulserverutils.getmetadata(service, this.properties.istagsasmetadata());
            secure = false;
            if (metadata != null && metadata.containskey("secure")) {
                secure = boolean.parseboolean((string)metadata.get("secure"));
            }
        }

    }

老版本通过调用consulserverutils.getmetadata(service, this.properties.istagsasmetadata());获取meta数据。

    @deprecated
    public static map<string, string> getmetadata(healthservice healthservice, boolean tagsasmetadata) {
        return tagsasmetadata ? getmetadata(healthservice.getservice().gettags()) : healthservice.getservice().getmeta();
    }

此版本中metadata通过tagsasmetadata 属性标识判断是否可从tags中获取metadata,由于该属性默认为true,因此我们之前注册到tags的属性可以被admin标记为metadata,从而参与到

defaultserviceinstanceconverter.convert()实现对registration对象的组装。

 protected mono<instanceid> registerinstance(serviceinstance instance) {
        try {
            registration registration = this.converter.convert(instance).tobuilder().source("discovery").build();
            log.debug("registering discovered instance {}", registration);
            return this.registry.register(registration);
        } catch (exception var3) {
            log.error("couldn't register instance for discovered instance ({})", this.tostring(instance), var3);
            return mono.empty();
        }
    }

此时,我们只需要让监控服务注册meta到consul注册中心即可。

根据spring-cloud-consul官方文档spring cloud consul, metadata使用实例如下(也可使用properties文件,自行转换即可)

此处有特别说明:

参考defaultserviceinstanceconverter所定义的key属性,于是我们修改配置文件,再次重新启动微服务。

此时,服务再次报错。

operationexception{statuscode=400, statusmessage='bad request', statuscontent='invalid service meta: couldn't load metadata pair ('management.context-path', '/xxx/actuator'): key contains invalid characters'}

根据异常栈,我们服务该异常由调用consul服务端rpc之后response响应体返回。

由于是远端服务返回,考虑版本兼容性问题,直接升级最高版本consul,升级之后发现异常仍然存在。

为了验证该问题,我们切换注册中心到nacos,发现meta注册正常,监控正常。

到此,该问题排查基本结束,定义为spring-boot-admin与spring-cloud-consul版本兼容性问题。

spring-cloud-consul在v3.0.0(不包含m1等版本)及之上,meta数据获取不再兼容tags,因此使用该版本的cloud在于admin集成时,若监控服务存在context-path或者自定义的健康检查路径等,由于admin定义meata属性key均包含"."字符,因此大概率均会出现监控异常问题(推测结论,未完整验证所有版本)。

目前,已提issue到github上spring-boot-admin中,参见https://github.com/codecentric/spring-boot-admin/issues/2076,等待官方解答中。

***************************************

该问题目前已解决,根据版本匹配关系,spring-boot需升级到2.7.0版本才可,而非2.6.8。

spring-boot-admin在2.7.4版本对metadatda做了兼容处理。

且官方文档也指出

同时,据回复,consul端也在推进处理此问题

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com