Commit 6c7f483b authored by Matthew Slipper's avatar Matthew Slipper

go/proxyd: ENG-1704 add support for additional SSL certs

parent b6bdea03
---
'@eth-optimism/proxyd': patch
---
Add support for additional SSL certificates in Docker container
...@@ -13,9 +13,17 @@ RUN make proxyd ...@@ -13,9 +13,17 @@ RUN make proxyd
FROM alpine:3.14.2 FROM alpine:3.14.2
COPY ./go/proxyd/entrypoint.sh /bin/entrypoint.sh
RUN apk update && \
apk add ca-certificates && \
chmod +x /bin/entrypoint.sh
EXPOSE 8080 EXPOSE 8080
VOLUME /etc/proxyd VOLUME /etc/proxyd
COPY --from=builder /app/bin/proxyd /bin/proxyd COPY --from=builder /app/bin/proxyd /bin/proxyd
ENTRYPOINT ["/bin/entrypoint.sh"]
CMD ["/bin/proxyd", "/etc/proxyd/proxyd.toml"] CMD ["/bin/proxyd", "/etc/proxyd/proxyd.toml"]
...@@ -21,3 +21,6 @@ See `metrics.go` for a list of all available metrics. ...@@ -21,3 +21,6 @@ See `metrics.go` for a list of all available metrics.
The metrics port is configurable via the `metrics.port` and `metrics.host` keys in the config. The metrics port is configurable via the `metrics.port` and `metrics.host` keys in the config.
## Adding Backend SSL Certificates in Docker
The Docker image runs on Alpine Linux. If you get SSL errors when connecting to a backend within Docker, you may need to add additional certificates to Alpine's certificate store. To do this, bind mount the certificate bundle into a file in `/usr/local/share/ca-certificates`. The `entrypoint.sh` script will then update the store with whatever is in the `ca-certificates` directory prior to starting `proxyd`.
\ No newline at end of file
...@@ -3,6 +3,7 @@ package proxyd ...@@ -3,6 +3,7 @@ package proxyd
import ( import (
"bytes" "bytes"
"context" "context"
"crypto/tls"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
...@@ -127,6 +128,15 @@ func WithMaxWSConns(maxConns int) BackendOpt { ...@@ -127,6 +128,15 @@ func WithMaxWSConns(maxConns int) BackendOpt {
} }
} }
func WithTLSConfig(tlsConfig *tls.Config) BackendOpt {
return func(b *Backend) {
if b.client.Transport == nil {
b.client.Transport = &http.Transport{}
}
b.client.Transport.(*http.Transport).TLSClientConfig = tlsConfig
}
}
func NewBackend( func NewBackend(
name string, name string,
rpcURL string, rpcURL string,
...@@ -188,6 +198,7 @@ func (b *Backend) Forward(ctx context.Context, req *RPCReq) (*RPCRes, error) { ...@@ -188,6 +198,7 @@ func (b *Backend) Forward(ctx context.Context, req *RPCReq) (*RPCRes, error) {
RecordRPCError(ctx, b.Name, req.Method, res.Error) RecordRPCError(ctx, b.Name, req.Method, res.Error)
log.Info( log.Info(
"backend responded with RPC error", "backend responded with RPC error",
"backend", b.Name,
"code", res.Error.Code, "code", res.Error.Code,
"msg", res.Error.Message, "msg", res.Error.Message,
"req_id", GetReqID(ctx), "req_id", GetReqID(ctx),
...@@ -196,6 +207,7 @@ func (b *Backend) Forward(ctx context.Context, req *RPCReq) (*RPCRes, error) { ...@@ -196,6 +207,7 @@ func (b *Backend) Forward(ctx context.Context, req *RPCReq) (*RPCRes, error) {
) )
} else { } else {
log.Info("forwarded RPC request", log.Info("forwarded RPC request",
"backend", b.Name,
"method", req.Method, "method", req.Method,
"auth", GetAuthCtx(ctx), "auth", GetAuthCtx(ctx),
"req_id", GetReqID(ctx), "req_id", GetReqID(ctx),
......
...@@ -31,7 +31,10 @@ type BackendConfig struct { ...@@ -31,7 +31,10 @@ type BackendConfig struct {
RPCURL string `toml:"rpc_url"` RPCURL string `toml:"rpc_url"`
WSURL string `toml:"ws_url"` WSURL string `toml:"ws_url"`
MaxRPS int `toml:"max_rps"` MaxRPS int `toml:"max_rps"`
MaxWSConns int `toml:"max_ws_conns"` MaxWSConns int `toml:"max_ws_conns"`
CAFile string `toml:"ca_file"`
ClientCertFile string `toml:"client_cert_file"`
ClientKeyFile string `toml:"client_key_file"`
} }
type BackendsConfig map[string]*BackendConfig type BackendsConfig map[string]*BackendConfig
......
#!/bin/sh
echo "Updating CA certificates."
update-ca-certificates
echo "Running CMD."
exec "$@"
\ No newline at end of file
...@@ -52,6 +52,12 @@ username = "" ...@@ -52,6 +52,12 @@ username = ""
password = "" password = ""
max_rps = 3 max_rps = 3
max_ws_conns = 1 max_ws_conns = 1
# Path to a custom root CA.
ca_file = ""
# Path to a custom client cert file.
client_cert_file = ""
# Path to a custom client key file.
client_key_file = ""
[backends.alchemy] [backends.alchemy]
# The URL to contact the backend at. # The URL to contact the backend at.
......
package proxyd package proxyd
import ( import (
"crypto/tls"
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -75,6 +76,14 @@ func Start(config *Config) error { ...@@ -75,6 +76,14 @@ func Start(config *Config) error {
if cfg.Password != "" { if cfg.Password != "" {
opts = append(opts, WithBasicAuth(cfg.Username, cfg.Password)) opts = append(opts, WithBasicAuth(cfg.Username, cfg.Password))
} }
tlsConfig, err := configureBackendTLS(cfg)
if err != nil {
return err
}
if tlsConfig != nil {
log.Info("using custom TLS config for backend", "name", name)
opts = append(opts, WithTLSConfig(tlsConfig))
}
back := NewBackend(name, cfg.RPCURL, cfg.WSURL, lim, opts...) back := NewBackend(name, cfg.RPCURL, cfg.WSURL, lim, opts...)
backendNames = append(backendNames, name) backendNames = append(backendNames, name)
backendsByName[name] = back backendsByName[name] = back
...@@ -168,3 +177,24 @@ func Start(config *Config) error { ...@@ -168,3 +177,24 @@ func Start(config *Config) error {
func secondsToDuration(seconds int) time.Duration { func secondsToDuration(seconds int) time.Duration {
return time.Duration(seconds) * time.Second return time.Duration(seconds) * time.Second
} }
func configureBackendTLS(cfg *BackendConfig) (*tls.Config, error) {
if cfg.CAFile == "" {
return nil, nil
}
tlsConfig, err := CreateTLSClient(cfg.CAFile)
if err != nil {
return nil, err
}
if cfg.ClientCertFile != "" && cfg.ClientKeyFile != "" {
cert, err := ParseKeyPair(cfg.ClientCertFile, cfg.ClientKeyFile)
if err != nil {
return nil, err
}
tlsConfig.Certificates = []tls.Certificate{cert}
}
return tlsConfig, nil
}
package proxyd
import (
"crypto/tls"
"crypto/x509"
"errors"
"io/ioutil"
)
func CreateTLSClient(ca string) (*tls.Config, error) {
pem, err := ioutil.ReadFile(ca)
if err != nil {
return nil, wrapErr(err, "error reading CA")
}
roots := x509.NewCertPool()
ok := roots.AppendCertsFromPEM(pem)
if !ok {
return nil, errors.New("error parsing TLS client cert")
}
return &tls.Config{
RootCAs: roots,
}, nil
}
func ParseKeyPair(crt, key string) (tls.Certificate, error) {
cert, err := tls.LoadX509KeyPair(crt, key)
if err != nil {
return tls.Certificate{}, wrapErr(err, "error loading x509 key pair")
}
return cert, nil
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment