diff --git a/pkg/base/replicated.go b/pkg/base/replicated.go index 6dc36c66f7..701f9da7a2 100644 --- a/pkg/base/replicated.go +++ b/pkg/base/replicated.go @@ -70,7 +70,7 @@ func renderReplicated(u *upstreamtypes.Upstream, renderOptions *RenderOptions) ( versionInfo := template.VersionInfoFromInstallation(renderOptions.Sequence, renderOptions.IsAirgap, kotsKinds.Installation.Spec) appInfo := template.ApplicationInfo{Slug: renderOptions.AppSlug} - renderedConfig, err := kotsconfig.TemplateConfigObjects(kotsKinds.Config, itemValues, kotsKinds.License, &kotsKinds.KotsApplication, registry, &versionInfo, &appInfo, kotsKinds.IdentityConfig, util.PodNamespace) + renderedConfig, err := kotsconfig.TemplateConfigObjects(kotsKinds.Config, itemValues, kotsKinds.License, &kotsKinds.KotsApplication, registry, &versionInfo, &appInfo, kotsKinds.IdentityConfig, util.PodNamespace, true) if err != nil { return nil, nil, errors.Wrap(err, "failed to template config objects") } diff --git a/pkg/base/templates.go b/pkg/base/templates.go index 35774684cc..5f5a6a98c1 100644 --- a/pkg/base/templates.go +++ b/pkg/base/templates.go @@ -65,6 +65,7 @@ func NewConfigContextTemplateBuilder(u *upstreamtypes.Upstream, renderOptions *R ApplicationInfo: &appInfo, IdentityConfig: kotsKinds.IdentityConfig, Namespace: renderOptions.Namespace, + DecryptValues: true, } builder, itemValues, err := template.NewBuilder(builderOptions) if err != nil { diff --git a/pkg/config/config.go b/pkg/config/config.go index ca382383fb..9b97f23154 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -16,8 +16,8 @@ import ( "k8s.io/client-go/kubernetes/scheme" ) -func TemplateConfigObjects(configSpec *kotsv1beta1.Config, configValues map[string]template.ItemValue, license *kotsv1beta1.License, app *kotsv1beta1.Application, localRegistry template.LocalRegistry, versionInfo *template.VersionInfo, appInfo *template.ApplicationInfo, identityconfig *kotsv1beta1.IdentityConfig, namespace string) (*kotsv1beta1.Config, error) { - templatedString, err := templateConfigObjects(configSpec, configValues, license, app, localRegistry, versionInfo, appInfo, identityconfig, namespace, MarshalConfig) +func TemplateConfigObjects(configSpec *kotsv1beta1.Config, configValues map[string]template.ItemValue, license *kotsv1beta1.License, app *kotsv1beta1.Application, localRegistry template.LocalRegistry, versionInfo *template.VersionInfo, appInfo *template.ApplicationInfo, identityconfig *kotsv1beta1.IdentityConfig, namespace string, decryptValues bool) (*kotsv1beta1.Config, error) { + templatedString, err := templateConfigObjects(configSpec, configValues, license, app, localRegistry, versionInfo, appInfo, identityconfig, namespace, decryptValues, MarshalConfig) if err != nil { return nil, errors.Wrap(err, "failed to template config") } @@ -38,7 +38,7 @@ func TemplateConfigObjects(configSpec *kotsv1beta1.Config, configValues map[stri return config, nil } -func templateConfigObjects(configSpec *kotsv1beta1.Config, configValues map[string]template.ItemValue, license *kotsv1beta1.License, app *kotsv1beta1.Application, localRegistry template.LocalRegistry, versionInfo *template.VersionInfo, appInfo *template.ApplicationInfo, identityconfig *kotsv1beta1.IdentityConfig, namespace string, marshalFunc func(config *kotsv1beta1.Config) (string, error)) (string, error) { +func templateConfigObjects(configSpec *kotsv1beta1.Config, configValues map[string]template.ItemValue, license *kotsv1beta1.License, app *kotsv1beta1.Application, localRegistry template.LocalRegistry, versionInfo *template.VersionInfo, appInfo *template.ApplicationInfo, identityconfig *kotsv1beta1.IdentityConfig, namespace string, decryptValues bool, marshalFunc func(config *kotsv1beta1.Config) (string, error)) (string, error) { if configSpec == nil { return "", nil } @@ -53,6 +53,7 @@ func templateConfigObjects(configSpec *kotsv1beta1.Config, configValues map[stri ApplicationInfo: appInfo, IdentityConfig: identityconfig, Namespace: namespace, + DecryptValues: decryptValues, } builder, configVals, err := template.NewBuilder(builderOptions) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 4e93b2b462..08d93d140c 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -363,7 +363,7 @@ spec: configObj, _, _ := decode([]byte(tt.configSpecData), nil, nil) localRegistry := template.LocalRegistry{} - got, err := templateConfigObjects(configObj.(*kotsv1beta1.Config), tt.configValuesData, license, app, localRegistry, versionInfo, appInfo, nil, "app-namespace", MarshalConfig) + got, err := templateConfigObjects(configObj.(*kotsv1beta1.Config), tt.configValuesData, license, app, localRegistry, versionInfo, appInfo, nil, "app-namespace", false, MarshalConfig) req.NoError(err) gotObj, _, err := decode([]byte(got), nil, nil) @@ -372,7 +372,7 @@ spec: req.Equal(wantObj, gotObj) // compare with oldMarshalConfig results - got, err = templateConfigObjects(configObj.(*kotsv1beta1.Config), tt.configValuesData, license, app, localRegistry, versionInfo, appInfo, nil, "app-namespace", oldMarshalConfig) + got, err = templateConfigObjects(configObj.(*kotsv1beta1.Config), tt.configValuesData, license, app, localRegistry, versionInfo, appInfo, nil, "app-namespace", false, oldMarshalConfig) if !tt.expectOldFail { req.NoError(err) diff --git a/pkg/handlers/config.go b/pkg/handlers/config.go index fdd2ff2e75..0e8061990f 100644 --- a/pkg/handlers/config.go +++ b/pkg/handlers/config.go @@ -242,7 +242,7 @@ func (h *Handler) LiveAppConfig(w http.ResponseWriter, r *http.Request) { versionInfo := template.VersionInfoFromInstallation(liveAppConfigRequest.Sequence+1, foundApp.IsAirgap, kotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate) appInfo := template.ApplicationInfo{Slug: foundApp.Slug} - renderedConfig, err := kotsconfig.TemplateConfigObjects(kotsKinds.Config, configValues, appLicense, &kotsKinds.KotsApplication, localRegistry, &versionInfo, &appInfo, kotsKinds.IdentityConfig, util.PodNamespace) + renderedConfig, err := kotsconfig.TemplateConfigObjects(kotsKinds.Config, configValues, appLicense, &kotsKinds.KotsApplication, localRegistry, &versionInfo, &appInfo, kotsKinds.IdentityConfig, util.PodNamespace, false) if err != nil { logger.Error(err) liveAppConfigResponse.Error = "failed to render templates" @@ -343,7 +343,7 @@ func (h *Handler) CurrentAppConfig(w http.ResponseWriter, r *http.Request) { versionInfo := template.VersionInfoFromInstallation(int64(sequence)+1, foundApp.IsAirgap, kotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate) appInfo := template.ApplicationInfo{Slug: foundApp.Slug} - renderedConfig, err := kotsconfig.TemplateConfigObjects(kotsKinds.Config, configValues, appLicense, &kotsKinds.KotsApplication, localRegistry, &versionInfo, &appInfo, kotsKinds.IdentityConfig, util.PodNamespace) + renderedConfig, err := kotsconfig.TemplateConfigObjects(kotsKinds.Config, configValues, appLicense, &kotsKinds.KotsApplication, localRegistry, &versionInfo, &appInfo, kotsKinds.IdentityConfig, util.PodNamespace, false) if err != nil { logger.Error(err) currentAppConfigResponse.Error = "failed to render templates" @@ -811,7 +811,7 @@ func (h *Handler) SetAppConfigValues(w http.ResponseWriter, r *http.Request) { versionInfo := template.VersionInfoFromInstallation(nextAppSequence, foundApp.IsAirgap, kotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate) appInfo := template.ApplicationInfo{Slug: foundApp.Slug} - renderedConfig, err := kotsconfig.TemplateConfigObjects(newConfig, configValueMap, kotsKinds.License, &kotsKinds.KotsApplication, localRegistry, &versionInfo, &appInfo, kotsKinds.IdentityConfig, util.PodNamespace) + renderedConfig, err := kotsconfig.TemplateConfigObjects(newConfig, configValueMap, kotsKinds.License, &kotsKinds.KotsApplication, localRegistry, &versionInfo, &appInfo, kotsKinds.IdentityConfig, util.PodNamespace, true) if err != nil { setAppConfigValuesResponse.Error = "failed to render templates" logger.Error(errors.Wrap(err, setAppConfigValuesResponse.Error)) diff --git a/pkg/kotsadmconfig/config.go b/pkg/kotsadmconfig/config.go index 1e7dbe2312..ccf8e0edd0 100644 --- a/pkg/kotsadmconfig/config.go +++ b/pkg/kotsadmconfig/config.go @@ -5,7 +5,6 @@ import ( "github.com/pkg/errors" kotsv1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1" - "github.com/replicatedhq/kots/pkg/config" kotsconfig "github.com/replicatedhq/kots/pkg/config" "github.com/replicatedhq/kots/pkg/k8sutil" "github.com/replicatedhq/kots/pkg/kotsutil" @@ -60,7 +59,7 @@ func NeedsConfiguration(appSlug string, sequence int64, isAirgap bool, kotsKinds if err != nil { return false, errors.Wrap(err, "failed to marshal configvalues spec") } - configValues, err := config.UnmarshalConfigValuesContent([]byte(configValuesSpec)) + configValues, err := kotsconfig.UnmarshalConfigValuesContent([]byte(configValuesSpec)) if err != nil { log.Error(errors.Wrap(err, "failed to create config values")) configValues = map[string]template.ItemValue{} @@ -78,7 +77,7 @@ func NeedsConfiguration(appSlug string, sequence int64, isAirgap bool, kotsKinds appInfo := template.ApplicationInfo{Slug: appSlug} // rendered, err := kotsconfig.TemplateConfig(logger.NewCLILogger(), configSpec, configValuesSpec, licenseSpec, appSpec, identityConfigSpec, localRegistry, util.PodNamespace) - config, err := kotsconfig.TemplateConfigObjects(kotsKinds.Config, configValues, kotsKinds.License, &kotsKinds.KotsApplication, localRegistry, &versionInfo, &appInfo, kotsKinds.IdentityConfig, util.PodNamespace) + config, err := kotsconfig.TemplateConfigObjects(kotsKinds.Config, configValues, kotsKinds.License, &kotsKinds.KotsApplication, localRegistry, &versionInfo, &appInfo, kotsKinds.IdentityConfig, util.PodNamespace, true) if err != nil { return false, errors.Wrap(err, "failed to template config") } diff --git a/pkg/render/render.go b/pkg/render/render.go index 1fc3ba2970..eeac3343cc 100644 --- a/pkg/render/render.go +++ b/pkg/render/render.go @@ -97,6 +97,7 @@ func NewBuilder(kotsKinds *kotsutil.KotsKinds, registrySettings registrytypes.Re VersionInfo: &versionInfo, IdentityConfig: kotsKinds.IdentityConfig, Namespace: namespace, + DecryptValues: true, } builder, _, err := template.NewBuilder(builderOptions) return &builder, errors.Wrap(err, "failed to create builder") diff --git a/pkg/template/builder.go b/pkg/template/builder.go index 61fc5fc0f6..33e43e58ad 100644 --- a/pkg/template/builder.go +++ b/pkg/template/builder.go @@ -31,6 +31,7 @@ type BuilderOptions struct { VersionInfo *VersionInfo IdentityConfig *kotsv1beta1.IdentityConfig Namespace string + DecryptValues bool } // NewBuilder creates a builder with all available contexts. @@ -56,7 +57,7 @@ func NewBuilder(opts BuilderOptions) (Builder, map[string]ItemValue, error) { } configCtx, err := b.newConfigContext(opts.ConfigGroups, opts.ExistingValues, opts.LocalRegistry, - opts.License, opts.Application, opts.VersionInfo, dockerHubRegistry, slug) + opts.License, opts.Application, opts.VersionInfo, dockerHubRegistry, slug, opts.DecryptValues) if err != nil { return Builder{}, nil, errors.Wrap(err, "create config context") } diff --git a/pkg/template/config_context.go b/pkg/template/config_context.go index 546d455e00..2fef78fcad 100644 --- a/pkg/template/config_context.go +++ b/pkg/template/config_context.go @@ -68,13 +68,14 @@ type ConfigCtx struct { LocalRegistry LocalRegistry DockerHubRegistry registry.RegistryOptions AppSlug string + DecryptValues bool license *kotsv1beta1.License // Another agument for unifying all these contexts app *kotsv1beta1.Application } // newConfigContext creates and returns a context for template rendering -func (b *Builder) newConfigContext(configGroups []kotsv1beta1.ConfigGroup, existingValues map[string]ItemValue, localRegistry LocalRegistry, license *kotsv1beta1.License, app *kotsv1beta1.Application, info *VersionInfo, dockerHubRegistry registry.RegistryOptions, appSlug string) (*ConfigCtx, error) { +func (b *Builder) newConfigContext(configGroups []kotsv1beta1.ConfigGroup, existingValues map[string]ItemValue, localRegistry LocalRegistry, license *kotsv1beta1.License, app *kotsv1beta1.Application, info *VersionInfo, dockerHubRegistry registry.RegistryOptions, appSlug string, decryptValues bool) (*ConfigCtx, error) { configCtx := &ConfigCtx{ ItemValues: existingValues, LocalRegistry: localRegistry, @@ -82,6 +83,7 @@ func (b *Builder) newConfigContext(configGroups []kotsv1beta1.ConfigGroup, exist AppSlug: appSlug, license: license, app: app, + DecryptValues: decryptValues, } builder := Builder{ @@ -100,7 +102,7 @@ func (b *Builder) newConfigContext(configGroups []kotsv1beta1.ConfigGroup, exist configItemsByName[configItem.Name] = configItem // decrypt password if it exists - if configItem.Type == "password" { + if configItem.Type == "password" && configCtx.DecryptValues { existingVal, ok := existingValues[configItem.Name] if ok && existingVal.HasValue() { val, err := decrypt(existingVal.ValueStr()) diff --git a/pkg/template/config_context_test.go b/pkg/template/config_context_test.go index bdeeb5d1aa..0d95514769 100644 --- a/pkg/template/config_context_test.go +++ b/pkg/template/config_context_test.go @@ -6,16 +6,24 @@ import ( kotsv1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1" "github.com/replicatedhq/kots/kotskinds/multitype" + "github.com/replicatedhq/kots/pkg/crypto" "github.com/replicatedhq/kots/pkg/docker/registry" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestBuilder_NewConfigContext(t *testing.T) { + err := crypto.NewAESCipher() + require.NoError(t, err) + + testValue := "this is a test value to be encrypted" + testValueEncrypted := base64.StdEncoding.EncodeToString(crypto.Encrypt([]byte(testValue))) + type args struct { configGroups []kotsv1beta1.ConfigGroup templateContext map[string]ItemValue license *kotsv1beta1.License + decryptValues bool } tests := []struct { name string @@ -358,6 +366,111 @@ func TestBuilder_NewConfigContext(t *testing.T) { }, }, }, + { + name: "chained configOption from password (no decryption)", + args: args{ + configGroups: []kotsv1beta1.ConfigGroup{ + { + Name: "abc", + Title: "abc", + Description: "abc", + Items: []kotsv1beta1.ConfigItem{ + { + Name: "abcItem", + Type: "password", + Title: "abcItem", + Default: multitype.BoolOrString{ + Type: multitype.String, + StrVal: "abcItemDefault", + }, + Value: multitype.BoolOrString{}, + }, + { + Name: "childItem1", + Type: "text", + ReadOnly: true, + Value: multitype.BoolOrString{ + Type: multitype.String, + StrVal: `hello world repl{{ ConfigOption "abcItem" }}`, + }, + }, + }, + }, + }, + templateContext: map[string]ItemValue{ + "abcItem": { + Value: testValueEncrypted, + }, + }, + decryptValues: false, + }, + want: &ConfigCtx{ + AppSlug: "app-slug", + ItemValues: map[string]ItemValue{ + "abcItem": { + Value: testValueEncrypted, + Default: "abcItemDefault", + }, + "childItem1": { + Value: "hello world " + testValueEncrypted, + Default: "", + }, + }, + }, + }, + { + name: "chained configOption from password (with decryption)", + args: args{ + configGroups: []kotsv1beta1.ConfigGroup{ + { + Name: "abc", + Title: "abc", + Description: "abc", + Items: []kotsv1beta1.ConfigItem{ + { + Name: "abcItem", + Type: "password", + Title: "abcItem", + Default: multitype.BoolOrString{ + Type: multitype.String, + StrVal: "abcItemDefault", + }, + Value: multitype.BoolOrString{}, + }, + { + Name: "childItem1", + Type: "text", + ReadOnly: true, + Value: multitype.BoolOrString{ + Type: multitype.String, + StrVal: `hello world repl{{ ConfigOption "abcItem" }}`, + }, + }, + }, + }, + }, + templateContext: map[string]ItemValue{ + "abcItem": { + Value: testValueEncrypted, + }, + }, + decryptValues: true, + }, + want: &ConfigCtx{ + DecryptValues: true, + AppSlug: "app-slug", + ItemValues: map[string]ItemValue{ + "abcItem": { + Value: testValue, + Default: "abcItemDefault", + }, + "childItem1": { + Value: "hello world " + testValue, + Default: "", + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -372,7 +485,7 @@ func TestBuilder_NewConfigContext(t *testing.T) { builder.AddCtx(StaticCtx{}) localRegistry := LocalRegistry{} - got, err := builder.newConfigContext(tt.args.configGroups, tt.args.templateContext, localRegistry, tt.args.license, nil, nil, registry.RegistryOptions{}, "app-slug") + got, err := builder.newConfigContext(tt.args.configGroups, tt.args.templateContext, localRegistry, tt.args.license, nil, nil, registry.RegistryOptions{}, "app-slug", tt.args.decryptValues) req.NoError(err) req.Equal(tt.want, got) }) diff --git a/pkg/upstream/replicated.go b/pkg/upstream/replicated.go index 33e4bf1b67..da9a1fc713 100644 --- a/pkg/upstream/replicated.go +++ b/pkg/upstream/replicated.go @@ -629,6 +629,7 @@ func createConfigValues(applicationName string, config *kotsv1beta1.Config, exis ApplicationInfo: appInfo, VersionInfo: versionInfo, IdentityConfig: identityConfig, + DecryptValues: true, } builder, _, err := template.NewBuilder(builderOptions) if err != nil {