Adding encryption config to airshipctl config
Design document: https://docs.google.com/document/d/1EjiCuXoiy8DEEXe15KxVJ4iWrwogCyG113_0LdzcWzQ/edit?usp=drive_web&ouid=102644738301620637153 This patchset comprises of: - airship config now supports encryption configs to store encryption and decryption keys from local file system or the kubernetes api server that will be used to encrypt and decrypt secrets in a future patchset This is the first of multiple patchsets to support encryption and decryption in airshipctl Complete feature: https://review.opendev.org/#/c/742695/ Change-Id: I195e8e254b7cc6b3e04e45d67e0a0e3797183816
This commit is contained in:
parent
3601c2b59c
commit
f328c43295
@ -62,6 +62,9 @@ type Config struct {
|
|||||||
// Manifests is a map of referenceable names to documents
|
// Manifests is a map of referenceable names to documents
|
||||||
Manifests map[string]*Manifest `json:"manifests"`
|
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 is the name of the context that you would like to use by default
|
||||||
CurrentContext string `json:"currentContext"`
|
CurrentContext string `json:"currentContext"`
|
||||||
|
|
||||||
@ -732,6 +735,9 @@ func (c *Config) ModifyContext(context *Context, theContext *ContextOptions) {
|
|||||||
if theContext.Manifest != "" {
|
if theContext.Manifest != "" {
|
||||||
context.Manifest = theContext.Manifest
|
context.Manifest = theContext.Manifest
|
||||||
}
|
}
|
||||||
|
if theContext.EncryptionConfig != "" {
|
||||||
|
context.EncryptionConfig = theContext.EncryptionConfig
|
||||||
|
}
|
||||||
if theContext.Namespace != "" {
|
if theContext.Namespace != "" {
|
||||||
kubeContext.Namespace = theContext.Namespace
|
kubeContext.Namespace = theContext.Namespace
|
||||||
}
|
}
|
||||||
@ -1105,6 +1111,57 @@ func (c *Config) ModifyRepository(repository *Repository, theManifest *ManifestO
|
|||||||
return nil
|
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
|
// CurrentContextManagementConfig returns the management options for the current context
|
||||||
func (c *Config) CurrentContextManagementConfig() (*ManagementConfiguration, error) {
|
func (c *Config) CurrentContextManagementConfig() (*ManagementConfiguration, error) {
|
||||||
currentCluster, err := c.CurrentContextCluster()
|
currentCluster, err := c.CurrentContextCluster()
|
||||||
|
@ -197,3 +197,34 @@ func RunSetManifest(o *ManifestOptions, airconfig *Config, writeToStorage bool)
|
|||||||
|
|
||||||
return modified, nil
|
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
|
||||||
|
}
|
||||||
|
@ -140,3 +140,59 @@ func TestRunSetManifest(t *testing.T) {
|
|||||||
assert.Equal(t, "/tmp/default", conf.Manifests["dummy_manifest"].TargetPath)
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -84,6 +84,10 @@ func TestString(t *testing.T) {
|
|||||||
name: "managementconfiguration",
|
name: "managementconfiguration",
|
||||||
stringer: testutil.DummyManagementConfiguration(),
|
stringer: testutil.DummyManagementConfiguration(),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "encryption-config",
|
||||||
|
stringer: testutil.DummyEncryptionConfig(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@ -223,6 +227,7 @@ func TestEnsureComplete(t *testing.T) {
|
|||||||
AuthInfos: map[string]*config.AuthInfo{"testAuthInfo": {}},
|
AuthInfos: map[string]*config.AuthInfo{"testAuthInfo": {}},
|
||||||
Contexts: map[string]*config.Context{"testContext": {Manifest: "testManifest"}},
|
Contexts: map[string]*config.Context{"testContext": {Manifest: "testManifest"}},
|
||||||
Manifests: map[string]*config.Manifest{"testManifest": {}},
|
Manifests: map[string]*config.Manifest{"testManifest": {}},
|
||||||
|
EncryptionConfigs: map[string]*config.EncryptionConfig{"testEncryptionConfig": {}},
|
||||||
CurrentContext: "testContext",
|
CurrentContext: "testContext",
|
||||||
},
|
},
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
@ -844,3 +849,39 @@ func TestModifyManifests(t *testing.T) {
|
|||||||
err = conf.ModifyManifest(manifest, mo)
|
err = conf.ModifyManifest(manifest, mo)
|
||||||
require.Error(t, err)
|
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)
|
||||||
|
}
|
||||||
|
@ -29,10 +29,14 @@ type Context struct {
|
|||||||
// NameInKubeconf is the Context name in kubeconf
|
// NameInKubeconf is the Context name in kubeconf
|
||||||
NameInKubeconf string `json:"contextKubeconf"`
|
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
|
// +optional
|
||||||
Manifest string `json:"manifest,omitempty"`
|
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
|
// KubeConfig Context Object
|
||||||
context *api.Context
|
context *api.Context
|
||||||
}
|
}
|
||||||
|
49
pkg/config/encryption_config.go
Normal file
49
pkg/config/encryption_config.go
Normal file
@ -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)
|
||||||
|
}
|
93
pkg/config/encryption_config_test.go
Normal file
93
pkg/config/encryption_config_test.go
Normal file
@ -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)
|
||||||
|
}
|
@ -143,6 +143,16 @@ func (e ErrManagementConfigurationNotFound) Error() string {
|
|||||||
return fmt.Sprintf("Unknown management configuration '%s'.", e.Name)
|
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
|
// ErrMissingCurrentContext returned in case --current used without setting current-context
|
||||||
type ErrMissingCurrentContext struct {
|
type ErrMissingCurrentContext struct {
|
||||||
}
|
}
|
||||||
@ -248,6 +258,42 @@ func (e ErrMissingManifestName) Error() string {
|
|||||||
return "missing manifest name"
|
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
|
// ErrMissingFlag is returned when flag is not provided
|
||||||
type ErrMissingFlag struct {
|
type ErrMissingFlag struct {
|
||||||
FlagName string
|
FlagName string
|
||||||
|
@ -42,6 +42,7 @@ type ContextOptions struct {
|
|||||||
Cluster string
|
Cluster string
|
||||||
AuthInfo string
|
AuthInfo string
|
||||||
Manifest string
|
Manifest string
|
||||||
|
EncryptionConfig string
|
||||||
Namespace string
|
Namespace string
|
||||||
Current bool
|
Current bool
|
||||||
}
|
}
|
||||||
@ -71,6 +72,15 @@ type ManifestOptions struct {
|
|||||||
TargetPath string
|
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
|
// 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
|
// 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
|
// is possible to create (and validate) these objects without using the command
|
||||||
@ -195,3 +205,45 @@ func (o *ManifestOptions) Validate() error {
|
|||||||
}
|
}
|
||||||
return nil
|
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 != ""
|
||||||
|
}
|
||||||
|
5
pkg/config/testdata/config-string.yaml
vendored
5
pkg/config/testdata/config-string.yaml
vendored
@ -11,8 +11,13 @@ clusters:
|
|||||||
contexts:
|
contexts:
|
||||||
dummy_context:
|
dummy_context:
|
||||||
contextKubeconf: dummy_cluster_ephemeral
|
contextKubeconf: dummy_cluster_ephemeral
|
||||||
|
encryptionConfig: dummy_encryption_config
|
||||||
manifest: dummy_manifest
|
manifest: dummy_manifest
|
||||||
currentContext: dummy_context
|
currentContext: dummy_context
|
||||||
|
encryptionConfigs:
|
||||||
|
dummy_encryption_config:
|
||||||
|
decryptionKeyPath: /tmp/decryption.pub
|
||||||
|
encryptionKeyPath: /tmp/encryption.key
|
||||||
kind: Config
|
kind: Config
|
||||||
managementConfiguration:
|
managementConfiguration:
|
||||||
dummy_management_config:
|
dummy_management_config:
|
||||||
|
1
pkg/config/testdata/context-string.yaml
vendored
1
pkg/config/testdata/context-string.yaml
vendored
@ -1,4 +1,5 @@
|
|||||||
contextKubeconf: dummy_cluster_ephemeral
|
contextKubeconf: dummy_cluster_ephemeral
|
||||||
|
encryptionConfig: dummy_encryption_config
|
||||||
manifest: dummy_manifest
|
manifest: dummy_manifest
|
||||||
|
|
||||||
LocationOfOrigin: ""
|
LocationOfOrigin: ""
|
||||||
|
2
pkg/config/testdata/encryption-config-string.yaml
vendored
Normal file
2
pkg/config/testdata/encryption-config-string.yaml
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
decryptionKeyPath: /tmp/decryption.pub
|
||||||
|
encryptionKeyPath: /tmp/encryption.key
|
@ -55,6 +55,9 @@ func DummyConfig() *config.Config {
|
|||||||
ManagementConfiguration: map[string]*config.ManagementConfiguration{
|
ManagementConfiguration: map[string]*config.ManagementConfiguration{
|
||||||
"dummy_management_config": DummyManagementConfiguration(),
|
"dummy_management_config": DummyManagementConfiguration(),
|
||||||
},
|
},
|
||||||
|
EncryptionConfigs: map[string]*config.EncryptionConfig{
|
||||||
|
"dummy_encryption_config": DummyEncryptionConfig(),
|
||||||
|
},
|
||||||
CurrentContext: "dummy_context",
|
CurrentContext: "dummy_context",
|
||||||
}
|
}
|
||||||
conf.SetKubeConfig(kubeconfig.NewConfig())
|
conf.SetKubeConfig(kubeconfig.NewConfig())
|
||||||
@ -74,6 +77,7 @@ func DummyContext() *config.Context {
|
|||||||
context.Namespace = "dummy_namespace"
|
context.Namespace = "dummy_namespace"
|
||||||
context.AuthInfo = "dummy_user"
|
context.AuthInfo = "dummy_user"
|
||||||
context.Cluster = "dummy_cluster_ephemeral"
|
context.Cluster = "dummy_cluster_ephemeral"
|
||||||
|
c.EncryptionConfig = "dummy_encryption_config"
|
||||||
c.SetKubeContext(context)
|
c.SetKubeContext(context)
|
||||||
|
|
||||||
return c
|
return c
|
||||||
@ -207,6 +211,7 @@ func DummyContextOptions() *config.ContextOptions {
|
|||||||
co.AuthInfo = "dummy_user"
|
co.AuthInfo = "dummy_user"
|
||||||
co.CurrentContext = false
|
co.CurrentContext = false
|
||||||
co.Namespace = "dummy_namespace"
|
co.Namespace = "dummy_namespace"
|
||||||
|
co.EncryptionConfig = "dummy_encryption_config"
|
||||||
|
|
||||||
return co
|
return co
|
||||||
}
|
}
|
||||||
@ -223,6 +228,27 @@ func DummyAuthInfoOptions() *config.AuthInfoOptions {
|
|||||||
return authinfo
|
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
|
// DummyManagementConfiguration creates a management configuration for unit testing
|
||||||
func DummyManagementConfiguration() *config.ManagementConfiguration {
|
func DummyManagementConfiguration() *config.ManagementConfiguration {
|
||||||
return &config.ManagementConfiguration{
|
return &config.ManagementConfiguration{
|
||||||
@ -281,6 +307,7 @@ contexts:
|
|||||||
contextKubeconf: def_target
|
contextKubeconf: def_target
|
||||||
onlyink:
|
onlyink:
|
||||||
contextKubeconf: onlyinkubeconf_target
|
contextKubeconf: onlyinkubeconf_target
|
||||||
|
encryptionConfigs: {}
|
||||||
currentContext: ""
|
currentContext: ""
|
||||||
kind: Config
|
kind: Config
|
||||||
manifests: {}
|
manifests: {}
|
||||||
|
Loading…
Reference in New Issue
Block a user