replace scrubconfig with packer.LogSecretFilter.Set

filter winrm password from logs
Add new root-level packer template option, sensitive-variables, to tell us what user variables to mark sensitive.
This commit is contained in:
Megan Marsh 2018-08-10 14:25:14 -07:00
parent 31fcfe4bc2
commit ff6a039d5b
32 changed files with 95 additions and 89 deletions

View File

@ -67,7 +67,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, errs
}
log.Println(common.ScrubConfig(b.config, b.config.AlicloudAccessKey, b.config.AlicloudSecretKey))
packer.LogSecretFilter.Set(b.config.AlicloudAccessKey, b.config.AlicloudSecretKey)
log.Println(b.config)
return nil, nil
}

View File

@ -176,7 +176,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return warns, errs
}
log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey, b.config.Token))
packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token)
log.Println(b.config)
return warns, nil
}

View File

@ -95,7 +95,9 @@ WaitLoop:
"Password (since debug is enabled): %s", s.Comm.WinRMPassword))
}
// store so that we can access this later during provisioning
commonhelper.SetSharedState("winrm_password", s.Comm.WinRMPassword, s.BuildName)
packer.LogSecretFilter.Set(s.Comm.WinRMPassword)
return multistep.ActionContinue
}

View File

@ -39,7 +39,6 @@ type Builder struct {
}
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
log.Printf("SECRET: matt")
b.config.ctx.Funcs = awscommon.TemplateFuncs
err := config.Decode(&b.config, &config.DecodeOpts{
Interpolate: true,
@ -82,7 +81,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, errs
}
log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey, b.config.Token))
packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token)
log.Println(b.config)
return nil, nil
}
@ -261,7 +261,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Run!
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
b.runner.Run(state)
// If there was an error, return that
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)

View File

@ -96,7 +96,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, errs
}
log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey, b.config.Token))
packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token)
log.Println(b.config)
return nil, nil
}

View File

@ -81,7 +81,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, errs
}
log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey, b.config.Token))
packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token)
log.Println(b.config)
return nil, nil
}

View File

@ -166,8 +166,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
if errs != nil && len(errs.Errors) > 0 {
return nil, errs
}
log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey, b.config.Token))
packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token)
log.Println(b.config)
return nil, nil
}

View File

@ -363,6 +363,7 @@ func setRuntimeValues(c *Config) {
c.tmpAdminPassword = tempName.AdminPassword
// store so that we can access this later during provisioning
commonhelper.SetSharedState("winrm_password", c.tmpAdminPassword, c.PackerConfig.PackerBuildName)
packer.LogSecretFilter.Set(c.tmpAdminPassword)
c.tmpCertificatePassword = tempName.CertificatePassword
if c.TempComputeName == "" {

View File

@ -5,6 +5,7 @@ import (
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
type StepSaveWinRMPassword struct {
@ -15,6 +16,7 @@ type StepSaveWinRMPassword struct {
func (s *StepSaveWinRMPassword) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
// store so that we can access this later during provisioning
commonhelper.SetSharedState("winrm_password", s.Password, s.BuildName)
packer.LogSecretFilter.Set(s.Password)
return multistep.ActionContinue
}

View File

@ -138,6 +138,6 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
return nil, nil, errs
}
common.ScrubConfig(c, c.APIToken)
packer.LogSecretFilter.Set(c.APIToken)
return c, nil, nil
}

View File

@ -33,6 +33,7 @@ func (s *StepCreateWindowsPassword) Run(_ context.Context, state multistep.State
if c.Comm.WinRMPassword != "" {
state.Put("winrm_password", c.Comm.WinRMPassword)
packer.LogSecretFilter.Set(c.Comm.WinRMPassword)
return multistep.ActionContinue
}
@ -114,6 +115,7 @@ func (s *StepCreateWindowsPassword) Run(_ context.Context, state multistep.State
state.Put("winrm_password", data.password)
commonhelper.SetSharedState("winrm_password", data.password, c.PackerConfig.PackerBuildName)
packer.LogSecretFilter.Set(data.password)
return multistep.ActionContinue
}

View File

@ -109,7 +109,6 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
if errs != nil && len(errs.Errors) > 0 {
return nil, nil, errs
}
common.ScrubConfig(c, c.Token)
packer.LogSecretFilter.Set(c.Token)
return &c, nil, nil
}

View File

@ -57,7 +57,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
b.config.InstanceName = b.config.ImageName
}
log.Println(common.ScrubConfig(b.config, b.config.Password))
packer.LogSecretFilter.Set(b.config.Password)
log.Println(b.config)
return nil, nil
}

