From 1c7f412d13257657194cd98297d0fc7d70f0a15c Mon Sep 17 00:00:00 2001 From: Alisdair McDiarmid Date: Mon, 16 Nov 2020 13:11:30 -0500 Subject: [PATCH] terraform: Unmark provider configuration arguments Before configuring a provider, we need to unmark the configuration object, in case it includes any sensitive values. This is required because configuration occurs over gRPC, which doesn't support sensitive marks. --- terraform/node_provider.go | 16 +++++-- terraform/node_provider_test.go | 74 +++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/terraform/node_provider.go b/terraform/node_provider.go index 9566a136b..e8979c5ba 100644 --- a/terraform/node_provider.go +++ b/terraform/node_provider.go @@ -68,8 +68,12 @@ func (n *NodeApplyableProvider) ValidateProvider(ctx EvalContext, provider provi return diags } + // If our config value contains any marked values, ensure those are + // stripped out before sending this to the provider + unmarkedConfigVal, _ := configVal.UnmarkDeep() + req := providers.PrepareProviderConfigRequest{ - Config: configVal, + Config: unmarkedConfigVal, } validateResp := provider.PrepareProviderConfig(req) @@ -109,10 +113,14 @@ func (n *NodeApplyableProvider) ConfigureProvider(ctx EvalContext, provider prov return diags } + // If our config value contains any marked values, ensure those are + // stripped out before sending this to the provider + unmarkedConfigVal, _ := configVal.UnmarkDeep() + // Allow the provider to validate and insert any defaults into the full // configuration. req := providers.PrepareProviderConfigRequest{ - Config: configVal, + Config: unmarkedConfigVal, } // PrepareProviderConfig is only used for validation. We are intentionally @@ -126,11 +134,11 @@ func (n *NodeApplyableProvider) ConfigureProvider(ctx EvalContext, provider prov // If the provider returns something different, log a warning to help // indicate to provider developers that the value is not used. preparedCfg := prepareResp.PreparedConfig - if preparedCfg != cty.NilVal && !preparedCfg.IsNull() && !preparedCfg.RawEquals(configVal) { + if preparedCfg != cty.NilVal && !preparedCfg.IsNull() && !preparedCfg.RawEquals(unmarkedConfigVal) { log.Printf("[WARN] PrepareProviderConfig from %q changed the config value, but that value is unused", n.Addr) } - configDiags := ctx.ConfigureProvider(n.Addr, configVal) + configDiags := ctx.ConfigureProvider(n.Addr, unmarkedConfigVal) diags = diags.Append(configDiags.InConfigBody(configBody)) return diags diff --git a/terraform/node_provider_test.go b/terraform/node_provider_test.go index 9ce13b9df..d85bd6431 100644 --- a/terraform/node_provider_test.go +++ b/terraform/node_provider_test.go @@ -115,3 +115,77 @@ func TestNodeApplyableProviderExecute_unknownApply(t *testing.T) { t.Errorf("wrong configuration value\ngot: %#v\nwant: %#v", got, want) } } + +func TestNodeApplyableProviderExecute_sensitive(t *testing.T) { + config := &configs.Provider{ + Name: "foo", + Config: configs.SynthBody("", map[string]cty.Value{ + "test_string": cty.StringVal("hello").Mark("sensitive"), + }), + } + provider := mockProviderWithConfigSchema(simpleTestSchema()) + providerAddr := addrs.AbsProviderConfig{ + Module: addrs.RootModule, + Provider: addrs.NewDefaultProvider("foo"), + } + + n := &NodeApplyableProvider{&NodeAbstractProvider{ + Addr: providerAddr, + Config: config, + }} + + ctx := &MockEvalContext{ProviderProvider: provider} + ctx.installSimpleEval() + if err := n.Execute(ctx, walkApply); err != nil { + t.Fatalf("err: %s", err) + } + + if !ctx.ConfigureProviderCalled { + t.Fatal("should be called") + } + + gotObj := ctx.ConfigureProviderConfig + if !gotObj.Type().HasAttribute("test_string") { + t.Fatal("configuration object does not have \"test_string\" attribute") + } + if got, want := gotObj.GetAttr("test_string"), cty.StringVal("hello"); !got.RawEquals(want) { + t.Errorf("wrong configuration value\ngot: %#v\nwant: %#v", got, want) + } +} + +func TestNodeApplyableProviderExecute_sensitiveValidate(t *testing.T) { + config := &configs.Provider{ + Name: "foo", + Config: configs.SynthBody("", map[string]cty.Value{ + "test_string": cty.StringVal("hello").Mark("sensitive"), + }), + } + provider := mockProviderWithConfigSchema(simpleTestSchema()) + providerAddr := addrs.AbsProviderConfig{ + Module: addrs.RootModule, + Provider: addrs.NewDefaultProvider("foo"), + } + + n := &NodeApplyableProvider{&NodeAbstractProvider{ + Addr: providerAddr, + Config: config, + }} + + ctx := &MockEvalContext{ProviderProvider: provider} + ctx.installSimpleEval() + if err := n.Execute(ctx, walkValidate); err != nil { + t.Fatalf("err: %s", err) + } + + if !provider.PrepareProviderConfigCalled { + t.Fatal("should be called") + } + + gotObj := provider.PrepareProviderConfigRequest.Config + if !gotObj.Type().HasAttribute("test_string") { + t.Fatal("configuration object does not have \"test_string\" attribute") + } + if got, want := gotObj.GetAttr("test_string"), cty.StringVal("hello"); !got.RawEquals(want) { + t.Errorf("wrong configuration value\ngot: %#v\nwant: %#v", got, want) + } +}