diff --git a/neutrino-proxy-core/src/main/java/org/dromara/neutrinoproxy/core/Constants.java b/neutrino-proxy-core/src/main/java/org/dromara/neutrinoproxy/core/Constants.java index 472f8ce9..ff647835 100644 --- a/neutrino-proxy-core/src/main/java/org/dromara/neutrinoproxy/core/Constants.java +++ b/neutrino-proxy-core/src/main/java/org/dromara/neutrinoproxy/core/Constants.java @@ -49,6 +49,8 @@ public interface Constants { AttributeKey IS_UDP_KEY = AttributeKey.newInstance("isUdp"); AttributeKey SENDER = AttributeKey.newInstance("sender"); + AttributeKey SERVER_PORT = AttributeKey.newInstance("serverPort"); + int HEADER_SIZE = 4; int TYPE_SIZE = 1; diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/base/proxy/ProxyConfiguration.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/base/proxy/ProxyConfiguration.java index 3a9ead13..1dc50a26 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/base/proxy/ProxyConfiguration.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/base/proxy/ProxyConfiguration.java @@ -20,6 +20,8 @@ import io.netty.channel.nio.NioEventLoopGroup; import org.dromara.neutrinoproxy.server.proxy.core.BytesMetricsHandler; import org.dromara.neutrinoproxy.server.proxy.core.TcpVisitorChannelHandler; import org.dromara.neutrinoproxy.server.proxy.core.UdpVisitorChannelHandler; +import org.dromara.neutrinoproxy.server.proxy.security.TcpVisitorSecurityChannelHandler; +import org.dromara.neutrinoproxy.server.proxy.security.UdpVisitorSecurityChannelHandler; import org.noear.solon.Solon; import org.noear.solon.annotation.Bean; import org.noear.solon.annotation.Configuration; @@ -74,6 +76,7 @@ public class ProxyConfiguration implements LifecycleBean { } ch.pipeline().addFirst(new BytesMetricsHandler()); // ch.pipeline().addLast(new ChannelTrafficShapingHandler(1024 * 1024 * 20, 1024 * 1024 * 20, 100, 20000)); + ch.pipeline().addLast(new TcpVisitorSecurityChannelHandler()); ch.pipeline().addLast(new TcpVisitorChannelHandler()); } }); @@ -111,6 +114,7 @@ public class ProxyConfiguration implements LifecycleBean { if (null != proxyConfig.getServer().getUdp().getTransferLogEnable() && proxyConfig.getServer().getUdp().getTransferLogEnable()) { ch.pipeline().addFirst(new LoggingHandler(UdpVisitorChannelHandler.class)); } + pipeline.addLast(udpServerWorkerGroup, new UdpVisitorSecurityChannelHandler()); pipeline.addLast(udpServerWorkerGroup, new UdpVisitorChannelHandler()); } }); 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 b859f868..dba437ec 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 @@ -7,18 +7,13 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOption; import io.netty.channel.SimpleChannelInboundHandler; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.dromara.neutrinoproxy.core.Constants; import org.dromara.neutrinoproxy.core.ProxyMessage; -import org.dromara.neutrinoproxy.core.util.IpUtil; 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.service.PortMappingService; -import org.dromara.neutrinoproxy.server.service.SecurityGroupService; import org.dromara.neutrinoproxy.server.util.ProxyUtil; import org.noear.solon.Solon; -import org.noear.solon.annotation.Inject; import java.net.InetSocketAddress; @@ -30,10 +25,6 @@ import java.net.InetSocketAddress; @Slf4j public class TcpVisitorChannelHandler extends SimpleChannelInboundHandler { - private final SecurityGroupService securityGroupService = Solon.context().getBean(SecurityGroupService.class); - - private final PortMappingService portMappingService = Solon.context().getBean(PortMappingService.class); - @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // 当出现异常就关闭连接 @@ -55,18 +46,6 @@ public class TcpVisitorChannelHandler extends SimpleChannelInboundHandler { - private final SecurityGroupService securityGroupService = Solon.context().getBean(SecurityGroupService.class); - - private final PortMappingService portMappingService = Solon.context().getBean(PortMappingService.class); - @Override protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket datagramPacket) throws Exception { - log.debug("chid>>>{}", ctx.channel().id().asLongText()); Channel visitorChannel = ctx.channel(); InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress(); - // 判断IP是否在该端口绑定的安全组允许的规则内 - if (!securityGroupService.judgeAllow(datagramPacket.sender().getAddress().getHostAddress(), portMappingService.getSecurityGroupIdByMappingPort(sa.getPort()))) { - return; - } - byte[] bytes = new byte[datagramPacket.content().readableBytes()]; datagramPacket.content().readBytes(bytes); datagramPacket.content().resetReaderIndex(); diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpProxy.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpProxy.java index 85ccaeba..19013cf2 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpProxy.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpProxy.java @@ -11,6 +11,7 @@ import lombok.extern.slf4j.Slf4j; import org.dromara.neutrinoproxy.server.base.proxy.ProxyConfig; import org.dromara.neutrinoproxy.server.proxy.core.BytesMetricsHandler; import org.dromara.neutrinoproxy.server.proxy.core.ProxyTunnelServer; +import org.dromara.neutrinoproxy.server.proxy.security.HttpVisitorSecurityChannelHandler; import org.noear.solon.annotation.Component; import org.noear.solon.annotation.Inject; import org.noear.solon.core.event.AppLoadEndEvent; @@ -46,7 +47,8 @@ public class HttpProxy implements EventListener { ch.pipeline().addFirst(new LoggingHandler(HttpProxy.class)); } ch.pipeline().addFirst(new BytesMetricsHandler()); - ch.pipeline().addLast(new HttpVisitorChannelHandler(proxyConfig.getServer().getTcp().getDomainName())); + ch.pipeline().addLast(new HttpVisitorSecurityChannelHandler(proxyConfig.getServer().getTcp().getDomainName())); + ch.pipeline().addLast(new HttpVisitorChannelHandler()); } }); bootstrap.bind("0.0.0.0", proxyConfig.getServer().getTcp().getHttpProxyPort()).sync(); diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpVisitorChannelHandler.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpVisitorChannelHandler.java index de4fe439..40a18a0f 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpVisitorChannelHandler.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpVisitorChannelHandler.java @@ -1,6 +1,5 @@ package org.dromara.neutrinoproxy.server.proxy.enhance; -import cn.hutool.core.util.StrUtil; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; @@ -10,17 +9,12 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.dromara.neutrinoproxy.core.Constants; import org.dromara.neutrinoproxy.core.ProxyMessage; -import org.dromara.neutrinoproxy.core.util.HttpUtil; -import org.dromara.neutrinoproxy.core.util.IpUtil; import org.dromara.neutrinoproxy.server.constant.NetworkProtocolEnum; import org.dromara.neutrinoproxy.server.proxy.domain.ProxyAttachment; import org.dromara.neutrinoproxy.server.proxy.domain.VisitorChannelAttachInfo; import org.dromara.neutrinoproxy.server.service.FlowReportService; -import org.dromara.neutrinoproxy.server.service.PortMappingService; -import org.dromara.neutrinoproxy.server.service.SecurityGroupService; import org.dromara.neutrinoproxy.server.util.ProxyUtil; import org.noear.solon.Solon; -import org.noear.solon.annotation.Inject; import java.net.InetSocketAddress; @@ -31,26 +25,8 @@ import java.net.InetSocketAddress; @Slf4j public class HttpVisitorChannelHandler extends SimpleChannelInboundHandler { - private final SecurityGroupService securityGroupService = Solon.context().getBean(SecurityGroupService.class); - - private final PortMappingService portMappingService = Solon.context().getBean(PortMappingService.class); - - /** - * 域名 - */ - private String domainName; - - public HttpVisitorChannelHandler(String domainName) { - this.domainName = domainName; - } - @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception { - if (StrUtil.isBlank(domainName)) { - ctx.channel().close(); - return; - } - byte[] bytes = new byte[byteBuf.readableBytes()]; byteBuf.readBytes(bytes); byteBuf.resetReaderIndex(); @@ -78,38 +54,9 @@ public class HttpVisitorChannelHandler extends SimpleChannelInboundHandler { } ch.pipeline().addLast(createSslHandler()); ch.pipeline().addFirst(new BytesMetricsHandler()); - ch.pipeline().addLast(new HttpVisitorChannelHandler(proxyConfig.getServer().getTcp().getDomainName())); + ch.pipeline().addLast(new HttpVisitorSecurityChannelHandler(proxyConfig.getServer().getTcp().getDomainName())); + ch.pipeline().addLast(new HttpVisitorChannelHandler()); } }); bootstrap.bind("0.0.0.0", proxyConfig.getServer().getTcp().getHttpsProxyPort()).sync(); diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/security/HttpVisitorSecurityChannelHandler.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/security/HttpVisitorSecurityChannelHandler.java new file mode 100644 index 00000000..d749c99c --- /dev/null +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/security/HttpVisitorSecurityChannelHandler.java @@ -0,0 +1,87 @@ +package org.dromara.neutrinoproxy.server.proxy.security; + +import cn.hutool.core.util.StrUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.dromara.neutrinoproxy.core.Constants; +import org.dromara.neutrinoproxy.core.util.HttpUtil; +import org.dromara.neutrinoproxy.core.util.IpUtil; +import org.dromara.neutrinoproxy.server.service.PortMappingService; +import org.dromara.neutrinoproxy.server.service.SecurityGroupService; +import org.dromara.neutrinoproxy.server.util.ProxyUtil; +import org.noear.solon.Solon; + +/** + * @author: aoshiguchen + * @date: 2023/12/14 + */ +@Slf4j +public class HttpVisitorSecurityChannelHandler extends ChannelInboundHandlerAdapter { + private final SecurityGroupService securityGroupService = Solon.context().getBean(SecurityGroupService.class); + private final PortMappingService portMappingService = Solon.context().getBean(PortMappingService.class); + /** + * 域名 + */ + private String domainName; + + public HttpVisitorSecurityChannelHandler(String domainName) { + this.domainName = domainName; + } + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + // 未配置域名则不支持通过域名访问 + if (StrUtil.isBlank(domainName)) { + ctx.channel().close(); + return; + } + + ByteBuf buf = (ByteBuf) msg; + byte[] bytes = new byte[buf.readableBytes()]; + buf.readBytes(bytes); + + // 获取Host请求头 + String httpContent = new String(bytes); + String host = HttpUtil.getHostIgnorePort(httpContent); + + log.debug("HttpProxy host: {}", host); + if (StringUtils.isBlank(host)) { + ctx.channel().close(); + return; + } + + // 根据Host匹配端口映射 + if (!host.endsWith(domainName)) { + ctx.channel().close(); + return; + } + int index = host.lastIndexOf("." + domainName); + String subdomain = host.substring(0, index); + + // 根据域名拿到绑定的映射对应的cmdChannel + Integer serverPort = ProxyUtil.getServerPortBySubdomain(subdomain); + if (null == serverPort) { + ctx.channel().close(); + return; + } + ctx.channel().attr(Constants.SERVER_PORT).set(serverPort); + + // 判断IP是否在该端口绑定的安全组允许的规则内 + String ip = IpUtil.getRealRemoteIp(httpContent); + if (ip == null) { + ip = IpUtil.getRemoteIp(ctx); + } + if (!securityGroupService.judgeAllow(ip, portMappingService.getSecurityGroupIdByMappingPort(serverPort))) { + // 不在安全组规则放行范围内 + ctx.channel().close(); + return; + } + + // 继续传播 + buf.resetReaderIndex(); + ctx.fireChannelRead(buf); + } + +} diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/security/TcpVisitorSecurityChannelHandler.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/security/TcpVisitorSecurityChannelHandler.java new file mode 100644 index 00000000..fead9490 --- /dev/null +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/security/TcpVisitorSecurityChannelHandler.java @@ -0,0 +1,65 @@ +package org.dromara.neutrinoproxy.server.proxy.security; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.dromara.neutrinoproxy.core.util.IpUtil; +import org.dromara.neutrinoproxy.server.service.PortMappingService; +import org.dromara.neutrinoproxy.server.service.SecurityGroupService; +import org.noear.solon.Solon; + +import java.net.InetSocketAddress; + +/** + * @author: aoshiguchen + * @date: 2023/12/14 + */ +@Slf4j +public class TcpVisitorSecurityChannelHandler extends ChannelInboundHandlerAdapter { + private final SecurityGroupService securityGroupService = Solon.context().getBean(SecurityGroupService.class); + private final PortMappingService portMappingService = Solon.context().getBean(PortMappingService.class); + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + Channel visitorChannel = ctx.channel(); + + ByteBuf buf = (ByteBuf) msg; + byte[] bytes = new byte[buf.readableBytes()]; + buf.readBytes(bytes); + + // 判断IP是否在该端口绑定的安全组允许的规则内 + String ip = IpUtil.getRealRemoteIp(new String(bytes)); + if (StringUtils.isEmpty(ip)) { + ip = IpUtil.getRemoteIp(ctx); + } + InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress(); + if (!securityGroupService.judgeAllow(ip, portMappingService.getSecurityGroupIdByMappingPort(sa.getPort()))) { + // 不在安全组规则放行范围内 + ctx.channel().close(); + return; + } + // 继续传播 + buf.resetReaderIndex(); + ctx.fireChannelRead(buf); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + Channel visitorChannel = ctx.channel(); + InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress(); + + // 判断IP是否在该端口绑定的安全组允许的规则内 + if (!securityGroupService.judgeAllow(IpUtil.getRemoteIp(ctx), portMappingService.getSecurityGroupIdByMappingPort(sa.getPort()))) { + // 不在安全组规则放行范围内 + ctx.channel().close(); + return; + } + + // 继续传播 + ctx.fireChannelActive(); + } + +} diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/security/UdpVisitorSecurityChannelHandler.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/security/UdpVisitorSecurityChannelHandler.java new file mode 100644 index 00000000..4e4db92e --- /dev/null +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/security/UdpVisitorSecurityChannelHandler.java @@ -0,0 +1,37 @@ +package org.dromara.neutrinoproxy.server.proxy.security; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.socket.DatagramPacket; +import lombok.extern.slf4j.Slf4j; +import org.dromara.neutrinoproxy.server.service.PortMappingService; +import org.dromara.neutrinoproxy.server.service.SecurityGroupService; +import org.noear.solon.Solon; + +import java.net.InetSocketAddress; + +/** + * @author: aoshiguchen + * @date: 2023/12/14 + */ +@Slf4j +public class UdpVisitorSecurityChannelHandler extends ChannelInboundHandlerAdapter { + private final SecurityGroupService securityGroupService = Solon.context().getBean(SecurityGroupService.class); + private final PortMappingService portMappingService = Solon.context().getBean(PortMappingService.class); + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + Channel visitorChannel = ctx.channel(); + InetSocketAddress sa = (InetSocketAddress) visitorChannel.localAddress(); + DatagramPacket datagramPacket = (DatagramPacket) msg; + + // 判断IP是否在该端口绑定的安全组允许的规则内 + if (!securityGroupService.judgeAllow(datagramPacket.sender().getAddress().getHostAddress(), portMappingService.getSecurityGroupIdByMappingPort(sa.getPort()))) { + return; + } + + // 继续传播 + ctx.fireChannelRead(msg); + } +}