View File

@ -53,7 +53,7 @@ func (s *stepGetDefaultCredentials) Run(ctx context.Context, state multistep.Sta
// store so that we can access this later during provisioning
commonhelper.SetSharedState("winrm_password", s.Comm.WinRMPassword, s.BuildName)
packer.LogSecretFilter.Set(s.Comm.WinRMPassword)
return multistep.ActionContinue
}

View File

@ -122,7 +122,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
if errs != nil && len(errs.Errors) > 0 {
return nil, nil, errs
}
common.ScrubConfig(c, c.PBUsername)
packer.LogSecretFilter.Set(c.PBUsername)
return &c, nil, nil
}

View File

@ -119,6 +119,6 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
return nil, nil, errs
}
common.ScrubConfig(c, c.Token)
packer.LogSecretFilter.Set(c.Token)
return c, nil, nil
}

View File

@ -110,7 +110,6 @@ func (c *BuildCommand) Run(args []string) int {
log.Printf("Build debug mode: %v", cfgDebug)
log.Printf("Force build: %v", cfgForce)
log.Printf("On error: %v", cfgOnError)
//log.Printf("my secrets: %v", c.CoreConfig.
// Set the debug and force mode and prepare all the builds
for _, b := range builds {

View File

@ -29,8 +29,6 @@ type Meta struct {
Cache packer.Cache
Ui packer.Ui
Version string
//Secrets []string
//secrets: []string{"matt"},
// These are set by command-line flags
flagBuildExcept []string

View File

@ -20,19 +20,6 @@ const PackerKeyEnv = "PACKER_KEY_INTERVAL"
// shorter delay (e.g. 10ms) can be used on a workstation. See PackerKeyEnv.
const PackerKeyDefault = 100 * time.Millisecond
// ScrubConfig is a helper that returns a string representation of
// any struct with the given values stripped out.
func ScrubConfig(target interface{}, values ...string) string {
conf := fmt.Sprintf("Config: %+v", target)
for _, value := range values {
if value == "" {
continue
}
conf = strings.Replace(conf, value, "<Filtered>", -1)
}
return conf
}
// ChooseString returns the first non-empty value.
func ChooseString(vals ...string) string {
for _, el := range vals {

View File

@ -310,20 +310,3 @@ func TestFileExistsLocally(t *testing.T) {
}
}
}
func TestScrubConfig(t *testing.T) {
type Inner struct {
Baz string
}
type Local struct {
Foo string
Bar string
Inner
}
c := Local{"foo", "bar", Inner{"bar"}}
expect := "Config: {Foo:foo Bar:<Filtered> Inner:{Baz:<Filtered>}}"
conf := ScrubConfig(c, c.Bar)
if conf != expect {
t.Fatalf("got %s, expected %s", conf, expect)
}
}

View File

@ -4,10 +4,11 @@ package common
// are sent by packer, properly tagged already so mapstructure can load
// them. Embed this structure into your configuration class to get it.
type PackerConfig struct {
PackerBuildName string `mapstructure:"packer_build_name"`
PackerBuilderType string `mapstructure:"packer_builder_type"`
PackerDebug bool `mapstructure:"packer_debug"`
PackerForce bool `mapstructure:"packer_force"`
PackerOnError string `mapstructure:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables"`
PackerBuildName string `mapstructure:"packer_build_name"`
PackerBuilderType string `mapstructure:"packer_builder_type"`
PackerDebug bool `mapstructure:"packer_debug"`
PackerForce bool `mapstructure:"packer_force"`
PackerOnError string `mapstructure:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables"`
}

View File

@ -70,12 +70,7 @@ func Run(ui packer.Ui, config *Config) (bool, error) {
// buffers and for reading the final exit status.
flattenedCmd := strings.Join(interpolatedCmds, " ")
cmd := &packer.RemoteCmd{Command: flattenedCmd}
sanitized := flattenedCmd
if len(getWinRMPassword(config.PackerBuildName)) > 0 {
sanitized = strings.Replace(flattenedCmd,
getWinRMPassword(config.PackerBuildName), "*****", -1)
}
log.Printf("[INFO] (shell-local): starting local command: %s", sanitized)
log.Printf("[INFO] (shell-local): starting local command: %s", flattenedCmd)
if err := cmd.StartWithUi(comm, ui); err != nil {
return false, fmt.Errorf(
"Error executing script: %s\n\n"+
@ -204,5 +199,6 @@ func createFlattenedEnvVars(config *Config) (string, error) {
func getWinRMPassword(buildName string) string {
winRMPass, _ := commonhelper.RetrieveSharedState("winrm_password", buildName)
packer.LogSecretFilter.Set(winRMPass)
return winRMPass
}

View File

@ -108,10 +108,11 @@ func Decode(target interface{}, config *DecodeOpts, raws ...interface{}) error {
// detecting things like user variables from the raw configuration params.
func DetectContext(raws ...interface{}) (*interpolate.Context, error) {
var s struct {
BuildName string `mapstructure:"packer_build_name"`
BuildType string `mapstructure:"packer_builder_type"`
TemplatePath string `mapstructure:"packer_template_path"`
Vars map[string]string `mapstructure:"packer_user_variables"`
BuildName string `mapstructure:"packer_build_name"`
BuildType string `mapstructure:"packer_builder_type"`
TemplatePath string `mapstructure:"packer_template_path"`
Vars map[string]string `mapstructure:"packer_user_variables"`
SensitiveVars []string `mapstructure:"packer_sensitive_variables"`
}
for _, r := range raws {
@ -121,10 +122,11 @@ func DetectContext(raws ...interface{}) (*interpolate.Context, error) {
}
return &interpolate.Context{
BuildName: s.BuildName,
BuildType: s.BuildType,
TemplatePath: s.TemplatePath,
UserVariables: s.Vars,
BuildName: s.BuildName,
BuildType: s.BuildType,
TemplatePath: s.TemplatePath,
UserVariables: s.Vars,
SensitiveVariables: s.SensitiveVars,
}, nil
}

View File

@ -25,10 +25,11 @@ type Core struct {
// CoreConfig is the structure for initializing a new Core. Once a CoreConfig
// is used to initialize a Core, it shouldn't be re-used or modified again.
type CoreConfig struct {
Components ComponentFinder
Template *template.Template
Variables map[string]string
Version string
Components ComponentFinder
Template *template.Template
Variables map[string]string
SensitiveVariables []string
Version string
}
// The function type used to lookup Builder implementations.
@ -61,14 +62,16 @@ func NewCore(c *CoreConfig) (*Core, error) {
variables: c.Variables,
version: c.Version,
}
if err := result.validate(); err != nil {
return nil, err
}
if err := result.init(); err != nil {
return nil, err
}
LogSecretFilter.Set("matt")
//log.Printf("NewCore: %+v", result.Template.Variables["efoo"])
for _, secret := range result.secrets {
LogSecretFilter.Set(secret)
}
// Go through and interpolate all the build names. We should be able
// to do this at this point with the variables.
@ -305,6 +308,16 @@ func (c *Core) init() error {
c.variables[k] = def
}
for _, v := range c.Template.SensitiveVariables {
def, err := interpolate.Render(v.Default, ctx)
if err != nil {
return fmt.Errorf(
"error interpolating default value for '%s': %s",
v, err)
}
c.secrets = append(c.secrets, def)
}
// Interpolate the push configuration
if _, err := interpolate.RenderInterface(&c.Template.Push, c.Context()); err != nil {
return fmt.Errorf("Error interpolating 'push': %s", err)

View File

@ -28,10 +28,11 @@ func (l *secretFilter) SetOutput(output io.Writer) {
func (l *secretFilter) Write(p []byte) (n int, err error) {
for s := range l.s {
p = bytes.Replace(p, []byte(s), []byte("<filtered>"), -1)
if s != "" {
p = bytes.Replace(p, []byte(s), []byte("<sensitive>"), -1)
}
}
return l.w.Write(p)
// return l.w.Write([]byte("foobar"))
}
func (l *secretFilter) get() (s []string) {

View File

@ -113,7 +113,8 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
return errs
}
log.Println(common.ScrubConfig(p.config, p.config.AlicloudAccessKey, p.config.AlicloudSecretKey))
packer.LogSecretFilter.Set(p.config.AlicloudAccessKey, p.config.AlicloudSecretKey)
log.Println(p.config)
return nil
}

View File

@ -92,7 +92,8 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
return errs
}
log.Println(common.ScrubConfig(p.config, p.config.AccessKey, p.config.SecretKey, p.config.Token))
packer.LogSecretFilter.Set(p.config.AccessKey, p.config.SecretKey, p.config.Token)
log.Println(p.config)
return nil
}

View File

@ -555,6 +555,7 @@ func newSigner(privKeyFile string) (*signer, error) {
func getWinRMPassword(buildName string) string {
winRMPass, _ := commonhelper.RetrieveSharedState("winrm_password", buildName)
packer.LogSecretFilter.Set(winRMPass)
return winRMPass
}

View File

@ -481,6 +481,7 @@ func (p *Provisioner) createCommandTextNonPrivileged() (command string, err erro
func getWinRMPassword(buildName string) string {
winRMPass, _ := commonhelper.RetrieveSharedState("winrm_password", buildName)
packer.LogSecretFilter.Set(winRMPass)
return winRMPass
}

View File

@ -18,6 +18,9 @@ type Context struct {
// "user" function reads from.
UserVariables map[string]string
// SensitiveVariables is a list of variables to sanitize.
SensitiveVariables []string
// EnableEnv enables the env function
EnableEnv bool

View File

@ -7,6 +7,7 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"sort"
@ -23,11 +24,12 @@ type rawTemplate struct {
MinVersion string `mapstructure:"min_packer_version"`
Description string
Builders []map[string]interface{}
Push map[string]interface{}
PostProcessors []interface{} `mapstructure:"post-processors"`
Provisioners []map[string]interface{}
Variables map[string]interface{}
Builders []map[string]interface{}
Push map[string]interface{}
PostProcessors []interface{} `mapstructure:"post-processors"`
Provisioners []map[string]interface{}
Variables map[string]interface{}
SensitiveVariables []string `mapstructure:"sensitive-variables"`
RawContents []byte
}
@ -60,6 +62,12 @@ func (r *rawTemplate) Template() (*Template, error) {
continue
}
for _, sVar := range r.SensitiveVariables {
if sVar == k {
result.SensitiveVariables = append(result.SensitiveVariables, &v)
}
}
result.Variables[k] = &v
}

View File

@ -18,11 +18,12 @@ type Template struct {
Description string
MinVersion string
Variables map[string]*Variable
Builders map[string]*Builder
Provisioners []*Provisioner
PostProcessors [][]*PostProcessor
Push Push
Variables map[string]*Variable
SensitiveVariables []*Variable
Builders map[string]*Builder
Provisioners []*Provisioner
PostProcessors [][]*PostProcessor
Push Push
// RawContents is just the raw data for this template
RawContents []byte