github.com/teknogeek/dnscontrol/v2@v2.10.1-0.20200227202244-ae299b55ba42/pkg/acme/vaultStorage.go (about)

     1  package acme
     2  
     3  import (
     4  	"crypto/x509"
     5  	"encoding/json"
     6  	"encoding/pem"
     7  	"fmt"
     8  	"strings"
     9  
    10  	"github.com/go-acme/lego/certificate"
    11  
    12  	"github.com/hashicorp/vault/api"
    13  )
    14  
    15  type vaultStorage struct {
    16  	path   string
    17  	client *api.Logical
    18  }
    19  
    20  func makeVaultStorage(vaultPath string) (Storage, error) {
    21  	if !strings.HasSuffix(vaultPath, "/") {
    22  		vaultPath += "/"
    23  	}
    24  	client, err := api.NewClient(api.DefaultConfig())
    25  	if err != nil {
    26  		return nil, err
    27  	}
    28  	storage := &vaultStorage{
    29  		path:   vaultPath,
    30  		client: client.Logical(),
    31  	}
    32  	return storage, nil
    33  }
    34  
    35  func (v *vaultStorage) GetCertificate(name string) (*certificate.Resource, error) {
    36  	path := v.certPath(name)
    37  	secret, err := v.client.Read(path)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  	if secret == nil {
    42  		return nil, nil
    43  	}
    44  	cert := &certificate.Resource{}
    45  	if dat, err := v.getString("meta", secret.Data, path); err != nil {
    46  		return nil, err
    47  	} else if err = json.Unmarshal(dat, cert); err != nil {
    48  		return nil, err
    49  	}
    50  
    51  	if dat, err := v.getString("tls.cert", secret.Data, path); err != nil {
    52  		return nil, err
    53  	} else {
    54  		cert.Certificate = dat
    55  	}
    56  
    57  	if dat, err := v.getString("tls.key", secret.Data, path); err != nil {
    58  		return nil, err
    59  	} else {
    60  		cert.PrivateKey = dat
    61  	}
    62  
    63  	return cert, nil
    64  }
    65  
    66  func (v *vaultStorage) getString(key string, data map[string]interface{}, path string) ([]byte, error) {
    67  	dat, ok := data[key]
    68  	if !ok {
    69  		return nil, fmt.Errorf("Secret at %s does not have key %s", path, key)
    70  	}
    71  	str, ok := dat.(string)
    72  	if !ok {
    73  		return nil, fmt.Errorf("Secret at %s is not string", path)
    74  	}
    75  	return []byte(str), nil
    76  }
    77  
    78  func (v *vaultStorage) StoreCertificate(name string, cert *certificate.Resource) error {
    79  	jDat, err := json.MarshalIndent(cert, "", "  ")
    80  	if err != nil {
    81  		return err
    82  	}
    83  	pub := string(cert.Certificate)
    84  	key := string(cert.PrivateKey)
    85  	data := map[string]interface{}{
    86  		"tls.cert":     pub,
    87  		"tls.key":      key,
    88  		"tls.combined": pub + "\n" + key,
    89  		"meta":         string(jDat),
    90  	}
    91  	_, err = v.client.Write(v.certPath(name), data)
    92  	return err
    93  }
    94  
    95  func (v *vaultStorage) registrationPath(acmeHost string) string {
    96  	return v.path + ".letsencrypt/" + acmeHost
    97  }
    98  
    99  func (v *vaultStorage) certPath(name string) string {
   100  	return v.path + name
   101  }
   102  
   103  func (v *vaultStorage) GetAccount(acmeHost string) (*Account, error) {
   104  	path := v.registrationPath(acmeHost)
   105  	secret, err := v.client.Read(path)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	if secret == nil {
   110  		return nil, nil
   111  	}
   112  	acct := &Account{}
   113  	if dat, err := v.getString("registration", secret.Data, path); err != nil {
   114  		return nil, err
   115  	} else if err = json.Unmarshal(dat, acct); err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	if dat, err := v.getString("tls.key", secret.Data, path); err != nil {
   120  		return nil, err
   121  	} else if block, _ := pem.Decode(dat); block == nil {
   122  		return nil, fmt.Errorf("Error decoding account private key")
   123  	} else if key, err := x509.ParseECPrivateKey(block.Bytes); err != nil {
   124  		return nil, err
   125  	} else {
   126  		acct.key = key
   127  	}
   128  
   129  	return acct, nil
   130  }
   131  
   132  func (v *vaultStorage) StoreAccount(acmeHost string, account *Account) error {
   133  	acctBytes, err := json.MarshalIndent(account, "", "  ")
   134  	if err != nil {
   135  		return err
   136  	}
   137  	keyBytes, err := x509.MarshalECPrivateKey(account.key)
   138  	if err != nil {
   139  		return err
   140  	}
   141  	pemKey := &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}
   142  	pemBytes := pem.EncodeToMemory(pemKey)
   143  
   144  	_, err = v.client.Write(v.registrationPath(acmeHost), map[string]interface{}{
   145  		"registration": string(acctBytes),
   146  		"tls.key":      string(pemBytes),
   147  	})
   148  	return err
   149  }