dnscontrol/pkg/acme/registration.go
Andrew Rafferty 402fc449e2 Certs: Create directories with execute permissions so they can be opened (#395)
* Create directories with execute permissions so they can be opened
* Use 0700 permissions on certificate directories instead of 0755
2018-08-27 12:12:53 -04:00

124 lines
2.7 KiB
Go

package acme
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/json"
"encoding/pem"
"io/ioutil"
"log"
"os"
"path/filepath"
"github.com/xenolf/lego/acmev2"
)
func (c *certManager) loadOrCreateAccount() error {
f, err := os.Open(c.accountFile())
if err != nil && os.IsNotExist(err) {
return c.createAccount()
}
if err != nil {
return err
}
defer f.Close()
dec := json.NewDecoder(f)
acct := &account{}
if err = dec.Decode(acct); err != nil {
return err
}
c.account = acct
keyBytes, err := ioutil.ReadFile(c.accountKeyFile())
if err != nil {
return err
}
keyBlock, _ := pem.Decode(keyBytes)
if keyBlock == nil {
log.Fatal("WTF", keyBytes)
}
c.account.key, err = x509.ParseECPrivateKey(keyBlock.Bytes)
if err != nil {
return err
}
c.client, err = acme.NewClient(c.acmeDirectory, c.account, acme.RSA2048) // TODO: possibly make configurable on a cert-by cert basis
if err != nil {
return err
}
return nil
}
func (c *certManager) accountDirectory() string {
return filepath.Join(c.directory, ".letsencrypt", c.acmeHost)
}
func (c *certManager) accountFile() string {
return filepath.Join(c.accountDirectory(), "account.json")
}
func (c *certManager) accountKeyFile() string {
return filepath.Join(c.accountDirectory(), "account.key")
}
// TODO: probably lock these down more
const perms os.FileMode = 0644
const dirPerms os.FileMode = 0700
func (c *certManager) createAccount() error {
if err := os.MkdirAll(c.accountDirectory(), dirPerms); err != nil {
return err
}
privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
if err != nil {
return err
}
acct := &account{
key: privateKey,
Email: c.email,
}
c.account = acct
c.client, err = acme.NewClient(c.acmeDirectory, c.account, acme.EC384)
if err != nil {
return err
}
reg, err := c.client.Register(true)
if err != nil {
return err
}
c.account.Registration = reg
acctBytes, err := json.MarshalIndent(c.account, "", " ")
if err != nil {
return err
}
if err = ioutil.WriteFile(c.accountFile(), acctBytes, perms); err != nil {
return err
}
keyBytes, err := x509.MarshalECPrivateKey(privateKey)
if err != nil {
return err
}
pemKey := &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}
pemBytes := pem.EncodeToMemory(pemKey)
if err = ioutil.WriteFile(c.accountKeyFile(), pemBytes, perms); err != nil {
return err
}
return nil
}
type account struct {
Email string `json:"email"`
key crypto.PrivateKey
Registration *acme.RegistrationResource `json:"registration"`
}
func (a *account) GetEmail() string {
return a.Email
}
func (a *account) GetPrivateKey() crypto.PrivateKey {
return a.key
}
func (a *account) GetRegistration() *acme.RegistrationResource {
return a.Registration
}