From 9b48cf197e35f66e3cd17232fac44a53ed8a56db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Devs?= <144598460+NguyenDevs@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:03:56 +0700 Subject: [PATCH 01/10] Chore: Update project namespace to com.nguyendevs and bump version to v1.0.3_YSM_2.6.4 --- Freesia-Backend/src/main/resources/plugin.yml | 3 +-- Freesia-Velocity/build.gradle.kts | 1 - .../com/nguyendevs/freesia/velocity/BuildConstants.java | 1 - build.gradle.kts | 4 ++-- gradle.properties | 2 +- 5 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Freesia-Backend/src/main/resources/plugin.yml b/Freesia-Backend/src/main/resources/plugin.yml index a482339..3f557bb 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: 'v1.0.3_YSM_2.6.4' main: com.nguyendevs.freesia.backend.FreesiaBackend api-version: '1.16' - folia-supported: true diff --git a/Freesia-Velocity/build.gradle.kts b/Freesia-Velocity/build.gradle.kts index e0e5c65..a174788 100644 --- a/Freesia-Velocity/build.gradle.kts +++ b/Freesia-Velocity/build.gradle.kts @@ -44,7 +44,6 @@ sourceSets.named("main") { java.srcDir(generateTemplates.map { it.outputs }) } -// 确保 generateTemplates 任务在构建时执行 tasks.named("build") { dependsOn(generateTemplates) } 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/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..014a831 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ kotlin.code.style=official -version=v1.0.2_YSM_2.6.2 +version=v1.0.3_YSM_2.6.4 minecraft_version=1.21.1 parchment_version=2024.11.17 fabric_loader_version=0.16.13 From 56019edbec8bd65b11cae3ad6d85c37446060bf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Devs?= <144598460+NguyenDevs@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:04:15 +0700 Subject: [PATCH 02/10] Feature: Add TLS Encryption and Application-level IP Firewall for Proxy-Worker Networking --- .../nguyendevs/freesia/common/SslUtils.java | 30 ++++++++++ .../DefaultChannelPipelineLoader.java | 8 ++- .../communicating/FreesiaIpFilterHandler.java | 42 ++++++++++++++ .../communicating/NettySocketClient.java | 6 +- .../communicating/NettySocketServer.java | 8 ++- .../nguyendevs/freesia/velocity/Freesia.java | 19 ++++++- .../freesia/velocity/FreesiaConstants.java | 1 + .../velocity/FreesiaSecurityConfig.java | 55 +++++++++++++++++++ .../nguyendevs/freesia/waterfall/Freesia.java | 19 ++++++- .../freesia/waterfall/FreesiaConstants.java | 1 + .../waterfall/FreesiaSecurityConfig.java | 55 +++++++++++++++++++ .../freesia/worker/FreesiaWorkerConfig.java | 6 ++ .../freesia/worker/ServerLoader.java | 14 ++++- 13 files changed, 256 insertions(+), 8 deletions(-) create mode 100644 Freesia-Common/src/main/java/com/nguyendevs/freesia/common/SslUtils.java create mode 100644 Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/FreesiaIpFilterHandler.java create mode 100644 Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/FreesiaSecurityConfig.java create mode 100644 Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/FreesiaSecurityConfig.java 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..24446ea --- /dev/null +++ b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/SslUtils.java @@ -0,0 +1,30 @@ +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 io.netty.handler.ssl.util.SelfSignedCertificate; +import java.io.File; + +public class SslUtils { + public static SslContext createServerContext(boolean useSelfSigned, String certPath, String keyPath) throws Exception { + if (useSelfSigned) { + SelfSignedCertificate ssc = new SelfSignedCertificate(); + EntryPoint.LOGGER_INST.info("\u001B[32m[Security] Generated Self-Signed Certificate for Master Server\u001B[0m"); + return SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).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(); + } + } + + 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..73ec963 --- /dev/null +++ b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/FreesiaIpFilterHandler.java @@ -0,0 +1,42 @@ +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 java.net.InetSocketAddress; +import java.util.List; + +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..b4a74ca 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,11 +13,15 @@ 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() { @@ -28,7 +32,7 @@ public void bind() { .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-Velocity/src/main/java/com/nguyendevs/freesia/velocity/Freesia.java b/Freesia-Velocity/src/main/java/com/nguyendevs/freesia/velocity/Freesia.java index 68303fe..1c1dd5b 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.SslUtils.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-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/Freesia.java b/Freesia-Waterfall/src/main/java/com/nguyendevs/freesia/waterfall/Freesia.java index 4ac9a61..f16ca72 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.SslUtils.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-Worker/src/main/java/com/nguyendevs/freesia/worker/FreesiaWorkerConfig.java b/Freesia-Worker/src/main/java/com/nguyendevs/freesia/worker/FreesiaWorkerConfig.java index 231751d..ce7b5c1 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 = false; + 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(); From 885c29c9391903c2f253b57f537c63a2ffbc08da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Devs?= <144598460+NguyenDevs@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:12:39 +0700 Subject: [PATCH 03/10] Chore: Update project namespace to com.nguyendevs and bump version to v1.0.3-YSM-2.6.4 --- Freesia-Backend/src/main/resources/plugin.yml | 2 +- Freesia-Waterfall/src/main/resources/bungee.yml | 2 +- gradle.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Freesia-Backend/src/main/resources/plugin.yml b/Freesia-Backend/src/main/resources/plugin.yml index 3f557bb..f04d6e9 100644 --- a/Freesia-Backend/src/main/resources/plugin.yml +++ b/Freesia-Backend/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: Freesia-Backend -version: 'v1.0.3_YSM_2.6.4' +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-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/gradle.properties b/gradle.properties index 014a831..38a0fa8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ kotlin.code.style=official -version=v1.0.3_YSM_2.6.4 +version=1.0.3-YSM-2.6.4 minecraft_version=1.21.1 parchment_version=2024.11.17 fabric_loader_version=0.16.13 From 76dd705d0e47735c693b81a9f07d9874198d6d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Devs?= <144598460+NguyenDevs@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:13:11 +0700 Subject: [PATCH 04/10] UI: Add ANSI color codes to Worker connection logs for better visibility --- .../communicating/handler/NettyServerChannelHandlerLayer.java | 2 +- .../velocity/network/backend/MasterServerMessageHandler.java | 2 +- .../waterfall/network/backend/MasterServerMessageHandler.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/handler/NettyServerChannelHandlerLayer.java b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/handler/NettyServerChannelHandlerLayer.java index 592e001..efcb7c5 100644 --- a/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/handler/NettyServerChannelHandlerLayer.java +++ b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/communicating/handler/NettyServerChannelHandlerLayer.java @@ -20,7 +20,7 @@ public abstract class NettyServerChannelHandlerLayer extends SimpleChannelInboun @Override public void channelActive(@NotNull ChannelHandlerContext ctx) { this.channel = ctx.channel(); - EntryPoint.LOGGER_INST.info("Worker connected {}", this.channel); + EntryPoint.LOGGER_INST.info("\u001B[36mWorker connected \u001B[35m{}\u001B[0m", this.channel); } @Override 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-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; From f95a98a039676788db94179118aeb674166ac9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Devs?= <144598460+NguyenDevs@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:19:44 +0700 Subject: [PATCH 05/10] Fix: Resolve OpenJdkSelfSignedCertGenerator error with BouncyCastle and enforce exact semver definitions --- Freesia-Velocity/build.gradle.kts | 2 ++ Freesia-Waterfall/build.gradle.kts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Freesia-Velocity/build.gradle.kts b/Freesia-Velocity/build.gradle.kts index a174788..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") } 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 From df8b008e9de961d0e5269cd5ccb62971c02d6c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Devs?= <144598460+NguyenDevs@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:28:08 +0700 Subject: [PATCH 06/10] Security: enable TLS by default on Worker to match Proxy configuration --- Freesia-Common/build.gradle.kts | 2 + .../nguyendevs/freesia/common/SslUtils.java | 38 +++++++++++++++++-- .../freesia/worker/FreesiaWorkerConfig.java | 2 +- 3 files changed, 38 insertions(+), 4 deletions(-) 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/SslUtils.java b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/SslUtils.java index 24446ea..b6a30fd 100644 --- a/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/SslUtils.java +++ b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/SslUtils.java @@ -3,15 +3,47 @@ import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.handler.ssl.util.SelfSignedCertificate; +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 SslUtils { public static SslContext createServerContext(boolean useSelfSigned, String certPath, String keyPath) throws Exception { if (useSelfSigned) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); + 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(ssc.certificate(), ssc.privateKey()).build(); + 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-Worker/src/main/java/com/nguyendevs/freesia/worker/FreesiaWorkerConfig.java b/Freesia-Worker/src/main/java/com/nguyendevs/freesia/worker/FreesiaWorkerConfig.java index ce7b5c1..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,7 +15,7 @@ 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 = false; + public static boolean enableTls = true; public static boolean trustAll = true; public static String trustCertPath = "truststore.pem"; private static CommentedFileConfig CONFIG_INSTANCE; From 6529cf1b4c544be688b37b8fc95c37c7bd5640f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Devs?= <144598460+NguyenDevs@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:35:39 +0700 Subject: [PATCH 07/10] Fix: apply TCP_NODELAY to childOption instead of Server channel option to suppress Netty warnings --- .../freesia/common/communicating/NettySocketServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b4a74ca..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 @@ -28,7 +28,7 @@ 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) { From e7943dec421e9cf78495317e7c8d6b9ef7a40032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Devs?= <144598460+NguyenDevs@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:35:39 +0700 Subject: [PATCH 08/10] Fix: avoid NullPointerException when handling delayed player tracker callback in Velocity and Waterfall --- .../velocity/network/mc/FreesiaPlayerTracker.java | 10 ++++++---- .../waterfall/network/mc/FreesiaPlayerTracker.java | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) 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-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); + } } } From a749e10f518fc3034582aebdf8afa061439d6530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Devs?= <144598460+NguyenDevs@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:43:48 +0700 Subject: [PATCH 09/10] Fix: resolve network handler warnings and null pointer exceptions on worker shutdown --- .../common/communicating/FreesiaIpFilterHandler.java | 2 ++ .../handler/NettyClientChannelHandlerLayer.java | 8 ++++++++ .../handler/NettyServerChannelHandlerLayer.java | 8 ++++++++ .../freesia/worker/impl/WorkerMessageHandlerImpl.java | 6 +++++- 4 files changed, 23 insertions(+), 1 deletion(-) 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 index 73ec963..85d1a1f 100644 --- 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 @@ -5,9 +5,11 @@ 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; 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-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() { From 754e68bbf5119921b4c5320b555d6c71cf5f4ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Devs?= <144598460+NguyenDevs@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:55:39 +0700 Subject: [PATCH 10/10] Fix: extract BouncyCastle certificate generation to dedicated ServerSslUtils to bypass JVM linking NoClassDefFoundError on Worker module --- .../freesia/common/ServerSslUtils.java | 51 +++++++++++++++++++ .../nguyendevs/freesia/common/SslUtils.java | 42 --------------- .../nguyendevs/freesia/velocity/Freesia.java | 2 +- .../nguyendevs/freesia/waterfall/Freesia.java | 2 +- 4 files changed, 53 insertions(+), 44 deletions(-) create mode 100644 Freesia-Common/src/main/java/com/nguyendevs/freesia/common/ServerSslUtils.java 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 index b6a30fd..b787328 100644 --- a/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/SslUtils.java +++ b/Freesia-Common/src/main/java/com/nguyendevs/freesia/common/SslUtils.java @@ -3,52 +3,10 @@ import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -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 SslUtils { - 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(); - } - } public static SslContext createClientContext(boolean trustAll, String trustCertPath) throws Exception { if (trustAll) { 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 1c1dd5b..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 @@ -118,7 +118,7 @@ public void onProxyStart(ProxyInitializeEvent event) { io.netty.handler.ssl.SslContext sslContext = null; try { if (FreesiaSecurityConfig.enableTls) { - sslContext = com.nguyendevs.freesia.common.SslUtils.createServerContext( + 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() 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 f16ca72..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 @@ -112,7 +112,7 @@ public void onEnable() { io.netty.handler.ssl.SslContext sslContext = null; try { if (FreesiaSecurityConfig.enableTls) { - sslContext = com.nguyendevs.freesia.common.SslUtils.createServerContext( + 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()