multiple client CAs

This commit is contained in:
a10zn8
2023-07-12 00:03:37 +04:00
parent eab870f7eb
commit 6c49e19a2b
8 changed files with 37 additions and 16 deletions

View File

@@ -20,7 +20,8 @@ tls:
key: "server.p8.key" key: "server.p8.key"
client: client:
require: true require: true
ca: "ca.crt" cas:
- "ca.crt"
compression: compression:
grpc: grpc:
@@ -40,7 +41,7 @@ It configures following:
- compression is disabled for gRPC-server (enabled by default) - compression is disabled for gRPC-server (enabled by default)
- compression is disabled for upstream gRPC-client (enabled by default) - compression is disabled for upstream gRPC-client (enabled by default)
- server certificate is located at `server.crt` with the key for it at `server.p8.key` - server certificate is located at `server.crt` with the key for it at `server.p8.key`
- the server requires a client authentication by TLS client certificate signed by `ca.crt` certificate - the server requires a client authentication by TLS client certificate signed by `ca.crt` certificate.
- no JSON RPC is configured - no JSON RPC is configured
- upstreams configuration is configured in the file `upstreams.yaml` - upstreams configuration is configured in the file `upstreams.yaml`

View File

@@ -180,15 +180,17 @@ tls:
---- ----
| Path to certificate and certificate private key | Path to certificate and certificate private key
a| `client.ca`, `client.required` a| `client.ca`, `client.cas`, `client.required`
a| a|
[source,yaml] [source,yaml]
---- ----
tls: tls:
client: client:
ca: ca.crt cas:
- ca1.crt
- ca2.crt
required: true required: true
---- ----
a| Path to CA used to authenticate incoming connections, used if `required: true` a| Paths to CA used to authenticate incoming connections, used if `required: true`
|=== |===

View File

@@ -26,6 +26,7 @@ import org.apache.commons.lang3.StringUtils
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.security.cert.CertificateFactory
@Service @Service
open class TlsSetup( open class TlsSetup(
@@ -77,10 +78,18 @@ open class TlsSetup(
fileResolver.resolve(config.key!!) fileResolver.resolve(config.key!!)
) )
} }
if (StringUtils.isNotEmpty(config.clientCa)) { if (config.clientCAs.isNotEmpty()) {
log.info("Using TLS for client authentication for $category") log.info("Using TLS for client authentication for $category")
val cf = CertificateFactory.getInstance("X.509")
sslContextBuilder.trustManager( sslContextBuilder.trustManager(
fileResolver.resolve(config.clientCa!!)
config.clientCAs
.map { fileResolver.resolve(it) }
.map { file ->
file.inputStream().use {
cf.generateCertificate(it) as java.security.cert.X509Certificate
}
}
) )
if (config.clientRequire != null && config.clientRequire!!) { if (config.clientRequire != null && config.clientRequire!!) {
sslContextBuilder.clientAuth(ClientAuth.REQUIRE) sslContextBuilder.clientAuth(ClientAuth.REQUIRE)

View File

@@ -56,6 +56,6 @@ class AuthConfig {
var certificate: String? = null var certificate: String? = null
var key: String? = null var key: String? = null
var clientRequire: Boolean? = null var clientRequire: Boolean? = null
var clientCa: String? = null var clientCAs: MutableList<String> = mutableListOf()
} }
} }

View File

@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.config
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.yaml.snakeyaml.nodes.MappingNode import org.yaml.snakeyaml.nodes.MappingNode
import org.yaml.snakeyaml.nodes.ScalarNode
class AuthConfigReader : YamlConfigReader<AuthConfig>() { class AuthConfigReader : YamlConfigReader<AuthConfig>() {
@@ -77,7 +78,15 @@ class AuthConfigReader : YamlConfigReader<AuthConfig>() {
getValueAsBool(clientNode, "require")?.let { getValueAsBool(clientNode, "require")?.let {
auth.clientRequire = it auth.clientRequire = it
} }
auth.clientCa = getValueAsString(clientNode, "ca") getValueAsString(clientNode, "ca")?.let {
auth.clientCAs.add(it)
}
getList<ScalarNode>(clientNode, "cas")?.let {
println(it)
it.value?.let { crt ->
auth.clientCAs.addAll(crt.map { v -> v.value })
}
}
} }
auth auth
} }

View File

@@ -78,7 +78,7 @@ class TlsSetupSpec extends Specification {
certificate: "127.0.0.1.crt", certificate: "127.0.0.1.crt",
key: "127.0.0.1.p8.key", key: "127.0.0.1.p8.key",
clientRequire: true, clientRequire: true,
clientCa: "ca.myhost.dev.crt" clientCAs: ["ca.myhost.dev.crt"]
) )
when: when:
def act = tlsSetup.setupServer("test", config, false) def act = tlsSetup.setupServer("test", config, false)
@@ -182,12 +182,12 @@ class TlsSetupSpec extends Specification {
certificate: "127.0.0.1.crt", certificate: "127.0.0.1.crt",
key: "127.0.0.1.p8.key", key: "127.0.0.1.p8.key",
clientRequire: true, clientRequire: true,
clientCa: "none.crt" clientCAs: ["none.crt"]
) )
when: when:
tlsSetup.setupServer("test", config, false) tlsSetup.setupServer("test", config, false)
then: then:
def t = thrown(IllegalArgumentException) def t = thrown(FileNotFoundException)
} }
def "Fail if client certificate is invalid"() { def "Fail if client certificate is invalid"() {
@@ -197,11 +197,11 @@ class TlsSetupSpec extends Specification {
certificate: "127.0.0.1.crt", certificate: "127.0.0.1.crt",
key: "127.0.0.1.p8.key", key: "127.0.0.1.p8.key",
clientRequire: true, clientRequire: true,
clientCa: "ca.myhost.dev.key" clientCAs: ["ca.myhost.dev.key"]
) )
when: when:
tlsSetup.setupServer("test", config, false) tlsSetup.setupServer("test", config, false)
then: then:
def t = thrown(IllegalArgumentException) def t = thrown(java.security.cert.CertificateParsingException)
} }
} }

View File

@@ -72,6 +72,6 @@ class AuthConfigReaderSpec extends Specification {
act.key == "/etc/client1.myservice.com.key" act.key == "/etc/client1.myservice.com.key"
act.clientRequire != null act.clientRequire != null
!act.clientRequire !act.clientRequire
act.clientCa == "/etc/ca.myservice.com.crt" act.clientCAs == ["/etc/ca.myservice.com.crt"]
} }
} }

View File

@@ -39,7 +39,7 @@ class MainConfigReaderSpec extends Specification {
certificate == "/path/127.0.0.1.crt" certificate == "/path/127.0.0.1.crt"
key == "/path/127.0.0.1.p8.key" key == "/path/127.0.0.1.p8.key"
!clientRequire !clientRequire
clientCa == "/path/ca.dshackle.test.crt" clientCAs == ["/path/ca.dshackle.test.crt"]
} }
act.cache != null act.cache != null
with(act.cache) { with(act.cache) {