diff --git a/src/github.com/cloudfoundry-incubator/cf-mysql-bootstrap/config/config.go b/src/github.com/cloudfoundry-incubator/cf-mysql-bootstrap/config/config.go index e12f99d0e..7074e86a3 100644 --- a/src/github.com/cloudfoundry-incubator/cf-mysql-bootstrap/config/config.go +++ b/src/github.com/cloudfoundry-incubator/cf-mysql-bootstrap/config/config.go @@ -16,7 +16,7 @@ type Config struct { HealthcheckURLs []string `yaml:"HealthcheckURLs" validate:"nonzero"` BackendTLS BackendTLS `yaml:"BackendTLS"` Username string `yaml:"Username" validate:"nonzero"` - Password string `yaml:"Password" validate:"nonzero"` + Password string `yaml:"Password" validate:"nonzero" json:"-"` ShutDownMysql string MysqlStatus string GetSeqNumber string @@ -28,7 +28,7 @@ type Config struct { type BackendTLS struct { Enabled bool `yaml:"Enabled"` ServerName string `yaml:"ServerName"` - CA string `yaml:"CA"` + CA string `yaml:"CA" json:"-"` InsecureSkipVerify bool `yaml:"InsecureSkipVerify"` } diff --git a/src/github.com/cloudfoundry-incubator/cf-mysql-bootstrap/config/config_test.go b/src/github.com/cloudfoundry-incubator/cf-mysql-bootstrap/config/config_test.go index f3078d025..af9eb18e9 100644 --- a/src/github.com/cloudfoundry-incubator/cf-mysql-bootstrap/config/config_test.go +++ b/src/github.com/cloudfoundry-incubator/cf-mysql-bootstrap/config/config_test.go @@ -1,6 +1,7 @@ package config_test import ( + "encoding/json" "fmt" . "github.com/cloudfoundry-incubator/cf-mysql-bootstrap/config" @@ -89,6 +90,30 @@ var _ = Describe("Config", func() { }) }) + Describe("Sensitive field redaction", func() { + BeforeEach(func() { + rawConfig = `{ + "HealthcheckURLs": ["http://10.10.10.10:9200"], + "Username": "fake-username", + "Password": "fake-password", + "RepairMode": "bootstrap" + }` + }) + + It("does not serialize Password in JSON output", func() { + data, err := json.Marshal(rootConfig) + Expect(err).NotTo(HaveOccurred()) + Expect(string(data)).NotTo(ContainSubstring("fake-password")) + }) + + It("does not serialize BackendTLS CA in JSON output", func() { + rootConfig.BackendTLS.CA = "-----BEGIN CERTIFICATE-----\nfake-ca-cert\n-----END CERTIFICATE-----" + data, err := json.Marshal(rootConfig) + Expect(err).NotTo(HaveOccurred()) + Expect(string(data)).NotTo(ContainSubstring("fake-ca-cert")) + }) + }) + Describe("TLS Config", func() { When("BackendTLS params are provided", func() { BeforeEach(func() { diff --git a/src/github.com/cloudfoundry-incubator/cf-mysql-bootstrap/main.go b/src/github.com/cloudfoundry-incubator/cf-mysql-bootstrap/main.go index 7adc9f24a..0a2e5f1c9 100644 --- a/src/github.com/cloudfoundry-incubator/cf-mysql-bootstrap/main.go +++ b/src/github.com/cloudfoundry-incubator/cf-mysql-bootstrap/main.go @@ -41,7 +41,8 @@ func main() { if err != nil { logger.Error("Failed to repair cluster", err, lager.Data{ - "config": rootConfig, + "repair_mode": rootConfig.RepairMode, + "healthcheck_urls": rootConfig.HealthcheckURLs, }) printHumanReadableErr(err, rootConfig.RepairMode) os.Exit(1) diff --git a/src/github.com/cloudfoundry-incubator/cf-mysql-bootstrap/main_test.go b/src/github.com/cloudfoundry-incubator/cf-mysql-bootstrap/main_test.go index fa016d865..1ba8e8b18 100644 --- a/src/github.com/cloudfoundry-incubator/cf-mysql-bootstrap/main_test.go +++ b/src/github.com/cloudfoundry-incubator/cf-mysql-bootstrap/main_test.go @@ -1,6 +1,9 @@ package main_test import ( + "fmt" + "os/exec" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" @@ -16,4 +19,30 @@ var _ = Describe("Bootstrap Executable", func() { Expect(err).ToNot(HaveOccurred()) Expect(binaryPath).To(BeAnExistingFile()) }) + + Describe("credential safety", func() { + It("does not log the galera-agent password when bootstrap fails", func() { + binaryPath, err := gexec.Build("github.com/cloudfoundry-incubator/cf-mysql-bootstrap") + Expect(err).ToNot(HaveOccurred()) + + const sensitivePassword = "s3cr3t-galera-agent-password" + config := fmt.Sprintf(`{ + "HealthcheckURLs": ["http://127.0.0.1:19999"], + "Username": "galera-agent", + "Password": %q, + "RepairMode": "bootstrap" + }`, sensitivePassword) + + cmd := exec.Command(binaryPath, fmt.Sprintf("-config=%s", config)) + session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter) + Expect(err).ToNot(HaveOccurred()) + + Eventually(session, "10s").Should(gexec.Exit()) + Expect(session.ExitCode()).NotTo(Equal(0)) + + combinedOutput := string(session.Out.Contents()) + string(session.Err.Contents()) + Expect(combinedOutput).NotTo(ContainSubstring(sensitivePassword), + "galera-agent password must not appear in log output") + }) + }) })