diff options
Diffstat (limited to 'secrets')
-rw-r--r-- | secrets/client.go | 14 | ||||
-rw-r--r-- | secrets/config_file_client.go | 157 | ||||
-rw-r--r-- | secrets/config_file_client_test.go | 122 | ||||
-rw-r--r-- | secrets/go.mod | 5 | ||||
-rw-r--r-- | secrets/go.sum | 14 |
5 files changed, 302 insertions, 10 deletions
diff --git a/secrets/client.go b/secrets/client.go index 779f28c..aaa45d7 100644 --- a/secrets/client.go +++ b/secrets/client.go | |||
@@ -25,22 +25,22 @@ type Renewal struct { | |||
25 | } | 25 | } |
26 | 26 | ||
27 | type Credential struct { | 27 | type Credential struct { |
28 | Username string `json:"username" mapstructure:"username"` | 28 | Username string `json:"username" mapstructure:"username" yaml:"username"` |
29 | Password string `json:"password" mapstructure:"password"` | 29 | Password string `json:"password" mapstructure:"password" yaml:"password"` |
30 | } | 30 | } |
31 | 31 | ||
32 | type ApiKey struct { | 32 | type ApiKey struct { |
33 | Key string `json:"key" mapstructure:"key"` | 33 | Key string `json:"key" mapstructure:"key" yaml:"key"` |
34 | } | 34 | } |
35 | 35 | ||
36 | type AWSCredential struct { | 36 | type AWSCredential struct { |
37 | AccessKeyId string `json:"access_key" mapstructure:"access_key"` | 37 | AccessKeyId string `json:"access_key" mapstructure:"access_key" yaml:"access_key"` |
38 | SecretAccessKey string `json:"secret_key" mapstructure:"secret_key"` | 38 | SecretAccessKey string `json:"secret_key" mapstructure:"secret_key" yaml:"secret_key"` |
39 | SessionToken string `json:"security_token" mapstructure:"security_token"` | 39 | SessionToken string `json:"security_token" mapstructure:"security_token" yaml:"security_token"` |
40 | } | 40 | } |
41 | 41 | ||
42 | type RSAKey struct { | 42 | type RSAKey struct { |
43 | Key string `json:"key" mapstructure:"key"` | 43 | Key string `json:"key" mapstructure:"key" yaml:"key"` |
44 | } | 44 | } |
45 | 45 | ||
46 | func (k *RSAKey) RSAPrivateKey() (*rsa.PrivateKey, error) { | 46 | func (k *RSAKey) RSAPrivateKey() (*rsa.PrivateKey, error) { |
diff --git a/secrets/config_file_client.go b/secrets/config_file_client.go new file mode 100644 index 0000000..ef2907e --- /dev/null +++ b/secrets/config_file_client.go | |||
@@ -0,0 +1,157 @@ | |||
1 | package secrets | ||
2 | |||
3 | import ( | ||
4 | "context" | ||
5 | "encoding/json" | ||
6 | "fmt" | ||
7 | "io/fs" | ||
8 | "path/filepath" | ||
9 | "sync" | ||
10 | "time" | ||
11 | |||
12 | "github.com/mitchellh/mapstructure" | ||
13 | "gopkg.in/yaml.v2" | ||
14 | ) | ||
15 | |||
16 | type ConfigFileHandle struct{} | ||
17 | |||
18 | func (h *ConfigFileHandle) Reference() string { return "NOOP" } | ||
19 | |||
20 | var _ Handle = (*ConfigFileHandle)(nil) | ||
21 | |||
22 | // ConfigFileClient returns secrets from a JSON or YAML configuration | ||
23 | // file. This mode isn't as secure as using Vault or some other secret | ||
24 | // management service but can be useful for users who don't have access | ||
25 | // to such a service. | ||
26 | // | ||
27 | // Writes to this secret client will silently succeed while doing | ||
28 | // nothing. | ||
29 | type ConfigFileClient struct { | ||
30 | c chan Renewal | ||
31 | cfg map[string]any | ||
32 | } | ||
33 | |||
34 | var _ Client = (*ConfigFileClient)(nil) | ||
35 | var _ ClientManager = (*ConfigFileClient)(nil) | ||
36 | |||
37 | // NewConfigFileClient creates a new ConfigFileClient by loading a named | ||
38 | // config file from a filesystem and unmarshalling it. The config file | ||
39 | // can be in JSON or YAML format, determined by a .json, .yaml, or .yml | ||
40 | // extension. The configuration must be nested within a key in that file | ||
41 | // to support sharing the file with other subsystems. | ||
42 | // | ||
43 | // Credentials should be stored in the config file in a format that | ||
44 | // matches their definitions in client.go | ||
45 | func NewConfigFileClient(filesystem fs.FS, name, key string) (ClientManager, error) { | ||
46 | fp, err := filesystem.Open(name) | ||
47 | if err != nil { | ||
48 | return nil, err | ||
49 | } | ||
50 | defer fp.Close() | ||
51 | |||
52 | fc := map[string]any{} | ||
53 | |||
54 | switch e := filepath.Ext(name); e { | ||
55 | case ".yaml", ".yml": | ||
56 | if err := yaml.NewDecoder(fp).Decode(&fc); err != nil { | ||
57 | return nil, err | ||
58 | } | ||
59 | case ".json": | ||
60 | if err := json.NewDecoder(fp).Decode(&fc); err != nil { | ||
61 | return nil, err | ||
62 | } | ||
63 | default: | ||
64 | return nil, fmt.Errorf("Config files with extension %s are not supported", e) | ||
65 | } | ||
66 | |||
67 | ck, ok := fc[key] | ||
68 | if !ok { | ||
69 | return nil, fmt.Errorf("Key %s does not exist in config file", key) | ||
70 | } | ||
71 | |||
72 | cfg := map[string]any{} | ||
73 | if err := mapstructure.Decode(ck, &cfg); err != nil { | ||
74 | return nil, fmt.Errorf("Config file key was not of correct type: %w", err) | ||
75 | } | ||
76 | |||
77 | return &ConfigFileClient{ | ||
78 | c: make(chan Renewal), | ||
79 | cfg: cfg, | ||
80 | }, nil | ||
81 | } | ||
82 | |||
83 | func (c *ConfigFileClient) DatabaseCredential(ctx context.Context, path string) (*Credential, Handle, error) { | ||
84 | out := Credential{} | ||
85 | hnd, err := c.RawSecret(ctx, path, &out) | ||
86 | if err != nil { | ||
87 | return nil, nil, err | ||
88 | } | ||
89 | return &out, hnd, nil | ||
90 | } | ||
91 | |||
92 | func (c *ConfigFileClient) Secret(ctx context.Context, path string, out any) (Handle, error) { | ||
93 | return c.RawSecret(ctx, path, out) | ||
94 | } | ||
95 | |||
96 | func (c *ConfigFileClient) RawSecret(ctx context.Context, path string, out any) (Handle, error) { | ||
97 | v, ok := c.cfg[path] | ||
98 | if !ok { | ||
99 | return nil, fmt.Errorf("Secret %s was not found in config file", path) | ||
100 | } | ||
101 | |||
102 | if err := mapstructure.Decode(v, &out); err != nil { | ||
103 | return nil, err | ||
104 | } | ||
105 | |||
106 | return &ConfigFileHandle{}, nil | ||
107 | } | ||
108 | |||
109 | func (c *ConfigFileClient) AWSIAMUser(ctx context.Context, name string) (*AWSCredential, Handle, error) { | ||
110 | out := AWSCredential{} | ||
111 | hnd, err := c.RawSecret(ctx, name, &out) | ||
112 | if err != nil { | ||
113 | return nil, nil, err | ||
114 | } | ||
115 | return &out, hnd, nil | ||
116 | } | ||
117 | |||
118 | func (c *ConfigFileClient) AWSAssumeRoleSimple(ctx context.Context, name string) (*AWSCredential, Handle, error) { | ||
119 | return c.AWSAssumeRole(ctx, name, "", time.Second) | ||
120 | } | ||
121 | |||
122 | func (c *ConfigFileClient) AWSAssumeRole(ctx context.Context, name string, sessionName string, ttl time.Duration) (*AWSCredential, Handle, error) { | ||
123 | return nil, nil, fmt.Errorf("Assuming AWS roles is not supported by ConfigFileClient") | ||
124 | } | ||
125 | |||
126 | func (c *ConfigFileClient) WriteSecret(ctx context.Context, path string, in any) error { | ||
127 | return nil | ||
128 | } | ||
129 | |||
130 | func (c *ConfigFileClient) Destroy(h Handle) error { | ||
131 | return nil | ||
132 | } | ||
133 | |||
134 | func (c *ConfigFileClient) MakeNonCritical(h Handle) error { | ||
135 | return nil | ||
136 | } | ||
137 | |||
138 | func (c *ConfigFileClient) Authenticate(ctx context.Context) error { | ||
139 | return nil | ||
140 | } | ||
141 | |||
142 | func (c *ConfigFileClient) Notifications() <-chan Renewal { | ||
143 | return c.c | ||
144 | } | ||
145 | |||
146 | func (c *ConfigFileClient) Run(ctx context.Context, wg *sync.WaitGroup) error { | ||
147 | wg.Add(1) | ||
148 | defer wg.Done() | ||
149 | |||
150 | for { | ||
151 | select { | ||
152 | case <-ctx.Done(): | ||
153 | close(c.c) | ||
154 | return nil | ||
155 | } | ||
156 | } | ||
157 | } | ||
diff --git a/secrets/config_file_client_test.go b/secrets/config_file_client_test.go new file mode 100644 index 0000000..e4ba448 --- /dev/null +++ b/secrets/config_file_client_test.go | |||
@@ -0,0 +1,122 @@ | |||
1 | package secrets | ||
2 | |||
3 | import ( | ||
4 | "context" | ||
5 | "testing" | ||
6 | "testing/fstest" | ||
7 | "time" | ||
8 | |||
9 | "github.com/stretchr/testify/assert" | ||
10 | "github.com/stretchr/testify/suite" | ||
11 | ) | ||
12 | |||
13 | var testFs fstest.MapFS | ||
14 | |||
15 | var jsonTestConfig = []byte(`{ | ||
16 | "secrets": { | ||
17 | "database": { | ||
18 | "username": "dbuser", | ||
19 | "password": "dbpass" | ||
20 | }, | ||
21 | "iam": { | ||
22 | "access_key": "accesskey", | ||
23 | "secret_key": "secretkey" | ||
24 | }, | ||
25 | "apikey": { | ||
26 | "key": "api" | ||
27 | } | ||
28 | } | ||
29 | }`) | ||
30 | |||
31 | var yamlTestConfig = []byte(` | ||
32 | secrets: | ||
33 | database: | ||
34 | username: dbuser | ||
35 | password: dbpass | ||
36 | iam: | ||
37 | access_key: accesskey | ||
38 | secret_key: secretkey | ||
39 | apikey: | ||
40 | key: api | ||
41 | `) | ||
42 | |||
43 | func init() { | ||
44 | testFs = fstest.MapFS{} | ||
45 | testFs["secrets.foo"] = &fstest.MapFile{Data: jsonTestConfig} | ||
46 | testFs["secrets.json"] = &fstest.MapFile{Data: jsonTestConfig} | ||
47 | testFs["secrets.yaml"] = &fstest.MapFile{Data: yamlTestConfig} | ||
48 | testFs["secrets.yml"] = &fstest.MapFile{Data: yamlTestConfig} | ||
49 | } | ||
50 | |||
51 | type ConfigFileClientSuite struct { | ||
52 | suite.Suite | ||
53 | c Client | ||
54 | } | ||
55 | |||
56 | func (s *ConfigFileClientSuite) SetupTest() { | ||
57 | var err error | ||
58 | s.c, err = NewConfigFileClient(testFs, "secrets.yaml", "secrets") | ||
59 | assert.NoError(s.T(), err) | ||
60 | } | ||
61 | |||
62 | func (s *ConfigFileClientSuite) TestFromJSON() { | ||
63 | _, err := NewConfigFileClient(testFs, "secrets.json", "secrets") | ||
64 | assert.NoError(s.T(), err) | ||
65 | } | ||
66 | |||
67 | func (s *ConfigFileClientSuite) TestFromYAML() { | ||
68 | _, err := NewConfigFileClient(testFs, "secrets.yaml", "secrets") | ||
69 | assert.NoError(s.T(), err) | ||
70 | _, err = NewConfigFileClient(testFs, "secrets.yml", "secrets") | ||
71 | assert.NoError(s.T(), err) | ||
72 | } | ||
73 | |||
74 | func (s *ConfigFileClientSuite) TestFromInvalidExtension() { | ||
75 | _, err := NewConfigFileClient(testFs, "secrets.foo", "secrets") | ||
76 | assert.ErrorContains(s.T(), err, "extension .foo are not supported") | ||
77 | } | ||
78 | |||
79 | func (s *ConfigFileClientSuite) TestFromInvalidFile() { | ||
80 | _, err := NewConfigFileClient(testFs, "secrets.foo-", "secrets") | ||
81 | assert.ErrorContains(s.T(), err, "file does not exist") | ||
82 | } | ||
83 | |||
84 | func (s *ConfigFileClientSuite) TestFromInvalidKey() { | ||
85 | _, err := NewConfigFileClient(testFs, "secrets.json", "sekrets") | ||
86 | assert.ErrorContains(s.T(), err, "Key sekrets does not exist") | ||
87 | } | ||
88 | |||
89 | func (s *ConfigFileClientSuite) TestDatabaseCredential() { | ||
90 | db, hnd, err := s.c.DatabaseCredential(context.TODO(), "database") | ||
91 | assert.NoError(s.T(), err) | ||
92 | assert.NotNil(s.T(), hnd) | ||
93 | assert.Equal(s.T(), db.Username, "dbuser") | ||
94 | assert.Equal(s.T(), db.Password, "dbpass") | ||
95 | } | ||
96 | |||
97 | func (s *ConfigFileClientSuite) TestSecret() { | ||
98 | key := ApiKey{} | ||
99 | hnd, err := s.c.Secret(context.TODO(), "apikey", &key) | ||
100 | assert.NoError(s.T(), err) | ||
101 | assert.NotNil(s.T(), hnd) | ||
102 | assert.Equal(s.T(), key.Key, "api") | ||
103 | } | ||
104 | |||
105 | func (s *ConfigFileClientSuite) TestAWSIAMUser() { | ||
106 | iam, hnd, err := s.c.AWSIAMUser(context.TODO(), "iam") | ||
107 | assert.NoError(s.T(), err) | ||
108 | assert.NotNil(s.T(), hnd) | ||
109 | assert.Equal(s.T(), iam.AccessKeyId, "accesskey") | ||
110 | assert.Equal(s.T(), iam.SecretAccessKey, "secretkey") | ||
111 | } | ||
112 | |||
113 | func (s *ConfigFileClientSuite) TestAWSAssumeRole() { | ||
114 | _, _, err := s.c.AWSAssumeRole(context.TODO(), "role", "name", time.Second) | ||
115 | assert.ErrorContains(s.T(), err, "not supported") | ||
116 | _, _, err = s.c.AWSAssumeRoleSimple(context.TODO(), "role") | ||
117 | assert.ErrorContains(s.T(), err, "not supported") | ||
118 | } | ||
119 | |||
120 | func TestConfigFileClientSuite(t *testing.T) { | ||
121 | suite.Run(t, &ConfigFileClientSuite{}) | ||
122 | } | ||
diff --git a/secrets/go.mod b/secrets/go.mod index ca23257..89a03d8 100644 --- a/secrets/go.mod +++ b/secrets/go.mod | |||
@@ -7,12 +7,15 @@ require ( | |||
7 | github.com/hashicorp/vault/api v1.8.0 | 7 | github.com/hashicorp/vault/api v1.8.0 |
8 | github.com/hashicorp/vault/api/auth/approle v0.3.0 | 8 | github.com/hashicorp/vault/api/auth/approle v0.3.0 |
9 | github.com/mitchellh/mapstructure v1.5.0 | 9 | github.com/mitchellh/mapstructure v1.5.0 |
10 | github.com/stretchr/testify v1.8.1 | ||
11 | gopkg.in/yaml.v2 v2.2.5 | ||
10 | ) | 12 | ) |
11 | 13 | ||
12 | require ( | 14 | require ( |
13 | github.com/armon/go-metrics v0.3.9 // indirect | 15 | github.com/armon/go-metrics v0.3.9 // indirect |
14 | github.com/armon/go-radix v1.0.0 // indirect | 16 | github.com/armon/go-radix v1.0.0 // indirect |
15 | github.com/cenkalti/backoff/v3 v3.0.0 // indirect | 17 | github.com/cenkalti/backoff/v3 v3.0.0 // indirect |
18 | github.com/davecgh/go-spew v1.1.1 // indirect | ||
16 | github.com/fatih/color v1.7.0 // indirect | 19 | github.com/fatih/color v1.7.0 // indirect |
17 | github.com/golang/protobuf v1.5.2 // indirect | 20 | github.com/golang/protobuf v1.5.2 // indirect |
18 | github.com/golang/snappy v0.0.4 // indirect | 21 | github.com/golang/snappy v0.0.4 // indirect |
@@ -42,6 +45,7 @@ require ( | |||
42 | github.com/mitchellh/reflectwalk v1.0.0 // indirect | 45 | github.com/mitchellh/reflectwalk v1.0.0 // indirect |
43 | github.com/oklog/run v1.0.0 // indirect | 46 | github.com/oklog/run v1.0.0 // indirect |
44 | github.com/pierrec/lz4 v2.5.2+incompatible // indirect | 47 | github.com/pierrec/lz4 v2.5.2+incompatible // indirect |
48 | github.com/pmezard/go-difflib v1.0.0 // indirect | ||
45 | github.com/ryanuber/go-glob v1.0.0 // indirect | 49 | github.com/ryanuber/go-glob v1.0.0 // indirect |
46 | go.uber.org/atomic v1.9.0 // indirect | 50 | go.uber.org/atomic v1.9.0 // indirect |
47 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect | 51 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect |
@@ -53,4 +57,5 @@ require ( | |||
53 | google.golang.org/grpc v1.41.0 // indirect | 57 | google.golang.org/grpc v1.41.0 // indirect |
54 | google.golang.org/protobuf v1.26.0 // indirect | 58 | google.golang.org/protobuf v1.26.0 // indirect |
55 | gopkg.in/square/go-jose.v2 v2.5.1 // indirect | 59 | gopkg.in/square/go-jose.v2 v2.5.1 // indirect |
60 | gopkg.in/yaml.v3 v3.0.1 // indirect | ||
56 | ) | 61 | ) |
diff --git a/secrets/go.sum b/secrets/go.sum index b27d5e2..46fa656 100644 --- a/secrets/go.sum +++ b/secrets/go.sum | |||
@@ -219,14 +219,19 @@ github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIH | |||
219 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= | 219 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= |
220 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= | 220 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= |
221 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | 221 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
222 | github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= | ||
223 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | 222 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
223 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||
224 | github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= | ||
225 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | ||
224 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | 226 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= |
225 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | 227 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= |
226 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | 228 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= |
227 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | 229 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= |
228 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | ||
229 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | 230 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= |
231 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
232 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||
233 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= | ||
234 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | ||
230 | github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= | 235 | github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= |
231 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= | 236 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= |
232 | go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= | 237 | go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= |
@@ -322,6 +327,7 @@ google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/l | |||
322 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | 327 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= |
323 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= | 328 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= |
324 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | 329 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
330 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= | ||
325 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | 331 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
326 | gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= | 332 | gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= |
327 | gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= | 333 | gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= |
@@ -329,8 +335,10 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |||
329 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | 335 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
330 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | 336 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
331 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | 337 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
338 | gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= | ||
332 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | 339 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
333 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | ||
334 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | 340 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
341 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
342 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
335 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | 343 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |
336 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | 344 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |