solution: allow TLS configuration for proxy

This commit is contained in:
Igor Artamonov
2020-03-20 20:22:01 -04:00
parent 26f8dd294e
commit d4bd180e23
25 changed files with 882 additions and 72 deletions

View File

@@ -0,0 +1,191 @@
/**
* Copyright (c) 2020 ETCDEV GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.emeraldpay.dshackle
import io.emeraldpay.dshackle.config.AuthConfig
import io.netty.handler.ssl.ClientAuth
import io.netty.handler.ssl.OpenSslServerContext
import spock.lang.Specification
import sun.security.x509.X509CertImpl
class TlsSetupSpec extends Specification {
TlsSetup tlsSetup = new TlsSetup(new FileResolver(new File("src/test/resources/tls-local")))
def "TLS disabled"() {
setup:
def config = new AuthConfig.ServerTlsAuth(
enabled: false
)
when:
def act = tlsSetup.setupServer("test", config)
then:
act == null
}
def "TLS enabled"() {
setup:
def config = new AuthConfig.ServerTlsAuth(
enabled: true,
certificate: "127.0.0.1.crt",
key: "127.0.0.1.p8.key"
)
when:
def act = tlsSetup.setupServer("test", config)
then:
act != null
act.server
!act.client
with((OpenSslServerContext) act) {
act.clientAuth == ClientAuth.NONE
with((X509CertImpl) keyCertChain[0]) {
getIssuerDN().name == "CN=ca.myhost.dev, OU=Blockchain CA, O=My Company"
}
}
}
def "TLS enabled and required from client"() {
setup:
def config = new AuthConfig.ServerTlsAuth(
enabled: true,
certificate: "127.0.0.1.crt",
key: "127.0.0.1.p8.key",
clientRequire: true,
clientCa: "ca.myhost.dev.crt"
)
when:
def act = tlsSetup.setupServer("test", config)
then:
act != null
act.server
!act.client
with((OpenSslServerContext) act) {
act.clientAuth == ClientAuth.REQUIRE
with((X509CertImpl) keyCertChain[0]) {
getIssuerDN().name == "CN=ca.myhost.dev, OU=Blockchain CA, O=My Company"
}
}
}
def "Fail if certificate not set"() {
setup:
def config = new AuthConfig.ServerTlsAuth(
enabled: true,
key: "127.0.0.1.p8.key",
)
when:
tlsSetup.setupServer("test", config)
then:
def t = thrown(IllegalArgumentException)
t.message == "Certificate not set"
}
def "Fail if certificate key not set"() {
setup:
def config = new AuthConfig.ServerTlsAuth(
enabled: true,
certificate: "127.0.0.1.crt"
)
when:
tlsSetup.setupServer("test", config)
then:
def t = thrown(IllegalArgumentException)
t.message == "Certificate Key not set"
}
def "Fail if certificate not exists"() {
setup:
def config = new AuthConfig.ServerTlsAuth(
enabled: true,
certificate: "none.crt",
key: "127.0.0.1.p8.key",
)
when:
tlsSetup.setupServer("test", config)
then:
def t = thrown(IllegalArgumentException)
}
def "Fail if certificate key not exists"() {
setup:
def config = new AuthConfig.ServerTlsAuth(
enabled: true,
certificate: "127.0.0.1.crt",
key: "none.p8.key",
)
when:
tlsSetup.setupServer("test", config)
then:
def t = thrown(IllegalArgumentException)
}
def "Fail if certificate key is invalid"() {
setup:
def config = new AuthConfig.ServerTlsAuth(
enabled: true,
certificate: "127.0.0.1.crt",
key: "127.0.0.1.key",
)
when:
tlsSetup.setupServer("test", config)
then:
def t = thrown(IllegalArgumentException)
}
def "Fail if client certificate not set but required"() {
setup:
def config = new AuthConfig.ServerTlsAuth(
enabled: true,
certificate: "127.0.0.1.crt",
key: "127.0.0.1.p8.key",
clientRequire: true
)
when:
tlsSetup.setupServer("test", config)
then:
def t = thrown(IllegalArgumentException)
}
def "Fail if client certificate not exists"() {
setup:
def config = new AuthConfig.ServerTlsAuth(
enabled: true,
certificate: "127.0.0.1.crt",
key: "127.0.0.1.p8.key",
clientRequire: true,
clientCa: "none.crt"
)
when:
tlsSetup.setupServer("test", config)
then:
def t = thrown(IllegalArgumentException)
}
def "Fail if client certificate is invalid"() {
setup:
def config = new AuthConfig.ServerTlsAuth(
enabled: true,
certificate: "127.0.0.1.crt",
key: "127.0.0.1.p8.key",
clientRequire: true,
clientCa: "ca.myhost.dev.key"
)
when:
tlsSetup.setupServer("test", config)
then:
def t = thrown(IllegalArgumentException)
}
}

View File

@@ -0,0 +1,62 @@
package io.emeraldpay.dshackle.config
import spock.lang.Specification
class AuthConfigReaderSpec extends Specification {
AuthConfigReader reader = new AuthConfigReader()
def "Read basic-auth for client"() {
setup:
def yaml =
"basic-auth:\n" +
" username: 9c199ad8f281f20154fc258fe41a6814\n" +
" password: 258fe4149c199ad8f2811a68f20154fc"
when:
def act = reader.readClientBasicAuth(reader.readNode(yaml))
then:
act != null
act.username == "9c199ad8f281f20154fc258fe41a6814"
act.password == "258fe4149c199ad8f2811a68f20154fc"
}
def "Read tls for client"() {
setup:
def yaml =
"tls:\n" +
" ca: /etc/ca.myservice.com.crt\n" +
" certificate: /etc/client1.myservice.com.crt\n" +
" key: /etc/client1.myservice.com.key"
when:
def act = reader.readClientTls(reader.readNode(yaml))
then:
act != null
act.ca == "/etc/ca.myservice.com.crt"
act.certificate == "/etc/client1.myservice.com.crt"
act.key == "/etc/client1.myservice.com.key"
}
def "Read tls for server"() {
setup:
def yaml =
"tls:\n" +
" enabled: true\n" +
" server:\n" +
" certificate: \"/etc/client1.myservice.com.crt\"\n" +
" key: \"/etc/client1.myservice.com.key\"\n" +
" client:\n" +
" require: false\n" +
" ca: /etc/ca.myservice.com.crt"
when:
def act = reader.readServerTls(reader.readNode(yaml))
then:
act != null
act.enabled != null
act.enabled
act.certificate == "/etc/client1.myservice.com.crt"
act.key == "/etc/client1.myservice.com.key"
act.clientRequire != null
!act.clientRequire
act.clientCa == "/etc/ca.myservice.com.crt"
}
}

View File

@@ -64,7 +64,7 @@ class UpstreamsConfigReaderSpec extends Specification {
with((UpstreamsConfig.EthereumConnection)connection) {
rpc.url == new URI("https://mainnet.infura.io/v3/fa28c968191849c1aff541ad1d8511f2")
rpc.basicAuth != null
with((UpstreamsConfig.BasicAuth)rpc.basicAuth) {
with((AuthConfig.ClientBasicAuth) rpc.basicAuth) {
username == "4fc258fe41a68149c199ad8f281f2015"
password == "1a68f20154fc258fe4149c199ad8f281"
}

View File

@@ -17,6 +17,7 @@ package io.emeraldpay.dshackle.proxy
import io.emeraldpay.api.proto.BlockchainOuterClass
import io.emeraldpay.api.proto.Common
import io.emeraldpay.dshackle.TlsSetup
import io.emeraldpay.dshackle.config.ProxyConfig
import io.emeraldpay.dshackle.rpc.NativeCall
import io.emeraldpay.dshackle.test.TestingCommons
@@ -42,7 +43,8 @@ class ProxyServerSpec extends Specification {
new ProxyConfig(),
new ReadRpcJson(TestingCommons.objectMapper()),
writeRpcJson,
nativeCall
nativeCall,
new TlsSetup(TestingCommons.fileResolver())
)
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)

View File

@@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.Version
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
import io.emeraldpay.dshackle.FileResolver
import io.emeraldpay.dshackle.cache.Caches
import io.emeraldpay.dshackle.cache.CachesFactory
import io.emeraldpay.dshackle.upstream.AggregatedUpstream
@@ -79,4 +80,8 @@ class TestingCommons {
static CachesFactory emptyCaches() {
return new CachesFactory(objectMapper(), new StandardEnvironment())
}
static FileResolver fileResolver() {
return new FileResolver(new File("src/test/resources"))
}
}