Handle static variable secret flag (#2045)

Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
This commit is contained in:
Christian Mesh 2024-10-03 10:46:58 -04:00 committed by GitHub
parent 8638b815dc
commit 0d1e6cd5f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 95 additions and 20 deletions

View File

@ -1,8 +1,18 @@
variable "passphrase" {
type = string
default = "26281afb-83f1-47ec-9b2d-2aebf6417167"
sensitive = true
}
locals {
key_length = sensitive(32)
}
terraform {
encryption {
key_provider "pbkdf2" "basic" {
passphrase = "26281afb-83f1-47ec-9b2d-2aebf6417167"
key_length = 32
passphrase = var.passphrase
key_length = local.key_length
iterations = 200000
hash_function = "sha512"
salt_length = 12

View File

@ -13,6 +13,7 @@ import (
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/lang"
"github.com/opentofu/opentofu/internal/lang/marks"
"github.com/zclconf/go-cty/cty"
)
@ -62,6 +63,15 @@ func (b *Backend) Hash(schema *configschema.Block) (int, hcl.Diagnostics) {
val = cty.UnknownVal(schema.ImpliedType())
}
if marks.Contains(val, marks.Sensitive) {
return -1, diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Backend config contains sensitive values",
Detail: "The backend configuration is stored in .terraform/terraform.tfstate as well as plan files. It is recommended to instead supply sensitive credentials via backend specific environment variables",
Subject: b.DeclRange.Ptr(),
})
}
toHash := cty.TupleVal([]cty.Value{
cty.StringVal(b.Type),
val,

View File

@ -6,11 +6,14 @@
package configs
import (
"fmt"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/lang"
"github.com/opentofu/opentofu/internal/lang/marks"
"github.com/zclconf/go-cty/cty"
)
@ -90,21 +93,21 @@ func (s StaticEvaluator) Evaluate(expr hcl.Expression, ident StaticIdentifier) (
}
func (s StaticEvaluator) DecodeExpression(expr hcl.Expression, ident StaticIdentifier, val any) hcl.Diagnostics {
var diags hcl.Diagnostics
refs, refsDiags := lang.ReferencesInExpr(addrs.ParseRef, expr)
diags = append(diags, refsDiags.ToHCL()...)
srcVal, diags := s.Evaluate(expr, ident)
if diags.HasErrors() {
return diags
}
ctx, ctxDiags := s.scope(ident).EvalContext(refs)
diags = append(diags, ctxDiags.ToHCL()...)
if diags.HasErrors() {
return diags
if marks.Contains(srcVal, marks.Sensitive) {
return diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Sensitive value not allowed",
Detail: fmt.Sprintf("Sensitive values, or values derived from sensitive values, cannot be used as %s.", ident.String()),
Subject: expr.Range().Ptr(),
})
}
return gohcl.DecodeExpression(expr, ctx, val)
return diags.Extend(gohcl.DecodeValue(srcVal, expr.StartRange(), expr.Range(), val))
}
func (s StaticEvaluator) DecodeBlock(body hcl.Body, spec hcldec.Spec, ident StaticIdentifier) (cty.Value, hcl.Diagnostics) {

View File

@ -321,8 +321,11 @@ func TestStaticEvaluator_DecodeExpression(t *testing.T) {
}{{
expr: `"static"`,
}, {
expr: `count`,
diags: []string{`eval.tf:1,1-6: Invalid reference; The "count" object cannot be accessed directly. Instead, access one of its attributes.`},
expr: `count`,
diags: []string{
`eval.tf:1,1-6: Invalid reference; The "count" object cannot be accessed directly. Instead, access one of its attributes.`,
`:0,0-0: Dynamic value in static context; Unable to use count. in static context, which is required by local.test`,
},
}, {
expr: `module.foo.bar`,
diags: []string{`eval.tf:1,1-15: Module output not supported in static context; Unable to use module.foo.bar in static context, which is required by local.test`},
@ -372,8 +375,28 @@ terraform {
}
}`,
diags: []string{`eval.tf:4,11-25: Module output not supported in static context; Unable to use module.foo.bar in static context, which is required by backend.badeval`},
}, {
ident: "sensitive",
body: `
locals {
sens = sensitive("magic")
}
terraform {
backend "sensitive" {
thing = local.sens
}
}`,
diags: []string{`eval.tf:6,2-21: Backend config contains sensitive values; The backend configuration is stored in .terraform/terraform.tfstate as well as plan files. It is recommended to instead supply sensitive credentials via backend specific environment variables`},
}}
schema := &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"thing": &configschema.Attribute{
Type: cty.String,
},
},
}
for _, tc := range cases {
t.Run(tc.ident, func(t *testing.T) {
parser := testParser(map[string]string{"eval.tf": tc.body})
@ -383,13 +406,14 @@ terraform {
}
mod, _ := NewModule([]*File{file}, nil, RootModuleCallForTesting(), "dir", SelectiveLoadAll)
_, diags := mod.Backend.Decode(&configschema.Block{
Attributes: map[string]*configschema.Attribute{
"thing": &configschema.Attribute{
Type: cty.String,
},
},
})
_, diags := mod.Backend.Hash(schema)
if diags.HasErrors() {
assertExactDiagnostics(t, diags, tc.diags)
return
}
_, diags = mod.Backend.Decode(schema)
assertExactDiagnostics(t, diags, tc.diags)
})

View File

@ -16,6 +16,7 @@ import (
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/didyoumean"
"github.com/opentofu/opentofu/internal/lang"
"github.com/opentofu/opentofu/internal/lang/marks"
"github.com/opentofu/opentofu/internal/tfdiags"
)
@ -256,6 +257,9 @@ func (s staticScopeData) GetInputVariable(ident addrs.InputVariable, rng tfdiags
}
val, valDiags := s.eval.call.vars(variable)
if variable.Sensitive {
val = val.Mark(marks.Sensitive)
}
return val, s.enhanceDiagnostics(id, diags.Append(valDiags))
}

View File

@ -0,0 +1,5 @@
module "test" {
source = sensitive("hostname/namespace/name/system") # ERROR: Sensitive value not allowed
version = sensitive("1.0.0") # ERROR: Invalid version constraint
}

View File

@ -10,6 +10,7 @@ import (
version "github.com/hashicorp/go-version"
"github.com/hashicorp/hcl/v2"
"github.com/opentofu/opentofu/internal/lang/marks"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/convert"
)
@ -37,6 +38,15 @@ func decodeVersionConstraintValue(attr *hcl.Attribute, val cty.Value) (VersionCo
DeclRange: attr.Range,
}
if val.HasMark(marks.Sensitive) {
return ret, diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid version constraint",
Detail: fmt.Sprintf("Sensitive values, or values derived from sensitive values, cannot be used as %s arguments.", attr.Name),
Subject: attr.Expr.Range().Ptr(),
})
}
var err error
val, err = convert.Convert(val, cty.String)
if err != nil {

View File

@ -14,6 +14,7 @@ import (
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/encryption/config"
"github.com/opentofu/opentofu/internal/lang"
"github.com/opentofu/opentofu/internal/lang/marks"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
@ -187,6 +188,14 @@ func (e *targetBuilder) setupKeyProvider(cfg config.KeyProviderConfig, stack []c
return diags
}
// gohcl does not handle marks, we need to remove the sensitive marks from any input variables
// We assume that the entire configuration in the encryption block should be treated as sensitive
for key, sv := range evalCtx.Variables {
if marks.Contains(sv, marks.Sensitive) {
evalCtx.Variables[key], _ = sv.UnmarkDeep()
}
}
// Initialize the Key Provider
decodeDiags := gohcl.DecodeBody(cfg.Body, evalCtx, keyProviderConfig)
diags = append(diags, decodeDiags...)