Skip to content
Snippets Groups Projects
config.go 3.17 KiB
Newer Older
  • Learn to ignore specific revisions
  • ale's avatar
    ale committed
    package acmeserver
    
    import (
    
    ale's avatar
    ale committed
    	"errors"
    	"fmt"
    
    ale's avatar
    ale committed
    	"log"
    
    ale's avatar
    ale committed
    	"os"
    
    ale's avatar
    ale committed
    	"path/filepath"
    
    
    ale's avatar
    ale committed
    	"gopkg.in/yaml.v3"
    
    ale's avatar
    ale committed
    
    	"git.autistici.org/ai3/go-common/clientutil"
    )
    
    // Config holds the configuration for an acmeserver instance.
    
    ale's avatar
    ale committed
    //
    
    ale's avatar
    ale committed
    // 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.
    //
    
    ale's avatar
    ale committed
    // nolint: maligned
    
    ale's avatar
    ale committed
    type Config struct {
    
    ale's avatar
    ale committed
    	Testing              bool   `yaml:"testing"`
    	DirectoryURL         string `yaml:"directory_url"`
    	DefaultChallengeType string `yaml:"default_challenge"`
    	UseRSA               bool   `yaml:"use_rsa"`
    	RenewalDays          int    `yaml:"renewal_days"`
    
    
    ale's avatar
    ale committed
    	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"`
    
    ale's avatar
    ale committed
    
    	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"`
    
    ale's avatar
    ale committed
    }
    
    
    ale's avatar
    ale committed
    // This is all the configuration we need to generate a certificate.
    
    ale's avatar
    ale committed
    type certConfig struct {
    
    ale's avatar
    ale committed
    	// 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"`
    
    ale's avatar
    ale committed
    }
    
    
    ale's avatar
    ale committed
    func (c *certConfig) check() error {
    	if len(c.Names) == 0 {
    		return errors.New("empty names list")
    
    ale's avatar
    ale committed
    	}
    
    ale's avatar
    ale committed
    	// 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)
    
    ale's avatar
    ale committed
    	}
    
    ale's avatar
    ale committed
    	return nil
    
    ale's avatar
    ale committed
    }
    
    
    ale's avatar
    ale committed
    func readCertConfigs(path string) ([]*certConfig, error) {
    
    ale's avatar
    ale committed
    	f, err := os.Open(path)
    
    ale's avatar
    ale committed
    	if err != nil {
    		return nil, err
    	}
    
    ale's avatar
    ale committed
    	defer f.Close()
    
    ale's avatar
    ale committed
    	var cc []*certConfig
    
    ale's avatar
    ale committed
    	if err := yaml.NewDecoder(f).Decode(&cc); err != nil {
    
    ale's avatar
    ale committed
    		return nil, err
    	}
    	return cc, nil
    
    ale's avatar
    ale committed
    }
    
    
    ale's avatar
    ale committed
    func readCertConfigsFromDir(dir string, certs []*certConfig) ([]*certConfig, error) {
    
    ale's avatar
    ale committed
    	files, err := filepath.Glob(filepath.Join(dir, "*.yml"))
    	if err != nil {
    		return nil, err
    	}
    	for _, f := range files {
    
    ale's avatar
    ale committed
    		cc, err := readCertConfigs(f)
    
    ale's avatar
    ale committed
    		if err != nil {
    			log.Printf("error reading %s: %v", f, err)
    			continue
    		}
    
    ale's avatar
    ale committed
    		// 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
    			}
    
    ale's avatar
    ale committed
    			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
    
    ale's avatar
    ale committed
    		}
    
    ale's avatar
    ale committed
    		out = certs
    
    ale's avatar
    ale committed
    	}
    
    ale's avatar
    ale committed
    	return out, nil
    
    ale's avatar
    ale committed
    }