Skip to content

Commit ba97b64

Browse files
committed
grpc-native: Working mTLS on JVM
Signed-off-by: Johannes Zottele <[email protected]>
1 parent 0b3124f commit ba97b64

File tree

9 files changed

+339
-157
lines changed

9 files changed

+339
-157
lines changed

grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/ManagedChannel.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ public interface ManagedChannel {
6969
*/
7070
public expect abstract class ManagedChannelBuilder<T : ManagedChannelBuilder<T>> {
7171
public fun usePlaintext(): T
72+
73+
public abstract fun overrideAuthority(authority: String): T
7274
}
7375

7476
internal expect fun ManagedChannelBuilder(

grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/credentials.kt

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,53 @@ public expect class InsecureServerCredentials : ServerCredentials
1515
public expect class TlsChannelCredentials : ChannelCredentials
1616
public expect class TlsServerCredentials : ServerCredentials
1717

18-
public expect fun TlsChannelCredentials(): ChannelCredentials
19-
public expect fun TlsServerCredentials(certChain: String, privateKey: String): ServerCredentials
18+
public fun TlsChannelCredentials(configure: TlsChannelCredentialsBuilder.() -> Unit = {}): ChannelCredentials {
19+
val builder = TlsChannelCredentialsBuilder()
20+
builder.configure()
21+
return builder.build()
22+
}
23+
24+
public fun TlsServerCredentials(
25+
certChain: String,
26+
privateKey: String,
27+
configure: TlsServerCredentialsBuilder.() -> Unit = {},
28+
): ServerCredentials {
29+
val builder = TlsServerCredentialsBuilder(certChain, privateKey)
30+
builder.configure()
31+
return builder.build()
32+
}
2033

2134
public interface TlsChannelCredentialsBuilder {
2235
public fun trustManager(rootCertsPem: String): TlsChannelCredentialsBuilder
36+
public fun keyManager(certChainPem: String, privateKeyPem: String): TlsChannelCredentialsBuilder
2337
public fun build(): ChannelCredentials
2438
}
2539

40+
public enum class TlsClientAuth {
41+
/** Clients will not present any identity. */
42+
NONE,
43+
44+
/**
45+
* Clients are requested to present their identity, but clients without identities are
46+
* permitted.
47+
*/
48+
OPTIONAL,
49+
50+
/**
51+
* Clients are requested to present their identity, and are required to provide a valid
52+
* identity.
53+
*/
54+
REQUIRE
55+
}
56+
2657
public interface TlsServerCredentialsBuilder {
27-
public fun keyManager(certChainPem: String, privateKeyPem: String): TlsServerCredentialsBuilder
58+
public fun trustManager(rootCertsPem: String): TlsServerCredentialsBuilder
59+
public fun clientAuth(clientAuth: TlsClientAuth): TlsServerCredentialsBuilder
2860
public fun build(): ServerCredentials
2961
}
3062

31-
32-
public expect fun TlsChannelCredentialsBuilder(): TlsChannelCredentialsBuilder
33-
public expect fun TlsServerCredentialsBuilder(): TlsServerCredentialsBuilder
63+
internal expect fun TlsChannelCredentialsBuilder(): TlsChannelCredentialsBuilder
64+
internal expect fun TlsServerCredentialsBuilder(
65+
certChain: String,
66+
privateKey: String,
67+
): TlsServerCredentialsBuilder

grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/internal/GrpcChannel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ public expect abstract class GrpcChannel {
1313
callOptions: GrpcCallOptions,
1414
): ClientCall<RequestT, ResponseT>
1515

16-
public abstract fun authority(): String
16+
public abstract fun authority(): String?
1717
}

grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/proto/GrpcbInTlsTest.kt

Lines changed: 181 additions & 89 deletions
Large diffs are not rendered by default.

grpc/grpc-core/src/jvmMain/kotlin/kotlinx/rpc/grpc/credentials.jvm.kt

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,53 +8,66 @@ public actual typealias ChannelCredentials = io.grpc.ChannelCredentials
88

99
public actual typealias ServerCredentials = io.grpc.ServerCredentials
1010

11-
1211
// we need a wrapper for InsecureChannelCredentials as our constructor would conflict with the private
1312
// java constructor.
1413
public actual typealias InsecureChannelCredentials = io.grpc.InsecureChannelCredentials
1514
public actual typealias InsecureServerCredentials = io.grpc.InsecureServerCredentials
1615

17-
public actual typealias TlsServerCredentials = io.grpc.TlsServerCredentials
1816
public actual typealias TlsChannelCredentials = io.grpc.TlsChannelCredentials
17+
public actual typealias TlsServerCredentials = io.grpc.TlsServerCredentials
1918

20-
public actual fun TlsChannelCredentials(): ChannelCredentials {
21-
return TlsChannelCredentialsBuilder().build()
22-
}
19+
internal actual fun TlsChannelCredentialsBuilder(): TlsChannelCredentialsBuilder =
20+
object : TlsChannelCredentialsBuilder {
21+
private var cb = TlsChannelCredentials.newBuilder()
22+
23+
24+
override fun trustManager(rootCertsPem: String): TlsChannelCredentialsBuilder {
25+
cb.trustManager(rootCertsPem.byteInputStream())
26+
return this
27+
}
2328

24-
public actual fun TlsServerCredentials(
29+
override fun keyManager(
30+
certChainPem: String,
31+
privateKeyPem: String,
32+
): TlsChannelCredentialsBuilder {
33+
cb.keyManager(certChainPem.byteInputStream(), privateKeyPem.byteInputStream())
34+
return this
35+
}
36+
37+
override fun build(): ChannelCredentials {
38+
return cb.build()
39+
}
40+
}
41+
42+
internal actual fun TlsServerCredentialsBuilder(
2543
certChain: String,
2644
privateKey: String,
27-
): ServerCredentials {
28-
return TlsServerCredentialsBuilder().keyManager(certChain, privateKey).build()
29-
}
30-
31-
public actual fun TlsServerCredentialsBuilder(): TlsServerCredentialsBuilder = object : TlsServerCredentialsBuilder {
45+
): TlsServerCredentialsBuilder = object : TlsServerCredentialsBuilder {
3246
private var sb = TlsServerCredentials.newBuilder()
3347

34-
override fun keyManager(
35-
certChainPem: String,
36-
privateKeyPem: String,
37-
): TlsServerCredentialsBuilder {
38-
sb.keyManager(certChainPem.byteInputStream(), privateKeyPem.byteInputStream())
48+
override fun trustManager(rootCertsPem: String): TlsServerCredentialsBuilder {
49+
sb.trustManager(rootCertsPem.byteInputStream())
3950
return this
4051
}
4152

42-
override fun build(): ServerCredentials {
43-
return sb.build()
53+
override fun clientAuth(clientAuth: TlsClientAuth): TlsServerCredentialsBuilder {
54+
sb.clientAuth(clientAuth.toJava())
55+
return this
4456
}
45-
}
46-
47-
public actual fun TlsChannelCredentialsBuilder(): TlsChannelCredentialsBuilder = object : TlsChannelCredentialsBuilder {
48-
private var cb = TlsChannelCredentials.newBuilder()
4957

50-
override fun trustManager(rootCertsPem: String): TlsChannelCredentialsBuilder {
51-
cb.trustManager(rootCertsPem.byteInputStream())
52-
return this
58+
init {
59+
sb.keyManager(certChain.byteInputStream(), privateKey.byteInputStream())
5360
}
5461

55-
override fun build(): ChannelCredentials {
56-
return cb.build()
62+
override fun build(): ServerCredentials {
63+
return sb.build()
5764
}
5865
}
5966

67+
private fun TlsClientAuth.toJava(): io.grpc.TlsServerCredentials.ClientAuth = when (this) {
68+
TlsClientAuth.NONE -> io.grpc.TlsServerCredentials.ClientAuth.NONE
69+
TlsClientAuth.OPTIONAL -> io.grpc.TlsServerCredentials.ClientAuth.OPTIONAL
70+
TlsClientAuth.REQUIRE -> io.grpc.TlsServerCredentials.ClientAuth.REQUIRE
71+
}
72+
6073

grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/ManagedChannel.native.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,31 @@ public actual abstract class ManagedChannelBuilder<T : ManagedChannelBuilder<T>>
2222
public actual open fun usePlaintext(): T {
2323
error("Builder does not support usePlaintext()")
2424
}
25+
26+
public actual abstract fun overrideAuthority(authority: String): T
2527
}
2628

2729
internal class NativeManagedChannelBuilder(
2830
private val target: String,
2931
private var credentials: Lazy<ChannelCredentials>,
3032
) : ManagedChannelBuilder<NativeManagedChannelBuilder>() {
3133

34+
private var authority: String? = null
35+
3236
override fun usePlaintext(): NativeManagedChannelBuilder {
3337
credentials = lazy { InsecureChannelCredentials() }
3438
return this
3539
}
3640

41+
override fun overrideAuthority(authority: String): NativeManagedChannelBuilder {
42+
this.authority = authority
43+
return this
44+
}
45+
3746
fun buildChannel(): NativeManagedChannel {
3847
return NativeManagedChannel(
3948
target,
49+
authority = authority,
4050
credentials = credentials.value,
4151
)
4252
}

grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/credentials.native.kt

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ import libkgrpc.grpc_channel_credentials_release
1515
import libkgrpc.grpc_insecure_credentials_create
1616
import libkgrpc.grpc_insecure_server_credentials_create
1717
import libkgrpc.grpc_server_credentials_release
18+
import libkgrpc.grpc_ssl_client_certificate_request_type
1819
import libkgrpc.grpc_tls_certificate_provider_release
1920
import libkgrpc.grpc_tls_certificate_provider_static_data_create
2021
import libkgrpc.grpc_tls_credentials_create
2122
import libkgrpc.grpc_tls_credentials_options_create
2223
import libkgrpc.grpc_tls_credentials_options_destroy
24+
import libkgrpc.grpc_tls_credentials_options_set_cert_request_type
2325
import libkgrpc.grpc_tls_credentials_options_set_certificate_provider
2426
import libkgrpc.grpc_tls_credentials_options_watch_identity_key_cert_pairs
2527
import libkgrpc.grpc_tls_credentials_options_watch_root_certs
@@ -76,44 +78,51 @@ public fun InsecureServerCredentials(): ServerCredentials {
7678
)
7779
}
7880

79-
public actual fun TlsChannelCredentials(): ChannelCredentials {
80-
val raw = grpc_tls_credentials_options_create()?.let {
81-
grpc_tls_credentials_create(it)
82-
} ?: error("Failed to create TLS credentials")
83-
return TlsChannelCredentials(raw)
84-
}
81+
internal actual fun TlsChannelCredentialsBuilder(): TlsChannelCredentialsBuilder =
82+
object : TlsChannelCredentialsBuilder {
83+
var optionsBuilder = TlsCredentialsOptionsBuilder()
84+
85+
override fun trustManager(rootCertsPem: String): TlsChannelCredentialsBuilder {
86+
optionsBuilder.trustManager(rootCertsPem)
87+
return this
88+
}
89+
90+
override fun keyManager(
91+
certChainPem: String,
92+
privateKeyPem: String,
93+
): TlsChannelCredentialsBuilder {
94+
optionsBuilder.keyManager(certChainPem, privateKeyPem)
95+
return this
96+
}
97+
98+
override fun build(): ChannelCredentials {
99+
val opts = optionsBuilder.build()
100+
val creds = grpc_tls_credentials_create(opts)
101+
?: run {
102+
grpc_tls_credentials_options_destroy(opts);
103+
error("TLS channel credential creation failed")
104+
}
105+
return TlsChannelCredentials(creds)
106+
}
107+
}
85108

86-
public actual fun TlsServerCredentials(
109+
internal actual fun TlsServerCredentialsBuilder(
87110
certChain: String,
88111
privateKey: String,
89-
): ServerCredentials {
90-
return TlsServerCredentialsBuilder().keyManager(certChain, privateKey).build()
91-
}
92-
93-
public actual fun TlsChannelCredentialsBuilder(): TlsChannelCredentialsBuilder = object : TlsChannelCredentialsBuilder {
112+
): TlsServerCredentialsBuilder = object : TlsServerCredentialsBuilder {
94113
var optionsBuilder = TlsCredentialsOptionsBuilder()
95114

96-
override fun trustManager(rootCertsPem: String): TlsChannelCredentialsBuilder {
97-
optionsBuilder.trustManager(rootCertsPem)
98-
return this
115+
init {
116+
optionsBuilder.keyManager(certChain, privateKey)
99117
}
100118

101-
override fun build(): ChannelCredentials {
102-
val opts = optionsBuilder.build()
103-
val creds = grpc_tls_credentials_create(opts)
104-
?: run {
105-
grpc_tls_credentials_options_destroy(opts);
106-
error("TLS channel credential creation failed")
107-
}
108-
return TlsChannelCredentials(creds)
119+
override fun trustManager(rootCertsPem: String): TlsServerCredentialsBuilder {
120+
optionsBuilder.trustManager(rootCertsPem)
121+
return this
109122
}
110-
}
111-
112-
public actual fun TlsServerCredentialsBuilder(): TlsServerCredentialsBuilder = object : TlsServerCredentialsBuilder {
113-
var optionsBuilder = TlsCredentialsOptionsBuilder()
114123

115-
override fun keyManager(certChainPem: String, privateKeyPem: String): TlsServerCredentialsBuilder {
116-
optionsBuilder.keyManager(certChainPem, privateKeyPem)
124+
override fun clientAuth(clientAuth: TlsClientAuth): TlsServerCredentialsBuilder {
125+
optionsBuilder.clientAuth(clientAuth)
117126
return this
118127
}
119128

@@ -134,11 +143,20 @@ private class TlsCredentialsOptionsBuilder {
134143
private var cert: String? = null
135144
private var key: String? = null
136145

137-
fun trustManager(rootCertsPem: String) = apply { roots = rootCertsPem }
146+
private var clientAuth: TlsClientAuth? = null
147+
148+
fun trustManager(rootCertsPem: String) {
149+
roots = rootCertsPem
150+
}
151+
138152
fun keyManager(certChainPem: String, privateKeyPem: String) = apply {
139153
cert = certChainPem; key = privateKeyPem
140154
}
141155

156+
fun clientAuth(clientAuth: TlsClientAuth) {
157+
this.clientAuth = clientAuth
158+
}
159+
142160
fun build(): CPointer<grpc_tls_credentials_options> {
143161
val opts = grpc_tls_credentials_options_create() ?: error("alloc opts failed")
144162

@@ -160,6 +178,15 @@ private class TlsCredentialsOptionsBuilder {
160178
if (pairs != null) grpc_tls_credentials_options_watch_identity_key_cert_pairs(opts)
161179
if (roots != null) grpc_tls_credentials_options_watch_root_certs(opts)
162180

181+
val clientAuth = clientAuth
182+
if (clientAuth != null) grpc_tls_credentials_options_set_cert_request_type(opts, clientAuth.toRaw())
183+
163184
return opts
164185
}
165186
}
187+
188+
private fun TlsClientAuth.toRaw(): grpc_ssl_client_certificate_request_type = when (this) {
189+
TlsClientAuth.NONE -> grpc_ssl_client_certificate_request_type.GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE
190+
TlsClientAuth.OPTIONAL -> grpc_ssl_client_certificate_request_type.GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY
191+
TlsClientAuth.REQUIRE -> grpc_ssl_client_certificate_request_type.GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
192+
}

grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/internal/GrpcChannel.native.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ public actual abstract class GrpcChannel {
1313
callOptions: GrpcCallOptions,
1414
): ClientCall<RequestT, ResponseT>
1515

16-
public actual abstract fun authority(): String
16+
public actual abstract fun authority(): String?
1717
}

grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/internal/NativeManagedChannel.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import kotlin.time.Duration
3939
*/
4040
internal class NativeManagedChannel(
4141
target: String,
42+
val authority: String?,
4243
// we must store them, otherwise the credentials are getting released
4344
credentials: ChannelCredentials,
4445
) : ManagedChannel, ManagedChannelPlatform() {
@@ -122,26 +123,29 @@ internal class NativeManagedChannel(
122123
// to construct a valid HTTP/2 path, we must prepend the name with a slash.
123124
// the user does not do this to align it with the java implementation.
124125
val methodNameSlice = "/$methodFullName".toGrpcSlice()
126+
val authoritySlice = authority?.toGrpcSlice()
127+
125128
val rawCall = grpc_channel_create_call(
126129
channel = raw,
127130
parent_call = null,
128131
propagation_mask = GRPC_PROPAGATE_DEFAULTS,
129132
completion_queue = cq.raw,
130133
method = methodNameSlice,
131-
host = null,
134+
host = authoritySlice,
132135
deadline = gpr_inf_future(GPR_CLOCK_REALTIME),
133136
reserved = null
134137
) ?: error("Failed to create call")
135138

136139
grpc_slice_unref(methodNameSlice)
140+
authoritySlice?.let { grpc_slice_unref(it) }
137141

138142
return NativeClientCall(
139143
cq, rawCall, methodDescriptor, callJob
140144
)
141145
}
142146

143-
override fun authority(): String {
144-
TODO("Not yet implemented")
147+
override fun authority(): String? {
148+
return authority
145149
}
146150

147151
}

0 commit comments

Comments
 (0)