diff --git a/pkg/config/config.go b/pkg/config/config.go index b9e98cb2a..7d4995e13 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -62,6 +62,9 @@ type Config struct { // Manifests is a map of referenceable names to documents Manifests map[string]*Manifest `json:"manifests"` + // EncryptionConfigs is a map of referenceable names to encryption configs + EncryptionConfigs map[string]*EncryptionConfig `json:"encryptionConfigs"` + // CurrentContext is the name of the context that you would like to use by default CurrentContext string `json:"currentContext"` @@ -732,6 +735,9 @@ func (c *Config) ModifyContext(context *Context, theContext *ContextOptions) { if theContext.Manifest != "" { context.Manifest = theContext.Manifest } + if theContext.EncryptionConfig != "" { + context.EncryptionConfig = theContext.EncryptionConfig + } if theContext.Namespace != "" { kubeContext.Namespace = theContext.Namespace } @@ -1105,6 +1111,57 @@ func (c *Config) ModifyRepository(repository *Repository, theManifest *ManifestO return nil } +// GetEncryptionConfigs returns all the encryption configs associated with the Config sorted by name +func (c *Config) GetEncryptionConfigs() []*EncryptionConfig { + keys := make([]string, 0, len(c.EncryptionConfigs)) + for name := range c.EncryptionConfigs { + keys = append(keys, name) + } + sort.Strings(keys) + + encryptionConfigs := make([]*EncryptionConfig, 0, len(c.EncryptionConfigs)) + for _, name := range keys { + encryptionConfigs = append(encryptionConfigs, c.EncryptionConfigs[name]) + } + return encryptionConfigs +} + +// AddEncryptionConfig creates a new encryption config +func (c *Config) AddEncryptionConfig(options *EncryptionConfigOptions) *EncryptionConfig { + encryptionConfig := &EncryptionConfig{ + EncryptionKeyFileSource: EncryptionKeyFileSource{ + EncryptionKeyPath: options.EncryptionKeyPath, + DecryptionKeyPath: options.DecryptionKeyPath, + }, + EncryptionKeySecretSource: EncryptionKeySecretSource{ + KeySecretName: options.KeySecretName, + KeySecretNamespace: options.KeySecretNamespace, + }, + } + if c.EncryptionConfigs == nil { + c.EncryptionConfigs = make(map[string]*EncryptionConfig) + } + c.EncryptionConfigs[options.Name] = encryptionConfig + return encryptionConfig +} + +// ModifyEncryptionConfig sets existing values to existing encryption config +func (c *Config) ModifyEncryptionConfig(encryptionConfig *EncryptionConfig, options *EncryptionConfigOptions) { + if options.EncryptionKeyPath != "" { + encryptionConfig.EncryptionKeyPath = options.EncryptionKeyPath + } + if options.DecryptionKeyPath != "" { + encryptionConfig.DecryptionKeyPath = options.DecryptionKeyPath + } + if options.KeySecretName != "" { + encryptionConfig.KeySecretName = options.KeySecretName + } + if options.KeySecretNamespace != "" { + encryptionConfig.KeySecretNamespace = options.KeySecretNamespace + } + return +} + // CurrentContextManagementConfig returns the management options for the current context func (c *Config) CurrentContextManagementConfig() (*ManagementConfiguration, error) { currentCluster, err := c.CurrentContextCluster() diff --git a/pkg/config/config_helper.go b/pkg/config/config_helper.go index eb69191ad..4e9be8193 100644 --- a/pkg/config/config_helper.go +++ b/pkg/config/config_helper.go @@ -197,3 +197,34 @@ func RunSetManifest(o *ManifestOptions, airconfig *Config, writeToStorage bool) return modified, nil } + +// RunSetEncryptionConfig validates the given command line options +// and invokes AddEncryptionConfig/ModifyEncryptionConfig +func RunSetEncryptionConfig(o *EncryptionConfigOptions, airconfig *Config, writeToStorage bool) (bool, error) { + modified := false + err := o.Validate() + if err != nil { + return modified, err + } + + encryptionConfig, exists := airconfig.EncryptionConfigs[o.Name] + if !exists { + // encryption config didn't exist, create it + // ignoring the returned added encryption config + airconfig.AddEncryptionConfig(o) + modified = true + } else { + // encryption config exists, lets update + airconfig.ModifyEncryptionConfig(encryptionConfig, o) + modified = true + } + // Update configuration file just in time persistence approach + if writeToStorage { + if err := airconfig.PersistConfig(false); err != nil { + // Error that it didnt persist the changes + return modified, ErrConfigFailed{} + } + } + + return modified, nil +} diff --git a/pkg/config/config_helper_test.go b/pkg/config/config_helper_test.go index 46e521a1f..6d4272a3a 100644 --- a/pkg/config/config_helper_test.go +++ b/pkg/config/config_helper_test.go @@ -140,3 +140,59 @@ func TestRunSetManifest(t *testing.T) { assert.Equal(t, "/tmp/default", conf.Manifests["dummy_manifest"].TargetPath) }) } + +func TestRunSetEncryptionConfigLocalFile(t *testing.T) { + t.Run("testAddEncryptionConfig", func(t *testing.T) { + conf := testutil.DummyConfig() + dummyEncryptionConfig := testutil.DummyEncryptionConfigOptions() + dummyEncryptionConfig.Name = "test_encryption_config" + + modified, err := config.RunSetEncryptionConfig(dummyEncryptionConfig, conf, false) + assert.Error(t, err) + assert.False(t, modified) + }) + + t.Run("testModifyEncryptionConfig", func(t *testing.T) { + conf := testutil.DummyConfig() + dummyEncryptionConfigOptions := &config.EncryptionConfigOptions{ + Name: "testModifyEncryptionConfig", + EncryptionKeyPath: "testdata/ca.crt", + DecryptionKeyPath: "testdata/test-key.pem", + } + + modified, err := config.RunSetEncryptionConfig(dummyEncryptionConfigOptions, conf, false) + assert.NoError(t, err) + assert.True(t, modified) + assert.Equal(t, "testdata/ca.crt", conf.EncryptionConfigs["testModifyEncryptionConfig"].EncryptionKeyPath) + assert.Equal(t, "testdata/test-key.pem", conf.EncryptionConfigs["testModifyEncryptionConfig"].DecryptionKeyPath) + }) +} + +func TestRunSetEncryptionConfigAPIBackend(t *testing.T) { + t.Run("testAddEncryptionConfig", func(t *testing.T) { + conf := testutil.DummyConfig() + dummyEncryptionConfig := testutil.DummyEncryptionConfigOptions() + dummyEncryptionConfig.Name = "test_encryption_config" + + modified, err := config.RunSetEncryptionConfig(dummyEncryptionConfig, conf, false) + assert.Error(t, err) + assert.False(t, modified) + }) + + t.Run("testModifyEncryptionConfig", func(t *testing.T) { + conf := testutil.DummyConfig() + dummyEncryptionConfigOptions := &config.EncryptionConfigOptions{ + Name: "testModifyEncryptionConfig", + KeySecretName: "dummySecret", + KeySecretNamespace: "dummyNamespace", + EncryptionKeyPath: "", + DecryptionKeyPath: "", + } + + modified, err := config.RunSetEncryptionConfig(dummyEncryptionConfigOptions, conf, false) + assert.NoError(t, err) + assert.True(t, modified) + assert.Equal(t, "dummySecret", conf.EncryptionConfigs["testModifyEncryptionConfig"].KeySecretName) + assert.Equal(t, "dummyNamespace", conf.EncryptionConfigs["testModifyEncryptionConfig"].KeySecretNamespace) + }) +} diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 331725271..8e5fb1bef 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -84,6 +84,10 @@ func TestString(t *testing.T) { name: "managementconfiguration", stringer: testutil.DummyManagementConfiguration(), }, + { + name: "encryption-config", + stringer: testutil.DummyEncryptionConfig(), + }, } for _, tt := range tests { @@ -219,11 +223,12 @@ func TestEnsureComplete(t *testing.T) { { name: "complete config", config: config.Config{ - Clusters: map[string]*config.ClusterPurpose{"testCluster": {}}, - AuthInfos: map[string]*config.AuthInfo{"testAuthInfo": {}}, - Contexts: map[string]*config.Context{"testContext": {Manifest: "testManifest"}}, - Manifests: map[string]*config.Manifest{"testManifest": {}}, - CurrentContext: "testContext", + Clusters: map[string]*config.ClusterPurpose{"testCluster": {}}, + AuthInfos: map[string]*config.AuthInfo{"testAuthInfo": {}}, + Contexts: map[string]*config.Context{"testContext": {Manifest: "testManifest"}}, + Manifests: map[string]*config.Manifest{"testManifest": {}}, + EncryptionConfigs: map[string]*config.EncryptionConfig{"testEncryptionConfig": {}}, + CurrentContext: "testContext", }, expectedErr: nil, }, @@ -844,3 +849,39 @@ func TestModifyManifests(t *testing.T) { err = conf.ModifyManifest(manifest, mo) require.Error(t, err) } + +func TestGetDefaultEncryptionConfigs(t *testing.T) { + conf, cleanup := testutil.InitConfig(t) + defer cleanup(t) + + encryptionConfigs := conf.GetEncryptionConfigs() + require.NotNil(t, encryptionConfigs) + // by default, we dont expect any encryption configs + assert.Equal(t, 0, len(encryptionConfigs)) +} + +func TestModifyEncryptionConfigs(t *testing.T) { + conf, cleanup := testutil.InitConfig(t) + defer cleanup(t) + + eco := testutil.DummyEncryptionConfigOptions() + encryptionConfig := conf.AddEncryptionConfig(eco) + require.NotNil(t, encryptionConfig) + + eco.KeySecretName += stringDelta + conf.ModifyEncryptionConfig(encryptionConfig, eco) + modifiedConfig := conf.EncryptionConfigs[eco.Name] + assert.Equal(t, eco.KeySecretName, modifiedConfig.KeySecretName) + + eco.KeySecretNamespace += stringDelta + conf.ModifyEncryptionConfig(encryptionConfig, eco) + assert.Equal(t, eco.KeySecretNamespace, modifiedConfig.KeySecretNamespace) + + eco.EncryptionKeyPath += stringDelta + conf.ModifyEncryptionConfig(encryptionConfig, eco) + assert.Equal(t, eco.EncryptionKeyPath, modifiedConfig.EncryptionKeyPath) + + eco.DecryptionKeyPath += stringDelta + conf.ModifyEncryptionConfig(encryptionConfig, eco) + assert.Equal(t, eco.DecryptionKeyPath, modifiedConfig.DecryptionKeyPath) +} diff --git a/pkg/config/context.go b/pkg/config/context.go index 2d916e0dd..2af9dbd65 100644 --- a/pkg/config/context.go +++ b/pkg/config/context.go @@ -29,10 +29,14 @@ type Context struct { // NameInKubeconf is the Context name in kubeconf NameInKubeconf string `json:"contextKubeconf"` - // Manifest is the default manifest to be use with this context + // Manifest is the default manifest to be used with this context // +optional Manifest string `json:"manifest,omitempty"` + // EncryptionConfig is the default encryption config to be used with this context + // +optional + EncryptionConfig string `json:"encryptionConfig,omitempty"` + // KubeConfig Context Object context *api.Context } diff --git a/pkg/config/encryption_config.go b/pkg/config/encryption_config.go new file mode 100644 index 000000000..aab3527e3 --- /dev/null +++ b/pkg/config/encryption_config.go @@ -0,0 +1,49 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import "sigs.k8s.io/yaml" + +// EncryptionConfig holds the public and private key information +// used to encrypt and decrypt secrets +type EncryptionConfig struct { + EncryptionKeyFileSource `json:",inline"` + EncryptionKeySecretSource `json:",inline"` +} + +// EncryptionKeyFileSource hold the local file information for the public and private +// keys used for encryption and decryption +type EncryptionKeyFileSource struct { + EncryptionKeyPath string `json:"encryptionKeyPath,omitempty"` + DecryptionKeyPath string `json:"decryptionKeyPath,omitempty"` +} + +// EncryptionKeySecretSource holds the secret information for the public and private +// keys used for encryption and decryption +type EncryptionKeySecretSource struct { + KeySecretName string `json:"keySecretName,omitempty"` + KeySecretNamespace string `json:"keySecretNamespace,omitempty"` +} + +// String returns the encryption config in yaml format +func (ec *EncryptionConfig) String() string { + yamlData, err := yaml.Marshal(&ec) + if err != nil { + return "" + } + return string(yamlData) +} diff --git a/pkg/config/encryption_config_test.go b/pkg/config/encryption_config_test.go new file mode 100644 index 000000000..1b14b9729 --- /dev/null +++ b/pkg/config/encryption_config_test.go @@ -0,0 +1,93 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEncryptionConfigOutputString(t *testing.T) { + expectedEncryptionConfigYaml := `decryptionKeyPath: /tmp/decryption.pub +encryptionKeyPath: /tmp/encryption.key +` + encryptionConfig := &EncryptionConfig{ + EncryptionKeyFileSource: EncryptionKeyFileSource{ + EncryptionKeyPath: "/tmp/encryption.key", + DecryptionKeyPath: "/tmp/decryption.pub", + }, + } + + assert.Equal(t, expectedEncryptionConfigYaml, encryptionConfig.String()) +} + +func TestValidateEncryptionConfigInvalid(t *testing.T) { + encryptionConfig := &EncryptionConfigOptions{} + err := encryptionConfig.Validate() + assert.Error(t, err) +} + +func TestValidateEncryptionConfigInvalidOnlyEncKey(t *testing.T) { + encryptionConfig := &EncryptionConfigOptions{ + EncryptionKeyPath: "/tmp/encryption.key", + } + err := encryptionConfig.Validate() + assert.Error(t, err) +} + +func TestValidateEncryptionConfigInvalidOnlyDecKey(t *testing.T) { + encryptionConfig := &EncryptionConfigOptions{ + DecryptionKeyPath: "/tmp/decryption.pub", + } + err := encryptionConfig.Validate() + assert.Error(t, err) +} + +func TestValidateEncryptionConfigInvalidOnlySecretName(t *testing.T) { + encryptionConfig := &EncryptionConfigOptions{ + KeySecretName: "secretName", + } + err := encryptionConfig.Validate() + assert.Error(t, err) +} + +func TestValidateEncryptionConfigInvalidOnlySecretNamespace(t *testing.T) { + encryptionConfig := &EncryptionConfigOptions{ + KeySecretNamespace: "secretNamespace", + } + err := encryptionConfig.Validate() + assert.Error(t, err) +} + +func TestValidateEncryptionConfigValidWithSecret(t *testing.T) { + encryptionConfig := &EncryptionConfigOptions{ + KeySecretName: "secretName", + KeySecretNamespace: "secretNamespace", + } + err := encryptionConfig.Validate() + assert.Error(t, err) +} + +func TestValidateEncryptionConfigValidWithFile(t *testing.T) { + encryptionConfig := &EncryptionConfigOptions{ + EncryptionKeyPath: "/tmp/encryption.key", + DecryptionKeyPath: "/tmp/decryption.pub", + } + err := encryptionConfig.Validate() + assert.Error(t, err) +} diff --git a/pkg/config/errors.go b/pkg/config/errors.go index 2339b72db..c910ac05b 100644 --- a/pkg/config/errors.go +++ b/pkg/config/errors.go @@ -143,6 +143,16 @@ func (e ErrManagementConfigurationNotFound) Error() string { return fmt.Sprintf("Unknown management configuration '%s'.", e.Name) } +// ErrEncryptionConfigurationNotFound describes a situation in which a user has attempted to reference an encryption +// configuration that cannot be referenced. +type ErrEncryptionConfigurationNotFound struct { + Name string +} + +func (e ErrEncryptionConfigurationNotFound) Error() string { + return fmt.Sprintf("Unknown encryption configuration '%s'.", e.Name) +} + // ErrMissingCurrentContext returned in case --current used without setting current-context type ErrMissingCurrentContext struct { } @@ -248,6 +258,42 @@ func (e ErrMissingManifestName) Error() string { return "missing manifest name" } +// ErrMissingEncryptionConfigName is returned when encryption config name is empty +type ErrMissingEncryptionConfigName struct { +} + +func (e ErrMissingEncryptionConfigName) Error() string { + return "missing encryption config name" +} + +// ErrMutuallyExclusiveEncryptionConfigType is returned when encryption config specifies both +// local key files and secret information for keys stored as secrets in the apiserver +type ErrMutuallyExclusiveEncryptionConfigType struct { +} + +func (e ErrMutuallyExclusiveEncryptionConfigType) Error() string { + return "Specify mutually exclusive encryption config sources, use either: " + + "--decryption-key-path/--decryption-key-path or --secret-name/--secret-namespace." +} + +// ErrInvalidEncryptionKeyPath is returned when encryption config specifies only one of +// encryption and decryption keys +type ErrInvalidEncryptionKeyPath struct { +} + +func (e ErrInvalidEncryptionKeyPath) Error() string { + return "Specify both encryption and decryption keys when setting encryption config" +} + +// ErrInvalidEncryptionKey is returned when encryption config specifies only one of +// encryption keys secret name and namespace +type ErrInvalidEncryptionKey struct { +} + +func (e ErrInvalidEncryptionKey) Error() string { + return "Specify both secret name and namespace when setting encryption config" +} + // ErrMissingFlag is returned when flag is not provided type ErrMissingFlag struct { FlagName string diff --git a/pkg/config/options.go b/pkg/config/options.go index 356098d87..f3e0913f4 100644 --- a/pkg/config/options.go +++ b/pkg/config/options.go @@ -36,14 +36,15 @@ type AuthInfoOptions struct { // ContextOptions holds all configurable options for context type ContextOptions struct { - Name string - ClusterType string - CurrentContext bool - Cluster string - AuthInfo string - Manifest string - Namespace string - Current bool + Name string + ClusterType string + CurrentContext bool + Cluster string + AuthInfo string + Manifest string + EncryptionConfig string + Namespace string + Current bool } // ClusterOptions holds all configurable options for cluster configuration @@ -71,6 +72,15 @@ type ManifestOptions struct { TargetPath string } +// EncryptionConfigOptions holds all configurable options for encryption configuration +type EncryptionConfigOptions struct { + Name string + EncryptionKeyPath string + DecryptionKeyPath string + KeySecretName string + KeySecretNamespace string +} + // TODO(howell): The following functions are tightly coupled with flags passed // on the command line. We should find a way to remove this coupling, since it // is possible to create (and validate) these objects without using the command @@ -195,3 +205,45 @@ func (o *ManifestOptions) Validate() error { } return nil } + +// Validate checks for the possible errors with encryption config +// Error when invalid value, incompatible choice of values given or if the +// key file paths do not exist in the file system +func (o *EncryptionConfigOptions) Validate() error { + switch { + case o.Name == "": + return ErrMissingEncryptionConfigName{} + + case o.backedByFileSystem() == o.backedByAPIServer(): + return ErrMutuallyExclusiveEncryptionConfigType{} + + case o.backedByFileSystem(): + if o.EncryptionKeyPath == "" || o.DecryptionKeyPath == "" { + return ErrInvalidEncryptionKeyPath{} + } + + case o.backedByAPIServer(): + if o.KeySecretName == "" || o.KeySecretNamespace == "" { + return ErrInvalidEncryptionKey{} + } + } + + if o.backedByFileSystem() { + if err := checkExists("encryption-key-path", o.EncryptionKeyPath); err != nil { + return err + } + if err := checkExists("decryption-key-path", o.EncryptionKeyPath); err != nil { + return err + } + } + + return nil +} + +func (o EncryptionConfigOptions) backedByFileSystem() bool { + return o.EncryptionKeyPath != "" || o.DecryptionKeyPath != "" +} + +func (o EncryptionConfigOptions) backedByAPIServer() bool { + return o.KeySecretName != "" || o.KeySecretNamespace != "" +} diff --git a/pkg/config/testdata/config-string.yaml b/pkg/config/testdata/config-string.yaml index b4a3a65a7..5fcd1f976 100644 --- a/pkg/config/testdata/config-string.yaml +++ b/pkg/config/testdata/config-string.yaml @@ -11,8 +11,13 @@ clusters: contexts: dummy_context: contextKubeconf: dummy_cluster_ephemeral + encryptionConfig: dummy_encryption_config manifest: dummy_manifest currentContext: dummy_context +encryptionConfigs: + dummy_encryption_config: + decryptionKeyPath: /tmp/decryption.pub + encryptionKeyPath: /tmp/encryption.key kind: Config managementConfiguration: dummy_management_config: diff --git a/pkg/config/testdata/context-string.yaml b/pkg/config/testdata/context-string.yaml index 0f0c5975a..60b77d672 100644 --- a/pkg/config/testdata/context-string.yaml +++ b/pkg/config/testdata/context-string.yaml @@ -1,4 +1,5 @@ contextKubeconf: dummy_cluster_ephemeral +encryptionConfig: dummy_encryption_config manifest: dummy_manifest LocationOfOrigin: "" diff --git a/pkg/config/testdata/encryption-config-string.yaml b/pkg/config/testdata/encryption-config-string.yaml new file mode 100644 index 000000000..461287a2d --- /dev/null +++ b/pkg/config/testdata/encryption-config-string.yaml @@ -0,0 +1,2 @@ +decryptionKeyPath: /tmp/decryption.pub +encryptionKeyPath: /tmp/encryption.key diff --git a/testutil/testconfig.go b/testutil/testconfig.go index 44428242b..81b950d98 100644 --- a/testutil/testconfig.go +++ b/testutil/testconfig.go @@ -55,6 +55,9 @@ func DummyConfig() *config.Config { ManagementConfiguration: map[string]*config.ManagementConfiguration{ "dummy_management_config": DummyManagementConfiguration(), }, + EncryptionConfigs: map[string]*config.EncryptionConfig{ + "dummy_encryption_config": DummyEncryptionConfig(), + }, CurrentContext: "dummy_context", } conf.SetKubeConfig(kubeconfig.NewConfig()) @@ -74,6 +77,7 @@ func DummyContext() *config.Context { context.Namespace = "dummy_namespace" context.AuthInfo = "dummy_user" context.Cluster = "dummy_cluster_ephemeral" + c.EncryptionConfig = "dummy_encryption_config" c.SetKubeContext(context) return c @@ -207,6 +211,7 @@ func DummyContextOptions() *config.ContextOptions { co.AuthInfo = "dummy_user" co.CurrentContext = false co.Namespace = "dummy_namespace" + co.EncryptionConfig = "dummy_encryption_config" return co } @@ -223,6 +228,27 @@ func DummyAuthInfoOptions() *config.AuthInfoOptions { return authinfo } +// DummyEncryptionConfig creates EncryptionConfigOptions object +// for unit testing +func DummyEncryptionConfig() *config.EncryptionConfig { + return &config.EncryptionConfig{ + EncryptionKeyFileSource: config.EncryptionKeyFileSource{ + EncryptionKeyPath: "/tmp/encryption.key", + DecryptionKeyPath: "/tmp/decryption.pub", + }, + } +} + +// DummyEncryptionConfigOptions creates ManifestOptions config object +// for unit testing +func DummyEncryptionConfigOptions() *config.EncryptionConfigOptions { + return &config.EncryptionConfigOptions{ + Name: "dummy_encryption_config", + EncryptionKeyPath: "/tmp/encryption.key", + DecryptionKeyPath: "/tmp/decryption.pub", + } +} + // DummyManagementConfiguration creates a management configuration for unit testing func DummyManagementConfiguration() *config.ManagementConfiguration { return &config.ManagementConfiguration{ @@ -281,6 +307,7 @@ contexts: contextKubeconf: def_target onlyink: contextKubeconf: onlyinkubeconf_target +encryptionConfigs: {} currentContext: "" kind: Config manifests: {}