-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfig.go
More file actions
250 lines (219 loc) · 8.05 KB
/
config.go
File metadata and controls
250 lines (219 loc) · 8.05 KB
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
package vault
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
)
type ProviderType string
const (
ProviderTypeAES256 ProviderType = "aes256"
ProviderTypeAge ProviderType = "age"
ProviderTypeExternal ProviderType = "external"
ProviderTypeKeyring ProviderType = "keyring"
ProviderTypeUnencrypted ProviderType = "unencrypted"
)
type Config struct {
ID string `json:"id"`
Type ProviderType `json:"type"`
Age *AgeConfig `json:"age,omitempty"`
Aes *AesConfig `json:"aes,omitempty"`
External *ExternalConfig `json:"external,omitempty"`
Keyring *KeyringConfig `json:"keyring,omitempty"`
Unencrypted *UnencryptedConfig `json:"unencrypted,omitempty"`
}
func (c *Config) Validate() error {
if c.ID == "" {
return fmt.Errorf("%w: vault ID is required", ErrInvalidConfig)
}
switch c.Type {
case ProviderTypeAge:
if c.Age == nil {
return fmt.Errorf("%w: age configuration required for the age vault provider", ErrInvalidConfig)
}
return c.Age.Validate()
case ProviderTypeAES256:
if c.Aes == nil {
return fmt.Errorf("%w: aes configuration required for the aes256 vault provider", ErrInvalidConfig)
}
return c.Aes.Validate()
case ProviderTypeExternal:
if c.External == nil {
return fmt.Errorf("%w: external configuration required for external vault", ErrInvalidConfig)
}
return c.External.Validate()
case ProviderTypeKeyring:
if c.Keyring == nil {
return fmt.Errorf("%w: keyring configuration required for keyring vault provider", ErrInvalidConfig)
}
return c.Keyring.Validate()
case ProviderTypeUnencrypted:
if c.Unencrypted == nil {
return fmt.Errorf("%w: unencrypted configuration required for unencrypted vault provider", ErrInvalidConfig)
}
return c.Unencrypted.Validate()
default:
return fmt.Errorf("%w: unsupported vault type: %s", ErrInvalidConfig, c.Type)
}
}
// SaveConfigJSON saves the vault configuration to a file in JSON format
func SaveConfigJSON(config Config, path string) error {
data, err := json.MarshalIndent(config, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal config: %w", err)
}
if err := os.MkdirAll(filepath.Dir(path), 0750); err != nil {
return fmt.Errorf("failed to create config directory: %w", err)
}
if err := os.WriteFile(filepath.Clean(path), data, 0600); err != nil {
return fmt.Errorf("failed to write config file: %w", err)
}
return nil
}
// LoadConfigJSON loads the vault configuration from a file in JSON format
func LoadConfigJSON(path string) (Config, error) {
data, err := os.ReadFile(filepath.Clean(path))
if err != nil {
return Config{}, fmt.Errorf("failed to read config file: %w", err)
}
var config Config
if err := json.Unmarshal(data, &config); err != nil {
return Config{}, fmt.Errorf("failed to unmarshal config: %w", err)
}
return config, nil
}
// IdentitySource represents a source for the local vault identity keys
type IdentitySource struct {
// Type of identity source
// Must be one of: "env", "file"
Type string `json:"type"`
// Path to the identity file (for "file" type)
Path string `json:"fullPath,omitempty"`
// Environment variable name (for "env" type)
Name string `json:"name,omitempty"`
}
// AgeConfig contains local (age-based) vault configuration
type AgeConfig struct {
// Storage location for the vault file
StoragePath string `json:"storage_path"`
// Identity sources for decryption (in order of preference)
IdentitySources []IdentitySource `json:"identity_sources,omitempty"`
// Recipients who can decrypt secrets
Recipients []string `json:"recipients,omitempty"`
}
func (c *AgeConfig) Validate() error {
if c.StoragePath == "" {
return fmt.Errorf("%w: storage path is required for age vault", ErrInvalidConfig)
}
if len(c.IdentitySources) == 0 {
return fmt.Errorf("%w: at least one identity source is required for age vault", ErrInvalidConfig)
}
for _, source := range c.IdentitySources {
if source.Type != envSource && source.Type != fileSource {
return fmt.Errorf("%w: invalid identity source type: %s", ErrInvalidConfig, source.Type)
}
if source.Type == fileSource && source.Path == "" {
return fmt.Errorf("%w: path is required for file identity source", ErrInvalidConfig)
}
if source.Type == envSource && source.Name == "" {
return fmt.Errorf("%w: name is required for env identity source", ErrInvalidConfig)
}
}
return nil
}
// KeySource represents a source for the local vault encryption keys
type KeySource struct {
// Type of data encryption key source
// Must be one of: "env", "file"
Type string `json:"type"`
// Path to the identity file (for "file" type)
Path string `json:"fullPath,omitempty"`
// Environment variable name (for "env" type)
Name string `json:"name,omitempty"`
}
// AesConfig contains local (AES256-based) vault configuration
type AesConfig struct {
// Storage location for the vault file
StoragePath string `json:"storage_path"`
// DEK sources for decryption (in order of preference)
KeySource []KeySource `json:"key_sources,omitempty"`
}
func (c *AesConfig) Validate() error {
if c.StoragePath == "" {
return fmt.Errorf("%w: storage path is required for AES vault", ErrInvalidConfig)
}
if len(c.KeySource) == 0 {
return fmt.Errorf("%w: at least one key source is required for AES vault", ErrInvalidConfig)
}
for _, source := range c.KeySource {
if source.Type != envSource && source.Type != fileSource {
return fmt.Errorf("%w: invalid key source type: %s", ErrInvalidConfig, source.Type)
}
if source.Type == fileSource && source.Path == "" {
return fmt.Errorf("%w: path is required for file key source", ErrInvalidConfig)
}
if source.Type == envSource && source.Name == "" {
return fmt.Errorf("%w: name is required for env key source", ErrInvalidConfig)
}
}
return nil
}
// CommandConfig represents a command template to be executed with its arguments
type CommandConfig struct {
// CommandTemplate for building command arguments
CommandTemplate string `json:"cmd"`
// OutputTemplate for parsing command output
OutputTemplate string `json:"output,omitempty"`
// InputTemplate for providing input to the command
InputTemplate string `json:"input,omitempty"`
}
// ExternalConfig contains external (cli command-based) vault configuration
type ExternalConfig struct {
// Get CommandConfig for the get operation
Get CommandConfig `json:"get,omitempty"`
// Set CommandConfig for the set operation
Set CommandConfig `json:"set,omitempty"`
// Delete CommandConfig for the delete operation
Delete CommandConfig `json:"delete,omitempty"`
// List CommandConfig for the list operation
List CommandConfig `json:"list,omitempty"`
ListSeparator string `json:"separator,omitempty"`
// Exists CommandConfig for the exists operation
Exists CommandConfig `json:"exists,omitempty"`
// Metadata CommandConfig for the metadata operation
Metadata CommandConfig `json:"metadata,omitempty"`
// Environment variables for commands
Environment map[string]string `json:"environment,omitempty"`
// Timeout duration string for command execution
Timeout string `json:"timeout,omitempty"`
// WorkingDir for command execution
WorkingDir string `json:"working_dir,omitempty"`
}
func (c *ExternalConfig) Validate() error {
if c.Get.CommandTemplate == "" || c.Set.CommandTemplate == "" {
return fmt.Errorf("%w: get and set args template required for external vault", ErrInvalidConfig)
}
return nil
}
// UnencryptedConfig contains unencrypted (plain text) vault configuration
type UnencryptedConfig struct {
// Storage location for the vault file
StoragePath string `json:"storage_path"`
}
func (c *UnencryptedConfig) Validate() error {
if c.StoragePath == "" {
return fmt.Errorf("%w: storage path is required for unencrypted vault", ErrInvalidConfig)
}
return nil
}
// KeyringConfig contains keyring vault configuration
type KeyringConfig struct {
// Service name used for keyring operations
Service string `json:"service"`
}
func (c *KeyringConfig) Validate() error {
if c.Service == "" {
return fmt.Errorf("%w: service name is required for keyring vault", ErrInvalidConfig)
}
return nil
}