规则判断
This commit is contained in:
@@ -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>
|
||||
|
||||
+3
-2
@@ -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;
|
||||
|
||||
+2
-2
@@ -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> {
|
||||
}
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ public class SecurityGroupDO {
|
||||
* 启用状态
|
||||
* {@link EnableStatusEnum}
|
||||
*/
|
||||
private Integer enable;
|
||||
private EnableStatusEnum enable;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
|
||||
+14
-13
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+9
-6
@@ -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();
|
||||
|
||||
+76
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user