mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-28 21:01:08 +00:00
Allow alter trusted clone plugins and filter them via tag (#4074)
This commit is contained in:
parent
8e0af15e85
commit
3c8204a0e0
17 changed files with 151 additions and 58 deletions
|
@ -41,6 +41,7 @@ import (
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/linter"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/linter"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/matrix"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/matrix"
|
||||||
pipelineLog "go.woodpecker-ci.org/woodpecker/v2/pipeline/log"
|
pipelineLog "go.woodpecker-ci.org/woodpecker/v2/pipeline/log"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/shared/constant"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/shared/utils"
|
"go.woodpecker-ci.org/woodpecker/v2/shared/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -185,7 +186,10 @@ func execWithAxis(ctx context.Context, c *cli.Command, file, repoPath string, ax
|
||||||
}
|
}
|
||||||
|
|
||||||
// lint the yaml file
|
// lint the yaml file
|
||||||
err = linter.New(linter.WithTrusted(true)).Lint([]*linter.WorkflowConfig{{
|
err = linter.New(
|
||||||
|
linter.WithTrusted(true),
|
||||||
|
linter.WithTrustedClonePlugins(constant.TrustedClonePlugins),
|
||||||
|
).Lint([]*linter.WorkflowConfig{{
|
||||||
File: path.Base(file),
|
File: path.Base(file),
|
||||||
RawConfig: confStr,
|
RawConfig: confStr,
|
||||||
Workflow: conf,
|
Workflow: conf,
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/linter"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/linter"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/shared/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Command exports the info command.
|
// Command exports the info command.
|
||||||
|
@ -35,6 +36,14 @@ var Command = &cli.Command{
|
||||||
Usage: "lint a pipeline configuration file",
|
Usage: "lint a pipeline configuration file",
|
||||||
ArgsUsage: "[path/to/.woodpecker.yaml]",
|
ArgsUsage: "[path/to/.woodpecker.yaml]",
|
||||||
Action: lint,
|
Action: lint,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringSliceFlag{
|
||||||
|
Sources: cli.EnvVars("WOODPECKER_PLUGINS_TRUSTED_CLONE"),
|
||||||
|
Name: "plugins-trusted-clone",
|
||||||
|
Usage: "Plugins witch are trusted to handle the netrc info in clone steps",
|
||||||
|
Value: constant.TrustedClonePlugins,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func lint(ctx context.Context, c *cli.Command) error {
|
func lint(ctx context.Context, c *cli.Command) error {
|
||||||
|
@ -69,7 +78,7 @@ func lintDir(ctx context.Context, c *cli.Command, dir string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lintFile(_ context.Context, _ *cli.Command, file string) error {
|
func lintFile(_ context.Context, c *cli.Command, file string) error {
|
||||||
fi, err := os.Open(file)
|
fi, err := os.Open(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -83,7 +92,7 @@ func lintFile(_ context.Context, _ *cli.Command, file string) error {
|
||||||
|
|
||||||
rawConfig := string(buf)
|
rawConfig := string(buf)
|
||||||
|
|
||||||
c, err := yaml.ParseString(rawConfig)
|
parsedConfig, err := yaml.ParseString(rawConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -91,11 +100,14 @@ func lintFile(_ context.Context, _ *cli.Command, file string) error {
|
||||||
config := &linter.WorkflowConfig{
|
config := &linter.WorkflowConfig{
|
||||||
File: path.Base(file),
|
File: path.Base(file),
|
||||||
RawConfig: rawConfig,
|
RawConfig: rawConfig,
|
||||||
Workflow: c,
|
Workflow: parsedConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: lint multiple files at once to allow checks for sth like "depends_on" to work
|
// TODO: lint multiple files at once to allow checks for sth like "depends_on" to work
|
||||||
err = linter.New(linter.WithTrusted(true)).Lint([]*linter.WorkflowConfig{config})
|
err = linter.New(
|
||||||
|
linter.WithTrusted(true),
|
||||||
|
linter.WithTrustedClonePlugins(c.StringSlice("plugins-trusted-clone")),
|
||||||
|
).Lint([]*linter.WorkflowConfig{config})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str, err := FormatLintError(config.File, err)
|
str, err := FormatLintError(config.File, err)
|
||||||
|
|
||||||
|
|
|
@ -135,10 +135,11 @@ var flags = append([]cli.Flag{
|
||||||
Value: []string{"push", "pull_request"},
|
Value: []string{"push", "pull_request"},
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Sources: cli.EnvVars("WOODPECKER_DEFAULT_CLONE_IMAGE"),
|
Sources: cli.EnvVars("WOODPECKER_DEFAULT_CLONE_PLUGIN", "WOODPECKER_DEFAULT_CLONE_IMAGE"),
|
||||||
Name: "default-clone-image",
|
Name: "default-clone-plugin",
|
||||||
|
Aliases: []string{"default-clone-image"},
|
||||||
Usage: "The default docker image to be used when cloning the repo",
|
Usage: "The default docker image to be used when cloning the repo",
|
||||||
Value: constant.DefaultCloneImage,
|
Value: constant.DefaultClonePlugin,
|
||||||
},
|
},
|
||||||
&cli.IntFlag{
|
&cli.IntFlag{
|
||||||
Sources: cli.EnvVars("WOODPECKER_DEFAULT_PIPELINE_TIMEOUT"),
|
Sources: cli.EnvVars("WOODPECKER_DEFAULT_PIPELINE_TIMEOUT"),
|
||||||
|
@ -164,6 +165,12 @@ var flags = append([]cli.Flag{
|
||||||
Usage: "Allow plugins to run in privileged mode, if environment variable is defined but empty there will be none",
|
Usage: "Allow plugins to run in privileged mode, if environment variable is defined but empty there will be none",
|
||||||
Value: constant.PrivilegedPlugins,
|
Value: constant.PrivilegedPlugins,
|
||||||
},
|
},
|
||||||
|
&cli.StringSliceFlag{
|
||||||
|
Sources: cli.EnvVars("WOODPECKER_PLUGINS_TRUSTED_CLONE"),
|
||||||
|
Name: "plugins-trusted-clone",
|
||||||
|
Usage: "Plugins witch are trusted to handle the netrc info in clone steps",
|
||||||
|
Value: constant.TrustedClonePlugins,
|
||||||
|
},
|
||||||
&cli.StringSliceFlag{
|
&cli.StringSliceFlag{
|
||||||
Sources: cli.EnvVars("WOODPECKER_VOLUME"),
|
Sources: cli.EnvVars("WOODPECKER_VOLUME"),
|
||||||
Name: "volume",
|
Name: "volume",
|
||||||
|
|
|
@ -43,7 +43,6 @@ import (
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/store"
|
"go.woodpecker-ci.org/woodpecker/v2/server/store"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/store/datastore"
|
"go.woodpecker-ci.org/woodpecker/v2/server/store/datastore"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/store/types"
|
"go.woodpecker-ci.org/woodpecker/v2/server/store/types"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/shared/constant"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -165,8 +164,9 @@ func setupEvilGlobals(ctx context.Context, c *cli.Command, s store.Store) error
|
||||||
server.Config.Pipeline.AuthenticatePublicRepos = c.Bool("authenticate-public-repos")
|
server.Config.Pipeline.AuthenticatePublicRepos = c.Bool("authenticate-public-repos")
|
||||||
|
|
||||||
// Cloning
|
// Cloning
|
||||||
server.Config.Pipeline.DefaultCloneImage = c.String("default-clone-image")
|
server.Config.Pipeline.DefaultClonePlugin = c.String("default-clone-plugin")
|
||||||
constant.TrustedCloneImages = append(constant.TrustedCloneImages, server.Config.Pipeline.DefaultCloneImage)
|
server.Config.Pipeline.TrustedClonePlugins = c.StringSlice("plugins-trusted-clone")
|
||||||
|
server.Config.Pipeline.TrustedClonePlugins = append(server.Config.Pipeline.TrustedClonePlugins, server.Config.Pipeline.DefaultClonePlugin)
|
||||||
|
|
||||||
// Execution
|
// Execution
|
||||||
_events := c.StringSlice("default-cancel-previous-pipeline-events")
|
_events := c.StringSlice("default-cancel-previous-pipeline-events")
|
||||||
|
|
|
@ -319,11 +319,13 @@ Always use authentication to clone repositories even if they are public. Needed
|
||||||
|
|
||||||
List of event names that will be canceled when a new pipeline for the same context (tag, branch) is created.
|
List of event names that will be canceled when a new pipeline for the same context (tag, branch) is created.
|
||||||
|
|
||||||
### `WOODPECKER_DEFAULT_CLONE_IMAGE`
|
### `WOODPECKER_DEFAULT_CLONE_PLUGIN`
|
||||||
|
|
||||||
> Default is defined in [shared/constant/constant.go](https://github.com/woodpecker-ci/woodpecker/blob/main/shared/constant/constant.go)
|
> Default is defined in [shared/constant/constant.go](https://github.com/woodpecker-ci/woodpecker/blob/main/shared/constant/constant.go)
|
||||||
|
|
||||||
The default docker image to be used when cloning the repo
|
The default docker image to be used when cloning the repo.
|
||||||
|
|
||||||
|
It is also added to the trusted clone plugin list.
|
||||||
|
|
||||||
### `WOODPECKER_DEFAULT_PIPELINE_TIMEOUT`
|
### `WOODPECKER_DEFAULT_PIPELINE_TIMEOUT`
|
||||||
|
|
||||||
|
@ -352,6 +354,15 @@ a user can log into Woodpecker, without re-authentication.
|
||||||
|
|
||||||
Docker images to run in privileged mode. Only change if you are sure what you do!
|
Docker images to run in privileged mode. Only change if you are sure what you do!
|
||||||
|
|
||||||
|
### WOODPECKER_PLUGINS_TRUSTED_CLONE
|
||||||
|
|
||||||
|
> Defaults are defined in [shared/constant/constant.go](https://github.com/woodpecker-ci/woodpecker/blob/main/shared/constant/constant.go)
|
||||||
|
|
||||||
|
Plugins witch are trusted to handle the netrc info in clone steps.
|
||||||
|
If a clone step use an image not in this list, the netrc will not be injected and an user has to use other methods (e.g. secrets) to clone non public repos.
|
||||||
|
|
||||||
|
You should specify the tag of your images too, as this enforces exact matches.
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
### `WOODPECKER_VOLUME`
|
### `WOODPECKER_VOLUME`
|
||||||
> Default: empty
|
> Default: empty
|
||||||
|
|
|
@ -4,6 +4,8 @@ Some versions need some changes to the server configuration or the pipeline conf
|
||||||
|
|
||||||
## `next`
|
## `next`
|
||||||
|
|
||||||
|
- `WOODPECKER_DEFAULT_CLONE_IMAGE` got depricated use `WOODPECKER_DEFAULT_CLONE_PLUGIN`
|
||||||
|
- Check trusted-clone-plugins by image name and tag (if tag is set)
|
||||||
- Remove `plugins/docker`, `plugins/gcr` and `plugins/ecr` from the default list of privileged plugins ([modify the list via config if needed](./30-administration/10-server-config.md#woodpecker_escalate)).
|
- Remove `plugins/docker`, `plugins/gcr` and `plugins/ecr` from the default list of privileged plugins ([modify the list via config if needed](./30-administration/10-server-config.md#woodpecker_escalate)).
|
||||||
- Secret filters for plugins now check against tag if specified
|
- Secret filters for plugins now check against tag if specified
|
||||||
- Removed `WOODPECKER_DEV_OAUTH_HOST` and `WOODPECKER_DEV_GITEA_OAUTH_URL` use `WOODPECKER_EXPERT_FORGE_OAUTH_HOST`
|
- Removed `WOODPECKER_DEV_OAUTH_HOST` and `WOODPECKER_DEV_GITEA_OAUTH_URL` use `WOODPECKER_EXPERT_FORGE_OAUTH_HOST`
|
||||||
|
|
|
@ -92,30 +92,33 @@ type ResourceLimit struct {
|
||||||
|
|
||||||
// Compiler compiles the yaml.
|
// Compiler compiles the yaml.
|
||||||
type Compiler struct {
|
type Compiler struct {
|
||||||
local bool
|
local bool
|
||||||
escalated []string
|
escalated []string
|
||||||
prefix string
|
prefix string
|
||||||
volumes []string
|
volumes []string
|
||||||
networks []string
|
networks []string
|
||||||
env map[string]string
|
env map[string]string
|
||||||
cloneEnv map[string]string
|
cloneEnv map[string]string
|
||||||
workspaceBase string
|
workspaceBase string
|
||||||
workspacePath string
|
workspacePath string
|
||||||
metadata metadata.Metadata
|
metadata metadata.Metadata
|
||||||
registries []Registry
|
registries []Registry
|
||||||
secrets map[string]Secret
|
secrets map[string]Secret
|
||||||
reslimit ResourceLimit
|
reslimit ResourceLimit
|
||||||
defaultCloneImage string
|
defaultClonePlugin string
|
||||||
trustedPipeline bool
|
trustedClonePlugins []string
|
||||||
netrcOnlyTrusted bool
|
trustedPipeline bool
|
||||||
|
netrcOnlyTrusted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Compiler with options.
|
// New creates a new Compiler with options.
|
||||||
func New(opts ...Option) *Compiler {
|
func New(opts ...Option) *Compiler {
|
||||||
compiler := &Compiler{
|
compiler := &Compiler{
|
||||||
env: map[string]string{},
|
env: map[string]string{},
|
||||||
cloneEnv: map[string]string{},
|
cloneEnv: map[string]string{},
|
||||||
secrets: map[string]Secret{},
|
secrets: map[string]Secret{},
|
||||||
|
defaultClonePlugin: constant.DefaultClonePlugin,
|
||||||
|
trustedClonePlugins: constant.TrustedClonePlugins,
|
||||||
}
|
}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(compiler)
|
opt(compiler)
|
||||||
|
@ -163,20 +166,15 @@ func (c *Compiler) Compile(conf *yaml_types.Workflow) (*backend_types.Config, er
|
||||||
c.workspacePath = path.Clean(conf.Workspace.Path)
|
c.workspacePath = path.Clean(conf.Workspace.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
cloneImage := constant.DefaultCloneImage
|
|
||||||
if len(c.defaultCloneImage) > 0 {
|
|
||||||
cloneImage = c.defaultCloneImage
|
|
||||||
}
|
|
||||||
|
|
||||||
// add default clone step
|
// add default clone step
|
||||||
if !c.local && len(conf.Clone.ContainerList) == 0 && !conf.SkipClone {
|
if !c.local && len(conf.Clone.ContainerList) == 0 && !conf.SkipClone && len(c.defaultClonePlugin) != 0 {
|
||||||
cloneSettings := map[string]any{"depth": "0"}
|
cloneSettings := map[string]any{"depth": "0"}
|
||||||
if c.metadata.Curr.Event == metadata.EventTag {
|
if c.metadata.Curr.Event == metadata.EventTag {
|
||||||
cloneSettings["tags"] = "true"
|
cloneSettings["tags"] = "true"
|
||||||
}
|
}
|
||||||
container := &yaml_types.Container{
|
container := &yaml_types.Container{
|
||||||
Name: defaultCloneName,
|
Name: defaultCloneName,
|
||||||
Image: cloneImage,
|
Image: c.defaultClonePlugin,
|
||||||
Settings: cloneSettings,
|
Settings: cloneSettings,
|
||||||
Environment: make(map[string]any),
|
Environment: make(map[string]any),
|
||||||
}
|
}
|
||||||
|
@ -208,7 +206,7 @@ func (c *Compiler) Compile(conf *yaml_types.Workflow) (*backend_types.Config, er
|
||||||
}
|
}
|
||||||
|
|
||||||
// only inject netrc if it's a trusted repo or a trusted plugin
|
// only inject netrc if it's a trusted repo or a trusted plugin
|
||||||
if !c.netrcOnlyTrusted || c.trustedPipeline || (container.IsPlugin() && container.IsTrustedCloneImage()) {
|
if !c.netrcOnlyTrusted || c.trustedPipeline || (container.IsPlugin() && container.IsTrustedCloneImage(c.trustedClonePlugins)) {
|
||||||
for k, v := range c.cloneEnv {
|
for k, v := range c.cloneEnv {
|
||||||
step.Environment[k] = v
|
step.Environment[k] = v
|
||||||
}
|
}
|
||||||
|
@ -265,7 +263,7 @@ func (c *Compiler) Compile(conf *yaml_types.Workflow) (*backend_types.Config, er
|
||||||
}
|
}
|
||||||
|
|
||||||
// inject netrc if it's a trusted repo or a trusted clone-plugin
|
// inject netrc if it's a trusted repo or a trusted clone-plugin
|
||||||
if c.trustedPipeline || (container.IsPlugin() && container.IsTrustedCloneImage()) {
|
if c.trustedPipeline || (container.IsPlugin() && container.IsTrustedCloneImage(c.trustedClonePlugins)) {
|
||||||
for k, v := range c.cloneEnv {
|
for k, v := range c.cloneEnv {
|
||||||
step.Environment[k] = v
|
step.Environment[k] = v
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,7 @@ func TestCompilerCompile(t *testing.T) {
|
||||||
Steps: []*backend_types.Step{{
|
Steps: []*backend_types.Step{{
|
||||||
Name: "clone",
|
Name: "clone",
|
||||||
Type: backend_types.StepTypeClone,
|
Type: backend_types.StepTypeClone,
|
||||||
Image: constant.DefaultCloneImage,
|
Image: constant.DefaultClonePlugin,
|
||||||
OnSuccess: true,
|
OnSuccess: true,
|
||||||
Failure: "fail",
|
Failure: "fail",
|
||||||
Volumes: []string{defaultVolumes[0].Name + ":/woodpecker"},
|
Volumes: []string{defaultVolumes[0].Name + ":/woodpecker"},
|
||||||
|
|
|
@ -172,9 +172,15 @@ func WithResourceLimit(swap, mem, shmSize, cpuQuota, cpuShares int64, cpuSet str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithDefaultCloneImage(cloneImage string) Option {
|
func WithDefaultClonePlugin(cloneImage string) Option {
|
||||||
return func(compiler *Compiler) {
|
return func(compiler *Compiler) {
|
||||||
compiler.defaultCloneImage = cloneImage
|
compiler.defaultClonePlugin = cloneImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithTrustedClonePlugins(images []string) Option {
|
||||||
|
return func(compiler *Compiler) {
|
||||||
|
compiler.trustedClonePlugins = images
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/metadata"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/metadata"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/shared/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWithWorkspace(t *testing.T) {
|
func TestWithWorkspace(t *testing.T) {
|
||||||
|
@ -166,9 +167,17 @@ func TestWithEnviron(t *testing.T) {
|
||||||
assert.Equal(t, "true", compiler.env["SHOW"])
|
assert.Equal(t, "true", compiler.env["SHOW"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWithDefaultCloneImage(t *testing.T) {
|
func TestDefaultClonePlugin(t *testing.T) {
|
||||||
compiler := New(
|
compiler := New(
|
||||||
WithDefaultCloneImage("not-an-image"),
|
WithDefaultClonePlugin("not-an-image"),
|
||||||
)
|
)
|
||||||
assert.Equal(t, "not-an-image", compiler.defaultCloneImage)
|
assert.Equal(t, "not-an-image", compiler.defaultClonePlugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWithTrustedClonePlugins(t *testing.T) {
|
||||||
|
compiler := New(WithTrustedClonePlugins([]string{"not-an-image"}))
|
||||||
|
assert.ElementsMatch(t, []string{"not-an-image"}, compiler.trustedClonePlugins)
|
||||||
|
|
||||||
|
compiler = New()
|
||||||
|
assert.ElementsMatch(t, constant.TrustedClonePlugins, compiler.trustedClonePlugins)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,12 +25,14 @@ import (
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/linter/schema"
|
"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/types"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/utils"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/utils"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/shared/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Linter lints a pipeline configuration.
|
// A Linter lints a pipeline configuration.
|
||||||
type Linter struct {
|
type Linter struct {
|
||||||
trusted bool
|
trusted bool
|
||||||
privilegedPlugins *[]string
|
privilegedPlugins *[]string
|
||||||
|
trustedClonePlugins *[]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Linter with options.
|
// New creates a new Linter with options.
|
||||||
|
@ -73,6 +75,10 @@ func (l *Linter) lintFile(config *WorkflowConfig) error {
|
||||||
linterErr = multierr.Append(linterErr, newLinterError("Invalid or missing steps section", config.File, "steps", false))
|
linterErr = multierr.Append(linterErr, newLinterError("Invalid or missing steps section", config.File, "steps", false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := l.lintCloneSteps(config); err != nil {
|
||||||
|
linterErr = multierr.Append(linterErr, err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := l.lintContainers(config, "clone"); err != nil {
|
if err := l.lintContainers(config, "clone"); err != nil {
|
||||||
linterErr = multierr.Append(linterErr, err)
|
linterErr = multierr.Append(linterErr, err)
|
||||||
}
|
}
|
||||||
|
@ -96,6 +102,29 @@ func (l *Linter) lintFile(config *WorkflowConfig) error {
|
||||||
return linterErr
|
return linterErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Linter) lintCloneSteps(config *WorkflowConfig) error {
|
||||||
|
if len(config.Workflow.Clone.ContainerList) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
trustedClonePlugins := constant.TrustedClonePlugins
|
||||||
|
if l.trustedClonePlugins != nil {
|
||||||
|
trustedClonePlugins = *l.trustedClonePlugins
|
||||||
|
}
|
||||||
|
|
||||||
|
var linterErr error
|
||||||
|
for _, container := range config.Workflow.Clone.ContainerList {
|
||||||
|
if !utils.MatchImageDynamic(container.Image, trustedClonePlugins...) {
|
||||||
|
linterErr = multierr.Append(linterErr,
|
||||||
|
newLinterError(
|
||||||
|
"Specified clone image does not match allow list, netrc will not be injected",
|
||||||
|
config.File, fmt.Sprintf("clone.%s", container.Name), true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return linterErr
|
||||||
|
}
|
||||||
|
|
||||||
func (l *Linter) lintContainers(config *WorkflowConfig, area string) error {
|
func (l *Linter) lintContainers(config *WorkflowConfig, area string) error {
|
||||||
var linterErr error
|
var linterErr error
|
||||||
|
|
||||||
|
|
|
@ -173,6 +173,10 @@ func TestLintErrors(t *testing.T) {
|
||||||
from: "{steps: { build: { image: plugins/docker, settings: { test: 'true' } } }, when: { branch: main, event: push } } }",
|
from: "{steps: { build: { image: plugins/docker, settings: { test: 'true' } } }, when: { branch: main, event: push } } }",
|
||||||
want: "Cannot use once privileged plugins removed from WOODPECKER_ESCALATE, use 'woodpeckerci/plugin-docker-buildx' instead",
|
want: "Cannot use once privileged plugins removed from WOODPECKER_ESCALATE, use 'woodpeckerci/plugin-docker-buildx' instead",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
from: "{steps: { build: { image: golang, settings: { test: 'true' } } }, when: { branch: main, event: push }, clone: { git: { image: some-other/plugin-git:v1.1.0 } } }",
|
||||||
|
want: "Specified clone image does not match allow list, netrc will not be injected",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testdata {
|
for _, test := range testdata {
|
||||||
|
|
|
@ -30,3 +30,10 @@ func PrivilegedPlugins(plugins []string) Option {
|
||||||
linter.privilegedPlugins = &plugins
|
linter.privilegedPlugins = &plugins
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithTrustedClonePlugins adds the list of trusted clone plugins.
|
||||||
|
func WithTrustedClonePlugins(plugins []string) Option {
|
||||||
|
return func(linter *Linter) {
|
||||||
|
linter.trustedClonePlugins = &plugins
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/constraint"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/constraint"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/types/base"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/types/base"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/utils"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/utils"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/shared/constant"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -125,6 +124,6 @@ func (c *Container) IsPlugin() bool {
|
||||||
len(c.Secrets) == 0
|
len(c.Secrets) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) IsTrustedCloneImage() bool {
|
func (c *Container) IsTrustedCloneImage(trustedClonePlugins []string) bool {
|
||||||
return c.IsPlugin() && utils.MatchImage(c.Image, constant.TrustedCloneImages...)
|
return c.IsPlugin() && utils.MatchImageDynamic(c.Image, trustedClonePlugins...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,8 @@ var Config = struct {
|
||||||
Pipeline struct {
|
Pipeline struct {
|
||||||
AuthenticatePublicRepos bool
|
AuthenticatePublicRepos bool
|
||||||
DefaultCancelPreviousPipelineEvents []model.WebhookEvent
|
DefaultCancelPreviousPipelineEvents []model.WebhookEvent
|
||||||
DefaultCloneImage string
|
DefaultClonePlugin string
|
||||||
|
TrustedClonePlugins []string
|
||||||
Limits model.ResourceLimit
|
Limits model.ResourceLimit
|
||||||
Volumes []string
|
Volumes []string
|
||||||
Networks []string
|
Networks []string
|
||||||
|
|
|
@ -143,6 +143,7 @@ func (b *StepBuilder) genItemForWorkflow(workflow *model.Workflow, axis matrix.A
|
||||||
errorsAndWarnings = multierr.Append(errorsAndWarnings, linter.New(
|
errorsAndWarnings = multierr.Append(errorsAndWarnings, linter.New(
|
||||||
linter.WithTrusted(b.Repo.IsTrusted),
|
linter.WithTrusted(b.Repo.IsTrusted),
|
||||||
linter.PrivilegedPlugins(server.Config.Pipeline.PrivilegedPlugins),
|
linter.PrivilegedPlugins(server.Config.Pipeline.PrivilegedPlugins),
|
||||||
|
linter.WithTrustedClonePlugins(server.Config.Pipeline.TrustedClonePlugins),
|
||||||
).Lint([]*linter.WorkflowConfig{{
|
).Lint([]*linter.WorkflowConfig{{
|
||||||
Workflow: parsed,
|
Workflow: parsed,
|
||||||
File: workflow.Name,
|
File: workflow.Name,
|
||||||
|
@ -281,7 +282,8 @@ func (b *StepBuilder) toInternalRepresentation(parsed *yaml_types.Workflow, envi
|
||||||
),
|
),
|
||||||
b.Repo.IsSCMPrivate || server.Config.Pipeline.AuthenticatePublicRepos,
|
b.Repo.IsSCMPrivate || server.Config.Pipeline.AuthenticatePublicRepos,
|
||||||
),
|
),
|
||||||
compiler.WithDefaultCloneImage(server.Config.Pipeline.DefaultCloneImage),
|
compiler.WithDefaultClonePlugin(server.Config.Pipeline.DefaultClonePlugin),
|
||||||
|
compiler.WithTrustedClonePlugins(server.Config.Pipeline.TrustedClonePlugins),
|
||||||
compiler.WithRegistry(registries...),
|
compiler.WithRegistry(registries...),
|
||||||
compiler.WithSecret(secrets...),
|
compiler.WithSecret(secrets...),
|
||||||
compiler.WithPrefix(
|
compiler.WithPrefix(
|
||||||
|
|
|
@ -29,12 +29,14 @@ var DefaultConfigOrder = [...]string{
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// DefaultCloneImage can be changed by 'WOODPECKER_DEFAULT_CLONE_IMAGE' at runtime.
|
// DefaultClonePlugin can be changed by 'WOODPECKER_DEFAULT_CLONE_PLUGIN' at runtime.
|
||||||
// renovate: datasource=docker depName=woodpeckerci/plugin-git
|
// renovate: datasource=docker depName=woodpeckerci/plugin-git
|
||||||
DefaultCloneImage = "docker.io/woodpeckerci/plugin-git:2.5.2"
|
DefaultClonePlugin = "docker.io/woodpeckerci/plugin-git:2.5.2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var TrustedCloneImages = []string{
|
// TrustedClonePlugins can be changed by 'WOODPECKER_PLUGINS_TRUSTED_CLONE' at runtime.
|
||||||
DefaultCloneImage,
|
var TrustedClonePlugins = []string{
|
||||||
|
DefaultClonePlugin,
|
||||||
|
"docker.io/woodpeckerci/plugin-git",
|
||||||
"quay.io/woodpeckerci/plugin-git",
|
"quay.io/woodpeckerci/plugin-git",
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue