From 50c8662b9b32744e1d27eb055215c4cc405a6a34 Mon Sep 17 00:00:00 2001 From: ale <ale@incal.net> Date: Sat, 25 Aug 2018 09:49:54 +0100 Subject: [PATCH] Minor changes to configuration Support multiple config_dirs, name all paths explicitly instead of simply using predefined subdirectories. --- acme.go | 6 ++++-- cmd/acmeserver/acmeserver.go | 2 +- config.go | 36 ++++++++++++++++++++++++++++-------- manager.go | 22 ++++++++++++---------- manager_test.go | 19 +++++++++++++------ 5 files changed, 58 insertions(+), 27 deletions(-) diff --git a/acme.go b/acme.go index 51eccf9d..4cb17897 100644 --- a/acme.go +++ b/acme.go @@ -13,7 +13,6 @@ import ( "io/ioutil" "log" "net/http" - "path/filepath" "golang.org/x/crypto/acme" ) @@ -46,6 +45,9 @@ func NewACME(config *Config) (*ACME, error) { if config.Email == "" { return nil, errors.New("configuration parameter 'email' is unset") } + if config.AccountKeyPath == "" { + return nil, errors.New("configuration parameter 'account_key_path' is unset") + } var h http.Handler v := make(map[string]validator) @@ -77,7 +79,7 @@ func NewACME(config *Config) (*ACME, error) { return &ACME{ email: config.Email, - accountKeyPath: filepath.Join(config.Dir, "account.key"), + accountKeyPath: config.AccountKeyPath, directoryURL: directoryURL, handler: h, validators: v, diff --git a/cmd/acmeserver/acmeserver.go b/cmd/acmeserver/acmeserver.go index 31e4d0c8..2da3ef69 100644 --- a/cmd/acmeserver/acmeserver.go +++ b/cmd/acmeserver/acmeserver.go @@ -17,7 +17,7 @@ import ( var ( addr = flag.String("addr", ":2780", "tcp `address` to listen on") - configFile = flag.String("config", "/etc/acmeserver/config.yml", "configuration `file`") + configFile = flag.String("config", "/etc/acme/config.yml", "configuration `file`") ) // Config ties together the acmeserver Config and the standard diff --git a/config.go b/config.go index b009de49..ebbc87e2 100644 --- a/config.go +++ b/config.go @@ -14,19 +14,28 @@ import ( // Config holds the configuration for an acmeserver instance. // +// The reason for supporting multiple config_dirs is to allow +// integration with third-party automation systems: in some cases, +// automation tools need control of an entire directory in order to +// safely delete entries that no longer exist. +// // nolint: maligned type Config struct { - Addr string `yaml:"addr"` - Testing bool `yaml:"testing"` DirectoryURL string `yaml:"directory_url"` DefaultChallengeType string `yaml:"default_challenge"` UseRSA bool `yaml:"use_rsa"` RenewalDays int `yaml:"renewal_days"` - Email string `yaml:"email"` - Dir string `yaml:"cert_dir"` - ReplDS *clientutil.BackendConfig `yaml:"replds"` + AccountKeyPath string `yaml:"account_key_path"` + Email string `yaml:"email"` + + Dirs []string `yaml:"config_dirs"` + + Output struct { + Path string `yaml:"path"` + ReplDS *clientutil.BackendConfig `yaml:"replds"` + } `yaml:"output"` HTTP struct { Enabled bool `yaml:"enabled"` @@ -79,12 +88,11 @@ func readCertConfigs(path string) ([]*certConfig, error) { return cc, nil } -func readCertConfigsFromDir(dir string) ([]*certConfig, error) { +func readCertConfigsFromDir(dir string, certs []*certConfig) ([]*certConfig, error) { files, err := filepath.Glob(filepath.Join(dir, "*.yml")) if err != nil { return nil, err } - var out []*certConfig for _, f := range files { cc, err := readCertConfigs(f) if err != nil { @@ -97,8 +105,20 @@ func readCertConfigsFromDir(dir string) ([]*certConfig, error) { log.Printf("configuration error in %s: %v", f, err) continue } - out = append(out, c) + certs = append(certs, c) + } + } + return certs, nil +} + +func readCertConfigsFromDirs(dirs []string) ([]*certConfig, error) { + var out []*certConfig + for _, dir := range dirs { + certs, err := readCertConfigsFromDir(dir, out) + if err != nil { + return nil, err } + out = certs } return out, nil } diff --git a/manager.go b/manager.go index a2f099ed..a6845550 100644 --- a/manager.go +++ b/manager.go @@ -13,7 +13,6 @@ import ( "errors" "io" "log" - "path/filepath" "sync" "time" @@ -75,7 +74,7 @@ type CertGenerator interface { // Manager periodically renews certificates before they expire, and // responds to http-01 validation requests. type Manager struct { - configDir string + configDirs []string useRSA bool storage certStorage certs []*certInfo @@ -89,13 +88,16 @@ type Manager struct { // NewManager creates a new Manager with the given configuration. func NewManager(config *Config, certGen CertGenerator) (*Manager, error) { // Validate the configuration. - if config.Dir == "" { - return nil, errors.New("configuration parameter 'cert_dir' is unset") + if len(config.Dirs) == 0 { + return nil, errors.New("configuration parameter 'config_dirs' is unset") + } + if config.Output.Path == "" { + return nil, errors.New("'output.path' is unset") } m := &Manager{ useRSA: config.UseRSA, - configDir: filepath.Join(config.Dir, "config"), + configDirs: config.Dirs, doneCh: make(chan bool), configCh: make(chan []*certConfig, 1), certGen: certGen, @@ -105,11 +107,11 @@ func NewManager(config *Config, certGen CertGenerator) (*Manager, error) { m.renewalDays = 15 } - ds := &dirStorage{root: filepath.Join(config.Dir, "certs")} - if config.ReplDS == nil { + ds := &dirStorage{root: config.Output.Path} + if config.Output.ReplDS == nil { m.storage = ds } else { - r, err := replds.NewPublicClient(config.ReplDS) + r, err := replds.NewPublicClient(config.Output.ReplDS) if err != nil { return nil, err } @@ -126,7 +128,7 @@ func NewManager(config *Config, certGen CertGenerator) (*Manager, error) { // cause background processing to stop, interrupting all running // updates. func (m *Manager) Start(ctx context.Context) error { - domains, err := readCertConfigsFromDir(m.configDir) + domains, err := readCertConfigsFromDirs(m.configDirs) if err != nil { return err } @@ -145,7 +147,7 @@ func (m *Manager) Wait() { // Reload configuration. func (m *Manager) Reload() { - domains, err := readCertConfigsFromDir(m.configDir) + domains, err := readCertConfigsFromDirs(m.configDirs) if err != nil { log.Printf("error reading config: %v", err) return diff --git a/manager_test.go b/manager_test.go index ced4b0cb..895164e7 100644 --- a/manager_test.go +++ b/manager_test.go @@ -26,6 +26,16 @@ func (s *slowACME) GetCertificate(ctx context.Context, _ crypto.Signer, _ *certC } } +func newTestConfig(dir string) *Config { + c := Config{ + Dirs: []string{filepath.Join(dir, "config")}, + AccountKeyPath: filepath.Join(dir, "account.key"), + Email: "test@example.com", + } + c.Output.Path = filepath.Join(dir, "certs") + return &c +} + // Create a new test function. // The first function returned is a cleanup callback. // The second function returned is the cancel callback. @@ -42,10 +52,7 @@ func newTestManager(t testing.TB, g CertGenerator) (func(), context.CancelFunc, 0644, ) - m, err := NewManager(&Config{ - Dir: dir, - Email: "test@example.com", - }, g) + m, err := NewManager(newTestConfig(dir), g) if err != nil { t.Fatal(err) } @@ -114,11 +121,11 @@ func TestManager_NewCert(t *testing.T) { // Verify that the credentials have successfully been written // to storage. - p := filepath.Join(m.configDir, "../certs/example.com/cert.pem") + p := filepath.Join(m.configDirs[0], "../certs/example.com/cert.pem") if _, err := os.Stat(p); err != nil { t.Fatalf("file not created: %v", err) } - p = filepath.Join(m.configDir, "../certs/example.com/private_key.pem") + p = filepath.Join(m.configDirs[0], "../certs/example.com/private_key.pem") if _, err := os.Stat(p); err != nil { t.Fatalf("file not created: %v", err) } -- GitLab