美文网首页
soul从入门到放弃18--防火墙waf插件

soul从入门到放弃18--防火墙waf插件

作者: 滴流乱转的小胖子 | 来源:发表于2021-02-05 07:18 被阅读0次

一、前戏

waf插件,主要是用来对流量实现防火墙的核心功能:拦截非法请求、异常请求、拒绝策略

二、soul-admin配置

  • 插件配置

soul-admin –> 插件管理-> waf 设置为开启

注意模式的选择:

model 设置为 black 模式的时候,只有匹配的流量才会执行拒绝策略,不匹配的,直接会跳过。

model 设置为 mixed 模式的时候,所有的流量都会通过 waf插件,针对不同的匹配流量,用户可以设置是拒绝,还是通过。

  • 选择器配置

采用通配符匹配,匹配/http开头的请求

image
  • 规则配置

为了后面测试,配置两天规则,通过状态开关进行切换

规则1: 匹配 http开头的所有 如:http://127.0.0.1:9195/http/order/findById?id=2

image

测试结果如下:

image

规则2:匹配http开头,有userId参数的url,如:http://127.0.0.1:9195/http/test/findByUserId?userId=1

image

测试结果如下:

image.png

三、soul-bootstrap配置

  • pom引入依赖,重启网关
  <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>soul-spring-boot-starter-plugin-waf</artifactId>
      <version>${last.version}</version>
  </dependency>

四、WafPlugin源码分析

在父类AbstractSoulPlugin中判断插件是否可用、匹配选择器、匹配规则

public abstract class AbstractSoulPlugin implements SoulPlugin {
    public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
        String pluginName = named();
        // 获取插件配置
        final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName);
        // 是否开启插件,没有开启则传递下一下插件去处理
        if (pluginData != null && pluginData.getEnabled()) {
            // 获取选择器
            final Collection<SelectorData> selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName);
            if (CollectionUtils.isEmpty(selectors)) {
                return handleSelectorIsNull(pluginName, exchange, chain);
            }
            // 匹配选择器
            final SelectorData selectorData = matchSelector(exchange, selectors);
            if (Objects.isNull(selectorData)) {
                return handleSelectorIsNull(pluginName, exchange, chain);
            }
            selectorLog(selectorData, pluginName);
            // 获取规则列表
            final List<RuleData> rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId());
            if (CollectionUtils.isEmpty(rules)) {
                return handleRuleIsNull(pluginName, exchange, chain);
            }
            RuleData rule;
            // 如果是full,则获取最近的一个规则
            if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) {
                //get last
                rule = rules.get(rules.size() - 1);
            } else {
                // 根据规则匹配
                rule = matchRule(exchange, rules);
            }
            if (Objects.isNull(rule)) {
                return handleRuleIsNull(pluginName, exchange, chain);
            }
            ruleLog(rule, pluginName);
            // 交由子类WafPlugin去处理
            return doExecute(exchange, chain, selectorData, rule);
        }
        // 交由下一个插件去处理
        return chain.execute(exchange);
    }

对匹配规则的请求进行,进行拒绝策略的处理

public class WafPlugin extends AbstractSoulPlugin {
    protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
        // 获取Waf配置
        WafConfig wafConfig = Singleton.INST.get(WafConfig.class);
        // 如果没有匹配的选择器和规则
        if (Objects.isNull(selector) && Objects.isNull(rule)) {
            // 如果waf配置是black模式,则跳过。交由下一个插件处理
            if (WafModelEnum.BLACK.getName().equals(wafConfig.getModel())) {
                return chain.execute(exchange);
            }
            // 设置 403 status code
            exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
            // 设置 error 信息
            Object error = SoulResultWrap.error(HttpStatus.FORBIDDEN.value(), Constants.REJECT_MSG, null);
            // 返回response
            return WebFluxResultUtils.result(exchange, error);
        }
        // 获取规则配置信息
        String handle = rule.getHandle();
        WafHandle wafHandle = GsonUtils.getInstance().fromJson(handle, WafHandle.class);
        // 如规则为Null或者permission为空,交由下一个插件处理
        if (Objects.isNull(wafHandle) || StringUtils.isBlank(wafHandle.getPermission())) {
            log.error("waf handler can not configuration:{}", handle);
            return chain.execute(exchange);
        }
        // 如果permission是reject,则返回 403 status code
        if (WafEnum.REJECT.getName().equals(wafHandle.getPermission())) {
            exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
            Object error = SoulResultWrap.error(Integer.parseInt(wafHandle.getStatusCode()), Constants.REJECT_MSG, null);
            // 返回response
            return WebFluxResultUtils.result(exchange, error);
        }
        // 交由下一个插件处理
        return chain.execute(exchange);
    }
}

五、小结

  • 在AbstractSoulPlugin中匹配选择器和规则,在WafPlugin中处理拒绝策略
  • 日拱一卒

相关文章

网友评论

      本文标题:soul从入门到放弃18--防火墙waf插件

      本文链接:https://www.haomeiwen.com/subject/eehwtltx.html