Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion cmd/crc/cmd/config/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ const (
DefaultConfigViewFormat = "- {{.ConfigKey | printf \"%-38s\"}}: {{.ConfigValue}}"
)

var configViewFormat string
var (
configViewFormat string
showSecrets bool
)

type configViewTemplate struct {
ConfigKey string
Expand All @@ -38,6 +41,7 @@ func configViewCmd(config config.Storage) *cobra.Command {
}
configViewCmd.Flags().StringVar(&configViewFormat, "format", DefaultConfigViewFormat,
`Go template format to apply to the configuration file. For more information about Go templates, see: https://golang.org/pkg/text/template/`)
configViewCmd.Flags().BoolVar(&showSecrets, "show-secrets", false, "Show values of secret config properties")
return configViewCmd
}

Expand All @@ -55,6 +59,9 @@ func runConfigView(cfg map[string]config.SettingValue, tmpl *template.Template,
if v.IsDefault {
continue
}
if v.IsSecret && !showSecrets {
continue
}
viewTmplt := configViewTemplate{k, v.AsString()}
var buffer bytes.Buffer
if err := tmpl.Execute(&buffer, viewTmplt); err != nil {
Expand Down
6 changes: 3 additions & 3 deletions cmd/crc/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func init() {
logging.Fatal(err.Error())
}
var err error
config, viper, err = newViperConfig()
config, viper, err = newConfig()
if err != nil {
logging.Fatal(err.Error())
}
Expand Down Expand Up @@ -144,12 +144,12 @@ func setProxyDefaults() error {
return nil
}

func newViperConfig() (*crcConfig.Config, *crcConfig.ViperStorage, error) {
func newConfig() (*crcConfig.Config, *crcConfig.ViperStorage, error) {
viper, err := crcConfig.NewViperStorage(constants.ConfigPath, constants.CrcEnvPrefix)
if err != nil {
return nil, nil, err
}
cfg := crcConfig.New(viper)
cfg := crcConfig.New(viper, crcConfig.NewSecretStorage())
crcConfig.RegisterSettings(cfg)
preflight.RegisterSettings(cfg)
return cfg, viper, nil
Expand Down
6 changes: 6 additions & 0 deletions pkg/crc/api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@ func (h *Handler) GetConfig(c *context) error {
allConfigs := h.Config.AllConfigs()
configs := make(map[string]interface{})
for k, v := range allConfigs {
if v.IsSecret {
continue
}
configs[k] = v.Value
}
return c.JSON(http.StatusOK, client.GetConfigResult{
Expand All @@ -230,6 +233,9 @@ func (h *Handler) GetConfig(c *context) error {
if v.Invalid {
continue
}
if v.IsSecret {
continue
}
configs[key] = v.Value
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/crc/api/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func setupNewInMemoryConfig() *config.Config {
storage := config.NewEmptyInMemoryStorage()
cfg := config.New(&skipPreflights{
storage: storage,
})
}, config.NewEmptyInMemorySecretStorage())
cfg.AddSetting("a&a", "foo", nil, nil, "test special string")
cfg.AddSetting("b&&&b", "bar", nil, nil, "test special string")
config.RegisterSettings(cfg)
Expand Down
2 changes: 1 addition & 1 deletion pkg/crc/cluster/pullsecret_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestLoadPullSecret(t *testing.T) {
assert.NoError(t, err)
defer os.RemoveAll(dir)

cfg := config.New(config.NewEmptyInMemoryStorage())
cfg := config.New(config.NewEmptyInMemoryStorage(), config.NewEmptyInMemorySecretStorage())
config.RegisterSettings(cfg)

loader := &nonInteractivePullSecretLoader{
Expand Down
64 changes: 54 additions & 10 deletions pkg/crc/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ const (

type Config struct {
storage RawStorage
secretStorage RawStorage
settingsByName map[string]Setting
}

func New(storage RawStorage) *Config {
func New(storage, secretStorage RawStorage) *Config {
return &Config{
storage: storage,
secretStorage: secretStorage,
settingsByName: make(map[string]Setting),
}
}
Expand Down Expand Up @@ -55,6 +57,7 @@ func (c *Config) AddSetting(name string, defValue interface{}, validationFn Vali
defaultValue: defValue,
validationFn: validationFn,
callbackFn: callbackFn,
isSecret: isUnderlyingTypeSecret(defValue),
Help: help,
}
}
Expand All @@ -79,7 +82,7 @@ func (c *Config) Set(key string, value interface{}) (string, error) {
if err != nil {
return "", fmt.Errorf(invalidProp, value, key, err)
}
case string:
case string, Secret:
castValue = cast.ToString(value)
case bool:
castValue, err = cast.ToBoolE(value)
Expand All @@ -92,24 +95,34 @@ func (c *Config) Set(key string, value interface{}) (string, error) {
return "", fmt.Errorf(invalidType, value, key)
}

if err := c.storage.Set(key, castValue); err != nil {
return "", err
if setting.isSecret {
if err := c.secretStorage.Set(key, castValue); err != nil {
return "", err
}
} else {
if err := c.storage.Set(key, castValue); err != nil {
return "", err
}
}

return c.settingsByName[key].callbackFn(key, castValue), nil
}

// Unset unsets a given config key
func (c *Config) Unset(key string) (string, error) {
_, ok := c.settingsByName[key]
setting, ok := c.settingsByName[key]
if !ok {
return "", fmt.Errorf(configPropDoesntExistMsg, key)
}

if err := c.storage.Unset(key); err != nil {
return "", err
if setting.isSecret {
if err := c.secretStorage.Unset(key); err != nil {
return "", err
}
} else {
if err := c.storage.Unset(key); err != nil {
return "", err
}
}

return fmt.Sprintf("Successfully unset configuration property '%s'", key), nil
}

Expand All @@ -120,7 +133,12 @@ func (c *Config) Get(key string) SettingValue {
Invalid: true,
}
}
value := c.storage.Get(key)
var value interface{}
if setting.isSecret {
value = c.secretStorage.Get(key)
} else {
value = c.storage.Get(key)
}
if value == nil {
value = setting.defaultValue
}
Expand Down Expand Up @@ -149,6 +167,13 @@ func (c *Config) Get(key string) SettingValue {
Invalid: true,
}
}
case Secret:
value, err = toSecret(value)
if err != nil {
return SettingValue{
Invalid: true,
}
}
default:
return SettingValue{
Invalid: true,
Expand All @@ -157,5 +182,24 @@ func (c *Config) Get(key string) SettingValue {
return SettingValue{
Value: value,
IsDefault: reflect.DeepEqual(setting.defaultValue, value),
IsSecret: isUnderlyingTypeSecret(value),
}
}

func toSecret(value interface{}) (Secret, error) {
if isUnderlyingTypeSecret(value) {
return value.(Secret), nil
}
v, err := cast.ToStringE(value)
if err != nil {
return Secret(""), err
}
return Secret(v), nil
}

func isUnderlyingTypeSecret(value interface{}) bool {
if _, ok := value.(Secret); ok {
return true
}
return false
}
83 changes: 83 additions & 0 deletions pkg/crc/config/secret_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package config

import (
"errors"
"fmt"

"github.com/code-ready/crc/pkg/crc/logging"
"github.com/spf13/cast"
"github.com/zalando/go-keyring"
)

const secretServiceName = "crc"

var ErrSecretsNotAccessible = errors.New("secret store is not accessible")

type Secret string

func (s Secret) String() string {
return string(s)
}

type SecretStorage struct {
secretService string
storeAccessible bool
}

func NewSecretStorage() *SecretStorage {
return &SecretStorage{
secretService: secretServiceName,
storeAccessible: keyringAccessible(),
}
}

func (c *SecretStorage) Get(key string) interface{} {
if !c.storeAccessible {
return nil
}
secret, err := keyring.Get(c.secretService, key)
if errors.Is(err, keyring.ErrNotFound) {
return nil
}
return secret
}

func (c *SecretStorage) Set(key string, value interface{}) error {
if !c.storeAccessible {
return ErrSecretsNotAccessible
}
secret, err := cast.ToStringE(value)
if err != nil {
return fmt.Errorf("Failed to cast secret value to string: %w", err)
}
return keyring.Set(c.secretService, key, secret)
}

func (c *SecretStorage) Unset(key string) error {
if !c.storeAccessible {
return ErrSecretsNotAccessible
}
err := keyring.Delete(c.secretService, key)
if errors.Is(err, keyring.ErrNotFound) {
return nil
}
return err
}

func keyringAccessible() bool {
err := keyring.Set("crc-test", "foo", "bar")
if err == nil {
_ = keyring.Delete("crc-test", "foo")
return true
}
logging.Debugf("Keyring is not accessible: %v", err)
return false
}

func NewEmptyInMemorySecretStorage() *SecretStorage {
keyring.MockInit()
return &SecretStorage{
secretService: secretServiceName,
storeAccessible: true,
}
}
69 changes: 69 additions & 0 deletions pkg/crc/config/secret_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package config

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const (
password = "password"
secret = "secret"
)

func newTestConfigSecret() (*Config, error) {
cfg := New(NewEmptyInMemoryStorage(), NewEmptyInMemorySecretStorage())

cfg.AddSetting(password, Secret(""), ValidateString, SuccessfullyApplied, "")
cfg.AddSetting(secret, Secret("apples"), ValidateString, SuccessfullyApplied, "")
return cfg, nil
}

func TestGetSecret(t *testing.T) {
cfg, err := newTestConfigSecret()
require.NoError(t, err)

assert.Equal(t, SettingValue{
Value: Secret("apples"),
IsDefault: true,
IsSecret: true,
}, cfg.Get(secret))
}

func TestSecretConfigUnknown(t *testing.T) {
cfg, err := newTestConfigSecret()
require.NoError(t, err)

assert.Equal(t, SettingValue{
Invalid: true,
}, cfg.Get("baz"))
}

func TestSecretConfigSetAndGet(t *testing.T) {
cfg, err := newTestConfigSecret()
require.NoError(t, err)

_, err = cfg.Set(password, "pass123")
assert.NoError(t, err)

assert.Equal(t, SettingValue{
Value: Secret("pass123"),
IsDefault: false,
IsSecret: true,
}, cfg.Get(password))
}

func TestSecretConfigUnsetAndGet(t *testing.T) {
cfg, err := newTestConfigSecret()
require.NoError(t, err)

_, err = cfg.Unset(secret)
assert.NoError(t, err)

assert.Equal(t, SettingValue{
Value: Secret("apples"),
IsDefault: true,
IsSecret: true,
}, cfg.Get(secret))
}
2 changes: 2 additions & 0 deletions pkg/crc/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ type Setting struct {
defaultValue interface{}
validationFn ValidationFnType
callbackFn SetFn
isSecret bool
Help string
}

type SettingValue struct {
Value interface{}
Invalid bool
IsDefault bool
IsSecret bool
}

func (v SettingValue) AsBool() bool {
Expand Down
Loading