mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-09-26 21:50:22 +00:00
Add option to filter secrets by plugins with specific tags (#4069)
Co-authored-by: qwerty287 <80460567+qwerty287@users.noreply.github.com>
This commit is contained in:
parent
99d169bd3f
commit
fb6068d836
5 changed files with 140 additions and 2 deletions
|
@ -74,6 +74,11 @@ Please be careful when exposing secrets to pull requests. If your repository is
|
||||||
|
|
||||||
To prevent abusing your secrets from malicious usage, you can limit a secret to a list of plugins. If enabled they are not available to any other plugin (steps without user-defined commands). If you or an attacker defines explicit commands, the secrets will not be available to the container to prevent leaking them.
|
To prevent abusing your secrets from malicious usage, you can limit a secret to a list of plugins. If enabled they are not available to any other plugin (steps without user-defined commands). If you or an attacker defines explicit commands, the secrets will not be available to the container to prevent leaking them.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
If you specify a tag, the filter will respect it.
|
||||||
|
Just make sure you don't specify the same image without one, otherwise it will be ignored again.
|
||||||
|
:::
|
||||||
|
|
||||||
![plugins filter](./secrets-plugins-filter.png)
|
![plugins filter](./secrets-plugins-filter.png)
|
||||||
|
|
||||||
## Adding Secrets
|
## Adding Secrets
|
||||||
|
|
|
@ -4,6 +4,7 @@ Some versions need some changes to the server configuration or the pipeline conf
|
||||||
|
|
||||||
## `next`
|
## `next`
|
||||||
|
|
||||||
|
- 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`
|
||||||
- Compatibility mode of deprecated `pipeline:`, `platform:` and `branches:` pipeline config options are now removed and pipeline will now fail if still in use.
|
- Compatibility mode of deprecated `pipeline:`, `platform:` and `branches:` pipeline config options are now removed and pipeline will now fail if still in use.
|
||||||
- Removed `steps.[name].group` in favor of `steps.[name].depends_on` (see [workflow syntax](./20-usage/20-workflow-syntax.md#depends_on) to learn how to set dependencies)
|
- Removed `steps.[name].group` in favor of `steps.[name].depends_on` (see [workflow syntax](./20-usage/20-workflow-syntax.md#depends_on) to learn how to set dependencies)
|
||||||
|
|
|
@ -49,7 +49,7 @@ func (s *Secret) Available(event string, container *yaml_types.Container) error
|
||||||
return fmt.Errorf("secret %q only allowed to be used by plugins by step %q", s.Name, container.Name)
|
return fmt.Errorf("secret %q only allowed to be used by plugins by step %q", s.Name, container.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if onlyAllowSecretForPlugins && !utils.MatchImage(container.Image, s.AllowedPlugins...) {
|
if onlyAllowSecretForPlugins && !utils.MatchImageDynamic(container.Image, s.AllowedPlugins...) {
|
||||||
return fmt.Errorf("secret %q is not allowed to be used with image %q by step %q", s.Name, container.Image, container.Name)
|
return fmt.Errorf("secret %q is not allowed to be used with image %q by step %q", s.Name, container.Image, container.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,11 @@
|
||||||
|
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import "github.com/distribution/reference"
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/distribution/reference"
|
||||||
|
)
|
||||||
|
|
||||||
// trimImage returns the short image name without tag.
|
// trimImage returns the short image name without tag.
|
||||||
func trimImage(name string) string {
|
func trimImage(name string) string {
|
||||||
|
@ -57,6 +61,29 @@ func MatchImage(from string, to ...string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MatchImageDynamic check if image is in list based on list.
|
||||||
|
// If an list entry has a tag specified it only will match if both are the same, else the tag is ignored.
|
||||||
|
func MatchImageDynamic(from string, to ...string) bool {
|
||||||
|
fullFrom := expandImage(from)
|
||||||
|
trimFrom := trimImage(from)
|
||||||
|
for _, match := range to {
|
||||||
|
if imageHasTag(match) {
|
||||||
|
if fullFrom == expandImage(match) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if trimFrom == trimImage(match) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func imageHasTag(name string) bool {
|
||||||
|
return strings.Contains(name, ":")
|
||||||
|
}
|
||||||
|
|
||||||
// MatchHostname returns true if the image hostname
|
// MatchHostname returns true if the image hostname
|
||||||
// matches the specified hostname.
|
// matches the specified hostname.
|
||||||
func MatchHostname(image, hostname string) bool {
|
func MatchHostname(image, hostname string) bool {
|
||||||
|
|
|
@ -113,6 +113,10 @@ func Test_expandImage(t *testing.T) {
|
||||||
from: "gcr.io/golang:1.0.0",
|
from: "gcr.io/golang:1.0.0",
|
||||||
want: "gcr.io/golang:1.0.0",
|
want: "gcr.io/golang:1.0.0",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
from: "codeberg.org/6543/hello:latest@2c98dce11f78c2b4e40f513ca82f75035eb8cfa4957a6d8eb3f917ecaf77803",
|
||||||
|
want: "codeberg.org/6543/hello:latest@2c98dce11f78c2b4e40f513ca82f75035eb8cfa4957a6d8eb3f917ecaf77803",
|
||||||
|
},
|
||||||
// error cases, return input unmodified
|
// error cases, return input unmodified
|
||||||
{
|
{
|
||||||
from: "foo/bar?baz:boo",
|
from: "foo/bar?baz:boo",
|
||||||
|
@ -124,6 +128,57 @@ func Test_expandImage(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_imageHasTag(t *testing.T) {
|
||||||
|
testdata := []struct {
|
||||||
|
from string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
from: "golang",
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "golang:latest",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "golang:1.0.0",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "library/golang",
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "library/golang:latest",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "library/golang:1.0.0",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "index.docker.io/library/golang:1.0.0",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "gcr.io/golang",
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "gcr.io/golang:1.0.0",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "codeberg.org/6543/hello:latest@2c98dce11f78c2b4e40f513ca82f75035eb8cfa4957a6d8eb3f917ecaf77803",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testdata {
|
||||||
|
assert.Equal(t, test.want, imageHasTag(test.from))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Test_matchImage(t *testing.T) {
|
func Test_matchImage(t *testing.T) {
|
||||||
testdata := []struct {
|
testdata := []struct {
|
||||||
from, to string
|
from, to string
|
||||||
|
@ -205,6 +260,56 @@ func Test_matchImage(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_matchImageDynamic(t *testing.T) {
|
||||||
|
testdata := []struct {
|
||||||
|
name, from string
|
||||||
|
to []string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "simple compare",
|
||||||
|
from: "golang",
|
||||||
|
to: []string{"golang"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "compare non-taged image whit list who tag requirement",
|
||||||
|
from: "golang",
|
||||||
|
to: []string{"golang:v3.0"},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "compare taged image whit list who tag no requirement",
|
||||||
|
from: "golang:v3.0",
|
||||||
|
to: []string{"golang"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "compare taged image whit list who has image with no tag requirement",
|
||||||
|
from: "golang:1.0",
|
||||||
|
to: []string{"golang", "golang:2.0"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "compare taged image whit list who only has images with tag requirement",
|
||||||
|
from: "golang:1.0",
|
||||||
|
to: []string{"golang:latest", "golang:2.0"},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "compare taged image whit list who only has images with tag requirement",
|
||||||
|
from: "golang:1.0",
|
||||||
|
to: []string{"golang:latest", "golang:1.0"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testdata {
|
||||||
|
if !assert.Equal(t, test.want, MatchImageDynamic(test.from, test.to...)) {
|
||||||
|
t.Logf("test data: '%s' -> '%s'", test.from, test.to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Test_matchHostname(t *testing.T) {
|
func Test_matchHostname(t *testing.T) {
|
||||||
testdata := []struct {
|
testdata := []struct {
|
||||||
image, hostname string
|
image, hostname string
|
||||||
|
|
Loading…
Reference in a new issue