boulder/cmd/config.go

552 lines
15 KiB
Go

package cmd
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"strings"
"time"
cfsslConfig "github.com/cloudflare/cfssl/config"
"github.com/letsencrypt/pkcs11key"
"github.com/letsencrypt/boulder/core"
"github.com/letsencrypt/boulder/goodkey"
)
// Config stores configuration parameters that applications
// will need. For simplicity, we just lump them all into
// one struct, and use encoding/json to read it from a file.
//
// Note: NO DEFAULTS are provided.
type Config struct {
// Default AMQPConfig for services that don't specify one.
// TODO(jsha): Delete this after a deploy.
AMQP *AMQPConfig
CA CAConfig
RA struct {
ServiceConfig
HostnamePolicyConfig
RateLimitPoliciesFilename string
MaxConcurrentRPCServerRequests int64
MaxContactsPerRegistration int
// UseIsSafeDomain determines whether to call VA.IsSafeDomain
UseIsSafeDomain bool // TODO(jmhodges): remove after va IsSafeDomain deploy
// 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
VAService *GRPCClientConfig
MaxNames int
DoNotForceCN bool
// Controls behaviour of the RA when asked to create a new authz for
// a name/regID that already has a valid authz. False preserves historic
// behaviour and ignores the existing authz and creates a new one. True
// instructs the RA to reuse the previously created authz in lieu of
// creating another.
ReuseValidAuthz bool
}
SA struct {
ServiceConfig
DBConfig
MaxConcurrentRPCServerRequests int64
}
VA struct {
ServiceConfig
UserAgent string
IssuerDomain string
PortConfig PortConfig
MaxConcurrentRPCServerRequests int64
LookupIPv6 bool
GoogleSafeBrowsing *GoogleSafeBrowsingConfig
CAAService *GRPCClientConfig
CAADistributedResolver *CAADistributedResolverConfig
// 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
// Feature flag to enable enforcement of CAA SERVFAILs.
CAASERVFAILExceptions string
}
Statsd StatsdConfig
Syslog SyslogConfig
Revoker struct {
DBConfig
// The revoker isn't a long running service, so doesn't get a full
// ServiceConfig, just an AMQPConfig.
AMQP *AMQPConfig
}
Mailer struct {
ServiceConfig
DBConfig
SMTPConfig
From string
Subject string
CertLimit int
NagTimes []string
// How much earlier (than configured nag intervals) to
// send reminders, to account for the expected delay
// before the next expiration-mailer invocation.
NagCheckInterval string
// Path to a text/template email template
EmailTemplate string
}
OCSPResponder struct {
ServiceConfig
DBConfig
// Source indicates the source of pre-signed OCSP responses to be used. It
// can be a DBConnect string or a file URL. The file URL style is used
// when responding from a static file for intermediates and roots.
// If DBConfig has non-empty fields, it takes precedence over this.
Source string
Path string
ListenAddress string
// MaxAge is the max-age to set in the Cache-Control response
// header. It is a time.Duration formatted string.
MaxAge ConfigDuration
ShutdownStopTimeout string
ShutdownKillTimeout string
}
OCSPUpdater OCSPUpdaterConfig
Publisher struct {
ServiceConfig
SubmissionTimeout ConfigDuration
MaxConcurrentRPCServerRequests int64
}
PA PAConfig
Common struct {
BaseURL string
// Path to a PEM-encoded copy of the issuer certificate.
IssuerCert string
DNSResolver string
DNSTimeout string
DNSAllowLoopbackAddresses bool
CT struct {
Logs []LogDescription
IntermediateBundleFilename string
}
}
CertChecker struct {
DBConfig
HostnamePolicyConfig
Workers int
ReportDirectoryPath string
UnexpiredOnly bool
BadResultsOnly bool
CheckPeriod ConfigDuration
}
AllowedSigningAlgos *AllowedSigningAlgos
// TODO: remove after production configs use SubscriberAgreementURL in the wfe section
SubscriberAgreementURL string
}
// AllowedSigningAlgos defines which algorithms be used for keys that we will
// sign.
type AllowedSigningAlgos struct {
RSA bool
ECDSANISTP256 bool
ECDSANISTP384 bool
ECDSANISTP521 bool
}
// KeyPolicy returns a KeyPolicy reflecting the Boulder configuration.
func (asa *AllowedSigningAlgos) KeyPolicy() goodkey.KeyPolicy {
if asa != nil {
return goodkey.KeyPolicy{
AllowRSA: asa.RSA,
AllowECDSANISTP256: asa.ECDSANISTP256,
AllowECDSANISTP384: asa.ECDSANISTP384,
AllowECDSANISTP521: asa.ECDSANISTP521,
}
}
return goodkey.KeyPolicy{
AllowRSA: true,
}
}
// KeyPolicy returns a KeyPolicy reflecting the Boulder configuration.
// TODO: remove once WFE, RA and CA all use KeyPolicy belonging to the type AllowedSigningAlgos
func (config *Config) KeyPolicy() goodkey.KeyPolicy {
if config.AllowedSigningAlgos != nil {
return goodkey.KeyPolicy{
AllowRSA: config.AllowedSigningAlgos.RSA,
AllowECDSANISTP256: config.AllowedSigningAlgos.ECDSANISTP256,
AllowECDSANISTP384: config.AllowedSigningAlgos.ECDSANISTP384,
AllowECDSANISTP521: config.AllowedSigningAlgos.ECDSANISTP521,
}
}
return goodkey.KeyPolicy{
AllowRSA: true,
}
}
// PasswordConfig either contains a password or the path to a file
// containing a password
type PasswordConfig struct {
Password string
PasswordFile string
}
// Pass returns a password, either directly from the configuration
// struct or by reading from a specified file
func (pc *PasswordConfig) Pass() (string, error) {
if pc.PasswordFile != "" {
contents, err := ioutil.ReadFile(pc.PasswordFile)
if err != nil {
return "", err
}
return strings.TrimRight(string(contents), "\n"), nil
}
return pc.Password, nil
}
// ServiceConfig contains config items that are common to all our services, to
// be embedded in other config structs.
type ServiceConfig struct {
// DebugAddr is the address to run the /debug handlers on.
DebugAddr string
AMQP *AMQPConfig
GRPC *GRPCServerConfig
}
// DBConfig defines how to connect to a database. The connect string may be
// stored in a file separate from the config, because it can contain a password,
// which we want to keep out of configs.
type DBConfig struct {
DBConnect string
// A file containing a connect URL for the DB.
DBConnectFile string
MaxDBConns int
}
// URL returns the DBConnect URL represented by this DBConfig object, either
// loading it from disk or returning a default value.
func (d *DBConfig) URL() (string, error) {
if d.DBConnectFile != "" {
url, err := ioutil.ReadFile(d.DBConnectFile)
return string(url), err
}
return d.DBConnect, nil
}
type SMTPConfig struct {
PasswordConfig
Server string
Port string
Username string
}
// AMQPConfig describes how to connect to AMQP, and how to speak to each of the
// RPC services we offer via AMQP.
type AMQPConfig struct {
// A file from which the AMQP Server URL will be read. This allows secret
// values (like the password) to be stored separately from the main config.
ServerURLFile string
// AMQP server URL, including username and password.
Server string
Insecure bool
RA *RPCServerConfig
VA *RPCServerConfig
SA *RPCServerConfig
CA *RPCServerConfig
Publisher *RPCServerConfig
TLS *TLSConfig
// Queue name on which to listen, if this is an RPC service (vs acting only as
// an RPC client).
ServiceQueue string
ReconnectTimeouts struct {
Base ConfigDuration
Max ConfigDuration
}
}
// ServerURL returns the appropriate server URL for this object, which may
// involve reading from a file.
func (a *AMQPConfig) ServerURL() (string, error) {
if a.ServerURLFile != "" {
url, err := ioutil.ReadFile(a.ServerURLFile)
return strings.TrimRight(string(url), "\n"), err
}
if a.Server == "" {
return "", fmt.Errorf("Missing AMQP server URL")
}
return a.Server, nil
}
// CAConfig structs have configuration information for the certificate
// authority, including database parameters as well as controls for
// issued certificates.
type CAConfig struct {
ServiceConfig
DBConfig
HostnamePolicyConfig
RSAProfile string
ECDSAProfile string
TestMode bool
SerialPrefix int
// TODO(jsha): Remove Key field once we've migrated to Issuers
Key *IssuerConfig
// Issuers contains configuration information for each issuer cert and key
// this CA knows about. The first in the list is used as the default.
Issuers []IssuerConfig
// LifespanOCSP is how long OCSP responses are valid for; It should be longer
// than the minTimeToExpiry field for the OCSP Updater.
LifespanOCSP ConfigDuration
// How long issued certificates are valid for, should match expiry field
// in cfssl config.
Expiry string
// The maximum number of subjectAltNames in a single certificate
MaxNames int
CFSSL cfsslConfig.Config
MaxConcurrentRPCServerRequests int64
// DoNotForceCN is a temporary config setting. It controls whether
// to add a certificate's serial to its Subject, and whether to
// not pull a SAN entry to be the CN if no CN was given in a CSR.
DoNotForceCN bool
// EnableMustStaple governs whether the Must Staple extension in CSRs
// triggers issuance of certificates with Must Staple.
EnableMustStaple bool
PublisherService *GRPCClientConfig
}
// PAConfig specifies how a policy authority should connect to its
// database, what policies it should enforce, and what challenges
// it should offer.
type PAConfig struct {
DBConfig
EnforcePolicyWhitelist bool
Challenges map[string]bool
}
// HostnamePolicyConfig specifies a file from which to load a policy regarding
// what hostnames to issue for.
type HostnamePolicyConfig struct {
HostnamePolicyFile string
}
// CheckChallenges checks whether the list of challenges in the PA config
// actually contains valid challenge names
func (pc PAConfig) CheckChallenges() error {
if len(pc.Challenges) == 0 {
return errors.New("empty challenges map in the Policy Authority config is not allowed")
}
for name := range pc.Challenges {
if !core.ValidChallenge(name) {
return fmt.Errorf("Invalid challenge in PA config: %s", name)
}
}
return nil
}
// IssuerConfig contains info about an issuer: private key and issuer cert.
// It should contain either a File path to a PEM-format private key,
// or a PKCS11Config defining how to load a module for an HSM.
type IssuerConfig struct {
// A file from which a pkcs11key.Config will be read and parsed, if present
ConfigFile string
File string
PKCS11 *pkcs11key.Config
CertFile string
}
// TLSConfig reprents certificates and a key for authenticated TLS.
type TLSConfig struct {
CertFile *string
KeyFile *string
CACertFile *string
}
// RPCServerConfig contains configuration particular to a specific RPC server
// type (e.g. RA, SA, etc)
type RPCServerConfig struct {
Server string // Queue name where the server receives requests
RPCTimeout ConfigDuration
}
// OCSPUpdaterConfig provides the various window tick times and batch sizes needed
// for the OCSP (and SCT) updater
type OCSPUpdaterConfig struct {
ServiceConfig
DBConfig
NewCertificateWindow ConfigDuration
OldOCSPWindow ConfigDuration
MissingSCTWindow ConfigDuration
RevokedCertificateWindow ConfigDuration
NewCertificateBatchSize int
OldOCSPBatchSize int
MissingSCTBatchSize int
RevokedCertificateBatchSize int
OCSPMinTimeToExpiry ConfigDuration
OldestIssuedSCT ConfigDuration
AkamaiBaseURL string
AkamaiClientToken string
AkamaiClientSecret string
AkamaiAccessToken string
AkamaiPurgeRetries int
AkamaiPurgeRetryBackoff ConfigDuration
SignFailureBackoffFactor float64
SignFailureBackoffMax ConfigDuration
Publisher *GRPCClientConfig
}
// GoogleSafeBrowsingConfig is the JSON config struct for the VA's use of the
// Google Safe Browsing API.
type GoogleSafeBrowsingConfig struct {
APIKey string
DataDir string
}
// SyslogConfig defines the config for syslogging.
type SyslogConfig struct {
StdoutLevel int
SyslogLevel int
}
// StatsdConfig defines the config for Statsd.
type StatsdConfig struct {
Server string
Prefix string
}
// ConfigDuration is just an alias for time.Duration that allows
// serialization to YAML as well as JSON.
type ConfigDuration struct {
time.Duration
}
// ErrDurationMustBeString is returned when a non-string value is
// presented to be deserialized as a ConfigDuration
var ErrDurationMustBeString = errors.New("cannot JSON unmarshal something other than a string into a ConfigDuration")
// UnmarshalJSON parses a string into a ConfigDuration using
// time.ParseDuration. If the input does not unmarshal as a
// string, then UnmarshalJSON returns ErrDurationMustBeString.
func (d *ConfigDuration) UnmarshalJSON(b []byte) error {
s := ""
err := json.Unmarshal(b, &s)
if err != nil {
if _, ok := err.(*json.UnmarshalTypeError); ok {
return ErrDurationMustBeString
}
return err
}
dd, err := time.ParseDuration(s)
d.Duration = dd
return err
}
// MarshalJSON returns the string form of the duration, as a byte array.
func (d ConfigDuration) MarshalJSON() ([]byte, error) {
return []byte(d.Duration.String()), nil
}
// UnmarshalYAML uses the same frmat as JSON, but is called by the YAML
// parser (vs. the JSON parser).
func (d *ConfigDuration) UnmarshalYAML(unmarshal func(interface{}) error) error {
var s string
if err := unmarshal(&s); err != nil {
return err
}
dur, err := time.ParseDuration(s)
if err != nil {
return err
}
d.Duration = dur
return nil
}
// LogDescription contains the information needed to submit certificates
// to a CT log and verify returned receipts
type LogDescription struct {
URI string
Key string
}
// GRPCClientConfig contains the information needed to talk to the gRPC service
type GRPCClientConfig struct {
ServerAddresses []string
ServerIssuerPath string
ClientCertificatePath string
ClientKeyPath string
Timeout ConfigDuration
}
// GRPCServerConfig contains the information needed to run a gRPC service
type GRPCServerConfig struct {
Address string `json:"address" yaml:"address"`
ServerCertificatePath string `json:"serverCertificatePath" yaml:"server-certificate-path"`
ServerKeyPath string `json:"serverKeyPath" yaml:"server-key-path"`
ClientIssuerPath string `json:"clientIssuerPath" yaml:"client-issuer-path"`
}
// PortConfig specifies what ports the VA should call to on the remote
// host when performing its checks.
type PortConfig struct {
HTTPPort int
HTTPSPort int
TLSPort int
}
// CAADistributedResolverConfig specifies the HTTP client setup and interfaces
// needed to resolve CAA addresses over multiple paths
type CAADistributedResolverConfig struct {
Timeout ConfigDuration
MaxFailures int
Proxies []string
}