Skip to content

Commit

Permalink
Adding Mercury.TLS field CertPath for node communication with web ser…
Browse files Browse the repository at this point in the history
…vers + load balancers over TLS (#11492)

* feature/transmission_key: adding Transmission.TLS field CertPath

* [Transmission.TLS] --> [Mercury.TLS]
  • Loading branch information
patrickhuie19 authored Jan 9, 2024
1 parent 1bb33a4 commit 0f82c97
Show file tree
Hide file tree
Showing 23 changed files with 167 additions and 1 deletion.
5 changes: 5 additions & 0 deletions core/config/docs/core.toml
Original file line number Diff line number Diff line change
Expand Up @@ -574,3 +574,8 @@ MaxStaleAge = "1h" # Default
# LatestReportDeadline controls how long to wait for a response from the
# mercury server before retrying. Setting this to zero will wait indefinitely.
LatestReportDeadline = "5s" # Default

# Mercury.TLS controls client settings for when the node talks to traditional web servers or load balancers.
[Mercury.TLS]
# CertFile is the path to a PEM file of trusted root certificate authority certificates
CertFile = "/path/to/client/certs.pem" # Example
5 changes: 5 additions & 0 deletions core/config/mercury_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ type MercuryCache interface {
LatestReportDeadline() time.Duration
}

type MercuryTLS interface {
CertFile() string
}

type Mercury interface {
Credentials(credName string) *ocr2models.MercuryCredentials
Cache() MercuryCache
TLS() MercuryTLS
}
25 changes: 25 additions & 0 deletions core/config/toml/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1304,12 +1304,37 @@ func (mc *MercuryCache) setFrom(f *MercuryCache) {
}
}

type MercuryTLS struct {
CertFile *string
}

func (m *MercuryTLS) setFrom(f *MercuryTLS) {
if v := f.CertFile; v != nil {
m.CertFile = v
}
}

func (m *MercuryTLS) ValidateConfig() (err error) {
if *m.CertFile != "" {
if !isValidFilePath(*m.CertFile) {
err = multierr.Append(err, configutils.ErrInvalid{Name: "CertFile", Value: *m.CertFile, Msg: "must be a valid file path"})
}
}
return
}

type Mercury struct {
Cache MercuryCache `toml:",omitempty"`
TLS MercuryTLS `toml:",omitempty"`
}

func (m *Mercury) setFrom(f *Mercury) {
m.Cache.setFrom(&f.Cache)
m.TLS.setFrom(&f.TLS)
}

func (m *Mercury) ValidateConfig() (err error) {
return m.TLS.ValidateConfig()
}

type MercuryCredentials struct {
Expand Down
45 changes: 45 additions & 0 deletions core/config/toml/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,5 +531,50 @@ func TestTracing_ValidateMode(t *testing.T) {
}
}

func TestMercuryTLS_ValidateTLSCertPath(t *testing.T) {
tests := []struct {
name string
tlsCertPath *string
wantErr bool
errMsg string
}{
{
name: "valid file path",
tlsCertPath: ptr("/etc/ssl/certs/cert.pem"),
wantErr: false,
},
{
name: "relative file path",
tlsCertPath: ptr("certs/cert.pem"),
wantErr: false,
},
{
name: "excessively long file path",
tlsCertPath: ptr(strings.Repeat("z", 4097)),
wantErr: true,
errMsg: "CertFile: invalid value (" + strings.Repeat("z", 4097) + "): must be a valid file path",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mercury := &Mercury{
TLS: MercuryTLS{
CertFile: tt.tlsCertPath,
},
}

err := mercury.ValidateConfig()

if tt.wantErr {
assert.Error(t, err)
assert.Equal(t, tt.errMsg, err.Error())
} else {
assert.NoError(t, err)
}
})
}
}

// ptr is a utility function for converting a value to a pointer to the value.
func ptr[T any](t T) *T { return &t }
12 changes: 12 additions & 0 deletions core/services/chainlink/config_mercury.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ func (m *mercuryCacheConfig) LatestReportDeadline() time.Duration {
return m.c.LatestReportDeadline.Duration()
}

type mercuryTLSConfig struct {
c toml.MercuryTLS
}

