mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-26 20:01:02 +00:00
Implement secrets concealer in build logs
This commit is contained in:
parent
655cbf0f30
commit
5377c62844
5 changed files with 95 additions and 42 deletions
|
@ -22,17 +22,18 @@ type Logger interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Agent struct {
|
type Agent struct {
|
||||||
Update UpdateFunc
|
Update UpdateFunc
|
||||||
Logger LoggerFunc
|
Logger LoggerFunc
|
||||||
Engine build.Engine
|
Engine build.Engine
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
Platform string
|
Platform string
|
||||||
Namespace string
|
Namespace string
|
||||||
Disable []string
|
Disable []string
|
||||||
Escalate []string
|
Escalate []string
|
||||||
Netrc []string
|
Netrc []string
|
||||||
Local string
|
Local string
|
||||||
Pull bool
|
Pull bool
|
||||||
|
ConcealSecrets bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Agent) Poll() error {
|
func (a *Agent) Poll() error {
|
||||||
|
@ -188,6 +189,7 @@ func (a *Agent) exec(spec *yaml.Config, payload *model.Work, cancel <-chan bool)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
secretsReplacer := newSecretsReplacer(payload.Secrets)
|
||||||
timeout := time.After(time.Duration(payload.Repo.Timeout) * time.Minute)
|
timeout := time.After(time.Duration(payload.Repo.Timeout) * time.Minute)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -227,11 +229,25 @@ func (a *Agent) exec(spec *yaml.Config, payload *model.Work, cancel <-chan bool)
|
||||||
pipeline.Exec()
|
pipeline.Exec()
|
||||||
}
|
}
|
||||||
case line := <-pipeline.Pipe():
|
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)
|
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 {
|
func toEnv(w *model.Work) map[string]string {
|
||||||
envs := map[string]string{
|
envs := map[string]string{
|
||||||
"CI": "drone",
|
"CI": "drone",
|
||||||
|
|
23
agent/agent_test.go
Normal file
23
agent/agent_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -75,6 +75,11 @@ var AgentCmd = cli.Command{
|
||||||
Name: "drone-secret",
|
Name: "drone-secret",
|
||||||
Usage: "drone agent secret",
|
Usage: "drone agent secret",
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "conceal-secrets",
|
||||||
|
Usage: "conceal secrets from build logs",
|
||||||
|
EnvVar: "DRONE_CONCEAL_SECRETS",
|
||||||
|
},
|
||||||
cli.DurationFlag{
|
cli.DurationFlag{
|
||||||
EnvVar: "DRONE_BACKOFF",
|
EnvVar: "DRONE_BACKOFF",
|
||||||
Name: "backoff",
|
Name: "backoff",
|
||||||
|
@ -186,12 +191,13 @@ func start(c *cli.Context) {
|
||||||
drone: client,
|
drone: client,
|
||||||
docker: docker,
|
docker: docker,
|
||||||
config: config{
|
config: config{
|
||||||
platform: c.String("docker-os") + "/" + c.String("docker-arch"),
|
platform: c.String("docker-os") + "/" + c.String("docker-arch"),
|
||||||
timeout: c.Duration("timeout"),
|
timeout: c.Duration("timeout"),
|
||||||
namespace: c.String("namespace"),
|
namespace: c.String("namespace"),
|
||||||
privileged: c.StringSlice("privileged"),
|
privileged: c.StringSlice("privileged"),
|
||||||
pull: c.BoolT("pull"),
|
pull: c.BoolT("pull"),
|
||||||
logs: int64(c.Int("max-log-size")) * 1000000,
|
logs: int64(c.Int("max-log-size")) * 1000000,
|
||||||
|
concealSecrets: c.Bool("conceal-secrets"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,12 +13,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
platform string
|
platform string
|
||||||
namespace string
|
namespace string
|
||||||
privileged []string
|
privileged []string
|
||||||
pull bool
|
pull bool
|
||||||
logs int64
|
logs int64
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
|
concealSecrets bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type pipeline struct {
|
type pipeline struct {
|
||||||
|
@ -40,14 +41,15 @@ func (r *pipeline) run(w *model.Work) {
|
||||||
engine := docker.NewClient(r.docker)
|
engine := docker.NewClient(r.docker)
|
||||||
|
|
||||||
a := agent.Agent{
|
a := agent.Agent{
|
||||||
Update: agent.NewClientUpdater(r.drone),
|
Update: agent.NewClientUpdater(r.drone),
|
||||||
Logger: agent.NewClientLogger(r.drone, w.Job.ID, r.config.logs),
|
Logger: agent.NewClientLogger(r.drone, w.Job.ID, r.config.logs),
|
||||||
Engine: engine,
|
Engine: engine,
|
||||||
Timeout: r.config.timeout,
|
Timeout: r.config.timeout,
|
||||||
Platform: r.config.platform,
|
Platform: r.config.platform,
|
||||||
Namespace: r.config.namespace,
|
Namespace: r.config.namespace,
|
||||||
Escalate: r.config.privileged,
|
Escalate: r.config.privileged,
|
||||||
Pull: r.config.pull,
|
Pull: r.config.pull,
|
||||||
|
ConcealSecrets: r.config.concealSecrets,
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelFunc := func(m *stomp.Message) {
|
cancelFunc := func(m *stomp.Message) {
|
||||||
|
|
|
@ -48,6 +48,11 @@ var execCmd = cli.Command{
|
||||||
Usage: "build secrets file in KEY=VALUE format",
|
Usage: "build secrets file in KEY=VALUE format",
|
||||||
EnvVar: "DRONE_SECRETS_FILE",
|
EnvVar: "DRONE_SECRETS_FILE",
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "conceal-secrets",
|
||||||
|
Usage: "conceal secrets from build logs",
|
||||||
|
EnvVar: "DRONE_CONCEAL_SECRETS",
|
||||||
|
},
|
||||||
cli.StringSliceFlag{
|
cli.StringSliceFlag{
|
||||||
Name: "matrix",
|
Name: "matrix",
|
||||||
Usage: "build matrix in KEY=VALUE format",
|
Usage: "build matrix in KEY=VALUE format",
|
||||||
|
@ -326,17 +331,18 @@ func exec(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
a := agent.Agent{
|
a := agent.Agent{
|
||||||
Update: agent.NoopUpdateFunc,
|
Update: agent.NoopUpdateFunc,
|
||||||
Logger: agent.TermLoggerFunc,
|
Logger: agent.TermLoggerFunc,
|
||||||
Engine: engine,
|
Engine: engine,
|
||||||
Timeout: c.Duration("timeout.inactivity"),
|
Timeout: c.Duration("timeout.inactivity"),
|
||||||
Platform: "linux/amd64",
|
Platform: "linux/amd64",
|
||||||
Namespace: c.String("namespace"),
|
Namespace: c.String("namespace"),
|
||||||
Disable: c.StringSlice("plugin"),
|
Disable: c.StringSlice("plugin"),
|
||||||
Escalate: c.StringSlice("privileged"),
|
Escalate: c.StringSlice("privileged"),
|
||||||
Netrc: []string{},
|
Netrc: []string{},
|
||||||
Local: dir,
|
Local: dir,
|
||||||
Pull: c.Bool("pull"),
|
Pull: c.Bool("pull"),
|
||||||
|
ConcealSecrets: c.Bool("conceal-secrets"),
|
||||||
}
|
}
|
||||||
|
|
||||||
payload := &model.Work{
|
payload := &model.Work{
|
||||||
|
|
Loading…
Reference in a new issue