Spring Cloud Gateway中对admin端点进行认证

前言

我们被扫了一个漏洞,SpringBoot Actuator 未授权访问,漏洞描述是这样的:
Actuator 是 springboot 提供的用来对应用系统进行自省和监控的功能模块,借助于 Actuator 开发者可以很方便地对应用系统某些监控指标进行查看、统计等。在 Actuator 启用的情况下,如果没有做好相关权限控制,非法用户可通过访问默认的执行器端点(endpoints)来获取应用系统中的监控信息,从而导致信息泄露甚至服务器被接管的事件发生

正文

如果没有对admin的端点进行鉴权,那么对于开放的网关服务,可以直接通过xx/actuator访问,这将是非常危险的,如果你还暴露了所有端点,那么还可以获取环境中的账号密码信息,即使admin做了脱敏。

要对端点进行鉴权,也非常简单,只需要要引入spring-security依赖即可,下面是Spring Cloud Gateway中的配置。

1、引入xml依赖
spring-boot-starter-web scope 是provided,引入gateway中不能有web

xml 复制代码
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-security</artifactId>
 </dependency>
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
     <scope>provided</scope>
 </dependency>

2、针对Admin端点认证的配置,只对/actuator/**进行认证,其他地址放行,使用业务自身认证。

java 复制代码
package com.frame.ops.admin.client.config;

import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

/**
 * 对客户端的actuator接口进行鉴权
 * 引入后,gateway 有性能问题
 */
@EnableWebFluxSecurity
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class ReactiveAdminSecurityConfig {

    @Bean
    @Order(1)
    public SecurityWebFilterChain authorizationServerSecurityFilterChain(ServerHttpSecurity http) {
        http.authorizeExchange()
                //只对actuator接口认证
               .pathMatchers("/actuator/**").authenticated()
               .anyExchange().permitAll()
               .and().httpBasic().and().csrf().disable();
        return http.build();
    }

}

3、如果是Servlet,配置为

java 复制代码
package com.frame.ops.admin.client.config;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.regex.Pattern;

/**
 * 对客户端的actuator接口进行鉴权
 */
@Order(1)
@EnableWebSecurity
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class AdminSecurityConfig extends WebSecurityConfigurerAdapter {

	//这个InMemoryUserDetailsManager,如果你的业务中也使用了spring security,那么需要自定义一   //个,防止admin认证使用自定义的处理逻辑
    @Autowired
    private InMemoryUserDetailsManager inMemoryUserDetailsManager;

    private static final String NOOP_PASSWORD_PREFIX = "{noop}";

    private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern.compile("^\\{.+}.*$");

    private static final Log logger = LogFactory.getLog(AdminSecurityConfig.class);

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(inMemoryUserDetailsManager);
    }

    @Bean
    public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties,
                                                                 ObjectProvider<PasswordEncoder> passwordEncoder) {
        SecurityProperties.User user = properties.getUser();
        List<String> roles = user.getRoles();
        return new InMemoryUserDetailsManager(
                User.withUsername(user.getName()).password(getOrDeducePassword(user, passwordEncoder.getIfAvailable()))
                        .roles(StringUtils.toStringArray(roles)).build());
    }

    private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder encoder) {
        String password = user.getPassword();
        if (user.isPasswordGenerated()) {
            logger.warn(String.format(
                    "%n%nUsing generated security password: %s%n%nThis generated password is for development use only. "
                            + "Your security configuration must be updated before running your application in "
                            + "production.%n",
                    user.getPassword()));
        }
        if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) {
            return password;
        }
        return NOOP_PASSWORD_PREFIX + password;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests()
                //只有actuator开头的请求才需要认证
                .antMatchers("/actuator/**").authenticated()
                .anyRequest().permitAll()
                .and().httpBasic();
    }

}

总结

⁉️ 奇怪的事来了,在本地测试gateway 请求正常,访问/actuator端点需要认证,但是在服务器上测试一些业务接口就会卡住超时,请求也没到下游服务,过段时间后gateway 假死,任何请求不通。

这个问题只发生在gateway中,对应servlet并没有发现这个问题,不清楚跟Gateway 使用Reactive 有没有关系, 这个问题暂未解决。但也不影响处理漏洞,只要去掉admin依赖,actuator依赖就行了。


作者其他要推荐的文章,欢迎来学习:

Prometheus 系列文章

  1. Prometheus 的介绍和安装
  2. 直观感受PromQL及其数据类型
  3. PromQL之选择器和运算符
  4. PromQL之函数
  5. Prometheus 告警机制介绍及命令解读
  6. Prometheus 告警模块配置深度解析
  7. Prometheus 配置身份认证
  8. Prometheus 动态拉取监控服务
  9. Prometheus 监控云Mysql和自建Mysql

Grafana 系列文章,版本:OOS v9.3.1

  1. Grafana 的介绍和安装
  2. Grafana监控大屏配置参数介绍(一)
  3. Grafana监控大屏配置参数介绍(二)
  4. Grafana监控大屏可视化图表
  5. Grafana 查询数据和转换数据
  6. Grafana 告警模块介绍
  7. Grafana 告警接入飞书通知

相关推荐
vim怎么退出23 分钟前
万字长文带你了解微前端架构
前端·微服务·前端框架
述雾学java2 小时前
Spring Cloud Feign 整合 Sentinel 实现服务降级与熔断保护
java·spring cloud·sentinel
Mr_Xuhhh5 小时前
信号与槽的总结
java·开发语言·数据库·c++·qt·系统架构
何苏三月5 小时前
SpringCloud系列 - Sentinel 服务保护(四)
spring·spring cloud·sentinel
小马爱打代码12 小时前
微服务外联Feign调用:第三方API调用的负载均衡与容灾实战
微服务·架构·负载均衡
麦兜*15 小时前
Spring Boot启动优化7板斧(延迟初始化、组件扫描精准打击、JVM参数调优):砍掉70%启动时间的魔鬼实践
java·jvm·spring boot·后端·spring·spring cloud·系统架构
卿着飞翔18 小时前
系统架构设计师论文分享-论软件过程模型及应用
系统架构
一眼万年041 天前
Redis Cluster模式
redis·微服务
要开心吖ZSH1 天前
微服务架构的演进:迈向云原生
java·微服务·云原生
慌糖2 天前
微服务介绍
微服务·云原生·架构