diff --git a/cmd/server/flags.go b/cmd/server/flags.go index 697ff455f..772b96bc6 100644 --- a/cmd/server/flags.go +++ b/cmd/server/flags.go @@ -159,7 +159,7 @@ var flags = append([]cli.Flag{ Value: time.Hour * 72, }, &cli.StringSliceFlag{ - Sources: cli.EnvVars("WOODPECKER_ESCALATE"), + Sources: cli.EnvVars("WOODPECKER_ESCALATE", "WOODPECKER_PLUGINS_PRIVILEGED"), Name: "escalate", Usage: "images to run in privileged mode", Value: constant.PrivilegedPlugins, diff --git a/cmd/server/setup.go b/cmd/server/setup.go index 71c2ac71d..3b77816fd 100644 --- a/cmd/server/setup.go +++ b/cmd/server/setup.go @@ -226,10 +226,18 @@ func setupEvilGlobals(ctx context.Context, c *cli.Command, s store.Store) error server.Config.Server.CustomJsFile = strings.TrimSpace(c.String("custom-js-file")) server.Config.Pipeline.Networks = c.StringSlice("network") server.Config.Pipeline.Volumes = c.StringSlice("volume") - server.Config.Pipeline.Privileged = c.StringSlice("escalate") server.Config.WebUI.EnableSwagger = c.Bool("enable-swagger") server.Config.WebUI.SkipVersionCheck = c.Bool("skip-version-check") + // list has default value but should be able to be set to zero + server.Config.Pipeline.Privileged = c.StringSlice("escalate") + if val, set := os.LookupEnv("WOODPECKER_ESCALATE"); set && val == "" { + server.Config.Pipeline.Privileged = []string{} + } + if val, set := os.LookupEnv("WOODPECKER_PLUGINS_PRIVILEGED"); set && val == "" { + server.Config.Pipeline.Privileged = []string{} + } + // prometheus server.Config.Prometheus.AuthToken = c.String("prometheus-auth-token") diff --git a/pipeline/frontend/yaml/linter/linter.go b/pipeline/frontend/yaml/linter/linter.go index 26f654a04..0413debf3 100644 --- a/pipeline/frontend/yaml/linter/linter.go +++ b/pipeline/frontend/yaml/linter/linter.go @@ -24,11 +24,14 @@ import ( errorTypes "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors/types" "go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/linter/schema" "go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/types" + "go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/utils" + "go.woodpecker-ci.org/woodpecker/v2/shared/constant" ) // A Linter lints a pipeline configuration. type Linter struct { - trusted bool + trusted bool + privilegedPlugins *[]string } // New creates a new Linter with options. @@ -120,6 +123,9 @@ func (l *Linter) lintContainers(config *WorkflowConfig, area string) error { if err := l.lintSettings(config, container, area); err != nil { linterErr = multierr.Append(linterErr, err) } + if err := l.lintPrivilegedPlugins(config, container, area); err != nil { + linterErr = multierr.Append(linterErr, err) + } } return linterErr @@ -132,6 +138,18 @@ func (l *Linter) lintImage(config *WorkflowConfig, c *types.Container, area stri return nil } +func (l *Linter) lintPrivilegedPlugins(config *WorkflowConfig, c *types.Container, area string) error { + if utils.MatchImage(c.Image, constant.PrivilegedPlugins...) { + msg := fmt.Sprintf("Cannot use once by default privileged plugin '%s', if needed add it too WOODPECKER_PLUGINS_PRIVILEGED", c.Image) + // check first if user did not add them back + if l.privilegedPlugins != nil && !utils.MatchImageDynamic(c.Image, *l.privilegedPlugins...) { + return newLinterError(msg, config.File, fmt.Sprintf("%s.%s", area, c.Name), false) + } + } + + return nil +} + func (l *Linter) lintSettings(config *WorkflowConfig, c *types.Container, field string) error { if len(c.Settings) == 0 { return nil diff --git a/pipeline/frontend/yaml/linter/linter_test.go b/pipeline/frontend/yaml/linter/linter_test.go index 68a441cbb..cf1443b6e 100644 --- a/pipeline/frontend/yaml/linter/linter_test.go +++ b/pipeline/frontend/yaml/linter/linter_test.go @@ -165,13 +165,19 @@ func TestLintErrors(t *testing.T) { from: "steps: { build: { image: golang, settings: { test: 'true' }, environment: [ 'TEST=true' ] } }", want: "Should not configure both environment and settings", }, + { + from: "{steps: { build: { image: plugins/docker, settings: { test: 'true' } } }, when: { branch: main, event: push } } }", + want: "Cannot use once by default privileged plugin 'plugins/docker', if needed add it too WOODPECKER_PLUGINS_PRIVILEGED", + }, } for _, test := range testdata { conf, err := yaml.ParseString(test.from) assert.NoError(t, err) - lerr := linter.New().Lint([]*linter.WorkflowConfig{{ + lerr := linter.New( + linter.PrivilegedPlugins([]string{"woodpeckerci/plugin-docker-buildx"}), + ).Lint([]*linter.WorkflowConfig{{ File: test.from, RawConfig: test.from, Workflow: conf, diff --git a/pipeline/frontend/yaml/linter/option.go b/pipeline/frontend/yaml/linter/option.go index 0fe2af76f..3a3cbba44 100644 --- a/pipeline/frontend/yaml/linter/option.go +++ b/pipeline/frontend/yaml/linter/option.go @@ -23,3 +23,10 @@ func WithTrusted(trusted bool) Option { linter.trusted = trusted } } + +// PrivilegedPlugins adds the list of privileged plugins. +func PrivilegedPlugins(plugins []string) Option { + return func(linter *Linter) { + linter.privilegedPlugins = &plugins + } +} diff --git a/server/pipeline/stepbuilder/stepBuilder.go b/server/pipeline/stepbuilder/stepBuilder.go index 593ff0fc1..61c93f039 100644 --- a/server/pipeline/stepbuilder/stepBuilder.go +++ b/server/pipeline/stepbuilder/stepBuilder.go @@ -142,6 +142,7 @@ func (b *StepBuilder) genItemForWorkflow(workflow *model.Workflow, axis matrix.A // lint pipeline errorsAndWarnings = multierr.Append(errorsAndWarnings, linter.New( linter.WithTrusted(b.Repo.IsTrusted), + linter.PrivilegedPlugins(server.Config.Pipeline.Privileged), ).Lint([]*linter.WorkflowConfig{{ Workflow: parsed, File: workflow.Name,