func (m *mercuryTLSConfig) CertFile() string {
return *m.c.CertFile
}

type mercuryConfig struct {
c toml.Mercury
s toml.MercurySecrets
Expand All @@ -47,3 +55,7 @@ func (m *mercuryConfig) Credentials(credName string) *models.MercuryCredentials
func (m *mercuryConfig) Cache() config.MercuryCache {
return &mercuryCacheConfig{c: m.c.Cache}
}

func (m *mercuryConfig) TLS() config.MercuryTLS {
return &mercuryTLSConfig{c: m.c.TLS}
}
14 changes: 14 additions & 0 deletions core/services/chainlink/config_mercury_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/stretchr/testify/require"

"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models"

"github.com/smartcontractkit/chainlink/v2/core/config/toml"
)

const (
Expand Down Expand Up @@ -34,3 +36,15 @@ func TestMercuryConfig(t *testing.T) {
assert.Equal(t, &models.MercuryCredentials{URL: "https://chain1.link", Username: "username1", Password: "password1"}, m.Credentials("cred1"))
assert.Equal(t, &models.MercuryCredentials{URL: "https://chain2.link", Username: "username2", Password: "password2"}, m.Credentials("cred2"))
}

func TestMercuryTLS(t *testing.T) {
certPath := "/path/to/cert.pem"
transmission := toml.Mercury{
TLS: toml.MercuryTLS{
CertFile: &certPath,
},
}
cfg := mercuryConfig{c: transmission}

assert.Equal(t, certPath, cfg.TLS().CertFile())
}
6 changes: 6 additions & 0 deletions core/services/chainlink/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,9 @@ func TestConfig_Marshal(t *testing.T) {
MaxStaleAge: models.MustNewDuration(101 * time.Second),
LatestReportDeadline: models.MustNewDuration(102 * time.Second),
},
TLS: toml.MercuryTLS{
CertFile: ptr("/path/to/cert.pem"),
},
}

for _, tt := range []struct {
Expand Down Expand Up @@ -1097,6 +1100,9 @@ URL = 'http://stark.node'
LatestReportTTL = '1m40s'
MaxStaleAge = '1m41s'
LatestReportDeadline = '1m42s'
[Mercury.TLS]
CertFile = '/path/to/cert.pem'
`},
{"full", full, fullTOML},
{"multi-chain", multiChain, multiChainTOML},
Expand Down
3 changes: 3 additions & 0 deletions core/services/chainlink/testdata/config-empty-effective.toml
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,6 @@ TLSCertPath = ''
LatestReportTTL = '1s'
MaxStaleAge = '1h0m0s'
LatestReportDeadline = '5s'

[Mercury.TLS]
CertFile = ''
3 changes: 3 additions & 0 deletions core/services/chainlink/testdata/config-full.toml
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ LatestReportTTL = '1m40s'
MaxStaleAge = '1m41s'
LatestReportDeadline = '1m42s'

[Mercury.TLS]
CertFile = '/path/to/cert.pem'

[[EVM]]
ChainID = '1'
Enabled = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@ LatestReportTTL = '1s'
MaxStaleAge = '1h0m0s'
LatestReportDeadline = '5s'

[Mercury.TLS]
CertFile = ''

[[EVM]]
ChainID = '1'
AutoCreateKey = true
Expand Down
1 change: 1 addition & 0 deletions core/services/ocr2/delegate.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ type jobPipelineConfig interface {
type mercuryConfig interface {
Credentials(credName string) *models.MercuryCredentials
Cache() coreconfig.MercuryCache
TLS() coreconfig.MercuryTLS
}

type thresholdConfig interface {
Expand Down
3 changes: 3 additions & 0 deletions core/web/resolver/testdata/config-empty-effective.toml
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,6 @@ TLSCertPath = ''
LatestReportTTL = '1s'
MaxStaleAge = '1h0m0s'
LatestReportDeadline = '5s'

[Mercury.TLS]
CertFile = ''
3 changes: 3 additions & 0 deletions core/web/resolver/testdata/config-full.toml
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ LatestReportTTL = '1m40s'
MaxStaleAge = '1m41s'
LatestReportDeadline = '1m42s'

[Mercury.TLS]
CertFile = ''

[[EVM]]
ChainID = '1'
Enabled = false
Expand Down
3 changes: 3 additions & 0 deletions core/web/resolver/testdata/config-multi-chain-effective.toml
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@ LatestReportTTL = '1s'
MaxStaleAge = '1h0m0s'
LatestReportDeadline = '5s'

[Mercury.TLS]
CertFile = ''

[[EVM]]
ChainID = '1'
AutoCreateKey = true
Expand Down
3 changes: 2 additions & 1 deletion docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Two new prom metrics for mercury, nops should consider adding alerting on these:
- `mercury_insufficient_blocks_count`
- `mercury_zero_blocks_count`
- Added new `Mercury.TLS` TOML config field `CertFile` for configuring transport credentials when the node acts as a client and initiates a TLS handshake.
### Changed
- `PromReporter` no longer directly reads txm related status from the db, and instead uses the txStore API.
Expand Down
13 changes: 13 additions & 0 deletions docs/CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,19 @@ LatestReportDeadline = "5s" # Default
LatestReportDeadline controls how long to wait for a response from the
mercury server before retrying. Setting this to zero will wait indefinitely.

## Mercury.TLS
```toml
[Mercury.TLS]
CertFile = "/path/to/client/certs.pem" # Example
```
Mercury.TLS controls client settings for when the node talks to traditional web servers or load balancers.

### CertFile
```toml
CertFile = "/path/to/client/certs.pem" # Example
```
CertFile is the path to a PEM file of trusted root certificate authority certificates

## EVM
EVM defaults depend on ChainID:

Expand Down
3 changes: 3 additions & 0 deletions testdata/scripts/node/validate/default.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,9 @@ LatestReportTTL = '1s'
MaxStaleAge = '1h0m0s'
LatestReportDeadline = '5s'

[Mercury.TLS]
CertFile = ''

Invalid configuration: invalid secrets: 2 errors:
- Database.URL: empty: must be provided and non-empty
- Password.Keystore: empty: must be provided and non-empty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,9 @@ LatestReportTTL = '1s'
MaxStaleAge = '1h0m0s'
LatestReportDeadline = '5s'

[Mercury.TLS]
CertFile = ''

[[EVM]]
ChainID = '1'
AutoCreateKey = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,9 @@ LatestReportTTL = '1s'
MaxStaleAge = '1h0m0s'
LatestReportDeadline = '5s'

[Mercury.TLS]
CertFile = ''

[[EVM]]
ChainID = '1'
AutoCreateKey = true
Expand Down
3 changes: 3 additions & 0 deletions testdata/scripts/node/validate/disk-based-logging.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,9 @@ LatestReportTTL = '1s'
MaxStaleAge = '1h0m0s'
LatestReportDeadline = '5s'

[Mercury.TLS]
CertFile = ''

[[EVM]]
ChainID = '1'
AutoCreateKey = true
Expand Down
3 changes: 3 additions & 0 deletions testdata/scripts/node/validate/invalid.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,9 @@ LatestReportTTL = '1s'
MaxStaleAge = '1h0m0s'
LatestReportDeadline = '5s'

[Mercury.TLS]
CertFile = ''

[[EVM]]
ChainID = '1'
AutoCreateKey = true
Expand Down
3 changes: 3 additions & 0 deletions testdata/scripts/node/validate/valid.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,9 @@ LatestReportTTL = '1s'
MaxStaleAge = '1h0m0s'
LatestReportDeadline = '5s'

[Mercury.TLS]
CertFile = ''

[[EVM]]
ChainID = '1'
AutoCreateKey = true
Expand Down
3 changes: 3 additions & 0 deletions testdata/scripts/node/validate/warnings.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,9 @@ LatestReportTTL = '1s'
MaxStaleAge = '1h0m0s'
LatestReportDeadline = '5s'

[Mercury.TLS]
CertFile = ''

# Configuration warning:
Tracing.TLSCertPath: invalid value (something): must be empty when Tracing.Mode is 'unencrypted'
Valid configuration.

0 comments on commit 0f82c97

Please sign in to comment.