Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new remoteva binary #7437

Merged
merged 24 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 8 additions & 29 deletions cmd/boulder-va/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,18 @@ import (

"github.com/letsencrypt/boulder/bdns"
"github.com/letsencrypt/boulder/cmd"
"github.com/letsencrypt/boulder/config"
"github.com/letsencrypt/boulder/features"
bgrpc "github.com/letsencrypt/boulder/grpc"
"github.com/letsencrypt/boulder/va"
vacfg "github.com/letsencrypt/boulder/va/config"
pgporada marked this conversation as resolved.
Show resolved Hide resolved
vapb "github.com/letsencrypt/boulder/va/proto"
)

type Config struct {
VA struct {
cmd.ServiceConfig

UserAgent string

IssuerDomain string

// DNSTries is the number of times to try a DNS query (that has a temporary error)
// before giving up. May be short-circuited by deadlines. A zero value
// will be turned into 1.
DNSTries int
DNSProvider *cmd.DNSProvider `validate:"required_without=DNSStaticResolvers"`
// DNSStaticResolvers is a list of DNS resolvers. Each entry must
// be a host or IP and port separated by a colon. IPv6 addresses
// must be enclosed in square brackets.
DNSStaticResolvers []string `validate:"required_without=DNSProvider,dive,hostname_port"`
DNSTimeout config.Duration `validate:"required"`
DNSAllowLoopbackAddresses bool

vacfg.Common
RemoteVAs []cmd.GRPCClientConfig `validate:"omitempty,dive"`
MaxRemoteValidationFailures int `validate:"omitempty,min=0,required_with=RemoteVAs"`

Features features.Config
pgporada marked this conversation as resolved.
Show resolved Hide resolved

AccountURIPrefixes []string `validate:"min=1,dive,required,url"`
}

Syslog cmd.SyslogConfig
Expand Down Expand Up @@ -81,7 +60,6 @@ func main() {
if dnsTries < 1 {
dnsTries = 1
}
clk := cmd.Clock()

var servers bdns.ServerProvider
proto := "udp"
Expand All @@ -101,6 +79,12 @@ func main() {
tlsConfig, err := c.VA.TLS.Load(scope)
cmd.FailOnError(err, "tlsConfig config")

if c.VA.DNSTimeout.Duration == 0 {
cmd.Fail("'dnsTimeout' is required")
}
pgporada marked this conversation as resolved.
Show resolved Hide resolved

clk := cmd.Clock()

var resolver bdns.Client
if !c.VA.DNSAllowLoopbackAddresses {
resolver = bdns.New(
Expand All @@ -121,7 +105,6 @@ func main() {
logger,
tlsConfig)
}

var remotes []va.RemoteVA
if len(c.VA.RemoteVAs) > 0 {
for _, rva := range c.VA.RemoteVAs {
Expand Down Expand Up @@ -157,13 +140,9 @@ func main() {
&vapb.VA_ServiceDesc, vai).Add(
&vapb.CAA_ServiceDesc, vai).Build(tlsConfig, scope, clk)
cmd.FailOnError(err, "Unable to setup VA gRPC server")

cmd.FailOnError(start(), "VA gRPC service failed")
}

func init() {
cmd.RegisterCommand("boulder-va", main, &cmd.ConfigValidator{Config: &Config{}})
// We register under two different names, because it's convenient for the
// remote VAs to show up under a different program name when looking at logs.
cmd.RegisterCommand("boulder-remoteva", main, &cmd.ConfigValidator{Config: &Config{}})
}
1 change: 0 additions & 1 deletion cmd/boulder-va/main_test.go

This file was deleted.

1 change: 1 addition & 0 deletions cmd/boulder/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
_ "github.com/letsencrypt/boulder/cmd/nonce-service"
_ "github.com/letsencrypt/boulder/cmd/notify-mailer"
_ "github.com/letsencrypt/boulder/cmd/ocsp-responder"
_ "github.com/letsencrypt/boulder/cmd/remoteva"
_ "github.com/letsencrypt/boulder/cmd/reversed-hostname-checker"
_ "github.com/letsencrypt/boulder/cmd/rocsp-tool"
"github.com/letsencrypt/boulder/core"
Expand Down
14 changes: 9 additions & 5 deletions cmd/boulder/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,19 @@ func TestConfigValidation(t *testing.T) {
fileNames = []string{"publisher.json"}
case "boulder-ra":
fileNames = []string{"ra.json"}
case "boulder-remoteva":
case "boulder-sa":
fileNames = []string{"sa.json"}
case "boulder-va":
fileNames = []string{
"va.json",
"va-remote-a.json",
"va-remote-b.json",
}
case "boulder-sa":
fileNames = []string{"sa.json"}
case "boulder-va":
fileNames = []string{"va.json"}
case "remoteva":
fileNames = []string{
"remoteva-a.json",
"remoteva-b.json",
}
case "boulder-wfe2":
fileNames = []string{"wfe2.json"}
case "nonce-service":
Expand Down
101 changes: 95 additions & 6 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,95 @@ type HostnamePolicyConfig struct {

// TLSConfig represents certificates and a key for authenticated TLS.
type TLSConfig struct {
CertFile string `validate:"required"`
KeyFile string `validate:"required"`
CertFile string `validate:"required"`
KeyFile string `validate:"required"`
// The CACertFile file may contain any number of root certificates and will
// be deduplicated internally.
CACertFile string `validate:"required"`
// Valid ClientAuth values can be found at
// https://pkg.go.dev/crypto/tls#ClientAuthType. Boulder will select its own
// default value, rather than the default value from //crypto/tls, if no
// client auth is configured
ClientAuth string `validate:"omitempty"`
// Each CipherSuites value must be supported by //crypto/tls. Boulder will
// select its own default value, rather than the default value from
// //crypto/tls, if no cipher suite(s) is configured. Cipher suites will be
// ignored by tls.Config for TLS v1.3. Valid cipher suites can be found at
// https://cs.opensource.google/go/go/+/refs/tags/go1.22.2:src/crypto/tls/cipher_suites.go;l=59-68
CipherSuites []string `validate:"omitempty"`
}

// makeCipherSuitesFromConfig takes a slice of human-readable TLS cipher suite
// names declared in a config file and constructs a new slice of cipher suites
// usable by a tls.Config or returns an error.
func (t *TLSConfig) makeCipherSuitesFromConfig() ([]uint16, error) {
// We'll set a sane default, rather than use the default from //crypto/tls.
if len(t.CipherSuites) == 0 {
return []uint16{tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, nil
}

// Construct a reverse lookup to validate the operator provided name(s) is
// usable. The "legacy names" are unfortunately not included.
cipherSuiteNameToId := make(map[string]uint16, len(tls.CipherSuites()))
for _, id := range tls.CipherSuites() {
cipherSuiteNameToId[id.Name] = id.ID
}

var configuredCipherSuiteIds []uint16
for _, cs := range t.CipherSuites {
id, ok := cipherSuiteNameToId[cs]
if !ok {
return nil, fmt.Errorf("unsupported TLS cipher suite: %s", cs)
}
configuredCipherSuiteIds = append(configuredCipherSuiteIds, id)
}

return configuredCipherSuiteIds, nil
}

// makeClientAuthFromConfig converts the value of clientAuth from config file
// and returns the equivalent tls.ClientAuthType value or an error.
func (t *TLSConfig) makeClientAuthFromConfig() (tls.ClientAuthType, error) {
// Construct some lookup tables to map the configured ClientAuthType to the
// appropriate //crypto/tls const integer.
clientAuthTypeNameToId := make(map[string]int)
clientAuthTypeIdToName := make(map[int]string)

for i := 0; ; i++ {
name := tls.ClientAuthType(i).String()
if strings.Contains(name, "ClientAuthType") {
// A const block using iota doesn't have length so to speak.
// Non-existent values will return the name of the underlying type
// (not the concrete type!). We can use that to bail out early as
// soon as our maps contain only the values used inside
// //crypto/tls.
break
}
clientAuthTypeNameToId[name] = i
clientAuthTypeIdToName[i] = name
}

pgporada marked this conversation as resolved.
Show resolved Hide resolved
// We'll set a sane default, rather than use the default from //crypto/tls
// which is "NoClientCert".
var defaultId int
if t.ClientAuth == "" {
defaultId = clientAuthTypeNameToId["RequireAndVerifyClientCert"]
t.ClientAuth = clientAuthTypeIdToName[defaultId]
}

id, ok := clientAuthTypeNameToId[t.ClientAuth]
if !ok {
return -1, fmt.Errorf("unsupported TLS client auth value: %s", t.ClientAuth)
}

return tls.ClientAuthType(id), nil
}

// Load reads and parses the certificates and key listed in the TLSConfig, and
// returns a *tls.Config suitable for either client or server use. Prometheus
// metrics for various certificate fields will be exported.
// returns a *tls.Config suitable for either client or server use. The
// CACertFile file may contain any number of root certificates and will be
pgporada marked this conversation as resolved.
Show resolved Hide resolved
// deduplicated internally. Prometheus metrics for various certificate fields
pgporada marked this conversation as resolved.
Show resolved Hide resolved
// will be exported.
func (t *TLSConfig) Load(scope prometheus.Registerer) (*tls.Config, error) {
if t == nil {
return nil, fmt.Errorf("nil TLS section in config")
Expand All @@ -158,6 +239,14 @@ func (t *TLSConfig) Load(scope prometheus.Registerer) (*tls.Config, error) {
return nil, fmt.Errorf("loading key pair from %q and %q: %s",
t.CertFile, t.KeyFile, err)
}
cipherSuites, err := t.makeCipherSuitesFromConfig()
if err != nil {
return nil, err
}
clientAuth, err := t.makeClientAuthFromConfig()
if err != nil {
return nil, err
}

tlsNotBefore := prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Expand Down Expand Up @@ -203,13 +292,13 @@ func (t *TLSConfig) Load(scope prometheus.Registerer) (*tls.Config, error) {
return &tls.Config{
RootCAs: rootCAs,
ClientCAs: rootCAs,
ClientAuth: tls.RequireAndVerifyClientCert,
ClientAuth: clientAuth,
Certificates: []tls.Certificate{cert},
// Set the only acceptable TLS to v1.2 and v1.3.
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS13,
// CipherSuites will be ignored for TLS v1.3.
CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305},
CipherSuites: cipherSuites,
}, nil
}

Expand Down
Loading