Skip to content
This repository was archived by the owner on Dec 10, 2025. It is now read-only.

Commit 9cbd485

Browse files
committed
feat: enhance SSL/TLS certificate management and update encryption setup
1 parent e288f3f commit 9cbd485

File tree

7 files changed

+236
-65
lines changed

7 files changed

+236
-65
lines changed

cert_new/INSTRUCTIONS.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# SSL Certificate Generation Guide for Netty Server and Clients
2+
3+
This guide explains how to generate and manage SSL/TLS certificates for your Netty-based server and
4+
clients using OpenSSL. Properly generated certificates ensure secure communication between your
5+
server and clients.
6+
7+
> **Note:** If you already have a server set up and only need to add a new client, you can skip directly
8+
> to **[Step 4](#step-4-generate-client-certificates-repeatable-for-each-client)**. Ensure the following files are already present in your working directory:
9+
> - `openssl.cnf`
10+
> - `ca.key`
11+
> - `ca.crt`
12+
13+
---
14+
15+
## Prerequisites
16+
17+
- Install [OpenSSL](https://slproweb.com/products/Win32OpenSSL.html) or use WSL/Git Bash.
18+
- Ensure OpenSSL is available in your command prompt:
19+
20+
```bash
21+
openssl version
22+
```
23+
24+
---
25+
26+
## Step-by-Step Guide
27+
28+
### Step 1: Create an OpenSSL Configuration File (`openssl.cnf`)
29+
30+
Create a file named `openssl.cnf` in your working directory:
31+
32+
```ini
33+
[ req ]
34+
default_bits = 2048
35+
distinguished_name = req_distinguished_name
36+
req_extensions = v3_req
37+
prompt = no
38+
39+
[ req_distinguished_name ]
40+
C = DE
41+
ST = Remote
42+
L = Internet
43+
O = Surf Cloud
44+
CN = YOUR_SERVER_IP_OR_DNS
45+
46+
[ v3_req ]
47+
subjectAltName = @alt_names
48+
keyUsage = critical, digitalSignature, keyEncipherment
49+
extendedKeyUsage = serverAuth, clientAuth
50+
51+
[ alt_names ]
52+
IP.1 = YOUR_SERVER_IP
53+
DNS.1 = YOUR_SERVER_DNS
54+
```
55+
56+
Replace `YOUR_SERVER_IP` and `YOUR_SERVER_DNS` with your actual server IP address and DNS name.
57+
58+
Example:
59+
60+
```ini
61+
CN = 192.168.1.23
62+
63+
[ alt_names ]
64+
IP.1 = 192.168.1.23
65+
DNS.1 = myserver.local
66+
```
67+
68+
---
69+
70+
### Step 2: Generate Your Own Certificate Authority (CA)
71+
72+
Run the following commands once to create your CA:
73+
74+
```bash
75+
openssl genrsa -out ca.key 2048
76+
77+
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt -subj "/C=DE/ST=Remote/L=Internet/O=Surf Cloud/CN=SurfCloud CA"
78+
```
79+
80+
**Important:**
81+
82+
- `ca.key`: Private key of your CA (keep this secret and safe).
83+
- `ca.crt`: Public certificate of your CA (clients and server use this to establish trust).
84+
85+
---
86+
87+
### Step 3: Generate Server Certificate
88+
89+
```bash
90+
# Generate private key
91+
openssl genrsa -out server.key 2048
92+
93+
# Generate CSR (Certificate Signing Request)
94+
openssl req -new -key server.key -out server.csr -config openssl.cnf
95+
96+
# Sign CSR with your CA
97+
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365 -extfile openssl.cnf -extensions v3_req
98+
```
99+
100+
**Files produced:**
101+
102+
- `server.key`: Server's private key (use on your Netty server).
103+
- `server.crt`: Server's signed certificate (distribute to clients).
104+
105+
---
106+
107+
### Step 4: Generate Client Certificates (repeatable for each client)
108+
109+
Modify `openssl.cnf` by changing the `CN` to your client name (`client01`, `client02`, etc.):
110+
111+
```ini
112+
CN = test-server01
113+
```
114+
115+
Then run:
116+
117+
```bash
118+
openssl genrsa -out client.key 2048
119+
120+
openssl req -new -key client.key -out client.csr -config openssl.cnf
121+
122+
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365 -extfile openssl.cnf -extensions v3_req
123+
```
124+
125+
**Files produced:**
126+
127+
- `client.key`: Client's private key (keep secret and only on the client).
128+
- `client.crt`: Client's signed certificate (put this on your server to authenticate the
129+
client).
130+
131+
---
132+
133+
## File Management
134+
135+
### Files to Keep and Their Usage
136+
137+
| File | Purpose | Store on |
138+
|--------------|----------------------------------------------|--------------------------------------|
139+
| `ca.key` | CA private key (needed for signing) | Secure offline storage (never share) |
140+
| `ca.crt` | CA certificate (trusted by all participants) | Server and Clients |
141+
| `server.key` | Server private key | Server (secure) |
142+
| `server.crt` | Server certificate (signed by CA) | Server (send to clients) |
143+
| `client.key` | Client private key | Client (secure) |
144+
| `client.crt` | Client certificate (signed by CA) | Server (trust this certificate) |
145+
146+
### Directory Structure
147+
148+
**On Server:**
149+
150+
```
151+
certificates/
152+
├── server.key
153+
├── server.crt
154+
└── ca.crt
155+
```
156+
157+
**On Client:**
158+
159+
```
160+
certificates/
161+
├── client.key
162+
├── client.crt
163+
└── ca.crt
164+
```
165+
166+
---
167+
168+
## Common Errors and Fixes
169+
170+
- **Hostname/IP undefined:** Ensure your server certificate includes the correct IP/DNS in SAN.
171+
- **certificate_unknown:** Ensure the client certificate is correctly placed in
172+
`certificates/clients/` and signed by the CA.
173+
174+
---

surf-cloud-api/surf-cloud-api-common/src/main/kotlin/dev/slne/surf/cloud/api/common/netty/network/codec/kotlinx/java/UUIDSerializer.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import dev.slne.surf.cloud.api.common.netty.network.codec.kotlinx.CloudBufSerial
44
import dev.slne.surf.cloud.api.common.netty.protocol.buffer.SurfByteBuf
55
import kotlinx.serialization.Serializable
66
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
7-
import org.gradle.internal.impldep.kotlinx.serialization.descriptors.element
7+
import kotlinx.serialization.descriptors.element
88
import java.util.UUID
99

1010
typealias SerializableUUID = @Serializable(with = UUIDSerializer::class) UUID
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,41 @@
11
package dev.slne.surf.cloud.core.client.netty.network
22

3-
import dev.slne.surf.cloud.api.common.config.properties.CloudProperties
3+
import dev.slne.surf.cloud.core.common.config.cloudConfig
44
import dev.slne.surf.cloud.core.common.netty.network.EncryptionManager
55
import dev.slne.surf.cloud.core.common.netty.network.HandlerNames
66
import dev.slne.surf.surfapi.core.api.util.logger
77
import io.netty.channel.Channel
88
import io.netty.handler.ssl.SslContext
99
import io.netty.handler.ssl.SslContextBuilder
10+
import io.netty.handler.ssl.SslProvider
11+
import kotlin.io.path.div
1012

1113
object ClientEncryptionManager : EncryptionManager() {
12-
private val log = logger()
1314
private lateinit var sslContext: SslContext
1415

15-
private val clientCertificateFile =
16-
certificatesFolder.resolve("${CloudProperties.SERVER_NAME}.crt").toFile()
17-
private val clientKeyFile =
18-
certificatesFolder.resolve("${CloudProperties.SERVER_NAME}.key").toFile()
19-
private val serverCertificate = certificatesFolder.resolve("server.crt").toFile()
16+
private val clientCertificateFile = (certificatesFolder / "client.crt").toFile()
17+
private val clientKeyFile = (certificatesFolder / "client.key").toFile()
18+
private val trustManagerFile = (certificatesFolder / "ca.crt").toFile()
2019

2120
override fun setupEncryption(ch: Channel) {
21+
val config = cloudConfig.connectionConfig.nettyConfig
2222
ch.pipeline().addFirst(
2323
HandlerNames.SSL_HANDLER,
24-
sslContext.newHandler(ch.alloc())
24+
sslContext.newHandler(ch.alloc(), config.host, config.port)
2525
)
2626
}
2727

2828
override suspend fun init() {
29-
waitForFiles(clientCertificateFile, clientKeyFile, serverCertificate)
29+
waitForFiles(clientCertificateFile, clientKeyFile, trustManagerFile)
3030
sslContext = buildSslContext()
3131
}
3232

3333
private fun buildSslContext(): SslContext {
3434
return SslContextBuilder
3535
.forClient()
3636
.keyManager(clientCertificateFile, clientKeyFile)
37-
.trustManager(serverCertificate)
37+
.trustManager(trustManagerFile)
38+
.sslProvider(SslProvider.JDK)
3839
.build()
3940
}
4041
}

surf-cloud-core/surf-cloud-core-common/src/main/kotlin/dev/slne/surf/cloud/core/common/netty/network/ConnectionImpl.kt

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,15 @@ import dev.slne.surf.surfapi.core.api.util.logger
2525
import io.netty.bootstrap.Bootstrap
2626
import io.netty.channel.*
2727
import io.netty.channel.epoll.Epoll
28-
import io.netty.channel.epoll.EpollEventLoopGroup
28+
import io.netty.channel.epoll.EpollIoHandler
2929
import io.netty.channel.epoll.EpollSocketChannel
30-
import io.netty.channel.nio.NioEventLoopGroup
30+
import io.netty.channel.nio.NioIoHandler
3131
import io.netty.channel.socket.SocketChannel
3232
import io.netty.channel.socket.nio.NioSocketChannel
3333
import io.netty.handler.codec.EncoderException
3434
import io.netty.handler.codec.compression.ZstdDecoder
3535
import io.netty.handler.codec.compression.ZstdEncoder
3636
import io.netty.handler.flow.FlowControlHandler
37-
import io.netty.handler.logging.LogLevel
3837
import io.netty.handler.logging.LoggingHandler
3938
import io.netty.handler.timeout.ReadTimeoutHandler
4039
import io.netty.handler.timeout.TimeoutException
@@ -981,24 +980,22 @@ class ConnectionImpl(
981980
companion object {
982981
private val log = logger()
983982

984-
val NETWORK_WORKER_GROUP: NioEventLoopGroup by lazy {
985-
NioEventLoopGroup(
983+
val NETWORK_WORKER_GROUP: MultiThreadIoEventLoopGroup by lazy {
984+
MultiThreadIoEventLoopGroup(
986985
threadFactory {
987986
nameFormat("Netty Client IO #%d")
988987
daemon(true)
989988
uncaughtExceptionHandler(DefaultUncaughtExceptionHandlerWithName(log))
990-
}
989+
}, NioIoHandler.newFactory()
991990
)
992991
}
993992

994-
val NETWORK_EPOLL_WORKER_GROUP: EpollEventLoopGroup by lazy {
995-
EpollEventLoopGroup(
996-
threadFactory {
997-
nameFormat("Netty Epoll Client IO #%d")
998-
daemon(true)
999-
uncaughtExceptionHandler(DefaultUncaughtExceptionHandlerWithName(log))
1000-
}
1001-
)
993+
val NETWORK_EPOLL_WORKER_GROUP: MultiThreadIoEventLoopGroup by lazy {
994+
MultiThreadIoEventLoopGroup(threadFactory {
995+
nameFormat("Netty Epoll Client IO #%d")
996+
daemon(true)
997+
uncaughtExceptionHandler(DefaultUncaughtExceptionHandlerWithName(log))
998+
}, EpollIoHandler.newFactory())
1002999
}
10031000

10041001
private val INITIAL_PROTOCOL = HandshakeProtocols.SERVERBOUND

surf-cloud-core/surf-cloud-core-common/src/main/kotlin/dev/slne/surf/cloud/core/common/netty/network/EncryptionManager.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
package dev.slne.surf.cloud.core.common.netty.network
22

33
import dev.slne.surf.cloud.core.common.coreCloudInstance
4-
import dev.slne.surf.surfapi.core.api.util.logger
54
import io.netty.channel.Channel
65
import kotlinx.coroutines.delay
76
import java.io.File
87
import java.nio.file.Path
8+
import kotlin.io.path.createDirectories
9+
import kotlin.io.path.div
910
import kotlin.time.Duration.Companion.seconds
1011

1112
abstract class EncryptionManager {
12-
private val log = logger()
1313

1414
protected val certificatesFolder: Path by lazy {
15-
coreCloudInstance.dataFolder.resolve("certificates").also { it.toFile().mkdirs() }
15+
(coreCloudInstance.dataFolder / "certificates").createDirectories()
1616
}
1717

1818
abstract fun setupEncryption(ch: Channel)
@@ -24,8 +24,10 @@ abstract class EncryptionManager {
2424
val missingFiles = files.filter { !it.exists() }.toMutableList()
2525

2626
while (missingFiles.isNotEmpty()) {
27-
log.atInfo()
28-
.log("Waiting for missing files: ${missingFiles.joinToString { it.path }}")
27+
// log.atInfo()
28+
// .log("Waiting for missing files: ${missingFiles.joinToString { it.path }}")
29+
30+
println("[INFO] ${this::class.simpleName}: Waiting for missing files: ${missingFiles.joinToString { it.path }}")
2931

3032
delay(5.seconds)
3133
missingFiles.removeIf { it.exists() }

surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/netty/server/connection/ServerConnectionListener.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ import io.netty.bootstrap.ServerBootstrap
1919
import io.netty.channel.*
2020
import io.netty.channel.epoll.Epoll
2121
import io.netty.channel.epoll.EpollEventLoopGroup
22+
import io.netty.channel.epoll.EpollIoHandler
2223
import io.netty.channel.epoll.EpollServerDomainSocketChannel
2324
import io.netty.channel.epoll.EpollServerSocketChannel
2425
import io.netty.channel.nio.NioEventLoopGroup
26+
import io.netty.channel.nio.NioIoHandler
2527
import io.netty.channel.socket.nio.NioServerSocketChannel
2628
import io.netty.channel.unix.DomainSocketAddress
2729
import io.netty.handler.flush.FlushConsolidationHandler
@@ -215,22 +217,20 @@ class ServerConnectionListener(val server: NettyServerImpl) {
215217
private val log = logger()
216218

217219
val SERVER_EVENT_GROUP by lazy {
218-
NioEventLoopGroup(
219-
threadFactory {
220-
nameFormat("Netty Server IO #%d")
221-
daemon(true)
222-
uncaughtExceptionHandler(DefaultUncaughtExceptionHandlerWithName(log))
223-
}
224-
)
220+
MultiThreadIoEventLoopGroup(threadFactory {
221+
nameFormat("Netty Server IO #%d")
222+
daemon(true)
223+
uncaughtExceptionHandler(DefaultUncaughtExceptionHandlerWithName(log))
224+
}, NioIoHandler.newFactory())
225225
}
226226

227227
val SERVER_EPOLL_EVENT_GROUP by lazy {
228-
EpollEventLoopGroup(
228+
MultiThreadIoEventLoopGroup(
229229
threadFactory {
230230
nameFormat("Netty Epoll Server IO #%d")
231231
daemon(true)
232232
uncaughtExceptionHandler(DefaultUncaughtExceptionHandlerWithName(log))
233-
}
233+
}, EpollIoHandler.newFactory()
234234
)
235235
}
236236
}

0 commit comments

Comments
 (0)