forked from Caiyeon/goldfish
/
vault.go
154 lines (132 loc) · 3.42 KB
/
vault.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
152
153
154
package vault
import (
"errors"
"log"
"time"
"github.com/caiyeon/goldfish/config"
"github.com/hashicorp/vault/api"
)
type AuthInfo struct {
Type string `json:"Type" form:"Type" query:"Type"`
ID string `json:"ID" form:"ID" query:"ID"`
Pass string `json:"password" form:"Password" query:"Password"`
}
var (
vaultConfig config.VaultConfig
vaultToken string
errorChannel = make(chan error, 1)
)
func Bootstrapped() bool {
return vaultToken != ""
}
func SetConfig(c *config.VaultConfig) {
vaultConfig = *c
}
func NewVaultClient() (*api.Client, error) {
config := api.DefaultConfig()
err := config.ConfigureTLS(
&api.TLSConfig{
Insecure: vaultConfig.Tls_skip_verify,
},
)
if err != nil {
return nil, err
}
client, err := api.NewClient(config)
if err != nil {
return nil, err
}
client.SetAddress(vaultConfig.Address)
client.SetToken("")
return client, nil
}
func NewGoldfishVaultClient() (client *api.Client, err error) {
if client, err = NewVaultClient(); err == nil {
client.SetToken(vaultToken)
}
return client, err
}
func StartGoldfishWrapper(wrappingToken string) error {
if wrappingToken == "" {
return errors.New("Token must be provided in non-dev mode")
}
client, err := NewVaultClient()
if err != nil {
return err
}
// make a raw unwrap call. This will use the token as a header
client.SetToken(wrappingToken)
resp, err := client.Logical().Unwrap("")
if err != nil {
return errors.New("Failed to unwrap provided token, revoke it if possible\nReason:" + err.Error())
}
if resp == nil {
return errors.New("Vault response was nil. Please revoke token.\n" +
"If your vault cert is self-signed, you'll need to enable tls_skip_verify in goldfish config.")
}
// verify that a secret_id was wrapped
var secretID string
err = errors.New("Could not find secret_id in wrapped token. Was it wrapped properly?")
if raw, ok := resp.Data["secret_id"]; ok {
if secretID, ok = raw.(string); ok {
err = nil
}
}
if err != nil {
return err
}
// fetch vault token with secret_id
resp, err = client.Logical().Write(vaultConfig.Approle_login,
map[string]interface{}{
"role_id": vaultConfig.Approle_id,
"secret_id": secretID,
})
if err != nil {
return err
}
// verify that the secret_id is valid
vaultToken = resp.Auth.ClientToken
client.SetToken(resp.Auth.ClientToken)
if _, err := client.Auth().Token().LookupSelf(); err != nil {
return err
}
// verify that the client token is renewable
if err := renewServerToken(); err != nil {
return err
}
// errors that are not catastrophic can be logged here
go func() {
for err := range errorChannel {
if err != nil {
log.Println("[ERROR]: ", err.Error())
}
}
}()
log.Println("[INFO ]: Server token accessor:", resp.Auth.Accessor)
// start goroutines for loading config and renewing token
if err := LoadRuntimeConfig(vaultConfig.Runtime_config); err != nil {
return err
}
return nil
}
func LoadRuntimeConfig(configPath string) error {
// load config once to ensure validity
if err := loadConfigFromVault(configPath); err != nil {
return err
}
go loadConfigEvery(time.Minute, configPath)
go renewServerTokenEvery(time.Hour)
return nil
}
func loadConfigEvery(interval time.Duration, configPath string) {
for {
time.Sleep(interval)
errorChannel <- loadConfigFromVault(configPath)
}
}
func renewServerTokenEvery(interval time.Duration) {
for {
time.Sleep(interval)
errorChannel <- renewServerToken()
}
}