Skip to content

Commit 6a66795

Browse files
author
Mr Goran
committed
DK-1665 read write to vault path
1 parent 7931e76 commit 6a66795

File tree

11 files changed

+610
-51
lines changed

11 files changed

+610
-51
lines changed

common/vault/cache.go renamed to common/cache.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package vault
1+
package common
22

33
type Cache struct {
44
cache map[string]*Vault

common/db.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package common
2+
3+
import "github.com/doodlescheduling/kubedb/api/v1beta1"
4+
5+
type DatabaseCredentials []DatabaseCredential
6+
type DatabaseCredential struct {
7+
UserName string `json:"username"`
8+
Vault v1beta1.Vault `json:"vault"`
9+
}

common/vault.go

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
package common
2+
3+
import (
4+
"context"
5+
"errors"
6+
"github.com/doodlescheduling/kubedb/api/v1beta1"
7+
"github.com/doodlescheduling/kubedb/common/vault"
8+
"github.com/doodlescheduling/kubedb/common/vault/kubernetes"
9+
"github.com/go-logr/logr"
10+
"github.com/rs/xid"
11+
"os"
12+
13+
vaultapi "github.com/hashicorp/vault/api"
14+
)
15+
16+
type VaultRequest struct {
17+
Path string
18+
UserField string
19+
SecretField string
20+
}
21+
22+
type VaultResponse struct {
23+
User string
24+
Secret string
25+
}
26+
27+
type Vault struct {
28+
Host string
29+
}
30+
31+
func NewVault(host string) (*Vault, error) {
32+
// TODO implement Vault integration
33+
return &Vault{
34+
Host: host,
35+
}, nil
36+
}
37+
38+
func (v *Vault) Get(cred *DatabaseCredential, databaseName string, logger logr.Logger) (VaultResponse, error) {
39+
40+
// goran vault
41+
//h, err := FromCredential(binding, logger)
42+
h, err := FromCredential(cred, logger)
43+
44+
r := VaultRequest{
45+
Path: cred.Vault.Path,
46+
UserField: cred.Vault.UserField,
47+
SecretField: cred.Vault.SecretField,
48+
}
49+
50+
data, response, err := processRequest(h, r, databaseName)
51+
52+
if err != nil {
53+
return response, err
54+
}
55+
56+
return VaultResponse{
57+
User: data[r.UserField].(string),
58+
Secret: data[r.SecretField].(string),
59+
}, err
60+
}
61+
62+
func processRequest(h *VaultHandler, r VaultRequest, databaseName string) (map[string]interface{}, VaultResponse, error) {
63+
data, err := h.Read(r.Path)
64+
if err != nil {
65+
return nil, VaultResponse{}, err
66+
}
67+
var rewrite = false
68+
_, existingField := data[r.UserField]
69+
if !existingField {
70+
data[r.UserField] = databaseName
71+
rewrite = true
72+
}
73+
_, existingField = data[r.SecretField]
74+
if !existingField {
75+
data[r.SecretField] = xid.New().String()
76+
rewrite = true
77+
}
78+
if rewrite {
79+
_, err = h.c.Logical().Write(r.Path, data)
80+
if err != nil {
81+
return nil, VaultResponse{}, err
82+
}
83+
}
84+
return data, VaultResponse{}, nil
85+
}
86+
87+
// part from k8svault controller
88+
89+
// Common errors
90+
var (
91+
ErrVaultAddrNotFound = errors.New("Neither vault address nor a default vault address found")
92+
ErrK8sSecretFieldNotAvailable = errors.New("K8s secret field to be mapped does not exist")
93+
ErrUnsupportedAuthType = errors.New("Unsupported vault authentication")
94+
ErrVaultConfig = errors.New("Failed to setup default vault configuration")
95+
)
96+
97+
func ConvertPostgreSQLDatabaseCredential(cred v1beta1.PostgreSQLDatabaseCredential) *DatabaseCredential {
98+
return &DatabaseCredential{
99+
UserName: cred.UserName,
100+
Vault: cred.Vault,
101+
}
102+
}
103+
104+
func ConvertMongoDBDatabaseCredential(cred v1beta1.MongoDBDatabaseCredential) *DatabaseCredential {
105+
return &DatabaseCredential{
106+
UserName: cred.UserName,
107+
Vault: cred.Vault,
108+
}
109+
}
110+
111+
// Setup vault client & authentication from binding
112+
func setupAuth(h *VaultHandler) error {
113+
auth := vault.NewAuthHandler(&vault.AuthHandlerConfig{
114+
Logger: h.logger,
115+
Client: h.c,
116+
})
117+
118+
var method vault.AuthMethod
119+
120+
m, err := authKubernetes(h)
121+
if err != nil {
122+
return err
123+
}
124+
125+
method = m
126+
127+
if err := auth.Authenticate(context.TODO(), method); err != nil {
128+
return err
129+
}
130+
131+
return nil
132+
}
133+
134+
// Wrapper around vault kubernetes auth (taken from vault agent)
135+
// Injects env variables if not set on the binding
136+
func authKubernetes(h *VaultHandler) (vault.AuthMethod, error) {
137+
role := os.Getenv("VAULT_ROLE")
138+
tokenPath := os.Getenv("VAULT_TOKEN_PATH")
139+
140+
return kubernetes.NewKubernetesAuthMethod(&vault.AuthConfig{
141+
Logger: h.logger,
142+
MountPath: "/auth/kubernetes",
143+
Config: map[string]interface{}{
144+
"role": role,
145+
"token_path": tokenPath,
146+
},
147+
})
148+
}
149+
150+
func convertTLSSpec(spec v1beta1.VaultTLSSpec) *vaultapi.TLSConfig {
151+
return &vaultapi.TLSConfig{
152+
CACert: spec.CACert,
153+
ClientCert: spec.ClientCert,
154+
ClientKey: spec.ClientKey,
155+
TLSServerName: spec.ServerName,
156+
Insecure: spec.Insecure,
157+
}
158+
}
159+
160+
// FromCredential creates a vault client handler
161+
// If the binding holds no vault address it will fallback to the env VAULT_ADDRESS
162+
func FromCredential(credential *DatabaseCredential, logger logr.Logger) (*VaultHandler, error) {
163+
cfg := vaultapi.DefaultConfig()
164+
165+
if cfg == nil {
166+
return nil, ErrVaultConfig
167+
}
168+
169+
if credential.Vault.Host != "" {
170+
cfg.Address = credential.Vault.Host
171+
}
172+
173+
client, err := vaultapi.NewClient(cfg)
174+
if err != nil {
175+
return nil, err
176+
}
177+
178+
h := &VaultHandler{
179+
cfg: cfg,
180+
c: client,
181+
logger: logger,
182+
}
183+
184+
logger.Info("setup vault client", "vault", cfg.Address)
185+
186+
if err = setupAuth(h); err != nil {
187+
return nil, err
188+
}
189+
190+
return h, nil
191+
}
192+
193+
// VaultHandler
194+
type VaultHandler struct {
195+
c *vaultapi.Client
196+
cfg *vaultapi.Config
197+
auth *vault.AuthHandler
198+
logger logr.Logger
199+
}
200+
201+
// ApplySecret applies the desired secret to vault
202+
//func (h *VaultHandler) ApplySecret(binding *v1beta1.VaultBinding, secret *corev1.Secret) (bool, error) {
203+
// var writeBack bool
204+
//
205+
// // TODO Is there such a thing as locking the path so we don't overwrite fields which would be changed at the same time?
206+
// data, err := h.Read(binding.Spec.Path)
207+
// if err != nil {
208+
// return writeBack, err
209+
// }
210+
//
211+
// // Loop through all mapping field and apply to the vault path data
212+
// for _, field := range binding.Spec.Fields {
213+
// k8sField := field.Name
214+
// vaultField := k8sField
215+
// if field.Rename != "" {
216+
// vaultField = field.Rename
217+
// }
218+
//
219+
// h.logger.Info("applying k8s field to vault", "k8sField", k8sField, "vaultField", vaultField, "vaultPath", binding.Spec.Path)
220+
//
221+
// // If k8s secret field does not exists return an error
222+
// k8sValue, ok := secret.Data[k8sField]
223+
// if !ok {
224+
// return writeBack, ErrK8sSecretFieldNotAvailable
225+
// }
226+
//
227+
// secret := string(k8sValue)
228+
//
229+
// _, existingField := data[vaultField]
230+
//
231+
// switch {
232+
// case !existingField:
233+
// h.logger.Info("found new field to write", "vaultField", vaultField)
234+
// data[vaultField] = secret
235+
// writeBack = true
236+
// case data[vaultField] == secret:
237+
// h.logger.Info("skipping field, no update required", "vaultField", vaultField)
238+
// case binding.Spec.ForceApply == true:
239+
// data[vaultField] = secret
240+
// writeBack = true
241+
// default:
242+
// h.logger.Info("skipping field, it already exists in vault and force apply is disabled", "vaultField", vaultField)
243+
// }
244+
// }
245+
//
246+
// if writeBack == true {
247+
// // Finally write the secret back
248+
// _, err = h.c.Logical().Write(binding.Spec.Path, data)
249+
// if err != nil {
250+
// return writeBack, err
251+
// }
252+
// }
253+
//
254+
// return writeBack, nil
255+
//}
256+
257+
// Read vault path and return data map
258+
// Return empty map if no data exists
259+
func (h *VaultHandler) Read(path string) (map[string]interface{}, error) {
260+
s, err := h.c.Logical().Read(path)
261+
if err != nil {
262+
return nil, err
263+
}
264+
265+
// Return empty map if no data exists
266+
if s == nil || s.Data == nil {
267+
return make(map[string]interface{}), nil
268+
}
269+
270+
return s.Data, nil
271+
}

0 commit comments

Comments
 (0)