From 3e608ee8b9c017a736277df188ae878af4883b61 Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Thu, 10 Jul 2014 12:01:26 -0700 Subject: [PATCH] terraform: Do not persist sensitive state --- terraform/state.go | 35 ++++++++++++++++++++++++++++++++++- terraform/state_test.go | 19 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/terraform/state.go b/terraform/state.go index 73069f847..49dca37f1 100644 --- a/terraform/state.go +++ b/terraform/state.go @@ -131,6 +131,21 @@ func (s *State) String() string { return buf.String() } +// sensitiveState is used to store sensitive state information +// that should not be serialized. This is only used temporarily +// and is restored into the state. +type sensitiveState struct { + ConnInfo map[string]*ResourceConnectionInfo + + once sync.Once +} + +func (s *sensitiveState) init() { + s.once.Do(func() { + s.ConnInfo = make(map[string]*ResourceConnectionInfo) + }) +} + // The format byte is prefixed into the state file format so that we have // the ability in the future to change the file format if we want for any // reason. @@ -172,7 +187,25 @@ func WriteState(d *State, dst io.Writer) error { return errors.New("failed to write state version byte") } - return gob.NewEncoder(dst).Encode(d) + // Prevent sensitive information from being serialized + sensitive := &sensitiveState{} + sensitive.init() + for name, r := range d.Resources { + if r.ConnInfo != nil { + sensitive.ConnInfo[name] = r.ConnInfo + r.ConnInfo = nil + } + } + + // Serialize the state + err = gob.NewEncoder(dst).Encode(d) + + // Restore the state + for name, info := range sensitive.ConnInfo { + d.Resources[name].ConnInfo = info + } + + return err } // ResourceConnectionInfo holds addresses, credentials and configuration diff --git a/terraform/state_test.go b/terraform/state_test.go index 8b4944387..53af97908 100644 --- a/terraform/state_test.go +++ b/terraform/state_test.go @@ -98,20 +98,39 @@ func TestReadWriteState(t *testing.T) { Resources: map[string]*ResourceState{ "foo": &ResourceState{ ID: "bar", + ConnInfo: &ResourceConnectionInfo{ + Type: "ssh", + Raw: map[string]string{ + "user": "root", + "password": "supersecret", + }, + }, }, }, } + // Checksum before the write + chksum := checksumStruct(t, state) + buf := new(bytes.Buffer) if err := WriteState(state, buf); err != nil { t.Fatalf("err: %s", err) } + // Checksum after the write + chksumAfter := checksumStruct(t, state) + if chksumAfter != chksum { + t.Fatalf("structure changed during serialization!") + } + actual, err := ReadState(buf) if err != nil { t.Fatalf("err: %s", err) } + // ReadState should not restore sensitive information! + state.Resources["foo"].ConnInfo = nil + if !reflect.DeepEqual(actual, state) { t.Fatalf("bad: %#v", actual) }