package acmeserver import ( "errors" "fmt" "io/ioutil" "log" "path/filepath" "gopkg.in/yaml.v2" "git.autistici.org/ai3/go-common/clientutil" ) // 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 { Testing bool `yaml:"testing"` DirectoryURL string `yaml:"directory_url"` DefaultChallengeType string `yaml:"default_challenge"` UseRSA bool `yaml:"use_rsa"` RenewalDays int `yaml:"renewal_days"` 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"` } `yaml:"http"` DNS struct { Enabled bool `yaml:"enabled"` Nameservers []string `yaml:"nameservers"` TSIGKeyName string `yaml:"tsig_key_name"` TSIGKeyAlgo string `yaml:"tsig_key_algo"` TSIGKeySecret string `yaml:"tsig_key_secret"` } `yaml:"dns"` } // This is all the configuration we need to generate a certificate. type certConfig struct { // List of names for this certificate. The first one will be // the certificate CN, all of them will be subjectAltNames. Names []string `yaml:"names"` // Challenge type to use for this domain. If empty, the // defaults will be applied. ChallengeType string `yaml:"challenge,omitempty"` } func (c *certConfig) check() error { if len(c.Names) == 0 { return errors.New("empty names list") } // We can't check here if the challenge type is actually // configured or not, but at least we can report unknown / // unsupported types as syntax errors. switch c.ChallengeType { case "", dns01Challenge, http01Challenge: default: return fmt.Errorf("unkown or unsupported challenge type '%s'", c.ChallengeType) } return nil } func readCertConfigs(path string) ([]*certConfig, error) { data, err := ioutil.ReadFile(path) // nolint: gosec if err != nil { return nil, err } var cc []*certConfig if err := yaml.Unmarshal(data, &cc); err != nil { return nil, err } return cc, nil } func readCertConfigsFromDir(dir string, certs []*certConfig) ([]*certConfig, error) { files, err := filepath.Glob(filepath.Join(dir, "*.yml")) if err != nil { return nil, err } for _, f := range files { cc, err := readCertConfigs(f) if err != nil { log.Printf("error reading %s: %v", f, err) continue } // Validate the cert configs, skip ones with errors. for _, c := range cc { if err := c.check(); err != nil { log.Printf("configuration error in %s: %v", f, err) continue } 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 }