From 7a173757f0d9d22e5a920d8b3c85a7a15f722659 Mon Sep 17 00:00:00 2001 From: = <=> Date: Thu, 7 Dec 2023 11:03:54 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AB=AF=E5=8F=A3=E6=98=A0=E5=B0=84=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E8=A7=84=E5=88=99=E7=95=8C=E9=9D=A2=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=90=8E=E5=8D=B3=E6=97=B6=E7=94=9F=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/system/securityRule.vue | 2 +- .../src/main/resources/app.yml | 2 +- .../req/proxy/PortMappingCreateReq.java | 6 +++ .../server/dal/entity/SecurityRuleDO.java | 6 ++- .../proxy/core/TcpVisitorChannelHandler.java | 8 ++-- .../proxy/core/UdpVisitorChannelHandler.java | 8 ++-- .../server/service/PortMappingService.java | 43 +++++++++++++++---- .../server/service/SecurityGroupService.java | 16 +++++-- 8 files changed, 64 insertions(+), 27 deletions(-) diff --git a/neutrino-proxy-admin/src/views/system/securityRule.vue b/neutrino-proxy-admin/src/views/system/securityRule.vue index fb9c8a64..d103e6e6 100644 --- a/neutrino-proxy-admin/src/views/system/securityRule.vue +++ b/neutrino-proxy-admin/src/views/system/securityRule.vue @@ -86,7 +86,7 @@
规则描述:
-
单个ip:192.168.1.1,0:0:0:0:0:0:10.0.0.1, ipv6只支持单个ip判断
+
单个ip:192.168.1.1, AA22:BB11:1122:CDEF:1234:AA99:7654:7410, ipv6只支持单个ip判断
范围类型:192.168.1.0-192.168.1.255
掩码类型:192.168.1.0/24
泛型:0.0.0.0/ALL
diff --git a/neutrino-proxy-client/src/main/resources/app.yml b/neutrino-proxy-client/src/main/resources/app.yml index c513862e..7ed0a997 100644 --- a/neutrino-proxy-client/src/main/resources/app.yml +++ b/neutrino-proxy-client/src/main/resources/app.yml @@ -44,7 +44,7 @@ neutrino: # 是否启用SSL(注意:该配置必须和server-port对应上) ssl-enable: ${SSL_ENABLE:true} # 客户端连接唯一凭证 - license-key: ${LICENSE_KEY:} + license-key: ${LICENSE_KEY:b0a907332b474b25897c4dcb31fc7eb6} # 客户端唯一身份标识(可忽略,若不设置首次启动会自动生成) client-id: ${CLIENT_ID:} # 是否开启隧道传输报文日志(日志级别为debug时开启才有效) diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/PortMappingCreateReq.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/PortMappingCreateReq.java index 5270e8c6..1ee94e0c 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/PortMappingCreateReq.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/PortMappingCreateReq.java @@ -62,6 +62,12 @@ public class PortMappingCreateReq { * 代理超时时间 */ private Long proxyTimeoutMs; + + /** + * 安全组Id + */ + private Integer securityGroupId; + /** * 描述 */ diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/SecurityRuleDO.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/SecurityRuleDO.java index 048ca204..9a3240d4 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/SecurityRuleDO.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/SecurityRuleDO.java @@ -86,7 +86,7 @@ public class SecurityRuleDO { * @param ip 指定的IP * @return 放行状态 */ - public SecurityRulePassTypeEnum allow(String ip) { + public SecurityRulePassTypeEnum judge(String ip) { // 被判断的IP地址为空,不做判断 if (StrUtil.isEmpty(ip)) { @@ -108,9 +108,11 @@ public class SecurityRuleDO { String[] rules = this.rule.split(","); for (String rule : rules) { + rule = rule.trim(); + // 单个ip,ipv6在此步已处理,后面不需要额外判断ipv6的情况 if (rule.matches("(\\d+\\.){3}\\d+") || isIpv6) { - if (rule.equals(ip)) { + if (rule.equalsIgnoreCase(ip)) { return passType == SecurityRulePassTypeEnum.ALLOW ? SecurityRulePassTypeEnum.ALLOW : SecurityRulePassTypeEnum.DENY; } } diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/core/TcpVisitorChannelHandler.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/core/TcpVisitorChannelHandler.java index 8dcb9bda..d9aa25a9 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/core/TcpVisitorChannelHandler.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/core/TcpVisitorChannelHandler.java @@ -29,11 +29,9 @@ import java.net.InetSocketAddress; @Slf4j public class TcpVisitorChannelHandler extends SimpleChannelInboundHandler { - @Inject - private SecurityGroupService securityGroupService; + private final SecurityGroupService securityGroupService = Solon.context().getBean(SecurityGroupService.class); - @Inject - private PortMappingService portMappingService; + private final PortMappingService portMappingService = Solon.context().getBean(PortMappingService.class); @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { @@ -74,7 +72,7 @@ public class TcpVisitorChannelHandler extends SimpleChannelInboundHandler { - @Inject - private SecurityGroupService securityGroupService; + private final SecurityGroupService securityGroupService = Solon.context().getBean(SecurityGroupService.class); - @Inject - private PortMappingService portMappingService; + private final PortMappingService portMappingService = Solon.context().getBean(PortMappingService.class); @Override protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket datagramPacket) throws Exception { @@ -130,7 +128,7 @@ public class UdpVisitorChannelHandler extends SimpleChannelInboundHandler portToSecurityGroupMap = new ConcurrentHashMap<>(); + private final Map mappingPortToSecurityGroupMap = new ConcurrentHashMap<>(); public PageInfo page(PageQuery pageQuery, PortMappingListReq req) { if (StringUtils.isNotEmpty(req.getDescription())) { @@ -166,6 +167,9 @@ public class PortMappingService implements LifecycleBean { if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(proxyConfig.getServer().getTcp().getDomainName()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) { ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort()); } + + updateMappingPortToSecurityGroupMap(portMappingDO.getServerPort(), req.getSecurityGroupId()); + return new PortMappingCreateRes(); } @@ -203,6 +207,8 @@ public class PortMappingService implements LifecycleBean { if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(proxyConfig.getServer().getTcp().getDomainName()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) { ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort()); } + + updateMappingPortToSecurityGroupMap(portMappingDO.getServerPort(), req.getSecurityGroupId()); } public PortMappingDetailRes detail(Integer id) { @@ -277,6 +283,8 @@ public class PortMappingService implements LifecycleBean { if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) { ProxyUtil.removeSubdomainToServerPort(portMappingDO.getSubdomain()); } + + updateMappingPortToSecurityGroupMap(portMappingDO.getServerPort(), null); } public void portBindSecurityGroup(Integer portMappingId, Integer groupId) { @@ -287,7 +295,7 @@ public class PortMappingService implements LifecycleBean { mappingDO.setSecurityGroupId(groupId); mappingDO.setUpdateTime(new Date()); portMappingMapper.updateById(mappingDO); - portToSecurityGroupMap.put(mappingDO.getServerPort(), groupId); + updateMappingPortToSecurityGroupMap(mappingDO.getServerPort(), groupId); } public void portUnbindSecurityGroup(Integer portMappingId) { @@ -298,7 +306,7 @@ public class PortMappingService implements LifecycleBean { mappingDO.setSecurityGroupId(0); mappingDO.setUpdateTime(new Date()); portMappingMapper.updateById(mappingDO); - portToSecurityGroupMap.remove(mappingDO.getServerPort()); + updateMappingPortToSecurityGroupMap(mappingDO.getServerPort(), null); } /** @@ -311,8 +319,8 @@ public class PortMappingService implements LifecycleBean { return portMappingMapper.findEnableListByLicenseId(licenseId); } - public Integer getSecurityGroupIdByMappingPor(Integer port) { - return portToSecurityGroupMap.get(port); + public Integer getSecurityGroupIdByMappingPort(Integer port) { + return mappingPortToSecurityGroupMap.get(port); } @@ -327,11 +335,22 @@ public class PortMappingService implements LifecycleBean { } portMappingMapper.updateOnlineStatus(OnlineStatusEnum.OFFLINE.getStatus(), new Date()); + List allMappingDOList = portMappingMapper.selectList(Wrappers.lambdaQuery(PortMappingDO.class)); + allMappingDOList.forEach(item -> { + Integer securityGroupId = item.getSecurityGroupId(); + if (securityGroupId != null && securityGroupId > 0) { + updateMappingPortToSecurityGroupMap(item.getServerPort(), item.getSecurityGroupId()); + } + }); + // 未配置域名,则不需要处理域名映射逻辑 if (StrUtil.isBlank(proxyConfig.getServer().getTcp().getDomainName())) { return; } - List portMappingDOList = portMappingMapper.selectList(new LambdaQueryWrapper().eq(PortMappingDO::getProtocal, NetworkProtocolEnum.HTTP.getDesc()).isNotNull(PortMappingDO::getSubdomain)); + List portMappingDOList = allMappingDOList.stream() + .filter(item -> NetworkProtocolEnum.HTTP.getDesc().equals(item.getProtocal()) && item.getSubdomain() != null) + .collect(Collectors.toList()); +// List portMappingDOList = portMappingMapper.selectList(new LambdaQueryWrapper().eq(PortMappingDO::getProtocal, NetworkProtocolEnum.HTTP.getDesc()).isNotNull(PortMappingDO::getSubdomain)); if (CollectionUtil.isEmpty(portMappingDOList)) { return; } @@ -340,12 +359,18 @@ public class PortMappingService implements LifecycleBean { return; } ProxyUtil.setSubdomainToServerPort(item.getSubdomain(), item.getServerPort()); - if (item.getSecurityGroupId() != null) { - portToSecurityGroupMap.put(item.getServerPort(), item.getSecurityGroupId()); - } + }); } + private void updateMappingPortToSecurityGroupMap(Integer serverPort, Integer securityGroupId) { + if (securityGroupId == null || securityGroupId == 0) { + mappingPortToSecurityGroupMap.remove(serverPort); + return; + } + mappingPortToSecurityGroupMap.put(serverPort, securityGroupId); + } + @Override public void start() throws Throwable { diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/SecurityGroupService.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/SecurityGroupService.java index 88732379..6411379a 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/SecurityGroupService.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/SecurityGroupService.java @@ -149,24 +149,30 @@ public class SecurityGroupService { * @return 是否放行 */ public boolean judgeAllow(String ip, Integer groupId) { - + ip = ip.toLowerCase(); // 不能判断当前连接的IP,保守处理,拒绝放行 if (StrUtil.isEmpty(ip)) { + log.debug("【安全组】不能正确获取到IP地址,保守处理,拒绝放行"); return false; } // 黑名单规则,没有该安全组,则放行 if (groupId == null) { + log.debug("【安全组】{}:该IP访问的端口映射没有绑定安全组(1), 放行", ip); return true; } SecurityGroupDO groupDO = securityGroupMap.get(groupId); if (groupDO == null) { + log.debug("【安全组】{}:该IP访问的端口映射没有绑定安全组(2), 放行", ip); return true; } + Boolean allow = null; String judgeAllowMapKey = ip + groupId; if (ipAllowControlCache.containsKey(judgeAllowMapKey)) { - return ipAllowControlCache.get(judgeAllowMapKey); + allow = ipAllowControlCache.get(judgeAllowMapKey); + log.debug("【安全组】{}-安全组{}:该IP在缓存中,缓存策略为{}", ip, groupId, allow ? "允许" : "拒绝"); + return allow; } List ruleDOList = securityRuleMapper.selectList(Wrappers.lambdaQuery(SecurityRuleDO.class) @@ -174,15 +180,16 @@ public class SecurityGroupService { .eq(SecurityRuleDO::getEnable, EnableStatusEnum.ENABLE) .orderByAsc(SecurityRuleDO::getPriority) ); - Boolean allow = null; for (SecurityRuleDO ruleDO : ruleDOList) { - SecurityRulePassTypeEnum passType = ruleDO.allow(ip); + SecurityRulePassTypeEnum passType = ruleDO.judge(ip); if (passType == SecurityRulePassTypeEnum.ALLOW) { allow = true; + log.debug("【安全组】{}-安全组{}:匹配到安全规则{}行为:{}", ip, groupId, ruleDO.getId(), "允许"); break; } if (passType == SecurityRulePassTypeEnum.DENY) { allow = false; + log.info("【安全组】{}-安全组{}:匹配到安全规则{}行为:{}", ip, groupId, ruleDO.getId(), "拒绝"); break; } } @@ -190,6 +197,7 @@ public class SecurityGroupService { // 当前IP没有匹配到任何一条规则,则使用安全组默认规则 if (allow == null) { allow = groupDO.getDefaultPassType() == SecurityRulePassTypeEnum.ALLOW; + log.debug("【安全组】{}-安全组{}:使用安全组默认放行类型:{}", ip, groupId, allow ? "允许" : "拒绝"); } ipAllowControlCache.put(judgeAllowMapKey, allow);