solution: allow TLS configuration for proxy
This commit is contained in:
@@ -29,6 +29,31 @@ It configures following:
|
|||||||
- 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`
|
||||||
|
|
||||||
|
.Configuration
|
||||||
|
|===
|
||||||
|
| Name | Example | Description
|
||||||
|
|
||||||
|
a| `version`
|
||||||
|
|
|
||||||
|
| Version of the format of the config
|
||||||
|
|
||||||
|
a| `port`
|
||||||
|
a| `port: 12449`
|
||||||
|
| Port to bind gRPC server. `2449` by default
|
||||||
|
|
||||||
|
a| `tls`
|
||||||
|
|
|
||||||
|
| TLS configuration for gRPC. See link:07-authentication.adoc[Authentication] for details
|
||||||
|
|
||||||
|
a| `proxy`
|
||||||
|
|
|
||||||
|
| Proxy configuration
|
||||||
|
|
||||||
|
a| `upstreams`
|
||||||
|
|
|
||||||
|
| Upstreams configuration
|
||||||
|
|===
|
||||||
|
|
||||||
=== Enabling JSON RPC proxy
|
=== Enabling JSON RPC proxy
|
||||||
|
|
||||||
.Example proxy:
|
.Example proxy:
|
||||||
@@ -51,4 +76,49 @@ upstreams:
|
|||||||
With that configuration Dshackle starts a JSON RPC proxy:
|
With that configuration Dshackle starts a JSON RPC proxy:
|
||||||
|
|
||||||
- JSON RPC server is listening on `0.0.0.0:8080`
|
- JSON RPC server is listening on `0.0.0.0:8080`
|
||||||
- `http://0.0.0.0:8080/eth` provides access to Ethereum API routed to an available upstream
|
- `http://0.0.0.0:8080/eth` provides access to Ethereum API routed to an available upstream
|
||||||
|
|
||||||
|
.Full configuration:
|
||||||
|
[source,yaml]
|
||||||
|
----
|
||||||
|
proxy:
|
||||||
|
port: 8080
|
||||||
|
tls:
|
||||||
|
enabled: true
|
||||||
|
server:
|
||||||
|
certificate: server.crt
|
||||||
|
key: server.p8.key
|
||||||
|
client:
|
||||||
|
require: true
|
||||||
|
ca: ca.crt
|
||||||
|
routes:
|
||||||
|
- id: eth
|
||||||
|
blockchain: ethereum
|
||||||
|
- id: etc
|
||||||
|
blockchain: ethereum_classic
|
||||||
|
----
|
||||||
|
|
||||||
|
.Proxy configuration
|
||||||
|
|===
|
||||||
|
| Name | Example | Description
|
||||||
|
|
||||||
|
a| `host`
|
||||||
|
a| `host: 0.0.0.0`
|
||||||
|
| Host to bind proxy server. `127.0.0.1` by default
|
||||||
|
|
||||||
|
a| `port`
|
||||||
|
a| `port: 8545`
|
||||||
|
| Port to bind proxy server. `8080` by default
|
||||||
|
|
||||||
|
a| `enabled`
|
||||||
|
a| `enabled: true`
|
||||||
|
| Enable/disable proxy server
|
||||||
|
|
||||||
|
a| `tls`
|
||||||
|
|
|
||||||
|
| TLS configuration for proxy. See link:07-authentication.adoc[Authentication] for details
|
||||||
|
|
||||||
|
a| `routes`
|
||||||
|
|
|
||||||
|
| List of endpoints to proxy
|
||||||
|
|===
|
||||||
@@ -12,16 +12,22 @@ generate certificates, but the traditional `openssl` tool can be used as well.
|
|||||||
|
|
||||||
==== Setup Server certificate
|
==== Setup Server certificate
|
||||||
|
|
||||||
.Generate a server certificate
|
.Generate a Certificate Authority
|
||||||
[source,bash]
|
[source,bash]
|
||||||
----
|
----
|
||||||
SERVER_CA="ca.myhost.dev"
|
export SERVER_CA="ca.myhost.dev"
|
||||||
SERVER_IP="127.0.0.1"
|
export ORG="My Company"
|
||||||
ORG="My Company"
|
export ORG_UNIT="Blockchain"
|
||||||
ORG_UNIT="Blockchain"
|
|
||||||
|
|
||||||
certstrap init --common-name "$SERVER_CA" --passphrase "" -o "$ORG" -ou "$ORG_UNIT CA"
|
certstrap init --common-name "$SERVER_CA" --passphrase "" -o "$ORG" -ou "$ORG_UNIT CA"
|
||||||
certstrap request-cert -ip $SERVER_IP --passphrase "" -o "$ORG" -ou "$ORG_UNIT Server"
|
openssl pkcs8 -topk8 -inform PEM -outform PEM -in out/$SERVER_CA.key -out out/$SERVER_CA.p8.key -nocrypt
|
||||||
|
----
|
||||||
|
|
||||||
|
.Generate a Server Certificate
|
||||||
|
----
|
||||||
|
export SERVER_IP="127.0.0.1"
|
||||||
|
|
||||||
|
certstrap request-cert -ip $SERVER_IP --common-name $SERVER_IP --passphrase "" -o "$ORG" -ou "$ORG_UNIT Server"
|
||||||
certstrap sign $SERVER_IP --CA $SERVER_CA
|
certstrap sign $SERVER_IP --CA $SERVER_CA
|
||||||
|
|
||||||
openssl pkcs8 -topk8 -inform PEM -outform PEM -in out/$SERVER_IP.key -out out/$SERVER_IP.p8.key -nocrypt
|
openssl pkcs8 -topk8 -inform PEM -outform PEM -in out/$SERVER_IP.key -out out/$SERVER_IP.p8.key -nocrypt
|
||||||
@@ -30,8 +36,9 @@ openssl pkcs8 -topk8 -inform PEM -outform PEM -in out/$SERVER_IP.key -out out/$S
|
|||||||
You are going to get following files now in `out` directory:
|
You are going to get following files now in `out` directory:
|
||||||
|
|
||||||
- `ca.myhost.dev.crt` your Certification Authority to sign or verify other certificates
|
- `ca.myhost.dev.crt` your Certification Authority to sign or verify other certificates
|
||||||
- `127.0.0.1.crt` server certificate, 127.0.0.1 is an IP address supposed to be used by that instance, and we use local
|
- `ca.myhost.dev.p8.key` is the private key for Certification Authority in PKSC 8 format
|
||||||
host for demo purposes. For production use a real IP must be used.
|
- `127.0.0.1.crt` server certificate, 127.0.0.1 is an IP address supposed to be used by that instance, and we use local host for demo purposes.
|
||||||
|
For production use a real IP must be used.
|
||||||
- `127.0.0.1.p8.key` is private key for certificate in PKCS 8 format, it needed by Dshackle server to use certificate
|
- `127.0.0.1.p8.key` is private key for certificate in PKCS 8 format, it needed by Dshackle server to use certificate
|
||||||
|
|
||||||
Copy those files to directory with Dshackle and update configuration.
|
Copy those files to directory with Dshackle and update configuration.
|
||||||
@@ -144,5 +151,43 @@ upstreams:
|
|||||||
key: client_1.p8.key
|
key: client_1.p8.key
|
||||||
----
|
----
|
||||||
|
|
||||||
Now if you run second server it will connect to first server ("upstream") running on port 2449, will verify upstream
|
Now if you run second server it will connect to first server ("upstream") running on port 2449, will verify upstream with certificate `ca.myhost.dev.crt` and authenticate itself by using pair of `client_1.crt` and `client_1.p8.key`
|
||||||
with certificate `ca.myhost.dev.crt` and authenticate itself by using pair of `client_1.crt` and `client_1.p8.key`
|
|
||||||
|
=== Server TLS configuration
|
||||||
|
|
||||||
|
|===
|
||||||
|
| Name | Example | Description
|
||||||
|
|
||||||
|
a| `enabled`
|
||||||
|
a|
|
||||||
|
[source,yaml]
|
||||||
|
----
|
||||||
|
tls:
|
||||||
|
enabled: true
|
||||||
|
----
|
||||||
|
| Enabled or disable TLS. By default it checks if certificate is set, and then enables it. But if you enable the TLS
|
||||||
|
but didn't specify the certificate or key, then the DShackle will fails to start with error.
|
||||||
|
|
||||||
|
a| `server.certificate`, `server.key`
|
||||||
|
a|
|
||||||
|
[source,yaml]
|
||||||
|
----
|
||||||
|
tls:
|
||||||
|
server:
|
||||||
|
certificate: server.com.crt
|
||||||
|
key: server.com.p8.key
|
||||||
|
----
|
||||||
|
| Path to certificate and certificate private key
|
||||||
|
|
||||||
|
a| `client.ca`, `client.required`
|
||||||
|
a|
|
||||||
|
[source,yaml]
|
||||||
|
----
|
||||||
|
tls:
|
||||||
|
client:
|
||||||
|
ca: ca.crt
|
||||||
|
required: true
|
||||||
|
----
|
||||||
|
a| Path to CA used to authenticate incoming connections, used if `required: true`
|
||||||
|
|
||||||
|
|===
|
||||||
@@ -34,7 +34,8 @@ class ProxyStarter(
|
|||||||
@Autowired private val env: Environment,
|
@Autowired private val env: Environment,
|
||||||
@Autowired private val readRpcJson: ReadRpcJson,
|
@Autowired private val readRpcJson: ReadRpcJson,
|
||||||
@Autowired private val writeRpcJson: WriteRpcJson,
|
@Autowired private val writeRpcJson: WriteRpcJson,
|
||||||
@Autowired private val nativeCall: NativeCall
|
@Autowired private val nativeCall: NativeCall,
|
||||||
|
@Autowired private val tlsSetup: TlsSetup
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -48,7 +49,7 @@ class ProxyStarter(
|
|||||||
log.debug("Proxy server is not configured")
|
log.debug("Proxy server is not configured")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val server = ProxyServer(config, readRpcJson, writeRpcJson, nativeCall)
|
val server = ProxyServer(config, readRpcJson, writeRpcJson, nativeCall, tlsSetup)
|
||||||
server.start()
|
server.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
83
src/main/kotlin/io/emeraldpay/dshackle/TlsSetup.kt
Normal file
83
src/main/kotlin/io/emeraldpay/dshackle/TlsSetup.kt
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* 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.grpc.netty.GrpcSslContexts
|
||||||
|
import io.netty.handler.ssl.ClientAuth
|
||||||
|
import io.netty.handler.ssl.SslContext
|
||||||
|
import io.netty.handler.ssl.SslContextBuilder
|
||||||
|
import org.apache.commons.lang3.StringUtils
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class TlsSetup(
|
||||||
|
@Autowired val fileResolver: FileResolver
|
||||||
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val log = LoggerFactory.getLogger(TlsSetup::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setupServer(category: String, config: AuthConfig.ServerTlsAuth): SslContext? {
|
||||||
|
val mustBeSecure = config.enabled != null && config.enabled!!
|
||||||
|
val tlsDisabled = config.enabled != null && !config.enabled!!
|
||||||
|
var hasServerCertificate = true
|
||||||
|
if (!tlsDisabled) {
|
||||||
|
if (StringUtils.isEmpty(config.certificate)) {
|
||||||
|
if (mustBeSecure) {
|
||||||
|
log.error("tls.server.certificate property for $category is not set (path to server TLS certificate) but TLS is enabled")
|
||||||
|
throw IllegalArgumentException("Certificate not set")
|
||||||
|
}
|
||||||
|
hasServerCertificate = false
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(config.key)) {
|
||||||
|
if (mustBeSecure) {
|
||||||
|
log.error("tls.server.key property for $category is not set (path to server TLS certificate key) but TLS is enabled")
|
||||||
|
throw IllegalArgumentException("Certificate Key not set")
|
||||||
|
}
|
||||||
|
hasServerCertificate = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mustBeSecure || (!tlsDisabled && hasServerCertificate)) {
|
||||||
|
log.info("Using TLS for $category")
|
||||||
|
val sslContextBuilder = SslContextBuilder.forServer(
|
||||||
|
fileResolver.resolve(config.certificate!!),
|
||||||
|
fileResolver.resolve(config.key!!)
|
||||||
|
)
|
||||||
|
if (StringUtils.isNotEmpty(config.clientCa)) {
|
||||||
|
log.info("Using TLS for client authentication for $category")
|
||||||
|
sslContextBuilder.trustManager(
|
||||||
|
fileResolver.resolve(config.clientCa!!)
|
||||||
|
)
|
||||||
|
if (config.clientRequire != null && config.clientRequire!!) {
|
||||||
|
sslContextBuilder.clientAuth(ClientAuth.REQUIRE)
|
||||||
|
}
|
||||||
|
} else if (config.clientRequire != null && config.clientRequire!!) {
|
||||||
|
throw IllegalArgumentException("Client Certificate not set")
|
||||||
|
} else {
|
||||||
|
log.warn("Trust all clients for $category")
|
||||||
|
}
|
||||||
|
return sslContextBuilder.build()
|
||||||
|
} else {
|
||||||
|
log.warn("Using insecure transport for $category")
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
60
src/main/kotlin/io/emeraldpay/dshackle/config/AuthConfig.kt
Normal file
60
src/main/kotlin/io/emeraldpay/dshackle/config/AuthConfig.kt
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* 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.config
|
||||||
|
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
class AuthConfig {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val log = LoggerFactory.getLogger(AuthConfig::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
open class ClientAuth {
|
||||||
|
var type: String? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClientBasicAuth(
|
||||||
|
val username: String,
|
||||||
|
val password: String
|
||||||
|
) : ClientAuth()
|
||||||
|
|
||||||
|
class ClientTlsAuth(
|
||||||
|
var ca: String? = null,
|
||||||
|
var certificate: String? = null,
|
||||||
|
var key: String? = null
|
||||||
|
) : ClientAuth()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example config:
|
||||||
|
* ```
|
||||||
|
* enabled: false
|
||||||
|
* server:
|
||||||
|
* certificate: "127.0.0.1.crt"
|
||||||
|
* key: "127.0.0.1.p8.key"
|
||||||
|
* client:
|
||||||
|
* require: false
|
||||||
|
* ca: "ca.dshackle.test.crt"
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
open class ServerTlsAuth {
|
||||||
|
var enabled: Boolean? = null
|
||||||
|
var certificate: String? = null
|
||||||
|
var key: String? = null
|
||||||
|
var clientRequire: Boolean? = null
|
||||||
|
var clientCa: String? = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* 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.config
|
||||||
|
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
|
|
||||||
|
class AuthConfigReader : YamlConfigReader() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val log = LoggerFactory.getLogger(AuthConfigReader::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readClientBasicAuth(node: MappingNode?): AuthConfig.ClientBasicAuth? {
|
||||||
|
return getMapping(node, "basic-auth")?.let { authNode ->
|
||||||
|
val username = getValueAsString(authNode, "username")
|
||||||
|
val password = getValueAsString(authNode, "password")
|
||||||
|
if (username != null && password != null) {
|
||||||
|
AuthConfig.ClientBasicAuth(username, password)
|
||||||
|
} else {
|
||||||
|
log.warn("Basic auth is not fully configured")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readClientTls(node: MappingNode?): AuthConfig.ClientTlsAuth? {
|
||||||
|
return getMapping(node, "tls")?.let { authNode ->
|
||||||
|
val auth = AuthConfig.ClientTlsAuth()
|
||||||
|
auth.ca = getValueAsString(authNode, "ca")
|
||||||
|
auth.certificate = getValueAsString(authNode, "certificate")
|
||||||
|
auth.key = getValueAsString(authNode, "key")
|
||||||
|
auth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example config:
|
||||||
|
* ```
|
||||||
|
* enabled: false
|
||||||
|
* server:
|
||||||
|
* certificate: "127.0.0.1.crt"
|
||||||
|
* key: "127.0.0.1.p8.key"
|
||||||
|
* client:
|
||||||
|
* require: false
|
||||||
|
* ca: "ca.dshackle.test.crt"
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
fun readServerTls(node: MappingNode?): AuthConfig.ServerTlsAuth? {
|
||||||
|
return getMapping(node, "tls")?.let { node ->
|
||||||
|
val auth = AuthConfig.ServerTlsAuth()
|
||||||
|
getValueAsBool(node, "enabled")?.let {
|
||||||
|
auth.enabled = it
|
||||||
|
}
|
||||||
|
getMapping(node, "server")?.let { node ->
|
||||||
|
auth.certificate = getValueAsString(node, "certificate")
|
||||||
|
auth.key = getValueAsString(node, "key")
|
||||||
|
}
|
||||||
|
getMapping(node, "client")?.let { node ->
|
||||||
|
getValueAsBool(node, "require")?.let {
|
||||||
|
auth.clientRequire = it
|
||||||
|
}
|
||||||
|
auth.clientCa = getValueAsString(node, "ca")
|
||||||
|
}
|
||||||
|
auth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -38,6 +38,11 @@ class ProxyConfig {
|
|||||||
*/
|
*/
|
||||||
var port: Int = 8080
|
var port: Int = 8080
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TLS Auth required from clients.
|
||||||
|
*/
|
||||||
|
var tls: AuthConfig.ServerTlsAuth? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of available routes
|
* List of available routes
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -33,10 +33,10 @@ class ProxyConfigReader : YamlConfigReader() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var filename = "dshackle.yaml"
|
private var filename = "dshackle.yaml"
|
||||||
|
private val authConfigReader = AuthConfigReader()
|
||||||
|
|
||||||
fun read(input: InputStream): ProxyConfig? {
|
fun read(input: InputStream): ProxyConfig? {
|
||||||
val yaml = Yaml()
|
val configNode = readNode(input)
|
||||||
val configNode = asMappingNode(yaml.compose(InputStreamReader(input)))
|
|
||||||
return read(getMapping(configNode, "proxy"))
|
return read(getMapping(configNode, "proxy"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,8 +73,10 @@ class ProxyConfigReader : YamlConfigReader() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (config.routes.isEmpty()) {
|
if (config.routes.isEmpty()) {
|
||||||
|
log.warn("Proxy config has no routes")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
config.tls = authConfigReader.readServerTls(input)
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ package io.emeraldpay.dshackle.config
|
|||||||
|
|
||||||
import io.emeraldpay.dshackle.Defaults
|
import io.emeraldpay.dshackle.Defaults
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.time.Duration
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
@@ -81,7 +80,7 @@ class UpstreamsConfig {
|
|||||||
class GrpcConnection : UpstreamConnection() {
|
class GrpcConnection : UpstreamConnection() {
|
||||||
var host: String? = null
|
var host: String? = null
|
||||||
var port: Int = 0
|
var port: Int = 0
|
||||||
var auth: TlsAuth? = null
|
var auth: AuthConfig.ClientTlsAuth? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
class EthereumConnection : UpstreamConnection() {
|
class EthereumConnection : UpstreamConnection() {
|
||||||
@@ -90,29 +89,16 @@ class UpstreamsConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class HttpEndpoint(val url: URI) {
|
class HttpEndpoint(val url: URI) {
|
||||||
var basicAuth: BasicAuth? = null
|
var basicAuth: AuthConfig.ClientBasicAuth? = null
|
||||||
var tls: TlsAuth? = null
|
var tls: AuthConfig.ClientTlsAuth? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
class WsEndpoint(val url: URI) {
|
class WsEndpoint(val url: URI) {
|
||||||
var origin: URI? = null
|
var origin: URI? = null
|
||||||
var basicAuth: BasicAuth? = null
|
var basicAuth: AuthConfig.ClientBasicAuth? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
open class Auth {
|
|
||||||
var type: String? = null
|
|
||||||
}
|
|
||||||
|
|
||||||
class BasicAuth(
|
|
||||||
val username: String,
|
|
||||||
val password: String
|
|
||||||
) : Auth()
|
|
||||||
|
|
||||||
class TlsAuth(
|
|
||||||
var ca: String? = null,
|
|
||||||
var certificate: String? = null,
|
|
||||||
var key: String? = null
|
|
||||||
) : Auth()
|
|
||||||
|
|
||||||
//TODO make it unmodifiable after initial load
|
//TODO make it unmodifiable after initial load
|
||||||
class Labels: HashMap<String, String>() {
|
class Labels: HashMap<String, String>() {
|
||||||
|
|||||||
@@ -18,24 +18,21 @@ package io.emeraldpay.dshackle.config
|
|||||||
import org.apache.commons.lang3.StringUtils
|
import org.apache.commons.lang3.StringUtils
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.yaml.snakeyaml.Yaml
|
import org.yaml.snakeyaml.Yaml
|
||||||
import org.yaml.snakeyaml.nodes.CollectionNode
|
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
import org.yaml.snakeyaml.nodes.Node
|
|
||||||
import org.yaml.snakeyaml.nodes.ScalarNode
|
import org.yaml.snakeyaml.nodes.ScalarNode
|
||||||
import reactor.util.function.Tuples
|
import reactor.util.function.Tuples
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.lang.IllegalArgumentException
|
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
|
||||||
class UpstreamsConfigReader : YamlConfigReader() {
|
class UpstreamsConfigReader : YamlConfigReader() {
|
||||||
|
|
||||||
private val log = LoggerFactory.getLogger(UpstreamsConfigReader::class.java)
|
private val log = LoggerFactory.getLogger(UpstreamsConfigReader::class.java)
|
||||||
|
private val authConfigReader = AuthConfigReader()
|
||||||
|
|
||||||
fun read(input: InputStream): UpstreamsConfig {
|
fun read(input: InputStream): UpstreamsConfig {
|
||||||
val yaml = Yaml()
|
val configNode = readNode(input)
|
||||||
val configNode = asMappingNode(yaml.compose(InputStreamReader(input)))
|
|
||||||
|
|
||||||
val config = UpstreamsConfig()
|
val config = UpstreamsConfig()
|
||||||
config.version = getValueAsString(configNode, "version")
|
config.version = getValueAsString(configNode, "version")
|
||||||
@@ -65,8 +62,8 @@ class UpstreamsConfigReader : YamlConfigReader() {
|
|||||||
getValueAsString(node, "url")?.let { url ->
|
getValueAsString(node, "url")?.let { url ->
|
||||||
val http = UpstreamsConfig.HttpEndpoint(URI(url))
|
val http = UpstreamsConfig.HttpEndpoint(URI(url))
|
||||||
connection.rpc = http
|
connection.rpc = http
|
||||||
http.basicAuth = readBasicAuth(node)
|
http.basicAuth = authConfigReader.readClientBasicAuth(node)
|
||||||
http.tls = readTls(node)
|
http.tls = authConfigReader.readClientTls(node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getMapping(connConfigNode, "ws")?.let { node ->
|
getMapping(connConfigNode, "ws")?.let { node ->
|
||||||
@@ -76,7 +73,7 @@ class UpstreamsConfigReader : YamlConfigReader() {
|
|||||||
getValueAsString(node, "origin")?.let { origin ->
|
getValueAsString(node, "origin")?.let { origin ->
|
||||||
ws.origin = URI(origin)
|
ws.origin = URI(origin)
|
||||||
}
|
}
|
||||||
ws.basicAuth = readBasicAuth(node)
|
ws.basicAuth = authConfigReader.readClientBasicAuth(node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -97,7 +94,7 @@ class UpstreamsConfigReader : YamlConfigReader() {
|
|||||||
getValueAsInt(connConfigNode, "port")?.let {
|
getValueAsInt(connConfigNode, "port")?.let {
|
||||||
connection.port = it
|
connection.port = it
|
||||||
}
|
}
|
||||||
connection.auth = readTls(connConfigNode)
|
connection.auth = authConfigReader.readClientTls(connConfigNode)
|
||||||
} else {
|
} else {
|
||||||
log.error("Upstream at #0 has invalid configuration")
|
log.error("Upstream at #0 has invalid configuration")
|
||||||
}
|
}
|
||||||
@@ -194,27 +191,4 @@ class UpstreamsConfigReader : YamlConfigReader() {
|
|||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readBasicAuth(node: MappingNode?): UpstreamsConfig.BasicAuth? {
|
|
||||||
return getMapping(node, "basic-auth")?.let { authNode ->
|
|
||||||
val username = getValueAsString(authNode, "username")
|
|
||||||
val password = getValueAsString(authNode, "password")
|
|
||||||
if (username != null && password != null) {
|
|
||||||
UpstreamsConfig.BasicAuth(username, password)
|
|
||||||
} else {
|
|
||||||
log.warn("Basic auth is not fully configured")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readTls(node: MappingNode?): UpstreamsConfig.TlsAuth? {
|
|
||||||
return getMapping(node, "tls")?.let { authNode ->
|
|
||||||
val auth = UpstreamsConfig.TlsAuth()
|
|
||||||
auth.ca = getValueAsString(authNode, "ca")
|
|
||||||
auth.certificate = getValueAsString(authNode, "certificate")
|
|
||||||
auth.key = getValueAsString(authNode, "key")
|
|
||||||
auth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -16,14 +16,26 @@
|
|||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
import io.emeraldpay.grpc.Chain
|
import io.emeraldpay.grpc.Chain
|
||||||
|
import org.yaml.snakeyaml.Yaml
|
||||||
import org.yaml.snakeyaml.nodes.CollectionNode
|
import org.yaml.snakeyaml.nodes.CollectionNode
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
import org.yaml.snakeyaml.nodes.Node
|
import org.yaml.snakeyaml.nodes.Node
|
||||||
import org.yaml.snakeyaml.nodes.ScalarNode
|
import org.yaml.snakeyaml.nodes.ScalarNode
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
|
||||||
open class YamlConfigReader {
|
open class YamlConfigReader {
|
||||||
private val envVariables = EnvVariables()
|
private val envVariables = EnvVariables()
|
||||||
|
|
||||||
|
fun readNode(input: String): MappingNode {
|
||||||
|
return readNode(input.byteInputStream())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readNode(input: InputStream): MappingNode {
|
||||||
|
val yaml = Yaml()
|
||||||
|
return asMappingNode(yaml.compose(InputStreamReader(input)))
|
||||||
|
}
|
||||||
|
|
||||||
protected fun hasAny(mappingNode: MappingNode?, key: String): Boolean {
|
protected fun hasAny(mappingNode: MappingNode?, key: String): Boolean {
|
||||||
if (mappingNode == null) {
|
if (mappingNode == null) {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -17,9 +17,11 @@ package io.emeraldpay.dshackle.proxy
|
|||||||
|
|
||||||
import io.emeraldpay.api.proto.BlockchainOuterClass
|
import io.emeraldpay.api.proto.BlockchainOuterClass
|
||||||
import io.emeraldpay.api.proto.Common
|
import io.emeraldpay.api.proto.Common
|
||||||
|
import io.emeraldpay.dshackle.TlsSetup
|
||||||
import io.emeraldpay.dshackle.config.ProxyConfig
|
import io.emeraldpay.dshackle.config.ProxyConfig
|
||||||
import io.emeraldpay.dshackle.rpc.NativeCall
|
import io.emeraldpay.dshackle.rpc.NativeCall
|
||||||
import io.netty.buffer.Unpooled
|
import io.netty.buffer.Unpooled
|
||||||
|
import io.netty.handler.ssl.SslContextBuilder
|
||||||
import org.reactivestreams.Publisher
|
import org.reactivestreams.Publisher
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.http.HttpHeaders
|
import org.springframework.http.HttpHeaders
|
||||||
@@ -29,6 +31,7 @@ import reactor.netty.http.server.HttpServer
|
|||||||
import reactor.netty.http.server.HttpServerRequest
|
import reactor.netty.http.server.HttpServerRequest
|
||||||
import reactor.netty.http.server.HttpServerResponse
|
import reactor.netty.http.server.HttpServerResponse
|
||||||
import reactor.netty.http.server.HttpServerRoutes
|
import reactor.netty.http.server.HttpServerRoutes
|
||||||
|
import java.io.File
|
||||||
import java.util.function.BiFunction
|
import java.util.function.BiFunction
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,7 +41,8 @@ class ProxyServer(
|
|||||||
private var config: ProxyConfig,
|
private var config: ProxyConfig,
|
||||||
private val readRpcJson: ReadRpcJson,
|
private val readRpcJson: ReadRpcJson,
|
||||||
private val writeRpcJson: WriteRpcJson,
|
private val writeRpcJson: WriteRpcJson,
|
||||||
private val nativeCall: NativeCall
|
private val nativeCall: NativeCall,
|
||||||
|
private val tlsSetup: TlsSetup
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -51,9 +55,17 @@ class ProxyServer(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.info("Listening Proxy on ${config.host}:${config.port}")
|
log.info("Listening Proxy on ${config.host}:${config.port}")
|
||||||
val server: DisposableServer = HttpServer.create()
|
var serverBuilder = HttpServer.create()
|
||||||
.host(config.host)
|
.host(config.host)
|
||||||
.port(config.port)
|
.port(config.port)
|
||||||
|
|
||||||
|
config.tls?.let { tls ->
|
||||||
|
tlsSetup.setupServer("proxy", tls)?.let { sslContext ->
|
||||||
|
serverBuilder = serverBuilder.secure { secure -> secure.sslContext(sslContext) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val server: DisposableServer = serverBuilder
|
||||||
.route(this::setupRoutes)
|
.route(this::setupRoutes)
|
||||||
.bindNow()
|
.bindNow()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.upstream.ethereum
|
|||||||
import io.emeraldpay.dshackle.Defaults
|
import io.emeraldpay.dshackle.Defaults
|
||||||
import io.emeraldpay.dshackle.cache.Caches
|
import io.emeraldpay.dshackle.cache.Caches
|
||||||
import io.emeraldpay.dshackle.cache.CachesEnabled
|
import io.emeraldpay.dshackle.cache.CachesEnabled
|
||||||
|
import io.emeraldpay.dshackle.config.AuthConfig
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
import io.emeraldpay.dshackle.reader.EmptyReader
|
import io.emeraldpay.dshackle.reader.EmptyReader
|
||||||
import io.emeraldpay.dshackle.reader.Reader
|
import io.emeraldpay.dshackle.reader.Reader
|
||||||
@@ -45,7 +46,7 @@ class EthereumWs(
|
|||||||
.builder<BlockJson<TransactionRefJson>>()
|
.builder<BlockJson<TransactionRefJson>>()
|
||||||
.name("new-blocks")
|
.name("new-blocks")
|
||||||
.build()
|
.build()
|
||||||
var basicAuth: UpstreamsConfig.BasicAuth? = null
|
var basicAuth: AuthConfig.ClientBasicAuth? = null
|
||||||
|
|
||||||
private var blockCache: Reader<BlockHash, BlockJson<TransactionRefJson>> = EmptyReader()
|
private var blockCache: Reader<BlockHash, BlockJson<TransactionRefJson>> = EmptyReader()
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import io.emeraldpay.api.proto.BlockchainOuterClass
|
|||||||
import io.emeraldpay.api.proto.ReactorBlockchainGrpc
|
import io.emeraldpay.api.proto.ReactorBlockchainGrpc
|
||||||
import io.emeraldpay.dshackle.Defaults
|
import io.emeraldpay.dshackle.Defaults
|
||||||
import io.emeraldpay.dshackle.FileResolver
|
import io.emeraldpay.dshackle.FileResolver
|
||||||
|
import io.emeraldpay.dshackle.config.AuthConfig
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
||||||
import io.emeraldpay.dshackle.startup.UpstreamChange
|
import io.emeraldpay.dshackle.startup.UpstreamChange
|
||||||
@@ -45,7 +46,7 @@ class GrpcUpstreams(
|
|||||||
private val host: String,
|
private val host: String,
|
||||||
private val port: Int,
|
private val port: Int,
|
||||||
private val objectMapper: ObjectMapper,
|
private val objectMapper: ObjectMapper,
|
||||||
private val auth: UpstreamsConfig.TlsAuth? = null,
|
private val auth: AuthConfig.ClientTlsAuth? = null,
|
||||||
private val fileResolver: FileResolver
|
private val fileResolver: FileResolver
|
||||||
) {
|
) {
|
||||||
private val log = LoggerFactory.getLogger(GrpcUpstreams::class.java)
|
private val log = LoggerFactory.getLogger(GrpcUpstreams::class.java)
|
||||||
@@ -133,7 +134,7 @@ class GrpcUpstreams(
|
|||||||
return Flux.fromIterable(removed + added)
|
return Flux.fromIterable(removed + added)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun withTls(auth: UpstreamsConfig.TlsAuth): SslContext {
|
internal fun withTls(auth: AuthConfig.ClientTlsAuth): SslContext {
|
||||||
val sslContext = SslContextBuilder.forClient()
|
val sslContext = SslContextBuilder.forClient()
|
||||||
.clientAuth(ClientAuth.REQUIRE)
|
.clientAuth(ClientAuth.REQUIRE)
|
||||||
sslContext.trustManager(fileResolver.resolve(auth.ca!!).inputStream())
|
sslContext.trustManager(fileResolver.resolve(auth.ca!!).inputStream())
|
||||||
|
|||||||
191
src/test/groovy/io/emeraldpay/dshackle/TlsSetupSpec.groovy
Normal file
191
src/test/groovy/io/emeraldpay/dshackle/TlsSetupSpec.groovy
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -64,7 +64,7 @@ class UpstreamsConfigReaderSpec extends Specification {
|
|||||||
with((UpstreamsConfig.EthereumConnection)connection) {
|
with((UpstreamsConfig.EthereumConnection)connection) {
|
||||||
rpc.url == new URI("https://mainnet.infura.io/v3/fa28c968191849c1aff541ad1d8511f2")
|
rpc.url == new URI("https://mainnet.infura.io/v3/fa28c968191849c1aff541ad1d8511f2")
|
||||||
rpc.basicAuth != null
|
rpc.basicAuth != null
|
||||||
with((UpstreamsConfig.BasicAuth)rpc.basicAuth) {
|
with((AuthConfig.ClientBasicAuth) rpc.basicAuth) {
|
||||||
username == "4fc258fe41a68149c199ad8f281f2015"
|
username == "4fc258fe41a68149c199ad8f281f2015"
|
||||||
password == "1a68f20154fc258fe4149c199ad8f281"
|
password == "1a68f20154fc258fe4149c199ad8f281"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package io.emeraldpay.dshackle.proxy
|
|||||||
|
|
||||||
import io.emeraldpay.api.proto.BlockchainOuterClass
|
import io.emeraldpay.api.proto.BlockchainOuterClass
|
||||||
import io.emeraldpay.api.proto.Common
|
import io.emeraldpay.api.proto.Common
|
||||||
|
import io.emeraldpay.dshackle.TlsSetup
|
||||||
import io.emeraldpay.dshackle.config.ProxyConfig
|
import io.emeraldpay.dshackle.config.ProxyConfig
|
||||||
import io.emeraldpay.dshackle.rpc.NativeCall
|
import io.emeraldpay.dshackle.rpc.NativeCall
|
||||||
import io.emeraldpay.dshackle.test.TestingCommons
|
import io.emeraldpay.dshackle.test.TestingCommons
|
||||||
@@ -42,7 +43,8 @@ class ProxyServerSpec extends Specification {
|
|||||||
new ProxyConfig(),
|
new ProxyConfig(),
|
||||||
new ReadRpcJson(TestingCommons.objectMapper()),
|
new ReadRpcJson(TestingCommons.objectMapper()),
|
||||||
writeRpcJson,
|
writeRpcJson,
|
||||||
nativeCall
|
nativeCall,
|
||||||
|
new TlsSetup(TestingCommons.fileResolver())
|
||||||
)
|
)
|
||||||
|
|
||||||
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
|
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.Version
|
|||||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import com.fasterxml.jackson.databind.module.SimpleModule
|
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||||
|
import io.emeraldpay.dshackle.FileResolver
|
||||||
import io.emeraldpay.dshackle.cache.Caches
|
import io.emeraldpay.dshackle.cache.Caches
|
||||||
import io.emeraldpay.dshackle.cache.CachesFactory
|
import io.emeraldpay.dshackle.cache.CachesFactory
|
||||||
import io.emeraldpay.dshackle.upstream.AggregatedUpstream
|
import io.emeraldpay.dshackle.upstream.AggregatedUpstream
|
||||||
@@ -79,4 +80,8 @@ class TestingCommons {
|
|||||||
static CachesFactory emptyCaches() {
|
static CachesFactory emptyCaches() {
|
||||||
return new CachesFactory(objectMapper(), new StandardEnvironment())
|
return new CachesFactory(objectMapper(), new StandardEnvironment())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static FileResolver fileResolver() {
|
||||||
|
return new FileResolver(new File("src/test/resources"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
src/test/resources/tls-local/127.0.0.1.crt
Normal file
27
src/test/resources/tls-local/127.0.0.1.crt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEmDCCAoCgAwIBAgIQDTubkUXjTKQ5k1GApHel5jANBgkqhkiG9w0BAQsFADBF
|
||||||
|
MRMwEQYDVQQKEwpNeSBDb21wYW55MRYwFAYDVQQLEw1CbG9ja2NoYWluIENBMRYw
|
||||||
|
FAYDVQQDEw1jYS5teWhvc3QuZGV2MB4XDTIwMDMyMDIzMTEyN1oXDTIxMDkyMDIz
|
||||||
|
MTAzNFowRTETMBEGA1UEChMKTXkgQ29tcGFueTEaMBgGA1UECxMRQmxvY2tjaGFp
|
||||||
|
biBTZXJ2ZXIxEjAQBgNVBAMTCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||||
|
ggEPADCCAQoCggEBANMdYDhfCvUhcEPcKIQXvxFrIy2HUOM9soplutOAUv6dbTOs
|
||||||
|
TeCcpUVgt62sJZcPnE2Iy1nYHeVl+N7vuovUxtzYtfW4rGWprjth4rVcRHkWPwz5
|
||||||
|
QcyH6GwxsUi/0dEtboOxyqqf3W5C+V0+AJYGZ/bI94pGFOMC4+2HTkF8gCDORWKu
|
||||||
|
SG67SqiD9HL0LDrEdpq+IJOevW7t/Oqz+DWcO+VAi1T62xup4JL+WJI1UGtDPeET
|
||||||
|
0SRq3kXb8n4te58Y6NXjyodYlon+BESY42JPw4Uw6rjcmDHQVphg5Dtl343OcfN9
|
||||||
|
CNehtXavuszQdRU9uY1RUdVsvg44xhR60huFJ80CAwEAAaOBgzCBgDAOBgNVHQ8B
|
||||||
|
Af8EBAMCA7gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQW
|
||||||
|
BBT9J7UEZRalIioijC1h4WsrRr9bpDAfBgNVHSMEGDAWgBRkRRFBxmIJWihWWm42
|
||||||
|
q3T1nI5IYDAPBgNVHREECDAGhwR/AAABMA0GCSqGSIb3DQEBCwUAA4ICAQAC3AN0
|
||||||
|
8Ptp6xCOhZ8ja/RLKdF97WGyJS04KoPXyyB0qmh1zpYzHtcUcy6Zw41CiwS9d+7s
|
||||||
|
XfEi780ifmL9XOGxNWTFBvwLMJGq4ukdt//q/y5nhNdINbbZ+LNvZna9gPEBW0cK
|
||||||
|
YR5HEqMMk2eW2Lgo/pg4ska7leI/7ESoVqehNv3Pt9ewVLq38xecRC1K/Q2Z6vlU
|
||||||
|
zt+RbLYW7W7jIXc/cH7TUCDG9sT4tRptTlJdq9sBPVFFBXEZ+QZlY6b0ipeelqnL
|
||||||
|
Wrbsc8XWc/WogEEovBgDpqtqas+jfPI5x9DnopfaSeMP9m9BXYEk+D2nnukw5anp
|
||||||
|
tid4kSqTUOL8q8wTkEnvTd8nqXfFTC3H+Xe5OMGifXkJTRoQsqFS4M/2jLI36MpN
|
||||||
|
KHlNG3O5B1ML+vB0SedxaOD4PwWYQwq0kDWMOHOhe4OwyGgdr8MmPCzNFLbx5ppm
|
||||||
|
apMAco1RXzN/MaBJsYaq5MrTJw4XF8H1CMIEb4GHWb/9gLf4cPetFWzud48eg+YL
|
||||||
|
XADs+uzCGB0+lDMllVpyU+BSZtDUF77PWk1C5/33JNMg8Wzdj/tXs489B6AG1yED
|
||||||
|
fqcihzkQNp5WvwCtO5gY/+AYWZQ/zD0ICqjPfzofs7rb+s3qi8mRVfJJ/qOdGzxw
|
||||||
|
9NZPNkcfB8utd/pGbr4jEeHh+8Ds9vUgZFLXNw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
27
src/test/resources/tls-local/127.0.0.1.key
Normal file
27
src/test/resources/tls-local/127.0.0.1.key
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpQIBAAKCAQEA0x1gOF8K9SFwQ9wohBe/EWsjLYdQ4z2yimW604BS/p1tM6xN
|
||||||
|
4JylRWC3rawllw+cTYjLWdgd5WX43u+6i9TG3Ni19bisZamuO2HitVxEeRY/DPlB
|
||||||
|
zIfobDGxSL/R0S1ug7HKqp/dbkL5XT4AlgZn9sj3ikYU4wLj7YdOQXyAIM5FYq5I
|
||||||
|
brtKqIP0cvQsOsR2mr4gk569bu386rP4NZw75UCLVPrbG6ngkv5YkjVQa0M94RPR
|
||||||
|
JGreRdvyfi17nxjo1ePKh1iWif4ERJjjYk/DhTDquNyYMdBWmGDkO2Xfjc5x830I
|
||||||
|
16G1dq+6zNB1FT25jVFR1Wy+DjjGFHrSG4UnzQIDAQABAoIBAQCX1uj9ol4fMI2u
|
||||||
|
QQpi9zFVNdl3RXvH9PgU0lYtCH6o4lFIeQUKJ6A25fk10Dq5C2E/4sNfOzFFbLIy
|
||||||
|
pfll2QOuk69LrCdSd1f5Hc4Q4uvcq0Nt8ViB4r4oExWPXWdrK2HxFk7NqW15gHIZ
|
||||||
|
vh5tyO29cY2Yxg7/t3R3wnlmYEVHUcS7HmhzgDveNzA0VLza3765ntgwXypY8N2j
|
||||||
|
heEQC1h5kMCurcKJyRXmlsXPRWizX0UBWDrMHFeqyhrH0BlRSFTNC3sKmyYaJQmp
|
||||||
|
daPNRr4zO0yfm8utVSbNHX2OM5DpIO1Ecq9Sd43QI+ATAtxFrhPoYK1rwll267CV
|
||||||
|
cJCRbz+BAoGBAPzz/1Xq8s1lGZ6eWz7H1JlzxYLH+TzQfrV1ym7xJgR/b3rVXiJ+
|
||||||
|
D+qL8zUJDa5xZyflXB7zCg4I7ALmNwMJzVLdIOpEntHK2NtRJ2rp4qH+kMXojays
|
||||||
|
zOGYfbRLNVe+mgAK9Pu8eOi8NzXqkB/S8rml3xqSOpUFsvlc2qiYhGdXAoGBANWo
|
||||||
|
XcQDpisRFcrn3J0+pKU57ZIRjxyOTDlEwH7k+x+PprCRFki80kW22u4l22FdDaip
|
||||||
|
s4vCuAm5tmEogEjINU6ZhSKHxonjaGXfzuZ3gAMk/PN7zFazlgfYEKng+fa1YuZ+
|
||||||
|
3Ubzq6py8enoffJ/PSF/lClKlV5sxjyilxeZmOd7AoGBAMtaJHUf0l4I3tXDnLsV
|
||||||
|
zvYmOixvMxEO1C5xKXKS7utCv45SJcE48vat17FVO+h3RmSuYKaI4BZ0Wbfi92q7
|
||||||
|
4JKzLpjm3X7uwfNehH/Q0t9EVYKk9/BPYs4h1zywEYwesJNEO7p8w/7mAMSZc5AB
|
||||||
|
+BwDGx6zW+EdmcoaOba8FgU1AoGAV2WftWaoukUq3O0rWUceolenznBQUiYDGAn/
|
||||||
|
k+imsKpaTS+MJgTXHp1FwNTLgHBH/g4s26azEYdeCzA+CYecBqLVyuIvXIghVErQ
|
||||||
|
n4WSX7bpoc+qLm0Xme3QIy1cEobwBckvSq6yMe8C9eOcYW2a2/EL8jgIEa/9ByCb
|
||||||
|
HZQ+77ECgYEAxR1eoxc/XV9rcftdaRl7+Db9Qvnhfwu7MFQ3lgomol1N1ckSjwnO
|
||||||
|
wo/HX4+8cMS5QN11d8l2hf+7TyuRCrQBLrYYkrVWb4Z+ote3ejrAsDg90xjOFYWf
|
||||||
|
MWSy7kPeDl3JTEdNPFIIa28EQhZYupD0ihBoVspz2eQ1Y66BOfqC9nU=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
28
src/test/resources/tls-local/127.0.0.1.p8.key
Normal file
28
src/test/resources/tls-local/127.0.0.1.p8.key
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDTHWA4Xwr1IXBD
|
||||||
|
3CiEF78RayMth1DjPbKKZbrTgFL+nW0zrE3gnKVFYLetrCWXD5xNiMtZ2B3lZfje
|
||||||
|
77qL1Mbc2LX1uKxlqa47YeK1XER5Fj8M+UHMh+hsMbFIv9HRLW6Dscqqn91uQvld
|
||||||
|
PgCWBmf2yPeKRhTjAuPth05BfIAgzkVirkhuu0qog/Ry9Cw6xHaaviCTnr1u7fzq
|
||||||
|
s/g1nDvlQItU+tsbqeCS/liSNVBrQz3hE9Ekat5F2/J+LXufGOjV48qHWJaJ/gRE
|
||||||
|
mONiT8OFMOq43Jgx0FaYYOQ7Zd+NznHzfQjXobV2r7rM0HUVPbmNUVHVbL4OOMYU
|
||||||
|
etIbhSfNAgMBAAECggEBAJfW6P2iXh8wja5BCmL3MVU12XdFe8f0+BTSVi0Ifqji
|
||||||
|
UUh5BQonoDbl+TXQOrkLYT/iw187MUVssjKl+WXZA66Tr0usJ1J3V/kdzhDi69yr
|
||||||
|
Q23xWIHivigTFY9dZ2srYfEWTs2pbXmAchm+Hm3I7b1xjZjGDv+3dHfCeWZgRUdR
|
||||||
|
xLseaHOAO943MDRUvNrfvrme2DBfKljw3aOF4RALWHmQwK6twonJFeaWxc9FaLNf
|
||||||
|
RQFYOswcV6rKGsfQGVFIVM0LewqbJholCal1o81GvjM7TJ+by61VJs0dfY4zkOkg
|
||||||
|
7URyr1J3jdAj4BMC3EWuE+hgrWvCWXbrsJVwkJFvP4ECgYEA/PP/VeryzWUZnp5b
|
||||||
|
PsfUmXPFgsf5PNB+tXXKbvEmBH9vetVeIn4P6ovzNQkNrnFnJ+VcHvMKDgjsAuY3
|
||||||
|
AwnNUt0g6kSe0crY21Enauniof6QxeiNrKzM4Zh9tEs1V76aAAr0+7x46Lw3NeqQ
|
||||||
|
H9LyuaXfGpI6lQWy+VzaqJiEZ1cCgYEA1ahdxAOmKxEVyufcnT6kpTntkhGPHI5M
|
||||||
|
OUTAfuT7H4+msJEWSLzSRbba7iXbYV0NqKmzi8K4Cbm2YSiASMg1TpmFIofGieNo
|
||||||
|
Zd/O5neAAyT883vMVrOWB9gQqeD59rVi5n7dRvOrqnLx6eh98n89IX+UKUqVXmzG
|
||||||
|
PKKXF5mY53sCgYEAy1okdR/SXgje1cOcuxXO9iY6LG8zEQ7ULnEpcpLu60K/jlIl
|
||||||
|
wTjy9q3XsVU76HdGZK5gpojgFnRZt+L3arvgkrMumObdfu7B816Ef9DS30RVgqT3
|
||||||
|
8E9iziHXPLARjB6wk0Q7unzD/uYAxJlzkAH4HAMbHrNb4R2Zyho5trwWBTUCgYBX
|
||||||
|
ZZ+1Zqi6RSrc7StZRx6iV6fOcFBSJgMYCf+T6KawqlpNL4wmBNcenUXA1MuAcEf+
|
||||||
|
DizbprMRh14LMD4Jh5wGotXK4i9ciCFUStCfhZJftumhz6oubReZ7dAjLVwShvAF
|
||||||
|
yS9KrrIx7wL145xhbZrb8QvyOAgRr/0HIJsdlD7vsQKBgQDFHV6jFz9dX2tx+11p
|
||||||
|
GXv4Nv1C+eF/C7swVDeWCiaiXU3VyRKPCc7Cj8dfj7xwxLlA3XV3yXaF/7tPK5EK
|
||||||
|
tAEuthiStVZvhn6i17d6OsCwOD3TGM4VhZ8xZLLuQ94OXclMR008UghrbwRCFli6
|
||||||
|
kPSKEGhWynPZ5DVjroE5+oL2dQ==
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
31
src/test/resources/tls-local/ca.myhost.dev.crt
Normal file
31
src/test/resources/tls-local/ca.myhost.dev.crt
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
wkLM79un1zrcbPHQUcA=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
51
src/test/resources/tls-local/ca.myhost.dev.key
Normal file
51
src/test/resources/tls-local/ca.myhost.dev.key
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
52
src/test/resources/tls-local/ca.myhost.dev.p8.key
Normal file
52
src/test/resources/tls-local/ca.myhost.dev.p8.key
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
/rUweL14cs7iUki+lWEGjjvXmOFkORI=
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
Reference in New Issue
Block a user