Sensitive locals (#10509)
* Allow locals to be delcared as individual blocks, and give them the Sensitive flag * add docs for new local block * linting * add tests * modified parsing to use schema, check for dupes properly * update comment fix wording a liiitle * add tests for duplicate variables definition in two different files * remove unnecessary slice initialisation * fix crash by returning when decode error is hit * parseLocalVariables: only treat a local vars if its not nil also return in case of error return locals in case of error too * fix duplicate_locals test for windows Co-authored-by: Adrien Delorme <azr@users.noreply.github.com>
This commit is contained in:
parent
ea7fef699f
commit
fbbda0f9d9
|
@ -20,6 +20,7 @@ const (
|
||||||
variablesLabel = "variables"
|
variablesLabel = "variables"
|
||||||
variableLabel = "variable"
|
variableLabel = "variable"
|
||||||
localsLabel = "locals"
|
localsLabel = "locals"
|
||||||
|
localLabel = "local"
|
||||||
dataSourceLabel = "data"
|
dataSourceLabel = "data"
|
||||||
buildLabel = "build"
|
buildLabel = "build"
|
||||||
communicatorLabel = "communicator"
|
communicatorLabel = "communicator"
|
||||||
|
@ -32,6 +33,7 @@ var configSchema = &hcl.BodySchema{
|
||||||
{Type: variablesLabel},
|
{Type: variablesLabel},
|
||||||
{Type: variableLabel, LabelNames: []string{"name"}},
|
{Type: variableLabel, LabelNames: []string{"name"}},
|
||||||
{Type: localsLabel},
|
{Type: localsLabel},
|
||||||
|
{Type: localLabel, LabelNames: []string{"name"}},
|
||||||
{Type: dataSourceLabel, LabelNames: []string{"type", "name"}},
|
{Type: dataSourceLabel, LabelNames: []string{"type", "name"}},
|
||||||
{Type: buildLabel},
|
{Type: buildLabel},
|
||||||
{Type: communicatorLabel, LabelNames: []string{"type", "name"}},
|
{Type: communicatorLabel, LabelNames: []string{"type", "name"}},
|
||||||
|
@ -257,17 +259,8 @@ func sniffCoreVersionRequirements(body hcl.Body) ([]VersionConstraint, hcl.Diagn
|
||||||
return constraints, diags
|
return constraints, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *PackerConfig) Initialize(opts packer.InitializeOptions) hcl.Diagnostics {
|
func filterVarsFromLogs(inputOrLocal Variables) {
|
||||||
var diags hcl.Diagnostics
|
for _, variable := range inputOrLocal {
|
||||||
|
|
||||||
_, moreDiags := cfg.InputVariables.Values()
|
|
||||||
diags = append(diags, moreDiags...)
|
|
||||||
_, moreDiags = cfg.LocalVariables.Values()
|
|
||||||
diags = append(diags, moreDiags...)
|
|
||||||
diags = append(diags, cfg.evaluateDatasources(opts.SkipDatasourcesExecution)...)
|
|
||||||
diags = append(diags, cfg.evaluateLocalVariables(cfg.LocalBlocks)...)
|
|
||||||
|
|
||||||
for _, variable := range cfg.InputVariables {
|
|
||||||
if !variable.Sensitive {
|
if !variable.Sensitive {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -279,6 +272,20 @@ func (cfg *PackerConfig) Initialize(opts packer.InitializeOptions) hcl.Diagnosti
|
||||||
return true, nil
|
return true, nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *PackerConfig) Initialize(opts packer.InitializeOptions) hcl.Diagnostics {
|
||||||
|
var diags hcl.Diagnostics
|
||||||
|
|
||||||
|
_, moreDiags := cfg.InputVariables.Values()
|
||||||
|
diags = append(diags, moreDiags...)
|
||||||
|
_, moreDiags = cfg.LocalVariables.Values()
|
||||||
|
diags = append(diags, moreDiags...)
|
||||||
|
diags = append(diags, cfg.evaluateDatasources(opts.SkipDatasourcesExecution)...)
|
||||||
|
diags = append(diags, cfg.evaluateLocalVariables(cfg.LocalBlocks)...)
|
||||||
|
|
||||||
|
filterVarsFromLogs(cfg.InputVariables)
|
||||||
|
filterVarsFromLogs(cfg.LocalVariables)
|
||||||
|
|
||||||
// decode the actual content
|
// decode the actual content
|
||||||
for _, file := range cfg.files {
|
for _, file := range cfg.files {
|
||||||
|
|
|
@ -38,3 +38,8 @@ locals {
|
||||||
{id = "c"},
|
{id = "c"},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local "supersecret" {
|
||||||
|
expression = "${var.image_id}-password"
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
|
@ -36,3 +36,8 @@ locals {
|
||||||
service_name = "forum"
|
service_name = "forum"
|
||||||
owner = "Community Team"
|
owner = "Community Team"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local "supersecret" {
|
||||||
|
sensitive = true
|
||||||
|
expression = "secretvar"
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
local "sensible" {
|
||||||
|
expression = "something"
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
local "sensible" {
|
||||||
|
expression = "something"
|
||||||
|
}
|
|
@ -150,10 +150,20 @@ func (c *PackerConfig) parseLocalVariables(f *hcl.File) ([]*LocalBlock, hcl.Diag
|
||||||
|
|
||||||
content, moreDiags := f.Body.Content(configSchema)
|
content, moreDiags := f.Body.Content(configSchema)
|
||||||
diags = append(diags, moreDiags...)
|
diags = append(diags, moreDiags...)
|
||||||
var locals []*LocalBlock
|
|
||||||
|
locals := c.LocalBlocks
|
||||||
|
|
||||||
for _, block := range content.Blocks {
|
for _, block := range content.Blocks {
|
||||||
switch block.Type {
|
switch block.Type {
|
||||||
|
case localLabel:
|
||||||
|
l, moreDiags := decodeLocalBlock(block, locals)
|
||||||
|
diags = append(diags, moreDiags...)
|
||||||
|
if l != nil {
|
||||||
|
locals = append(locals, l)
|
||||||
|
}
|
||||||
|
if moreDiags.HasErrors() {
|
||||||
|
return locals, diags
|
||||||
|
}
|
||||||
case localsLabel:
|
case localsLabel:
|
||||||
attrs, moreDiags := block.Body.JustAttributes()
|
attrs, moreDiags := block.Body.JustAttributes()
|
||||||
diags = append(diags, moreDiags...)
|
diags = append(diags, moreDiags...)
|
||||||
|
@ -166,7 +176,7 @@ func (c *PackerConfig) parseLocalVariables(f *hcl.File) ([]*LocalBlock, hcl.Diag
|
||||||
Subject: attr.NameRange.Ptr(),
|
Subject: attr.NameRange.Ptr(),
|
||||||
Context: block.DefRange.Ptr(),
|
Context: block.DefRange.Ptr(),
|
||||||
})
|
})
|
||||||
return nil, diags
|
return locals, diags
|
||||||
}
|
}
|
||||||
locals = append(locals, &LocalBlock{
|
locals = append(locals, &LocalBlock{
|
||||||
Name: name,
|
Name: name,
|
||||||
|
@ -176,6 +186,7 @@ func (c *PackerConfig) parseLocalVariables(f *hcl.File) ([]*LocalBlock, hcl.Diag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.LocalBlocks = locals
|
||||||
return locals, diags
|
return locals, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,14 +232,14 @@ func (c *PackerConfig) evaluateLocalVariables(locals []*LocalBlock) hcl.Diagnost
|
||||||
|
|
||||||
func (c *PackerConfig) evaluateLocalVariable(local *LocalBlock) hcl.Diagnostics {
|
func (c *PackerConfig) evaluateLocalVariable(local *LocalBlock) hcl.Diagnostics {
|
||||||
var diags hcl.Diagnostics
|
var diags hcl.Diagnostics
|
||||||
|
|
||||||
value, moreDiags := local.Expr.Value(c.EvalContext(nil))
|
value, moreDiags := local.Expr.Value(c.EvalContext(nil))
|
||||||
diags = append(diags, moreDiags...)
|
diags = append(diags, moreDiags...)
|
||||||
if moreDiags.HasErrors() {
|
if moreDiags.HasErrors() {
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
c.LocalVariables[local.Name] = &Variable{
|
c.LocalVariables[local.Name] = &Variable{
|
||||||
Name: local.Name,
|
Name: local.Name,
|
||||||
|
Sensitive: local.Sensitive,
|
||||||
Values: []VariableAssignment{{
|
Values: []VariableAssignment{{
|
||||||
Value: value,
|
Value: value,
|
||||||
Expr: local.Expr,
|
Expr: local.Expr,
|
||||||
|
|
|
@ -104,6 +104,13 @@ func TestParser_complete(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
"supersecret": &Variable{
|
||||||
|
Name: "supersecret",
|
||||||
|
Values: []VariableAssignment{{From: "default",
|
||||||
|
Value: cty.StringVal("image-id-default-password")}},
|
||||||
|
Type: cty.String,
|
||||||
|
Sensitive: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Datasources: Datasources{
|
Datasources: Datasources{
|
||||||
DatasourceRef{Type: "amazon-ami", Name: "test"}: Datasource{
|
DatasourceRef{Type: "amazon-ami", Name: "test"}: Datasource{
|
||||||
|
|
|
@ -24,6 +24,9 @@ const badIdentifierDetail = "A name must start with a letter or underscore and m
|
||||||
type LocalBlock struct {
|
type LocalBlock struct {
|
||||||
Name string
|
Name string
|
||||||
Expr hcl.Expression
|
Expr hcl.Expression
|
||||||
|
// When Sensitive is set to true Packer will try its best to hide/obfuscate
|
||||||
|
// the variable from the output stream. By replacing the text.
|
||||||
|
Sensitive bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// VariableAssignment represents a way a variable was set: the expression
|
// VariableAssignment represents a way a variable was set: the expression
|
||||||
|
@ -246,6 +249,56 @@ var variableBlockSchema = &hcl.BodySchema{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var localBlockSchema = &hcl.BodySchema{
|
||||||
|
Attributes: []hcl.AttributeSchema{
|
||||||
|
{
|
||||||
|
Name: "expression",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "sensitive",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeLocalBlock(block *hcl.Block, locals []*LocalBlock) (*LocalBlock, hcl.Diagnostics) {
|
||||||
|
name := block.Labels[0]
|
||||||
|
for _, loc := range locals {
|
||||||
|
if loc.Name == name {
|
||||||
|
return nil, []*hcl.Diagnostic{{
|
||||||
|
Severity: hcl.DiagError,
|
||||||
|
Summary: "Duplicate variable",
|
||||||
|
Detail: "Duplicate " + block.Labels[0] + " variable definition found.",
|
||||||
|
Context: block.DefRange.Ptr(),
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
content, diags := block.Body.Content(localBlockSchema)
|
||||||
|
if !hclsyntax.ValidIdentifier(name) {
|
||||||
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
|
Severity: hcl.DiagError,
|
||||||
|
Summary: "Invalid local name",
|
||||||
|
Detail: badIdentifierDetail,
|
||||||
|
Subject: &block.LabelRanges[0],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
l := &LocalBlock{
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr, exists := content.Attributes["sensitive"]; exists {
|
||||||
|
valDiags := gohcl.DecodeExpression(attr.Expr, nil, &l.Sensitive)
|
||||||
|
diags = append(diags, valDiags...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if def, ok := content.Attributes["expression"]; ok {
|
||||||
|
l.Expr = def.Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
return l, diags
|
||||||
|
}
|
||||||
|
|
||||||
// decodeVariableBlock decodes a "variable" block
|
// decodeVariableBlock decodes a "variable" block
|
||||||
// ectx is passed only in the evaluation of the default value.
|
// ectx is passed only in the evaluation of the default value.
|
||||||
func (variables *Variables) decodeVariableBlock(block *hcl.Block, ectx *hcl.EvalContext) hcl.Diagnostics {
|
func (variables *Variables) decodeVariableBlock(block *hcl.Block, ectx *hcl.EvalContext) hcl.Diagnostics {
|
||||||
|
@ -254,7 +307,6 @@ func (variables *Variables) decodeVariableBlock(block *hcl.Block, ectx *hcl.Eval
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, found := (*variables)[block.Labels[0]]; found {
|
if _, found := (*variables)[block.Labels[0]]; found {
|
||||||
|
|
||||||
return []*hcl.Diagnostic{{
|
return []*hcl.Diagnostic{{
|
||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
Summary: "Duplicate variable",
|
Summary: "Duplicate variable",
|
||||||
|
|
|
@ -89,6 +89,15 @@ func TestParse_variables(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
Type: cty.String,
|
Type: cty.String,
|
||||||
},
|
},
|
||||||
|
"supersecret": &Variable{
|
||||||
|
Name: "supersecret",
|
||||||
|
Values: []VariableAssignment{{
|
||||||
|
From: "default",
|
||||||
|
Value: cty.StringVal("secretvar"),
|
||||||
|
}},
|
||||||
|
Type: cty.String,
|
||||||
|
Sensitive: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
false, false,
|
false, false,
|
||||||
|
@ -135,6 +144,28 @@ func TestParse_variables(t *testing.T) {
|
||||||
[]packersdk.Build{},
|
[]packersdk.Build{},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
{"duplicate local block",
|
||||||
|
defaultParser,
|
||||||
|
parseTestArgs{"testdata/variables/duplicate_locals", nil, nil},
|
||||||
|
&PackerConfig{
|
||||||
|
Basedir: "testdata/variables/duplicate_locals",
|
||||||
|
LocalVariables: Variables{
|
||||||
|
"sensible": &Variable{
|
||||||
|
Values: []VariableAssignment{
|
||||||
|
{
|
||||||
|
From: "default",
|
||||||
|
Value: cty.StringVal("something"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: cty.String,
|
||||||
|
Name: "sensible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true, true,
|
||||||
|
[]packersdk.Build{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
{"invalid default type",
|
{"invalid default type",
|
||||||
defaultParser,
|
defaultParser,
|
||||||
parseTestArgs{"testdata/variables/invalid_default.pkr.hcl", nil, nil},
|
parseTestArgs{"testdata/variables/invalid_default.pkr.hcl", nil, nil},
|
||||||
|
|
|
@ -21,9 +21,17 @@ Guide_](/guides/hcl/variables).
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
Local values are defined in `locals` blocks:
|
Local values are defined in `local` or `locals` blocks:
|
||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
|
# Using the local block allows you to mark locals as sensitive, which will
|
||||||
|
# filter their values from logs.
|
||||||
|
local "mylocal" {
|
||||||
|
expression = "${var.secret_api_key}"
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Using the locals block is more compact and efficient for declaring many locals
|
||||||
# Ids for multiple sets of EC2 instances, merged together
|
# Ids for multiple sets of EC2 instances, merged together
|
||||||
locals {
|
locals {
|
||||||
instance_ids = "${concat(aws_instance.blue.*.id, aws_instance.green.*.id)}"
|
instance_ids = "${concat(aws_instance.blue.*.id, aws_instance.green.*.id)}"
|
||||||
|
@ -72,6 +80,12 @@ source "amazon-ebs" "server" {
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
|
The `local` block defines exactly one local variable within a folder. The block
|
||||||
|
label is the name of the local, and the "expression" is the expression that
|
||||||
|
should be evaluated to create the local. Using this block, you can optionally
|
||||||
|
supply a "sensitive" boolean to mark the variable as sensitive and filter it
|
||||||
|
from logs.
|
||||||
|
|
||||||
The `locals` block defines one or more local variables within a folder.
|
The `locals` block defines one or more local variables within a folder.
|
||||||
|
|
||||||
The names given for the items in the `locals` block must be unique throughout a
|
The names given for the items in the `locals` block must be unique throughout a
|
||||||
|
|
|
@ -6,4 +6,10 @@ locals {
|
||||||
# locals can also be set with other variables :
|
# locals can also be set with other variables :
|
||||||
baz = "Foo is '${var.foo}' but not '${local.wee}'"
|
baz = "Foo is '${var.foo}' but not '${local.wee}'"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Use the singular local block if you need to mark a local as sensitive
|
||||||
|
local "mylocal" {
|
||||||
|
expression = "${var.secret_api_key}"
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
Loading…
Reference in New Issue