diff --git a/agent/agent.go b/agent/agent.go index 02aac61df..1132cdd9e 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -22,17 +22,18 @@ type Logger interface { } type Agent struct { - Update UpdateFunc - Logger LoggerFunc - Engine build.Engine - Timeout time.Duration - Platform string - Namespace string - Disable []string - Escalate []string - Netrc []string - Local string - Pull bool + Update UpdateFunc + Logger LoggerFunc + Engine build.Engine + Timeout time.Duration + Platform string + Namespace string + Disable []string + Escalate []string + Netrc []string + Local string + Pull bool + ConcealSecrets bool } func (a *Agent) Poll() error { @@ -188,6 +189,7 @@ func (a *Agent) exec(spec *yaml.Config, payload *model.Work, cancel <-chan bool) return err } + secretsReplacer := newSecretsReplacer(payload.Secrets) timeout := time.After(time.Duration(payload.Repo.Timeout) * time.Minute) for { @@ -227,11 +229,25 @@ func (a *Agent) exec(spec *yaml.Config, payload *model.Work, cancel <-chan bool) pipeline.Exec() } case line := <-pipeline.Pipe(): + // FIXME(vaijab): avoid checking a.ConcealSecrets is true everytime new line is received + if a.ConcealSecrets { + line.Out = secretsReplacer.Replace(line.Out) + } a.Logger(line) } } } +// newSecretsReplacer takes []*model.Secret as secrets and returns a list of +// secret value, "*****" pairs. +func newSecretsReplacer(secrets []*model.Secret) *strings.Replacer { + var r []string + for _, s := range secrets { + r = append(r, s.Value, "*****") + } + return strings.NewReplacer(r...) +} + func toEnv(w *model.Work) map[string]string { envs := map[string]string{ "CI": "drone", diff --git a/agent/agent_test.go b/agent/agent_test.go new file mode 100644 index 000000000..5857e878d --- /dev/null +++ b/agent/agent_test.go @@ -0,0 +1,23 @@ +package agent + +import "testing" +import "github.com/drone/drone/model" + +func Test_newSecretsReplacer(t *testing.T) { + secrets := []*model.Secret{ + {Name: "SECRET", + Value: "secret_value", + Images: []string{"*"}, + Events: []string{"*"}, + }, + } + + text := "This is SECRET: secret_value" + expected := "This is SECRET: *****" + secretsReplacer := newSecretsReplacer(secrets) + result := secretsReplacer.Replace(text) + + if result != expected { + t.Errorf("Wanted %q, got %q.", expected, result) + } +} diff --git a/drone/agent/agent.go b/drone/agent/agent.go index c9a51eaaa..373254081 100644 --- a/drone/agent/agent.go +++ b/drone/agent/agent.go @@ -75,6 +75,11 @@ var AgentCmd = cli.Command{ Name: "drone-secret", Usage: "drone agent secret", }, + cli.BoolFlag{ + Name: "conceal-secrets", + Usage: "conceal secrets from build logs", + EnvVar: "DRONE_CONCEAL_SECRETS", + }, cli.DurationFlag{ EnvVar: "DRONE_BACKOFF", Name: "backoff", @@ -186,12 +191,13 @@ func start(c *cli.Context) { drone: client, docker: docker, config: config{ - platform: c.String("docker-os") + "/" + c.String("docker-arch"), - timeout: c.Duration("timeout"), - namespace: c.String("namespace"), - privileged: c.StringSlice("privileged"), - pull: c.BoolT("pull"), - logs: int64(c.Int("max-log-size")) * 1000000, + platform: c.String("docker-os") + "/" + c.String("docker-arch"), + timeout: c.Duration("timeout"), + namespace: c.String("namespace"), + privileged: c.StringSlice("privileged"), + pull: c.BoolT("pull"), + logs: int64(c.Int("max-log-size")) * 1000000, + concealSecrets: c.Bool("conceal-secrets"), }, } diff --git a/drone/agent/exec.go b/drone/agent/exec.go index 7ad0b458c..79a9de219 100644 --- a/drone/agent/exec.go +++ b/drone/agent/exec.go @@ -13,12 +13,13 @@ import ( ) type config struct { - platform string - namespace string - privileged []string - pull bool - logs int64 - timeout time.Duration + platform string + namespace string + privileged []string + pull bool + logs int64 + timeout time.Duration + concealSecrets bool } type pipeline struct { @@ -40,14 +41,15 @@ func (r *pipeline) run(w *model.Work) { engine := docker.NewClient(r.docker) a := agent.Agent{ - Update: agent.NewClientUpdater(r.drone), - Logger: agent.NewClientLogger(r.drone, w.Job.ID, r.config.logs), - Engine: engine, - Timeout: r.config.timeout, - Platform: r.config.platform, - Namespace: r.config.namespace, - Escalate: r.config.privileged, - Pull: r.config.pull, + Update: agent.NewClientUpdater(r.drone), + Logger: agent.NewClientLogger(r.drone, w.Job.ID, r.config.logs), + Engine: engine, + Timeout: r.config.timeout, + Platform: r.config.platform, + Namespace: r.config.namespace, + Escalate: r.config.privileged, + Pull: r.config.pull, + ConcealSecrets: r.config.concealSecrets, } cancelFunc := func(m *stomp.Message) { diff --git a/drone/exec.go b/drone/exec.go index f061f1bc9..17111c48d 100644 --- a/drone/exec.go +++ b/drone/exec.go @@ -48,6 +48,11 @@ var execCmd = cli.Command{ Usage: "build secrets file in KEY=VALUE format", EnvVar: "DRONE_SECRETS_FILE", }, + cli.BoolFlag{ + Name: "conceal-secrets", + Usage: "conceal secrets from build logs", + EnvVar: "DRONE_CONCEAL_SECRETS", + }, cli.StringSliceFlag{ Name: "matrix", Usage: "build matrix in KEY=VALUE format", @@ -326,17 +331,18 @@ func exec(c *cli.Context) error { } a := agent.Agent{ - Update: agent.NoopUpdateFunc, - Logger: agent.TermLoggerFunc, - Engine: engine, - Timeout: c.Duration("timeout.inactivity"), - Platform: "linux/amd64", - Namespace: c.String("namespace"), - Disable: c.StringSlice("plugin"), - Escalate: c.StringSlice("privileged"), - Netrc: []string{}, - Local: dir, - Pull: c.Bool("pull"), + Update: agent.NoopUpdateFunc, + Logger: agent.TermLoggerFunc, + Engine: engine, + Timeout: c.Duration("timeout.inactivity"), + Platform: "linux/amd64", + Namespace: c.String("namespace"), + Disable: c.StringSlice("plugin"), + Escalate: c.StringSlice("privileged"), + Netrc: []string{}, + Local: dir, + Pull: c.Bool("pull"), + ConcealSecrets: c.Bool("conceal-secrets"), } payload := &model.Work{