实现actuator访问安全

actuator被用于实现程序的监控,但是直接暴露相关接口非常危险,此处就需要探讨一下如何保证安全

危险来源

在SpringCloud的常规架构中,使用Gateway对外暴露服务,其他服务由Gateway代为转发。再搭建Admin用于GUI展示,利用注册中心获取实例地址端口。

一般情况下,只有Gateway会提供外网访问,其他微服务仅在内网访问,而Admin访问的时候也是获取的内网地址,比较安全。

所以最危险的就是Gateway。

最简单直接的安全方案,就是给Gateway加上Security,Admin访问的时候带上认证信息。

但是该方案过于繁琐,不是很和我心意。

经过一番搜索后,找到另一种方案,actuator支持使用别的端口。

更换端口

只需要如下配置

1
management.server.port=2999

这样程序会使用两个端口,一个普通的请求端口,一个actuator使用的端口。

但是,经过我的测试,实际上两个端口都能访问actuator,意思就是不管哪个端口,都可以访问http://ip:port/actuator/health等url。

关闭普通端口访问

既然新端口可以访问,那就把原本端口的相关路由给关闭了即可,给Gateway加入以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Bean
public RouteLocator locatorProd(RouteLocatorBuilder builder) {
//屏蔽监控端点
contract.route("actuator", f -> f.path("/actuator/**").filters(new Function<GatewayFilterSpec, UriSpec>() {
@Override
public UriSpec apply(GatewayFilterSpec gatewayFilterSpec) {
return gatewayFilterSpec.filter(new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 直接404
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.NOT_FOUND);
return response.writeWith(Mono.just(response.bufferFactory().wrap("404了".getBytes(StandardCharsets.UTF_8))));
}
});
}
}).uri("lb://404"));//反正不会到后面,地址随便填一个就行
return contract.build();
}

这里使用了一个过滤器,直接返回404状态。

admin访问

关闭的普通端口的访问,同时Admin那边也不能访问了,因为Admin还在访问原端口。

在翻阅admin文档后,只要在注册中心中把使用的新端口带上即可。于是增加以下配置:

1
eureka.instance.metadata-map.management.port=${management.server.port}

现在基本就可以正常访问了。但是我还想将这个新端口随机化,将来如果网关如果要更新,也能少设置一个端口,减少端口冲突的风险

随机端口

只需要port=0就是随机端口了。

但是这又有一个新问题,由于配置文件将端口设置为0,那么注册中心记录的端口也还是0,admin就无法访问了。


解决思路是将端口的获取由代码完成,并且将端口写入配置项中。

这里可以采用jasypt,这是一套主要用于配置项加密的库,但是两项功能没有本质区别。只需要实现一个自定义的解密器用来获取随机端口,后续的写入配置项操作就由库负责处理。


正如我之前不愿意引入security一样,引入jasypt也不和我心意。

于是我自己琢磨出了一套方案,使用EnvironmentPostProcessor,实现方法如下:

  • 创建一个EnvironmentPostProcessor实现类,基本代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class PortEnvironmentPostProcessor implements EnvironmentPostProcessor {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, S> pringApplication application) {
    Properties properties = new Properties();
    int availableTcpPort = SocketUtils.findAvailableTcpPort(6001, 12999);

    properties.put("management.server.port",availableTcpPort);
    properties.put("eureka.instance.metadata-map.management.port",availableTcpPort);
    PropertiesPropertySource source = new PropertiesPropertySource("CONSUME", properties);
    environment.getPropertySources().addFirst(source);
    }
    }

  • 创建META-INF/spring.factories文件,内容如下

    org.springframework.boot.env.EnvironmentPostProcessor=cn.inkroom.study.cloud.gateway.PortEnvironmentPostProcessor

需要特别注明几点:

  • 是否覆盖原本配置文件中的某个配置项是有调用addFirst还是addLast方法决定的,越在前面的优先级越高
  • 默认情况下,自定义的PortEnvironmentPostProcessor总是在第一个被调用,因此无法获取其他配置项,意思是不能用于加解密,但是可以用于提供一些来自别的途径,较为动态的配置项

除了更换端口外,还可以更换context-path,但是我就没有再做测试了。


实现actuator访问安全
http://blog.inkroom.cn/2021/07/23/2TE8ATE.html
作者
inkbox
发布于
2021年7月23日
许可协议