File: vault.go

package info (click to toggle)
gitlab-ci-multi-runner 14.10.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 31,248 kB
  • sloc: sh: 1,694; makefile: 384; asm: 79; ruby: 68
file content (157 lines) | stat: -rw-r--r-- 3,823 bytes parent folder | download
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
155
156
157
package service

import (
	"fmt"

	"gitlab.com/gitlab-org/gitlab-runner/helpers/vault"
	"gitlab.com/gitlab-org/gitlab-runner/helpers/vault/auth_methods"
	_ "gitlab.com/gitlab-org/gitlab-runner/helpers/vault/auth_methods/jwt" // register auth method
	"gitlab.com/gitlab-org/gitlab-runner/helpers/vault/secret_engines"
	_ "gitlab.com/gitlab-org/gitlab-runner/helpers/vault/secret_engines/kv_v1" // register secret engine
	_ "gitlab.com/gitlab-org/gitlab-runner/helpers/vault/secret_engines/kv_v2" // register secret engine
)

type Auth interface {
	AuthName() string
	AuthPath() string
	AuthData() auth_methods.Data
}

type Engine interface {
	EngineName() string
	EnginePath() string
}

type Secret interface {
	SecretPath() string
	SecretField() string
}

type Vault interface {
	GetField(engineDetails Engine, secretDetails Secret) (interface{}, error)
	Put(engineDetails Engine, secretDetails Secret, data map[string]interface{}) error
	Delete(engineDetails Engine, secretDetails Secret) error
}

type defaultVault struct {
	client vault.Client
}

var newVaultClient = vault.NewClient

func NewVault(url string, namespace string, auth Auth) (Vault, error) {
	v := new(defaultVault)

	err := v.initialize(url, namespace, auth)
	if err != nil {
		return nil, fmt.Errorf("initializing Vault service: %w", err)
	}

	return v, nil
}

func (v *defaultVault) initialize(url string, namespace string, auth Auth) error {
	err := v.prepareAuthenticatedClient(url, namespace, auth)
	if err != nil {
		return fmt.Errorf("preparing authenticated client: %w", err)
	}

	return nil
}

func (v *defaultVault) prepareAuthenticatedClient(url string, namespace string, authDetails Auth) error {
	client, err := newVaultClient(url, namespace)
	if err != nil {
		return err
	}

	auth, err := v.prepareAuthMethodAdapter(authDetails)
	if err != nil {
		return err
	}

	err = client.Authenticate(auth)
	if err != nil {
		return err
	}

	v.client = client

	return nil
}

func (v *defaultVault) prepareAuthMethodAdapter(authDetails Auth) (vault.AuthMethod, error) {
	authFactory, err := auth_methods.GetFactory(authDetails.AuthName())
	if err != nil {
		return nil, fmt.Errorf("initializing auth method factory: %w", err)
	}

	auth, err := authFactory(authDetails.AuthPath(), authDetails.AuthData())
	if err != nil {
		return nil, fmt.Errorf("initializing auth method adapter: %w", err)
	}

	return auth, nil
}

func (v *defaultVault) GetField(engineDetails Engine, secretDetails Secret) (interface{}, error) {
	engine, err := v.getSecretEngine(engineDetails)
	if err != nil {
		return nil, err
	}

	secret, err := engine.Get(secretDetails.SecretPath())
	if err != nil {
		return nil, fmt.Errorf("reading secret: %w", err)
	}

	field := secretDetails.SecretField()
	for key, data := range secret {
		if key != field {
			continue
		}

		return data, nil
	}

	return nil, nil
}

func (v *defaultVault) getSecretEngine(engineDetails Engine) (vault.SecretEngine, error) {
	engineFactory, err := secret_engines.GetFactory(engineDetails.EngineName())
	if err != nil {
		return nil, fmt.Errorf("requesting SecretEngine factory: %w", err)
	}

	engine := engineFactory(v.client, engineDetails.EnginePath())

	return engine, nil
}

func (v *defaultVault) Put(engineDetails Engine, secretDetails Secret, data map[string]interface{}) error {
	engine, err := v.getSecretEngine(engineDetails)
	if err != nil {
		return err
	}

	err = engine.Put(secretDetails.SecretPath(), data)
	if err != nil {
		return fmt.Errorf("writing secret: %w", err)
	}

	return nil
}

func (v *defaultVault) Delete(engineDetails Engine, secretDetails Secret) error {
	engine, err := v.getSecretEngine(engineDetails)
	if err != nil {
		return err
	}

	err = engine.Delete(secretDetails.SecretPath())
	if err != nil {
		return fmt.Errorf("deleting secret: %w", err)
	}

	return nil
}