github.com/hexonet/dnscontrol@v0.2.8/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/xenolf/lego/acme"
    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) (*acme.CertificateResource, 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 := &acme.CertificateResource{}
    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 *acme.CertificateResource) error {
    79  	jDat, err := json.MarshalIndent(cert, "", "  ")
    80  	if err != nil {
    81  		return err
    82  	}
    83  	data := map[string]interface{}{
    84  		"tls.cert": string(cert.Certificate),
    85  		"tls.key":  string(cert.PrivateKey),
    86  		"meta":     string(jDat),
    87  	}
    88  	_, err = v.client.Write(v.certPath(name), data)
    89  	return err
    90  }
    91  
    92  func (v *vaultStorage) registrationPath(acmeHost string) string {
    93  	return v.path + ".letsencrypt/" + acmeHost
    94  }
    95  
    96  func (v *vaultStorage) certPath(name string) string {
    97  	return v.path + name
    98  }
    99  
   100  func (v *vaultStorage) GetAccount(acmeHost string) (*Account, error) {
   101  	path := v.registrationPath(acmeHost)
   102  	secret, err := v.client.Read(path)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  	if secret == nil {
   107  		return nil, nil
   108  	}
   109  	acct := &Account{}
   110  	if dat, err := v.getString("registration", secret.Data, path); err != nil {
   111  		return nil, err
   112  	} else if err = json.Unmarshal(dat, acct); err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	if dat, err := v.getString("tls.key", secret.Data, path); err != nil {
   117  		return nil, err
   118  	} else if block, _ := pem.Decode(dat); block == nil {
   119  		return nil, fmt.Errorf("Error decoding account private key")
   120  	} else if key, err := x509.ParseECPrivateKey(block.Bytes); err != nil {
   121  		return nil, err
   122  	} else {
   123  		acct.key = key
   124  	}
   125  
   126  	return acct, nil
   127  }
   128  
   129  func (v *vaultStorage) StoreAccount(acmeHost string, account *Account) error {
   130  	acctBytes, err := json.MarshalIndent(account, "", "  ")
   131  	if err != nil {
   132  		return err
   133  	}
   134  	keyBytes, err := x509.MarshalECPrivateKey(account.key)
   135  	if err != nil {
   136  		return err
   137  	}
   138  	pemKey := &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}
   139  	pemBytes := pem.EncodeToMemory(pemKey)
   140  
   141  	_, err = v.client.Write(v.registrationPath(acmeHost), map[string]interface{}{
   142  		"registration": string(acctBytes),
   143  		"tls.key":      string(pemBytes),
   144  	})
   145  	return err
   146  }