multiple client CAs
This commit is contained in:
@@ -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`
|
||||||
|
|
||||||
|
|||||||
@@ -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`
|
||||||
|
|
||||||
|===
|
|===
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user