From e180142d73a37fcce5b4857ddef6e713f7ae2492 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Thu, 17 Dec 2015 15:50:01 +0100 Subject: Rename files --- cmd/acme/config.go | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++ cmd/acme/main.go | 112 +++++++++++++++++++++++++++++--------- 2 files changed, 240 insertions(+), 26 deletions(-) create mode 100644 cmd/acme/config.go (limited to 'cmd/acme') diff --git a/cmd/acme/config.go b/cmd/acme/config.go new file mode 100644 index 0000000..7f5ffbe --- /dev/null +++ b/cmd/acme/config.go @@ -0,0 +1,154 @@ +package main + +import ( + "crypto/rsa" + "errors" + "path" + "strings" + "time" + + "github.com/BurntSushi/toml" +) + +type Config struct { + Defaults defaults + Provider map[string]*provider + Account map[string]*account + Hook map[string]*hook + Desire map[string]*desire +} + +type defaults struct { + Gracetime duration + Listen string + Provider string + Account string + Basedir string + KeySize int +} + +type provider struct { + Directory string +} + +type account struct { + Mail string + Phone string + Key string + KeySize int + key *rsa.PrivateKey +} + +type hook struct { + CMD string +} + +type desire struct { + Provider string + Account string + Altnames []string + Key string + KeySize int + Cert string + Webroot string + Hooks []string + key *rsa.PrivateKey `toml:"-"` + account *account + provider *provider +} + +var ( + errNoProvider = errors.New("no provider specified") + errNoAccount = errors.New("no account specified") + errNoKey = errors.New("no key specified") + errNoCert = errors.New("no cert specified") + errNoAltNames = errors.New("no altnames specified") + errNoMail = errors.New("no mail specified") +) + +func LoadConfig(fname string) (*Config, error) { + c := &Config{} + _, err := toml.DecodeFile(fname, c) + if err != nil { + return nil, err + } + // apply defaults + if c.Defaults.KeySize == 0 { + c.Defaults.KeySize = 2048 + } + for k, v := range c.Account { + if v.KeySize == 0 { + v.KeySize = c.Defaults.KeySize + } + if v.Mail == "" { + return nil, errNoMail + } + if v.Key == "" { + return nil, errNoKey + } + if c.Defaults.Basedir != "" { + v.Key = path.Join(c.Defaults.Basedir, v.Key) + } + c.Account[k] = v + } + for k, v := range c.Desire { + if v.Provider == "" { + if c.Defaults.Provider != "" { + v.Provider = c.Defaults.Provider + } else { + return nil, errNoProvider + } + } + v.provider = c.Provider[v.Provider] + if v.Account == "" { + if c.Defaults.Account != "" { + v.Account = c.Defaults.Account + } else { + return nil, errNoAccount + } + } + v.account = c.Account[v.Account] + if v.KeySize == 0 { + v.KeySize = c.Defaults.KeySize + } + if v.Key == "" { + return nil, errNoKey + } + if v.Cert == "" { + return nil, errNoCert + } + if c.Defaults.Basedir != "" { + v.Key = path.Join(c.Defaults.Basedir, v.Key) + v.Cert = path.Join(c.Defaults.Basedir, v.Cert) + } + switch len(v.Altnames) { + case 0: + return nil, errNoAltNames + case 1: + an := v.Altnames[0] + if strings.HasPrefix(an, "www.") { + v.Altnames = append(v.Altnames, an[4:]) + } + } + c.Desire[k] = v + } + return c, nil +} + +type PrivKey interface { + Path() string + Size() int +} + +func (d desire) Path() string { return d.Key } +func (d desire) Size() int { return d.KeySize } +func (a account) Path() string { return a.Key } +func (a account) Size() int { return a.KeySize } + +type duration struct{ time.Duration } + +func (d *duration) UnmarshalText(s []byte) error { + var err error + d.Duration, err = time.ParseDuration(string(s)) + return err +} diff --git a/cmd/acme/main.go b/cmd/acme/main.go index c9faa85..058eefb 100644 --- a/cmd/acme/main.go +++ b/cmd/acme/main.go @@ -1,47 +1,107 @@ package main import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" "flag" + "io" + "io/ioutil" "log" + "os" + "path" "dim13.org/acme" ) -func must(err error) { +var confName = flag.String("conf", "acme.toml", "configuration file") + +func newKey(w io.Writer, size int) (*rsa.PrivateKey, error) { + key, err := rsa.GenerateKey(rand.Reader, size) if err != nil { - log.Fatal("must:", err) + return nil, err + } + block := &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(key), } + return key, pem.Encode(w, block) } -var ( - config = flag.String("config", "acme.toml", "configuration file") - port = flag.Int("port", 8443, "port to listen") -) +func chkKey(k PrivKey) (*rsa.PrivateKey, error) { + key := k.Path() + if _, err := os.Stat(key); os.IsNotExist(err) { + log.Println("allocating", key, k.Size()) + if err := os.MkdirAll(path.Dir(key), 0700); err != nil { + return nil, err + } + flags := os.O_WRONLY | os.O_CREATE | os.O_TRUNC + fd, err := os.OpenFile(key, flags, 0600) + if err != nil { + return nil, err + } + defer fd.Close() + return newKey(fd, k.Size()) + } else { + der, err := ioutil.ReadFile(key) + if err != nil { + return nil, err + } + block, _ := pem.Decode(der) + return x509.ParsePKCS1PrivateKey(block.Bytes) + } +} -func init() { - flag.Parse() +func chkKeys(c *Config) error { + var err error + for k, acc := range c.Account { + acc.key, err = chkKey(acc) + if err != nil { + return err + } + c.Account[k] = acc + } + for k, des := range c.Desire { + des.key, err = chkKey(des) + if err != nil { + return err + } + c.Desire[k] = des + } + return nil } func main() { - conf, err := acme.LoadConfig(*config) - must(err) - log.Printf("%+v\n", conf) - - for _, v := range conf.Desire { - acc := conf.Account[v.Account] - prov := conf.Provider[v.Provider] - a, err := acme.NewAccount(acc.Mail, acme.KeySize) - must(err) - - c, err := acme.NewClient(prov.Directory) - must(err) + flag.Parse() + conf, err := LoadConfig(*confName) + if err != nil { + log.Fatal(err) + } + err = chkKeys(conf) + if err != nil { + log.Fatal(err) + } - re, err := c.Register(a) - must(err) - log.Printf("%+v\n", re) + log.Println(conf) + for k, des := range conf.Desire { + log.Println(k, des.account) + } + return - re, err = c.Agree(a) - must(err) - log.Printf("%+v\n", re) + for k, des := range conf.Desire { + a, _ := acme.NewAccount(des.account.Mail, des.account.Phone, des.account.key) + log.Println(k, a) + c, err := acme.NewClient(des.provider.Directory) + if err != nil { + log.Println(err) + } + log.Println(k, c) + az, err := c.Authorize(a, des.Altnames[0]) + if err != nil { + log.Println(err) + } + log.Println(k, az) } + } -- cgit v1.2.3