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"
client:
require: true
ca: "ca.crt"
cas:
- "ca.crt"
compression:
grpc:
@@ -40,7 +41,7 @@ It configures following:
- compression is disabled for gRPC-server (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`
- 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
- upstreams configuration is configured in the file `upstreams.yaml`

View File

@@ -180,15 +180,17 @@ tls:
----
| Path to certificate and certificate private key
a| `client.ca`, `client.required`
a| `client.ca`, `client.cas`, `client.required`
a|
[source,yaml]
----
tls:
client:
ca: ca.crt
cas:
- ca1.crt
- ca2.crt
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.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import java.security.cert.CertificateFactory
@Service
open class TlsSetup(
@@ -77,10 +78,18 @@ open class TlsSetup(
fileResolver.resolve(config.key!!)
)
}
if (StringUtils.isNotEmpty(config.clientCa)) {
if (config.clientCAs.isNotEmpty()) {
log.info("Using TLS for client authentication for $category")
val cf = CertificateFactory.getInstance("X.509")
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!!) {
sslContextBuilder.clientAuth(ClientAuth.REQUIRE)

View File

@@ -56,6 +56,6 @@ class AuthConfig {
var certificate: String? = null
var key: String? = 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.yaml.snakeyaml.nodes.MappingNode
import org.yaml.snakeyaml.nodes.ScalarNode
class AuthConfigReader : YamlConfigReader<AuthConfig>() {
@@ -77,7 +78,15 @@ class AuthConfigReader : YamlConfigReader<AuthConfig>() {
getValueAsBool(clientNode, "require")?.let {
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
}

View File

@@ -78,7 +78,7 @@ class TlsSetupSpec extends Specification {
certificate: "127.0.0.1.crt",
key: "127.0.0.1.p8.key",
clientRequire: true,
clientCa: "ca.myhost.dev.crt"
clientCAs: ["ca.myhost.dev.crt"]
)
when:
def act = tlsSetup.setupServer("test", config, false)
@@ -182,12 +182,12 @@ class TlsSetupSpec extends Specification {
certificate: "127.0.0.1.crt",
key: "127.0.0.1.p8.key",
clientRequire: true,
clientCa: "none.crt"
clientCAs: ["none.crt"]
)
when:
tlsSetup.setupServer("test", config, false)
then:
def t = thrown(IllegalArgumentException)
def t = thrown(FileNotFoundException)
}
def "Fail if client certificate is invalid"() {
@@ -197,11 +197,11 @@ class TlsSetupSpec extends Specification {
certificate: "127.0.0.1.crt",
key: "127.0.0.1.p8.key",
clientRequire: true,
clientCa: "ca.myhost.dev.key"
clientCAs: ["ca.myhost.dev.key"]
)
when:
tlsSetup.setupServer("test", config, false)
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.clientRequire != null
!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"
key == "/path/127.0.0.1.p8.key"
!clientRequire
clientCa == "/path/ca.dshackle.test.crt"
clientCAs == ["/path/ca.dshackle.test.crt"]
}
act.cache != null
with(act.cache) {