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