规则判断

This commit is contained in:
=
2023-12-05 16:16:06 +08:00
parent a9fc8f4cb4
commit 306f2ed4d3
9 changed files with 146 additions and 28 deletions
+5
View File
@@ -30,6 +30,11 @@
<artifactId>hutool-core</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-cache</artifactId>
<version>${hutool.version}</version>
</dependency>
</dependencies>
<build>
@@ -6,8 +6,9 @@ import lombok.Getter;
@AllArgsConstructor
@Getter
public enum SecurityRulePassTypeEnum {
REJECT(0, "reject"),
ALLOW(1, "allow")
DENY(-1, "DENY"),
ALLOW(1, "allow"),
NONE(0, "none")
;
private final Integer code;
@@ -1,7 +1,7 @@
package org.dromara.neutrinoproxy.server.dal;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.dromara.neutrinoproxy.server.dal.entity.SecurityRule;
import org.dromara.neutrinoproxy.server.dal.entity.SecurityRuleDO;
public interface SecurityRuleMapper extends BaseMapper<SecurityRule> {
public interface SecurityRuleMapper extends BaseMapper<SecurityRuleDO> {
}
@@ -38,7 +38,7 @@ public class SecurityGroupDO {
* 启用状态
* {@link EnableStatusEnum}
*/
private Integer enable;
private EnableStatusEnum enable;
/**
* 创建时间
*/
@@ -10,7 +10,6 @@ import lombok.ToString;
import lombok.experimental.Accessors;
import org.dromara.neutrinoproxy.server.constant.EnableStatusEnum;
import org.dromara.neutrinoproxy.server.constant.SecurityRulePassTypeEnum;
import org.noear.solon.core.util.IpUtil;
import java.util.Date;
@@ -18,7 +17,7 @@ import java.util.Date;
@ToString
@Accessors(chain = true)
@TableName("security_rule")
public class SecurityRule {
public class SecurityRuleDO {
@TableId(type = IdType.AUTO)
private Integer id;
@@ -68,7 +67,7 @@ public class SecurityRule {
* 启用状态
* {@link EnableStatusEnum}
*/
private Integer enable;
private EnableStatusEnum enable;
/**
* 创建时间
*/
@@ -83,16 +82,16 @@ public class SecurityRule {
* @param ip
* @return
*/
public boolean allow(String ip) {
public SecurityRulePassTypeEnum allow(String ip) {
// 被判断的IP地址为空允许访问
// 被判断的IP地址为空做判断
if (StrUtil.isEmpty(ip)) {
return false;
return SecurityRulePassTypeEnum.NONE;
}
// 没有规则默认允许访问
if (StrUtil.isEmpty(rule)) {
return true;
return SecurityRulePassTypeEnum.ALLOW;
}
// ipv6只适配单ip形式
@@ -107,14 +106,16 @@ public class SecurityRule {
// 单个ip,ipv6在此步已处理后面不需要额外判断ipv6的情况
if (rule.matches("(\\d+\\.){3}\\d+") || isIpv6) {
return passType == SecurityRulePassTypeEnum.ALLOW && rule.equals(ip);
if (rule.equals(ip)) {
return passType == SecurityRulePassTypeEnum.ALLOW ? SecurityRulePassTypeEnum.ALLOW : SecurityRulePassTypeEnum.DENY;
}
}
// 范围类型
if (rule.matches("(\\d+\\.){3}\\d+-(\\d+\\.){3}\\d+")) {
String[] ipRange = rule.split("-");
if (ipRange[0].compareTo(ip) <= 0 && ip.compareTo(ipRange[1]) <= 0) {
return passType == SecurityRulePassTypeEnum.ALLOW;
return passType == SecurityRulePassTypeEnum.ALLOW ? SecurityRulePassTypeEnum.ALLOW : SecurityRulePassTypeEnum.DENY;
}
}
@@ -124,18 +125,18 @@ public class SecurityRule {
Long beginIp = Ipv4Util.getBeginIpLong(netIp[0], Integer.valueOf(netIp[1]));
Long endIp = Ipv4Util.getEndIpLong(netIp[0], Integer.valueOf(netIp[1]));
if (beginIp <= ipLong && ipLong <= endIp) {
return passType == SecurityRulePassTypeEnum.ALLOW;
return passType == SecurityRulePassTypeEnum.ALLOW ? SecurityRulePassTypeEnum.ALLOW : SecurityRulePassTypeEnum.DENY;
}
}
if (rule.equalsIgnoreCase("ALL") || rule.equals("0.0.0.0") || rule.equals("0..0.0.0/0")) {
return passType == SecurityRulePassTypeEnum.ALLOW;
return passType == SecurityRulePassTypeEnum.ALLOW ? SecurityRulePassTypeEnum.ALLOW : SecurityRulePassTypeEnum.DENY;
}
}
// 都没有匹配到默认放行
return true;
// 都没有匹配到
return SecurityRulePassTypeEnum.NONE;
}
}
@@ -1,6 +1,11 @@
package org.dromara.neutrinoproxy.server.proxy.core;
import cn.hutool.core.util.StrUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.dromara.neutrinoproxy.core.Constants;
import org.dromara.neutrinoproxy.core.ProxyMessage;
@@ -8,11 +13,6 @@ import org.dromara.neutrinoproxy.server.constant.NetworkProtocolEnum;
import org.dromara.neutrinoproxy.server.proxy.domain.VisitorChannelAttachInfo;
import org.dromara.neutrinoproxy.server.service.FlowReportService;
import org.dromara.neutrinoproxy.server.util.ProxyUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption;
import io.netty.channel.SimpleChannelInboundHandler;
import org.noear.solon.Solon;
import java.net.InetSocketAddress;
@@ -62,8 +62,11 @@ public class TcpVisitorChannelHandler extends SimpleChannelInboundHandler<ByteBu
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel visitorChannel = ctx.channel();
InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress();
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(sa.getPort());
// 判断IP是否在该端口绑定的安全组允许的规则内
Channel cmdChannel = ProxyUtil.getCmdChannelByServerPort(sa.getPort());
if (null == cmdChannel) {
// 该端口还没有代理客户端
ctx.channel().close();
@@ -0,0 +1,76 @@
package org.dromara.neutrinoproxy.server.service;
import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.apache.ibatis.solon.annotation.Db;
import org.dromara.neutrinoproxy.server.constant.EnableStatusEnum;
import org.dromara.neutrinoproxy.server.constant.SecurityRulePassTypeEnum;
import org.dromara.neutrinoproxy.server.dal.SecurityGroupMapper;
import org.dromara.neutrinoproxy.server.dal.SecurityRuleMapper;
import org.dromara.neutrinoproxy.server.dal.entity.SecurityGroupDO;
import org.dromara.neutrinoproxy.server.dal.entity.SecurityRuleDO;
import org.noear.solon.annotation.Component;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class SecurityGroupService {
@Db
private SecurityGroupMapper securityGroupMapper;
@Db
private SecurityRuleMapper securityRuleMapper;
private Map<Integer, SecurityGroupDO> securityGroupMap = new ConcurrentHashMap<>();
// 允许通过控制的缓存,缓存类型最近最久未使用缓存,容量100,超时时间5分钟
private Cache<String, Boolean> ipAllowControlCache = CacheUtil.newLRUCache(100, 1000 * 60 * 5);
public void init() {
List<SecurityGroupDO> groupDOList = securityGroupMapper.selectList(Wrappers.lambdaQuery(SecurityGroupDO.class));
groupDOList.forEach(securityGroupDO -> securityGroupMap.put(securityGroupDO.getId(), securityGroupDO));
}
public boolean judgeAllow(String ip, Integer groupId) {
SecurityGroupDO groupDO = securityGroupMap.get(groupId);
if (groupDO == null || groupDO.getEnable() == EnableStatusEnum.DISABLE) {
return true;
}
String judgeAllowMapKey = ip + groupId;
if (ipAllowControlCache.containsKey(judgeAllowMapKey)) {
return ipAllowControlCache.get(judgeAllowMapKey);
}
List<SecurityRuleDO> ruleDOList = securityRuleMapper.selectList(Wrappers.lambdaQuery(SecurityRuleDO.class)
.eq(SecurityRuleDO::getGroupId, groupId)
.orderByAsc(SecurityRuleDO::getPriority)
);
Boolean allow = null;
for (SecurityRuleDO ruleDO : ruleDOList) {
SecurityRulePassTypeEnum passType = ruleDO.allow(ip);
if (passType == SecurityRulePassTypeEnum.ALLOW) {
allow = true;
break;
}
if (passType == SecurityRulePassTypeEnum.DENY) {
allow = false;
break;
}
}
if (allow == null) {
allow = true;
}
// 当前IP没有匹配到任何一条规则,则放行
ipAllowControlCache.put(judgeAllowMapKey, allow);
return allow;
}
}
@@ -74,13 +74,15 @@ neutrino:
data:
db:
# 数据库类型,目前支持h2、mysql、mariadb
type: ${DB_TYPE:h2}
# type: ${DB_TYPE:h2}
type: ${DB_TYPE:mysql}
# 数据库连接URL
url: ${DB_URL:jdbc:h2:file:./data/db;MODE=MySQL;AUTO_SERVER=TRUE}
# url: ${DB_URL:jdbc:h2:file:./data/db;MODE=MySQL;AUTO_SERVER=TRUE}
url: ${DB_URL:jdbc:mysql://okfly.vip:37889/neutrino-proxy?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true}
# 数据库用户名
username: ${DB_USER:}
username: ${DB_USER:root}
# 数据库密码
password: ${DB_PASSWORD:}
password: ${DB_PASSWORD:Root1234@}
#添加MIME印射(如果有需要?)
#是否启用静态文件服务。(可不配,默认为启用)
@@ -50,6 +50,35 @@ CREATE TABLE IF NOT EXISTS `port_group` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
#
CREATE TABLE IF NOT EXISTS `security_group` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(20) NOT NULL COMMENT '安全组名称',
`description` varchar(255) COMMENT '安全组描述',
`user_id` int NOT NULL COMMENT '用户ID',
`enable` int(1) NOT NULL COMMENT '启用状态',
`create_time` datetime(3) NOT NULL COMMENT '创建时间',
`update_time` datetime(3) NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
#
CREATE TABLE IF NOT EXISTS `security_rule` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`group_id` int NOT NULL COMMENT '关联安全组',
`name` varchar(20) NOT NULL COMMENT '规则名称',
`description` varchar(255) NOT NULL COMMENT '规则描述',
`rule` text NOT NULL COMMENT '规则内容',
`pass_type` int(1) NOT NULL COMMENT '放行类型',
`priority` int(1) NOT NULL COMMENT '优先级',
`user_id` int NOT NULL COMMENT '用户ID',
`enable` int(1) NOT NULL COMMENT '启用状态',
`create_time` datetime(3) NOT NULL COMMENT '创建时间',
`update_time` datetime(3) NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `I_group_id_priority` (`group_id`, `priority`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
##########################################################
#license表
CREATE TABLE IF NOT EXISTS `license` (
@@ -207,3 +236,4 @@ CREATE TABLE IF NOT EXISTS `flow_report_month` (
KEY `I_flow_report_month_user_id` (`user_id`),
KEY `I_flow_report_month_license_id` (`license_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;