diff --git a/Freesia-Backend/src/main/resources/plugin.yml b/Freesia-Backend/src/main/resources/plugin.yml index a482339..f04d6e9 100644 --- a/Freesia-Backend/src/main/resources/plugin.yml +++ b/Freesia-Backend/src/main/resources/plugin.yml @@ -1,6 +1,5 @@ name: Freesia-Backend -version: 'v1.0.2_YSM_2.6.2' +version: '1.0.3-YSM-2.6.4' main: com.nguyendevs.freesia.backend.FreesiaBackend api-version: '1.16' - folia-supported: true diff --git a/Freesia-Common/build.gradle.kts b/Freesia-Common/build.gradle.kts index b78a9a5..c1b9d11 100644 --- a/Freesia-Common/build.gradle.kts +++ b/Freesia-Common/build.gradle.kts @@ -7,4 +7,6 @@ dependencies { compileOnly("org.slf4j:slf4j-api:2.0.13") compileOnly("org.jetbrains:annotations:24.1.0") compileOnly("ca.spottedleaf:concurrentutil:0.0.3") + compileOnly("org.bouncycastle:bcpkix-jdk18on:1.78.1") + compileOnly("org.bouncycastle:bcprov-jdk18on:1.78.1") } diff --git a/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/ServerSslUtils.java b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/ServerSslUtils.java new file mode 100644 index 0000000..3d77087 --- /dev/null +++ b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/ServerSslUtils.java @@ -0,0 +1,51 @@ +package com.nguyendevs.freesia.common; + +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; + +import java.io.File; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.Security; +import java.security.cert.X509Certificate; +import java.util.Date; + +public class ServerSslUtils { + public static SslContext createServerContext(boolean useSelfSigned, String certPath, String keyPath) throws Exception { + if (useSelfSigned) { + if (Security.getProvider("BC") == null) { + Security.addProvider(new BouncyCastleProvider()); + } + + KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC"); + kpGen.initialize(2048, new SecureRandom()); + KeyPair kp = kpGen.generateKeyPair(); + + X500Name issuer = new X500Name("CN=Freesia"); + BigInteger serial = BigInteger.valueOf(System.currentTimeMillis()); + Date notBefore = new Date(System.currentTimeMillis() - 86400000L); + Date notAfter = new Date(System.currentTimeMillis() + 86400000L * 365); + + X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder( + issuer, serial, notBefore, notAfter, issuer, kp.getPublic() + ); + ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider("BC").build(kp.getPrivate()); + X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certGen.build(sigGen)); + + EntryPoint.LOGGER_INST.info("\u001B[32m[Security] Generated Self-Signed Certificate for Master Server\u001B[0m"); + return SslContextBuilder.forServer(kp.getPrivate(), cert).build(); + } else { + EntryPoint.LOGGER_INST.info("\u001B[32m[Security] Loaded Certificate from " + certPath + " for Master Server\u001B[0m"); + return SslContextBuilder.forServer(new File(certPath), new File(keyPath)).build(); + } + } +} diff --git a/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/SslUtils.java b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/SslUtils.java new file mode 100644 index 0000000..b787328 --- /dev/null +++ b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/SslUtils.java @@ -0,0 +1,20 @@ +package com.nguyendevs.freesia.common; + +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; + +import java.io.File; + +public class SslUtils { + + public static SslContext createClientContext(boolean trustAll, String trustCertPath) throws Exception { + if (trustAll) { + EntryPoint.LOGGER_INST.info("\u001B[33m[Security] Client trusting all certificates (Insecure TrustManager)\u001B[0m"); + return SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build(); + } else { + EntryPoint.LOGGER_INST.info("\u001B[32m[Security] Client loading TrustStore from " + trustCertPath + "\u001B[0m"); + return SslContextBuilder.forClient().trustManager(new File(trustCertPath)).build(); + } + } +} diff --git a/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/DefaultChannelPipelineLoader.java b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/DefaultChannelPipelineLoader.java index 8388a35..d3c788d 100644 --- a/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/DefaultChannelPipelineLoader.java +++ b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/DefaultChannelPipelineLoader.java @@ -9,7 +9,13 @@ public class DefaultChannelPipelineLoader { - public static void loadDefaultHandlers(@NotNull Channel channel) { + public static void loadDefaultHandlers(@NotNull Channel channel, io.netty.handler.ssl.SslContext sslContext, com.nguyendevs.freesia.common.communicating.FreesiaIpFilterHandler ipFilterHandler) { + if (ipFilterHandler != null) { + channel.pipeline().addFirst("firewall", ipFilterHandler); + } + if (sslContext != null) { + channel.pipeline().addLast(sslContext.newHandler(channel.alloc())); + } channel.pipeline() .addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4)) .addLast(new LengthFieldPrepender(4)) diff --git a/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/FreesiaIpFilterHandler.java b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/FreesiaIpFilterHandler.java new file mode 100644 index 0000000..85d1a1f --- /dev/null +++ b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/FreesiaIpFilterHandler.java @@ -0,0 +1,44 @@ +package com.nguyendevs.freesia.common.communicating; + +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.ipfilter.AbstractRemoteAddressFilter; +import com.nguyendevs.freesia.common.EntryPoint; +import io.netty.channel.ChannelHandler; +import java.net.InetSocketAddress; +import java.util.List; + +@ChannelHandler.Sharable +public class FreesiaIpFilterHandler extends AbstractRemoteAddressFilter { + private final boolean enableIpFilter; + private final List allowedIps; + + public FreesiaIpFilterHandler(boolean enableIpFilter, List allowedIps) { + this.enableIpFilter = enableIpFilter; + this.allowedIps = allowedIps; + } + + @Override + protected boolean accept(ChannelHandlerContext ctx, InetSocketAddress remoteAddress) throws Exception { + if (!enableIpFilter) { + return true; + } + + String ip = remoteAddress.getAddress().getHostAddress(); + boolean isWhitelisted = allowedIps != null && allowedIps.contains(ip); + + if (!isWhitelisted) { + EntryPoint.LOGGER_INST.warn("\u001B[31m[Security] FIREWALL BLOCKED connection from unauthorized IP: " + ip + "\u001B[0m"); + } + + return isWhitelisted; + } + + @Override + protected ChannelFuture channelRejected(ChannelHandlerContext ctx, InetSocketAddress remoteAddress) { + ChannelFuture rejectFuture = ctx.channel().close(); + rejectFuture.addListener(ChannelFutureListener.CLOSE); + return rejectFuture; + } +} diff --git a/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/NettySocketClient.java b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/NettySocketClient.java index d513f83..bef88c6 100644 --- a/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/NettySocketClient.java +++ b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/NettySocketClient.java @@ -20,14 +20,16 @@ public class NettySocketClient { private final InetSocketAddress masterAddress; private final Queue> packetFlushQueue = new ConcurrentLinkedQueue<>(); private final Function> handlerCreator; + private final io.netty.handler.ssl.SslContext sslContext; private final int reconnectInterval; private volatile Channel channel; private volatile boolean isConnected = false; - public NettySocketClient(InetSocketAddress masterAddress, Function> handlerCreator, int reconnectInterval) { + public NettySocketClient(InetSocketAddress masterAddress, Function> handlerCreator, int reconnectInterval, io.netty.handler.ssl.SslContext sslContext) { this.masterAddress = masterAddress; this.handlerCreator = handlerCreator; this.reconnectInterval = reconnectInterval; + this.sslContext = sslContext; } public void connect() { @@ -40,7 +42,7 @@ public void connect() { .handler(new ChannelInitializer<>() { @Override protected void initChannel(@NotNull Channel channel) { - DefaultChannelPipelineLoader.loadDefaultHandlers(channel); + DefaultChannelPipelineLoader.loadDefaultHandlers(channel, NettySocketClient.this.sslContext, null); channel.pipeline().addLast(NettySocketClient.this.handlerCreator.apply(channel)); } }) diff --git a/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/NettySocketServer.java b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/NettySocketServer.java index 8442c73..db40262 100644 --- a/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/NettySocketServer.java +++ b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/NettySocketServer.java @@ -13,22 +13,26 @@ public class NettySocketServer { private final EventLoopGroup masterLoopGroup = NettyUtils.eventLoopGroup(); private final EventLoopGroup workerLoopGroup = NettyUtils.eventLoopGroup(); private final Function> handlerCreator; + private final io.netty.handler.ssl.SslContext sslContext; + private final com.nguyendevs.freesia.common.communicating.FreesiaIpFilterHandler ipFilterHandler; private volatile ChannelFuture channelFuture; - public NettySocketServer(InetSocketAddress bindAddress, Function> handlerCreator) { + public NettySocketServer(InetSocketAddress bindAddress, Function> handlerCreator, io.netty.handler.ssl.SslContext sslContext, com.nguyendevs.freesia.common.communicating.FreesiaIpFilterHandler ipFilterHandler) { this.bindAddress = bindAddress; this.handlerCreator = handlerCreator; + this.sslContext = sslContext; + this.ipFilterHandler = ipFilterHandler; } public void bind() { this.channelFuture = new ServerBootstrap() .group(this.masterLoopGroup, this.workerLoopGroup) .channel(NettyUtils.serverChannelClass()) - .option(ChannelOption.TCP_NODELAY, true) + .childOption(ChannelOption.TCP_NODELAY, true) .childHandler(new ChannelInitializer<>() { @Override protected void initChannel(@NotNull Channel channel) { - DefaultChannelPipelineLoader.loadDefaultHandlers(channel); + DefaultChannelPipelineLoader.loadDefaultHandlers(channel, NettySocketServer.this.sslContext, NettySocketServer.this.ipFilterHandler); channel.pipeline().addLast(NettySocketServer.this.handlerCreator.apply(channel)); } }) diff --git a/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/handler/NettyClientChannelHandlerLayer.java b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/handler/NettyClientChannelHandlerLayer.java index d50a185..357a021 100644 --- a/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/handler/NettyClientChannelHandlerLayer.java +++ b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/handler/NettyClientChannelHandlerLayer.java @@ -19,6 +19,14 @@ protected void channelRead0(ChannelHandlerContext ctx, IMessage packet) { if (!this.channel.isOpen()) { return; diff --git a/Freesia-Velocity/build.gradle.kts b/Freesia-Velocity/build.gradle.kts index e0e5c65..75dec7d 100644 --- a/Freesia-Velocity/build.gradle.kts +++ b/Freesia-Velocity/build.gradle.kts @@ -8,6 +8,8 @@ dependencies { implementation("com.electronwill.night-config:toml:3.6.6") implementation("org.geysermc.mcprotocollib:protocol:1.21-SNAPSHOT") implementation("ca.spottedleaf:concurrentutil:0.0.3") + implementation("org.bouncycastle:bcpkix-jdk18on:1.78.1") + implementation("org.bouncycastle:bcprov-jdk18on:1.78.1") annotationProcessor("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT") } @@ -44,7 +46,6 @@ sourceSets.named("main") { java.srcDir(generateTemplates.map { it.outputs }) } -// 确保 generateTemplates 任务在构建时执行 tasks.named("build") { dependsOn(generateTemplates) } diff --git a/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/Freesia.java b/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/Freesia.java index 68303fe..60f58bb 100644 --- a/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/Freesia.java +++ b/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/Freesia.java @@ -99,6 +99,7 @@ public void onProxyStart(ProxyInitializeEvent event) { LOGGER.info("Loading config file and i18n"); try { FreesiaConfig.init(); + FreesiaSecurityConfig.init(); languageManager.loadLanguageFile(FreesiaConfig.languageName); } catch (IOException e) { throw new RuntimeException(e); @@ -114,7 +115,23 @@ public void onProxyStart(ProxyInitializeEvent event) { virtualPlayerManager.init(); - masterServer = new NettySocketServer(FreesiaConfig.masterServiceAddress, c -> new MasterServerMessageHandler()); + io.netty.handler.ssl.SslContext sslContext = null; + try { + if (FreesiaSecurityConfig.enableTls) { + sslContext = com.nguyendevs.freesia.common.ServerSslUtils.createServerContext( + FreesiaSecurityConfig.useSelfSigned, + java.nio.file.Paths.get(FreesiaConstants.FileConstants.PLUGIN_DIR.getAbsolutePath()).resolve(FreesiaSecurityConfig.certPath).toFile().getAbsolutePath(), + java.nio.file.Paths.get(FreesiaConstants.FileConstants.PLUGIN_DIR.getAbsolutePath()).resolve(FreesiaSecurityConfig.keyPath).toFile().getAbsolutePath() + ); + } + } catch (Exception e) { + LOGGER.error("Failed to initialize SSL context!", e); + } + com.nguyendevs.freesia.common.communicating.FreesiaIpFilterHandler ipFilter = new com.nguyendevs.freesia.common.communicating.FreesiaIpFilterHandler( + FreesiaSecurityConfig.enableIpFilter, + FreesiaSecurityConfig.allowedWorkerIps + ); + masterServer = new NettySocketServer(FreesiaConfig.masterServiceAddress, c -> new MasterServerMessageHandler(), sslContext, ipFilter); masterServer.bind(); LOGGER.info("Initiating client kicker."); diff --git a/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/FreesiaConstants.java b/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/FreesiaConstants.java index 3110b07..0c53f1a 100644 --- a/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/FreesiaConstants.java +++ b/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/FreesiaConstants.java @@ -8,6 +8,7 @@ public static final class FileConstants { public static final File PLUGIN_DIR = new File(PLUGINS_DIR, "Freesia"); public static final File CONFIG_FILE = new File(PLUGIN_DIR, "freesia_config.toml"); + public static final File SECURITY_CONFIG_FILE = new File(PLUGIN_DIR, "security.toml"); public static final File PLAYER_DATA_DIR = new File(PLUGIN_DIR, "playerdata"); public static final File VIRTUAL_PLAYER_DATA_DIR = new File(PLUGIN_DIR, "playerdata_virtual"); diff --git a/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/FreesiaSecurityConfig.java b/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/FreesiaSecurityConfig.java new file mode 100644 index 0000000..8de31d3 --- /dev/null +++ b/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/FreesiaSecurityConfig.java @@ -0,0 +1,55 @@ +package com.nguyendevs.freesia.velocity; + +import com.electronwill.nightconfig.core.file.CommentedFileConfig; + +import java.io.IOException; + +public class FreesiaSecurityConfig { + public static boolean enableTls = false; + public static boolean useSelfSigned = true; + public static String certPath = "cert.pem"; + public static String keyPath = "key.pem"; + public static boolean enableIpFilter = true; + public static java.util.List allowedWorkerIps = java.util.Arrays.asList("127.0.0.1", "192.168.1.5"); + + private static CommentedFileConfig CONFIG_INSTANCE; + + private static void loadOrDefaultValues() { + enableTls = get("security.enable_tls", enableTls); + useSelfSigned = get("security.use_self_signed", useSelfSigned); + certPath = get("security.cert_path", certPath); + keyPath = get("security.key_path", keyPath); + enableIpFilter = get("firewall.enable_ip_filter", enableIpFilter); + allowedWorkerIps = get("firewall.allowed_worker_ips", allowedWorkerIps); + } + + private static T get(String key, T def) { + if (!CONFIG_INSTANCE.contains(key)) { + CONFIG_INSTANCE.add(key, def); + return def; + } + + return CONFIG_INSTANCE.get(key); + } + + public static void init() throws IOException { + Freesia.LOGGER.info("\u001B[36m[Security] Loading proxy security config.\u001B[0m"); + + if (!FreesiaConstants.FileConstants.SECURITY_CONFIG_FILE.exists()) { + Freesia.LOGGER.info("\u001B[33m[Security] Security config file not found! Creating new.\u001B[0m"); + FreesiaConstants.FileConstants.SECURITY_CONFIG_FILE.createNewFile(); + } + + CONFIG_INSTANCE = CommentedFileConfig.ofConcurrent(FreesiaConstants.FileConstants.SECURITY_CONFIG_FILE); + + CONFIG_INSTANCE.load(); + + try { + loadOrDefaultValues(); + } catch (Exception e) { + Freesia.LOGGER.error("Failed to load security config!", e); + } + + CONFIG_INSTANCE.save(); + } +} diff --git a/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/network/backend/MasterServerMessageHandler.java b/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/network/backend/MasterServerMessageHandler.java index 2c5b818..18344fe 100644 --- a/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/network/backend/MasterServerMessageHandler.java +++ b/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/network/backend/MasterServerMessageHandler.java @@ -153,7 +153,7 @@ public void onCommandDispatchResult(int traceId, @Nullable String result) { @Override public void updateWorkerInfo(UUID workerUUID, String workerName) { - EntryPoint.LOGGER_INST.info("Worker {} (UUID: {}) connected", workerName, workerUUID); + EntryPoint.LOGGER_INST.info("\u001B[36mWorker \u001B[32m{}\u001B[36m (UUID: \u001B[33m{}\u001B[36m) connected\u001B[0m", workerName, workerUUID); this.workerName = workerName; this.workerUUID = workerUUID; diff --git a/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/network/mc/FreesiaPlayerTracker.java b/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/network/mc/FreesiaPlayerTracker.java index f88f5dd..1903a14 100644 --- a/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/network/mc/FreesiaPlayerTracker.java +++ b/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/network/mc/FreesiaPlayerTracker.java @@ -58,10 +58,12 @@ public void onChannelMsg(@NotNull PluginMessageEvent event) { final Consumer> targetTask = this.pendingCanSeeTasks.remove(taskId); - try { - targetTask.accept(result); - } catch (Exception e) { - Freesia.LOGGER.error("Can not process tracker callback task !", e); + if (targetTask != null) { + try { + targetTask.accept(result); + } catch (Exception e) { + Freesia.LOGGER.error("Can not process tracker callback task !", e); + } } } diff --git a/Freesia-Velocity/src/main/templates/com/nguyendevs/freesia/velocity/BuildConstants.java b/Freesia-Velocity/src/main/templates/com/nguyendevs/freesia/velocity/BuildConstants.java index d9614b6..db7fe04 100644 --- a/Freesia-Velocity/src/main/templates/com/nguyendevs/freesia/velocity/BuildConstants.java +++ b/Freesia-Velocity/src/main/templates/com/nguyendevs/freesia/velocity/BuildConstants.java @@ -1,6 +1,5 @@ package com.nguyendevs.freesia.velocity; -// The constants are replaced before compilation public class BuildConstants { public static final String VERSION = "${version}"; diff --git a/Freesia-Waterfall/build.gradle.kts b/Freesia-Waterfall/build.gradle.kts index 9068361..9b56029 100644 --- a/Freesia-Waterfall/build.gradle.kts +++ b/Freesia-Waterfall/build.gradle.kts @@ -16,6 +16,8 @@ dependencies { implementation("net.kyori:adventure-api:4.14.0") implementation("net.kyori:adventure-text-minimessage:4.14.0") implementation("net.kyori:adventure-text-serializer-legacy:4.14.0") + implementation("org.bouncycastle:bcpkix-jdk18on:1.78.1") + implementation("org.bouncycastle:bcprov-jdk18on:1.78.1") } val targetJavaVersion = 21 diff --git a/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/Freesia.java b/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/Freesia.java index 4ac9a61..b65ea5f 100644 --- a/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/Freesia.java +++ b/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/Freesia.java @@ -89,6 +89,7 @@ public void onEnable() { LOGGER.info("Loading config file and i18n"); try { FreesiaConfig.init(); + FreesiaSecurityConfig.init(); languageManager.loadLanguageFile(FreesiaConfig.languageName); } catch (IOException e) { throw new RuntimeException(e); @@ -108,7 +109,23 @@ public void onEnable() { virtualPlayerManager.init(); - masterServer = new NettySocketServer(FreesiaConfig.masterServiceAddress, c -> new MasterServerMessageHandler()); + io.netty.handler.ssl.SslContext sslContext = null; + try { + if (FreesiaSecurityConfig.enableTls) { + sslContext = com.nguyendevs.freesia.common.ServerSslUtils.createServerContext( + FreesiaSecurityConfig.useSelfSigned, + java.nio.file.Paths.get(FreesiaConstants.FileConstants.PLUGIN_DIR.getAbsolutePath()).resolve(FreesiaSecurityConfig.certPath).toFile().getAbsolutePath(), + java.nio.file.Paths.get(FreesiaConstants.FileConstants.PLUGIN_DIR.getAbsolutePath()).resolve(FreesiaSecurityConfig.keyPath).toFile().getAbsolutePath() + ); + } + } catch (Exception e) { + LOGGER.log(java.util.logging.Level.SEVERE, "Failed to initialize SSL context!", e); + } + com.nguyendevs.freesia.common.communicating.FreesiaIpFilterHandler ipFilter = new com.nguyendevs.freesia.common.communicating.FreesiaIpFilterHandler( + FreesiaSecurityConfig.enableIpFilter, + FreesiaSecurityConfig.allowedWorkerIps + ); + masterServer = new NettySocketServer(FreesiaConfig.masterServiceAddress, c -> new MasterServerMessageHandler(), sslContext, ipFilter); PROXY_SERVER.getScheduler().runAsync(this, () -> { try { LOGGER.info("Binding master service to " + FreesiaConfig.masterServiceAddress); diff --git a/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/FreesiaConstants.java b/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/FreesiaConstants.java index c4d6e57..aa627b3 100644 --- a/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/FreesiaConstants.java +++ b/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/FreesiaConstants.java @@ -8,6 +8,7 @@ public static final class FileConstants { public static final File PLUGIN_DIR = new File(PLUGINS_DIR, "Freesia"); public static final File CONFIG_FILE = new File(PLUGIN_DIR, "freesia_config.toml"); + public static final File SECURITY_CONFIG_FILE = new File(PLUGIN_DIR, "security.toml"); public static final File PLAYER_DATA_DIR = new File(PLUGIN_DIR, "playerdata"); public static final File VIRTUAL_PLAYER_DATA_DIR = new File(PLUGIN_DIR, "playerdata_virtual"); diff --git a/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/FreesiaSecurityConfig.java b/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/FreesiaSecurityConfig.java new file mode 100644 index 0000000..8c1dbf3 --- /dev/null +++ b/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/FreesiaSecurityConfig.java @@ -0,0 +1,55 @@ +package com.nguyendevs.freesia.waterfall; + +import com.electronwill.nightconfig.core.file.CommentedFileConfig; + +import java.io.IOException; + +public class FreesiaSecurityConfig { + public static boolean enableTls = false; + public static boolean useSelfSigned = true; + public static String certPath = "cert.pem"; + public static String keyPath = "key.pem"; + public static boolean enableIpFilter = true; + public static java.util.List allowedWorkerIps = java.util.Arrays.asList("127.0.0.1", "192.168.1.5"); + + private static CommentedFileConfig CONFIG_INSTANCE; + + private static void loadOrDefaultValues() { + enableTls = get("security.enable_tls", enableTls); + useSelfSigned = get("security.use_self_signed", useSelfSigned); + certPath = get("security.cert_path", certPath); + keyPath = get("security.key_path", keyPath); + enableIpFilter = get("firewall.enable_ip_filter", enableIpFilter); + allowedWorkerIps = get("firewall.allowed_worker_ips", allowedWorkerIps); + } + + private static T get(String key, T def) { + if (!CONFIG_INSTANCE.contains(key)) { + CONFIG_INSTANCE.add(key, def); + return def; + } + + return CONFIG_INSTANCE.get(key); + } + + public static void init() throws IOException { + Freesia.LOGGER.info("\u001B[36m[Security] Loading proxy security config.\u001B[0m"); + + if (!FreesiaConstants.FileConstants.SECURITY_CONFIG_FILE.exists()) { + Freesia.LOGGER.info("\u001B[33m[Security] Security config file not found! Creating new.\u001B[0m"); + FreesiaConstants.FileConstants.SECURITY_CONFIG_FILE.createNewFile(); + } + + CONFIG_INSTANCE = CommentedFileConfig.ofConcurrent(FreesiaConstants.FileConstants.SECURITY_CONFIG_FILE); + + CONFIG_INSTANCE.load(); + + try { + loadOrDefaultValues(); + } catch (Exception e) { + Freesia.LOGGER.log(java.util.logging.Level.SEVERE, "Failed to load security config!", e); + } + + CONFIG_INSTANCE.save(); + } +} diff --git a/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/network/backend/MasterServerMessageHandler.java b/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/network/backend/MasterServerMessageHandler.java index 5a50a31..2868af5 100644 --- a/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/network/backend/MasterServerMessageHandler.java +++ b/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/network/backend/MasterServerMessageHandler.java @@ -155,7 +155,7 @@ public void onCommandDispatchResult(int traceId, @Nullable String result) { @Override public void updateWorkerInfo(UUID workerUUID, String workerName) { - EntryPoint.LOGGER_INST.info("Worker {} (UUID: {}) connected", workerName, workerUUID); + EntryPoint.LOGGER_INST.info("\u001B[36mWorker \u001B[32m{}\u001B[36m (UUID: \u001B[33m{}\u001B[36m) connected\u001B[0m", workerName, workerUUID); this.workerName = workerName; this.workerUUID = workerUUID; diff --git a/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/network/mc/FreesiaPlayerTracker.java b/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/network/mc/FreesiaPlayerTracker.java index 89b88c6..26f052f 100644 --- a/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/network/mc/FreesiaPlayerTracker.java +++ b/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/network/mc/FreesiaPlayerTracker.java @@ -57,10 +57,12 @@ public void onChannelMsg(@NotNull PluginMessageEvent event) { final Consumer> targetTask = this.pendingCanSeeTasks.remove(taskId); - try { - targetTask.accept(result); - } catch (Exception e) { - Freesia.LOGGER.log(java.util.logging.Level.SEVERE, "Can not process tracker callback task !", e); + if (targetTask != null) { + try { + targetTask.accept(result); + } catch (Exception e) { + Freesia.LOGGER.log(java.util.logging.Level.SEVERE, "Can not process tracker callback task !", e); + } } } diff --git a/Freesia-Waterfall/src/main/resources/bungee.yml b/Freesia-Waterfall/src/main/resources/bungee.yml index 9889594..37b85d4 100644 --- a/Freesia-Waterfall/src/main/resources/bungee.yml +++ b/Freesia-Waterfall/src/main/resources/bungee.yml @@ -1,5 +1,5 @@ name: Freesia -version: 'v1.0.2_YSM_2.6.2' +version: '1.0.3-YSM-2.6.4' main: com.nguyendevs.freesia.waterfall.Freesia author: NguyenDevs, Earthme, HappyRespawnanchor, xiaozhangup description: YSM performance optimization proxy model for BungeeCord/Waterfall. diff --git a/Freesia-Worker/src/main/java/com/nguyendevs/freesia/worker/FreesiaWorkerConfig.java b/Freesia-Worker/src/main/java/com/nguyendevs/freesia/worker/FreesiaWorkerConfig.java index 231751d..82d3d6c 100644 --- a/Freesia-Worker/src/main/java/com/nguyendevs/freesia/worker/FreesiaWorkerConfig.java +++ b/Freesia-Worker/src/main/java/com/nguyendevs/freesia/worker/FreesiaWorkerConfig.java @@ -15,6 +15,9 @@ public class FreesiaWorkerConfig { public static InetSocketAddress masterServiceAddress = new InetSocketAddress("127.0.0.1", 19200); public static int reconnectInterval = 1; public static int playerDataCacheInvalidateIntervalSeconds = 30; + public static boolean enableTls = true; + public static boolean trustAll = true; + public static String trustCertPath = "truststore.pem"; private static CommentedFileConfig CONFIG_INSTANCE; static { @@ -28,6 +31,9 @@ private static void loadOrDefaultValues() { ); reconnectInterval = get("worker.controller_reconnect_interval", reconnectInterval); playerDataCacheInvalidateIntervalSeconds = get("worker.player_data_cache_invalidate_interval_seconds", playerDataCacheInvalidateIntervalSeconds); + enableTls = get("security.enable_tls", enableTls); + trustAll = get("security.trust_all", trustAll); + trustCertPath = get("security.trust_cert_path", trustCertPath); } private static T get(String key, T def) { diff --git a/Freesia-Worker/src/main/java/com/nguyendevs/freesia/worker/ServerLoader.java b/Freesia-Worker/src/main/java/com/nguyendevs/freesia/worker/ServerLoader.java index 3b70dd0..cbba589 100644 --- a/Freesia-Worker/src/main/java/com/nguyendevs/freesia/worker/ServerLoader.java +++ b/Freesia-Worker/src/main/java/com/nguyendevs/freesia/worker/ServerLoader.java @@ -42,7 +42,19 @@ public void onInitializeServer() { .newBuilder() .expireAfterWrite(FreesiaWorkerConfig.playerDataCacheInvalidateIntervalSeconds, TimeUnit.SECONDS) .build(); - clientInstance = new NettySocketClient(FreesiaWorkerConfig.masterServiceAddress, c -> workerConnection = new WorkerMessageHandlerImpl(), FreesiaWorkerConfig.reconnectInterval) { + io.netty.handler.ssl.SslContext sslContext = null; + try { + if (FreesiaWorkerConfig.enableTls) { + sslContext = com.nguyendevs.freesia.common.SslUtils.createClientContext( + FreesiaWorkerConfig.trustAll, + new File("config", FreesiaWorkerConfig.trustCertPath).getAbsolutePath() + ); + } + } catch (Exception e) { + EntryPoint.LOGGER_INST.error("Failed to initialize SSL context!", e); + } + + clientInstance = new NettySocketClient(FreesiaWorkerConfig.masterServiceAddress, c -> workerConnection = new WorkerMessageHandlerImpl(), FreesiaWorkerConfig.reconnectInterval, sslContext) { @Override protected boolean shouldDoNextReconnect() { return SERVER_INST.isRunning(); diff --git a/Freesia-Worker/src/main/java/com/nguyendevs/freesia/worker/impl/WorkerMessageHandlerImpl.java b/Freesia-Worker/src/main/java/com/nguyendevs/freesia/worker/impl/WorkerMessageHandlerImpl.java index 83ac48d..1d08d1b 100644 --- a/Freesia-Worker/src/main/java/com/nguyendevs/freesia/worker/impl/WorkerMessageHandlerImpl.java +++ b/Freesia-Worker/src/main/java/com/nguyendevs/freesia/worker/impl/WorkerMessageHandlerImpl.java @@ -51,7 +51,11 @@ public void channelActive(@NotNull ChannelHandlerContext ctx) { public void channelInactive(@NotNull ChannelHandlerContext ctx) { this.retirePlayerFetchCallbacks(); super.channelInactive(ctx); - ServerLoader.SERVER_INST.execute(ServerLoader::connectToBackend); + if (ServerLoader.SERVER_INST != null) { + ServerLoader.SERVER_INST.execute(ServerLoader::connectToBackend); + } else { + new Thread(ServerLoader::connectToBackend).start(); + } } private void retirePlayerFetchCallbacks() { diff --git a/build.gradle.kts b/build.gradle.kts index d22fd19..91b8f60 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,11 +5,11 @@ plugins { id("io.github.goooler.shadow") version "8.1.2" } -group = "meow.kikir" +group = "com.nguyendevs" version = project.version allprojects { - group = "meow.kikir" + group = "com.nguyendevs" version = rootProject.version apply(plugin = "java") diff --git a/gradle.properties b/gradle.properties index c466a60..38a0fa8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ kotlin.code.style=official -version=v1.0.2_YSM_2.6.2 +version=1.0.3-YSM-2.6.4 minecraft_version=1.21.1 parchment_version=2024.11.17 fabric_loader_version=0.16.13