mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-01-03 14:18:42 +00:00
Split repo trusted setting (#4025)
This commit is contained in:
parent
383bfbb6de
commit
29474fc7d9
26 changed files with 373 additions and 193 deletions
|
@ -207,7 +207,11 @@ func execWithAxis(ctx context.Context, c *cli.Command, file, repoPath string, ax
|
||||||
|
|
||||||
// lint the yaml file
|
// lint the yaml file
|
||||||
err = linter.New(
|
err = linter.New(
|
||||||
linter.WithTrusted(true),
|
linter.WithTrusted(linter.TrustedConfiguration{
|
||||||
|
Security: c.Bool("repo-trusted-security"),
|
||||||
|
Network: c.Bool("repo-trusted-network"),
|
||||||
|
Volumes: c.Bool("repo-trusted-volumes"),
|
||||||
|
}),
|
||||||
linter.PrivilegedPlugins(privilegedPlugins),
|
linter.PrivilegedPlugins(privilegedPlugins),
|
||||||
linter.WithTrustedClonePlugins(constant.TrustedClonePlugins),
|
linter.WithTrustedClonePlugins(constant.TrustedClonePlugins),
|
||||||
).Lint([]*linter.WorkflowConfig{{
|
).Lint([]*linter.WorkflowConfig{{
|
||||||
|
|
|
@ -185,9 +185,19 @@ var flags = []cli.Flag{
|
||||||
Usage: "Set the metadata environment variable \"CI_REPO_PRIVATE\".",
|
Usage: "Set the metadata environment variable \"CI_REPO_PRIVATE\".",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Sources: cli.EnvVars("CI_REPO_TRUSTED"),
|
Sources: cli.EnvVars("CI_REPO_TRUSTED_NETWORK"),
|
||||||
Name: "repo-trusted",
|
Name: "repo-trusted-network",
|
||||||
Usage: "Set the metadata environment variable \"CI_REPO_TRUSTED\".",
|
Usage: "Set the metadata environment variable \"CI_REPO_TRUSTED_NETWORK\".",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Sources: cli.EnvVars("CI_REPO_TRUSTED_VOLUMES"),
|
||||||
|
Name: "repo-trusted-volumes",
|
||||||
|
Usage: "Set the metadata environment variable \"CI_REPO_TRUSTED_VOLUMES\".",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Sources: cli.EnvVars("CI_REPO_TRUSTED_SECURITY"),
|
||||||
|
Name: "repo-trusted-security",
|
||||||
|
Usage: "Set the metadata environment variable \"CI_REPO_TRUSTED_SECURITY\".",
|
||||||
},
|
},
|
||||||
&cli.IntFlag{
|
&cli.IntFlag{
|
||||||
Sources: cli.EnvVars("CI_PIPELINE_NUMBER"),
|
Sources: cli.EnvVars("CI_PIPELINE_NUMBER"),
|
||||||
|
|
|
@ -83,7 +83,9 @@ func metadataFromContext(_ context.Context, c *cli.Command, axis matrix.Axis, w
|
||||||
metadataFileAndOverrideOrDefault(c, "repo-clone-url", func(s string) { m.Repo.CloneURL = s }, c.String)
|
metadataFileAndOverrideOrDefault(c, "repo-clone-url", func(s string) { m.Repo.CloneURL = s }, c.String)
|
||||||
metadataFileAndOverrideOrDefault(c, "repo-clone-ssh-url", func(s string) { m.Repo.CloneSSHURL = s }, c.String)
|
metadataFileAndOverrideOrDefault(c, "repo-clone-ssh-url", func(s string) { m.Repo.CloneSSHURL = s }, c.String)
|
||||||
metadataFileAndOverrideOrDefault(c, "repo-private", func(b bool) { m.Repo.Private = b }, c.Bool)
|
metadataFileAndOverrideOrDefault(c, "repo-private", func(b bool) { m.Repo.Private = b }, c.Bool)
|
||||||
metadataFileAndOverrideOrDefault(c, "repo-trusted", func(b bool) { m.Repo.Trusted = b }, c.Bool)
|
metadataFileAndOverrideOrDefault(c, "repo-trusted-network", func(b bool) { m.Repo.Trusted.Network = b }, c.Bool)
|
||||||
|
metadataFileAndOverrideOrDefault(c, "repo-trusted-security", func(b bool) { m.Repo.Trusted.Security = b }, c.Bool)
|
||||||
|
metadataFileAndOverrideOrDefault(c, "repo-trusted-volumes", func(b bool) { m.Repo.Trusted.Volumes = b }, c.Bool)
|
||||||
|
|
||||||
// Current Pipeline
|
// Current Pipeline
|
||||||
metadataFileAndOverrideOrDefault(c, "pipeline-number", func(i int64) { m.Curr.Number = i }, c.Int)
|
metadataFileAndOverrideOrDefault(c, "pipeline-number", func(i int64) { m.Curr.Number = i }, c.Int)
|
||||||
|
|
|
@ -110,7 +110,11 @@ func lintFile(_ context.Context, c *cli.Command, file string) error {
|
||||||
|
|
||||||
// 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(
|
err = linter.New(
|
||||||
linter.WithTrusted(true),
|
linter.WithTrusted(linter.TrustedConfiguration{
|
||||||
|
Network: true,
|
||||||
|
Volumes: true,
|
||||||
|
Security: true,
|
||||||
|
}),
|
||||||
linter.PrivilegedPlugins(c.StringSlice("plugins-privileged")),
|
linter.PrivilegedPlugins(c.StringSlice("plugins-privileged")),
|
||||||
linter.WithTrustedClonePlugins(c.StringSlice("plugins-trusted-clone")),
|
linter.WithTrustedClonePlugins(c.StringSlice("plugins-trusted-clone")),
|
||||||
).Lint([]*linter.WorkflowConfig{config})
|
).Lint([]*linter.WorkflowConfig{config})
|
||||||
|
|
|
@ -5099,7 +5099,7 @@ const docTemplate = `{
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"trusted": {
|
"trusted": {
|
||||||
"type": "boolean"
|
"$ref": "#/definitions/model.TrustedConfiguration"
|
||||||
},
|
},
|
||||||
"visibility": {
|
"visibility": {
|
||||||
"$ref": "#/definitions/RepoVisibility"
|
"$ref": "#/definitions/RepoVisibility"
|
||||||
|
@ -5134,7 +5134,7 @@ const docTemplate = `{
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"trusted": {
|
"trusted": {
|
||||||
"type": "boolean"
|
"$ref": "#/definitions/model.TrustedConfigurationPatch"
|
||||||
},
|
},
|
||||||
"visibility": {
|
"visibility": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
@ -5555,7 +5555,7 @@ const docTemplate = `{
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"trusted": {
|
"trusted": {
|
||||||
"type": "boolean"
|
"$ref": "#/definitions/metadata.TrustedConfiguration"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -5590,6 +5590,20 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"metadata.TrustedConfiguration": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"network": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"volumes": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"metadata.Workflow": {
|
"metadata.Workflow": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -5628,6 +5642,34 @@ const docTemplate = `{
|
||||||
"ForgeTypeAddon"
|
"ForgeTypeAddon"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"model.TrustedConfiguration": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"network": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"volumes": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"model.TrustedConfigurationPatch": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"network": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"volumes": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"model.Workflow": {
|
"model.Workflow": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
@ -62,7 +62,9 @@ This is the reference list of all environment variables available to your pipeli
|
||||||
| `CI_REPO_CLONE_SSH_URL` | repository SSH clone URL | `git@git.example.com:john-doe/my-repo.git` |
|
| `CI_REPO_CLONE_SSH_URL` | repository SSH clone URL | `git@git.example.com:john-doe/my-repo.git` |
|
||||||
| `CI_REPO_DEFAULT_BRANCH` | repository default branch | `main` |
|
| `CI_REPO_DEFAULT_BRANCH` | repository default branch | `main` |
|
||||||
| `CI_REPO_PRIVATE` | repository is private | `true` |
|
| `CI_REPO_PRIVATE` | repository is private | `true` |
|
||||||
| `CI_REPO_TRUSTED` | repository is trusted | `false` |
|
| `CI_REPO_TRUSTED_NETWORK` | repository has trusted network access | `false` |
|
||||||
|
| `CI_REPO_TRUSTED_VOLUMES` | repository has trusted volumes access | `false` |
|
||||||
|
| `CI_REPO_TRUSTED_SECURITY` | repository has trusted security access | `false` |
|
||||||
| | **Current Commit** | |
|
| | **Current Commit** | |
|
||||||
| `CI_COMMIT_SHA` | commit SHA | `eba09b46064473a1d345da7abf28b477468e8dbd` |
|
| `CI_COMMIT_SHA` | commit SHA | `eba09b46064473a1d345da7abf28b477468e8dbd` |
|
||||||
| `CI_COMMIT_REF` | commit ref | `refs/heads/main` |
|
| `CI_COMMIT_REF` | commit ref | `refs/heads/main` |
|
||||||
|
|
|
@ -32,6 +32,7 @@ Some versions need some changes to the server configuration or the pipeline conf
|
||||||
- Removed `WOODPECKER_WEBHOOK_HOST` in favor of `WOODPECKER_EXPERT_WEBHOOK_HOST`
|
- Removed `WOODPECKER_WEBHOOK_HOST` in favor of `WOODPECKER_EXPERT_WEBHOOK_HOST`
|
||||||
- Migrated to rfc9421 for webhook signatures
|
- Migrated to rfc9421 for webhook signatures
|
||||||
- Renamed `start_time`, `end_time`, `created_at`, `started_at`, `finished_at` and `reviewed_at` JSON fields to `started`, `finished`, `created`, `started`, `finished`, `reviewed`
|
- Renamed `start_time`, `end_time`, `created_at`, `started_at`, `finished_at` and `reviewed_at` JSON fields to `started`, `finished`, `created`, `started`, `finished`, `reviewed`
|
||||||
|
- JSON field `trusted` on repo model was changed from boolean to object
|
||||||
- Update all webhooks by pressing the "Repair all" button in the admin settings as the webhook token claims have changed
|
- Update all webhooks by pressing the "Repair all" button in the admin settings as the webhook token claims have changed
|
||||||
- Crons now use standard Linux syntax without seconds
|
- Crons now use standard Linux syntax without seconds
|
||||||
- Replaced `configs` object by `netrc` in external configuration APIs
|
- Replaced `configs` object by `netrc` in external configuration APIs
|
||||||
|
|
|
@ -166,6 +166,9 @@ CI_REPO_PRIVATE=false
|
||||||
CI_REPO_REMOTE_ID=4
|
CI_REPO_REMOTE_ID=4
|
||||||
CI_REPO_SCM=git
|
CI_REPO_SCM=git
|
||||||
CI_REPO_TRUSTED=false
|
CI_REPO_TRUSTED=false
|
||||||
|
CI_REPO_TRUSTED_NETWORK=false
|
||||||
|
CI_REPO_TRUSTED_VOLUMES=false
|
||||||
|
CI_REPO_TRUSTED_SECURITY=false
|
||||||
CI_REPO_URL=http://1.2.3.4:3000/test/woodpecker-test
|
CI_REPO_URL=http://1.2.3.4:3000/test/woodpecker-test
|
||||||
CI_STEP_NAME=
|
CI_STEP_NAME=
|
||||||
CI_STEP_NUMBER=0
|
CI_STEP_NUMBER=0
|
||||||
|
|
|
@ -51,18 +51,22 @@ func (m *Metadata) Environ() map[string]string {
|
||||||
prevSourceBranch, prevTargetBranch := getSourceTargetBranches(m.Prev.Commit.Refspec)
|
prevSourceBranch, prevTargetBranch := getSourceTargetBranches(m.Prev.Commit.Refspec)
|
||||||
|
|
||||||
params := map[string]string{
|
params := map[string]string{
|
||||||
"CI": m.Sys.Name,
|
"CI": m.Sys.Name,
|
||||||
"CI_REPO": path.Join(m.Repo.Owner, m.Repo.Name),
|
"CI_REPO": path.Join(m.Repo.Owner, m.Repo.Name),
|
||||||
"CI_REPO_NAME": m.Repo.Name,
|
"CI_REPO_NAME": m.Repo.Name,
|
||||||
"CI_REPO_OWNER": m.Repo.Owner,
|
"CI_REPO_OWNER": m.Repo.Owner,
|
||||||
"CI_REPO_REMOTE_ID": m.Repo.RemoteID,
|
"CI_REPO_REMOTE_ID": m.Repo.RemoteID,
|
||||||
"CI_REPO_SCM": m.Repo.SCM,
|
"CI_REPO_SCM": m.Repo.SCM,
|
||||||
"CI_REPO_URL": m.Repo.ForgeURL,
|
"CI_REPO_URL": m.Repo.ForgeURL,
|
||||||
"CI_REPO_CLONE_URL": m.Repo.CloneURL,
|
"CI_REPO_CLONE_URL": m.Repo.CloneURL,
|
||||||
"CI_REPO_CLONE_SSH_URL": m.Repo.CloneSSHURL,
|
"CI_REPO_CLONE_SSH_URL": m.Repo.CloneSSHURL,
|
||||||
"CI_REPO_DEFAULT_BRANCH": m.Repo.Branch,
|
"CI_REPO_DEFAULT_BRANCH": m.Repo.Branch,
|
||||||
"CI_REPO_PRIVATE": strconv.FormatBool(m.Repo.Private),
|
"CI_REPO_PRIVATE": strconv.FormatBool(m.Repo.Private),
|
||||||
"CI_REPO_TRUSTED": strconv.FormatBool(m.Repo.Trusted),
|
"CI_REPO_TRUSTED_NETWORK": strconv.FormatBool(m.Repo.Trusted.Network),
|
||||||
|
"CI_REPO_TRUSTED_VOLUMES": strconv.FormatBool(m.Repo.Trusted.Volumes),
|
||||||
|
"CI_REPO_TRUSTED_SECURITY": strconv.FormatBool(m.Repo.Trusted.Security),
|
||||||
|
// Deprecated remove in 4.x
|
||||||
|
"CI_REPO_TRUSTED": strconv.FormatBool(m.Repo.Trusted.Security && m.Repo.Trusted.Network && m.Repo.Trusted.Volumes),
|
||||||
|
|
||||||
"CI_COMMIT_SHA": m.Curr.Commit.Sha,
|
"CI_COMMIT_SHA": m.Curr.Commit.Sha,
|
||||||
"CI_COMMIT_REF": m.Curr.Commit.Ref,
|
"CI_COMMIT_REF": m.Curr.Commit.Ref,
|
||||||
|
|
|
@ -29,17 +29,17 @@ type (
|
||||||
|
|
||||||
// Repo defines runtime metadata for a repository.
|
// Repo defines runtime metadata for a repository.
|
||||||
Repo struct {
|
Repo struct {
|
||||||
ID int64 `json:"id,omitempty"`
|
ID int64 `json:"id,omitempty"`
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
Owner string `json:"owner,omitempty"`
|
Owner string `json:"owner,omitempty"`
|
||||||
RemoteID string `json:"remote_id,omitempty"`
|
RemoteID string `json:"remote_id,omitempty"`
|
||||||
ForgeURL string `json:"forge_url,omitempty"`
|
ForgeURL string `json:"forge_url,omitempty"`
|
||||||
SCM string `json:"scm,omitempty"`
|
SCM string `json:"scm,omitempty"`
|
||||||
CloneURL string `json:"clone_url,omitempty"`
|
CloneURL string `json:"clone_url,omitempty"`
|
||||||
CloneSSHURL string `json:"clone_url_ssh,omitempty"`
|
CloneSSHURL string `json:"clone_url_ssh,omitempty"`
|
||||||
Private bool `json:"private,omitempty"`
|
Private bool `json:"private,omitempty"`
|
||||||
Branch string `json:"default_branch,omitempty"`
|
Branch string `json:"default_branch,omitempty"`
|
||||||
Trusted bool `json:"trusted,omitempty"`
|
Trusted TrustedConfiguration `json:"trusted,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pipeline defines runtime metadata for a pipeline.
|
// Pipeline defines runtime metadata for a pipeline.
|
||||||
|
@ -113,4 +113,10 @@ type (
|
||||||
// URL returns the root url of a configured forge
|
// URL returns the root url of a configured forge
|
||||||
URL() string
|
URL() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TrustedConfiguration struct {
|
||||||
|
Network bool `json:"network,omitempty"`
|
||||||
|
Volumes bool `json:"volumes,omitempty"`
|
||||||
|
Security bool `json:"security,omitempty"`
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -83,22 +83,22 @@ func (s *Secret) Match(event string) bool {
|
||||||
|
|
||||||
// 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
|
||||||
defaultClonePlugin string
|
defaultClonePlugin string
|
||||||
trustedClonePlugins []string
|
trustedClonePlugins []string
|
||||||
trustedPipeline bool
|
securityTrustedPipeline bool
|
||||||
netrcOnlyTrusted bool
|
netrcOnlyTrusted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Compiler with options.
|
// New creates a new Compiler with options.
|
||||||
|
@ -196,7 +196,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(c.trustedClonePlugins)) {
|
if !c.netrcOnlyTrusted || c.securityTrustedPipeline || (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
|
||||||
}
|
}
|
||||||
|
@ -253,7 +253,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(c.trustedClonePlugins)) {
|
if c.securityTrustedPipeline || (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
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,10 +169,10 @@ func WithTrustedClonePlugins(images []string) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithTrusted configures the compiler with the trusted repo option.
|
// WithTrustedSecurity configures the compiler with the trusted repo option.
|
||||||
func WithTrusted(trusted bool) Option {
|
func WithTrustedSecurity(trusted bool) Option {
|
||||||
return func(compiler *Compiler) {
|
return func(compiler *Compiler) {
|
||||||
compiler.trustedPipeline = trusted
|
compiler.securityTrustedPipeline = trusted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,11 +30,17 @@ import (
|
||||||
|
|
||||||
// A Linter lints a pipeline configuration.
|
// A Linter lints a pipeline configuration.
|
||||||
type Linter struct {
|
type Linter struct {
|
||||||
trusted bool
|
trusted TrustedConfiguration
|
||||||
privilegedPlugins *[]string
|
privilegedPlugins *[]string
|
||||||
trustedClonePlugins *[]string
|
trustedClonePlugins *[]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TrustedConfiguration struct {
|
||||||
|
Network bool
|
||||||
|
Volumes bool
|
||||||
|
Security bool
|
||||||
|
}
|
||||||
|
|
||||||
// New creates a new Linter with options.
|
// New creates a new Linter with options.
|
||||||
func New(opts ...Option) *Linter {
|
func New(opts ...Option) *Linter {
|
||||||
linter := new(Linter)
|
linter := new(Linter)
|
||||||
|
@ -143,10 +149,8 @@ func (l *Linter) lintContainers(config *WorkflowConfig, area string) error {
|
||||||
if err := l.lintImage(config, container, area); err != nil {
|
if err := l.lintImage(config, container, area); err != nil {
|
||||||
linterErr = multierr.Append(linterErr, err)
|
linterErr = multierr.Append(linterErr, err)
|
||||||
}
|
}
|
||||||
if !l.trusted {
|
if err := l.lintTrusted(config, container, area); err != nil {
|
||||||
if err := l.lintTrusted(config, container, area); err != nil {
|
linterErr = multierr.Append(linterErr, err)
|
||||||
linterErr = multierr.Append(linterErr, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if err := l.lintSettings(config, container, area); err != nil {
|
if err := l.lintSettings(config, container, area); err != nil {
|
||||||
linterErr = multierr.Append(linterErr, err)
|
linterErr = multierr.Append(linterErr, err)
|
||||||
|
@ -204,29 +208,35 @@ func (l *Linter) lintSettings(config *WorkflowConfig, c *types.Container, field
|
||||||
func (l *Linter) lintTrusted(config *WorkflowConfig, c *types.Container, area string) error {
|
func (l *Linter) lintTrusted(config *WorkflowConfig, c *types.Container, area string) error {
|
||||||
yamlPath := fmt.Sprintf("%s.%s", area, c.Name)
|
yamlPath := fmt.Sprintf("%s.%s", area, c.Name)
|
||||||
errors := []string{}
|
errors := []string{}
|
||||||
if c.Privileged {
|
if !l.trusted.Security {
|
||||||
errors = append(errors, "Insufficient privileges to use privileged mode")
|
if c.Privileged {
|
||||||
|
errors = append(errors, "Insufficient privileges to use privileged mode")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(c.DNS) != 0 {
|
if !l.trusted.Network {
|
||||||
errors = append(errors, "Insufficient privileges to use custom dns")
|
if len(c.DNS) != 0 {
|
||||||
|
errors = append(errors, "Insufficient privileges to use custom dns")
|
||||||
|
}
|
||||||
|
if len(c.DNSSearch) != 0 {
|
||||||
|
errors = append(errors, "Insufficient privileges to use dns_search")
|
||||||
|
}
|
||||||
|
if len(c.ExtraHosts) != 0 {
|
||||||
|
errors = append(errors, "Insufficient privileges to use extra_hosts")
|
||||||
|
}
|
||||||
|
if len(c.NetworkMode) != 0 {
|
||||||
|
errors = append(errors, "Insufficient privileges to use network_mode")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(c.DNSSearch) != 0 {
|
if !l.trusted.Volumes {
|
||||||
errors = append(errors, "Insufficient privileges to use dns_search")
|
if len(c.Devices) != 0 {
|
||||||
}
|
errors = append(errors, "Insufficient privileges to use devices")
|
||||||
if len(c.Devices) != 0 {
|
}
|
||||||
errors = append(errors, "Insufficient privileges to use devices")
|
if len(c.Volumes.Volumes) != 0 {
|
||||||
}
|
errors = append(errors, "Insufficient privileges to use volumes")
|
||||||
if len(c.ExtraHosts) != 0 {
|
}
|
||||||
errors = append(errors, "Insufficient privileges to use extra_hosts")
|
if len(c.Tmpfs) != 0 {
|
||||||
}
|
errors = append(errors, "Insufficient privileges to use tmpfs")
|
||||||
if len(c.NetworkMode) != 0 {
|
}
|
||||||
errors = append(errors, "Insufficient privileges to use network_mode")
|
|
||||||
}
|
|
||||||
if len(c.Volumes.Volumes) != 0 {
|
|
||||||
errors = append(errors, "Insufficient privileges to use volumes")
|
|
||||||
}
|
|
||||||
if len(c.Tmpfs) != 0 {
|
|
||||||
errors = append(errors, "Insufficient privileges to use tmpfs")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
|
|
|
@ -94,7 +94,11 @@ steps:
|
||||||
conf, err := yaml.ParseString(testd.Data)
|
conf, err := yaml.ParseString(testd.Data)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.NoError(t, linter.New(linter.WithTrusted(true)).Lint([]*linter.WorkflowConfig{{
|
assert.NoError(t, linter.New(linter.WithTrusted(linter.TrustedConfiguration{
|
||||||
|
Network: true,
|
||||||
|
Volumes: true,
|
||||||
|
Security: true,
|
||||||
|
})).Lint([]*linter.WorkflowConfig{{
|
||||||
File: testd.Title,
|
File: testd.Title,
|
||||||
RawConfig: testd.Data,
|
RawConfig: testd.Data,
|
||||||
Workflow: conf,
|
Workflow: conf,
|
||||||
|
|
|
@ -18,7 +18,7 @@ package linter
|
||||||
type Option func(*Linter)
|
type Option func(*Linter)
|
||||||
|
|
||||||
// WithTrusted adds the trusted option to the linter.
|
// WithTrusted adds the trusted option to the linter.
|
||||||
func WithTrusted(trusted bool) Option {
|
func WithTrusted(trusted TrustedConfiguration) Option {
|
||||||
return func(linter *Linter) {
|
return func(linter *Linter) {
|
||||||
linter.trusted = trusted
|
linter.trusted = trusted
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,10 +224,23 @@ func PatchRepo(c *gin.Context) {
|
||||||
c.String(http.StatusForbidden, fmt.Sprintf("Timeout is not allowed to be higher than max timeout (%d min)", server.Config.Pipeline.MaxTimeout))
|
c.String(http.StatusForbidden, fmt.Sprintf("Timeout is not allowed to be higher than max timeout (%d min)", server.Config.Pipeline.MaxTimeout))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if in.IsTrusted != nil && *in.IsTrusted != repo.IsTrusted && !user.Admin {
|
|
||||||
log.Trace().Msgf("user '%s' wants to make repo trusted without being an instance admin", user.Login)
|
if in.Trusted != nil {
|
||||||
c.String(http.StatusForbidden, "Insufficient privileges")
|
if (*in.Trusted.Network != repo.Trusted.Network || *in.Trusted.Volumes != repo.Trusted.Volumes || *in.Trusted.Security != repo.Trusted.Security) && !user.Admin {
|
||||||
return
|
log.Trace().Msgf("user '%s' wants to change trusted without being an instance admin", user.Login)
|
||||||
|
c.String(http.StatusForbidden, "Insufficient privileges")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.Trusted.Network != nil {
|
||||||
|
repo.Trusted.Network = *in.Trusted.Network
|
||||||
|
}
|
||||||
|
if in.Trusted.Security != nil {
|
||||||
|
repo.Trusted.Security = *in.Trusted.Security
|
||||||
|
}
|
||||||
|
if in.Trusted.Volumes != nil {
|
||||||
|
repo.Trusted.Volumes = *in.Trusted.Volumes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if in.AllowPull != nil {
|
if in.AllowPull != nil {
|
||||||
|
@ -239,9 +252,6 @@ func PatchRepo(c *gin.Context) {
|
||||||
if in.IsGated != nil {
|
if in.IsGated != nil {
|
||||||
repo.IsGated = *in.IsGated
|
repo.IsGated = *in.IsGated
|
||||||
}
|
}
|
||||||
if in.IsTrusted != nil {
|
|
||||||
repo.IsTrusted = *in.IsTrusted
|
|
||||||
}
|
|
||||||
if in.Timeout != nil {
|
if in.Timeout != nil {
|
||||||
repo.Timeout = *in.Timeout
|
repo.Timeout = *in.Timeout
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,31 +26,31 @@ type Repo struct {
|
||||||
UserID int64 `json:"-" xorm:"INDEX 'user_id'"`
|
UserID int64 `json:"-" xorm:"INDEX 'user_id'"`
|
||||||
ForgeID int64 `json:"forge_id,omitempty" xorm:"forge_id"`
|
ForgeID int64 `json:"forge_id,omitempty" xorm:"forge_id"`
|
||||||
// ForgeRemoteID is the unique identifier for the repository on the forge.
|
// ForgeRemoteID is the unique identifier for the repository on the forge.
|
||||||
ForgeRemoteID ForgeRemoteID `json:"forge_remote_id" xorm:"forge_remote_id"`
|
ForgeRemoteID ForgeRemoteID `json:"forge_remote_id" xorm:"forge_remote_id"`
|
||||||
OrgID int64 `json:"org_id" xorm:"INDEX 'org_id'"`
|
OrgID int64 `json:"org_id" xorm:"INDEX 'org_id'"`
|
||||||
Owner string `json:"owner" xorm:"UNIQUE(name) 'owner'"`
|
Owner string `json:"owner" xorm:"UNIQUE(name) 'owner'"`
|
||||||
Name string `json:"name" xorm:"UNIQUE(name) 'name'"`
|
Name string `json:"name" xorm:"UNIQUE(name) 'name'"`
|
||||||
FullName string `json:"full_name" xorm:"UNIQUE 'full_name'"`
|
FullName string `json:"full_name" xorm:"UNIQUE 'full_name'"`
|
||||||
Avatar string `json:"avatar_url,omitempty" xorm:"varchar(500) 'avatar'"`
|
Avatar string `json:"avatar_url,omitempty" xorm:"varchar(500) 'avatar'"`
|
||||||
ForgeURL string `json:"forge_url,omitempty" xorm:"varchar(1000) 'forge_url'"`
|
ForgeURL string `json:"forge_url,omitempty" xorm:"varchar(1000) 'forge_url'"`
|
||||||
Clone string `json:"clone_url,omitempty" xorm:"varchar(1000) 'clone'"`
|
Clone string `json:"clone_url,omitempty" xorm:"varchar(1000) 'clone'"`
|
||||||
CloneSSH string `json:"clone_url_ssh" xorm:"varchar(1000) 'clone_ssh'"`
|
CloneSSH string `json:"clone_url_ssh" xorm:"varchar(1000) 'clone_ssh'"`
|
||||||
Branch string `json:"default_branch,omitempty" xorm:"varchar(500) 'branch'"`
|
Branch string `json:"default_branch,omitempty" xorm:"varchar(500) 'branch'"`
|
||||||
SCMKind SCMKind `json:"scm,omitempty" xorm:"varchar(50) 'scm'"`
|
SCMKind SCMKind `json:"scm,omitempty" xorm:"varchar(50) 'scm'"`
|
||||||
PREnabled bool `json:"pr_enabled" xorm:"DEFAULT TRUE 'pr_enabled'"`
|
PREnabled bool `json:"pr_enabled" xorm:"DEFAULT TRUE 'pr_enabled'"`
|
||||||
Timeout int64 `json:"timeout,omitempty" xorm:"timeout"`
|
Timeout int64 `json:"timeout,omitempty" xorm:"timeout"`
|
||||||
Visibility RepoVisibility `json:"visibility" xorm:"varchar(10) 'visibility'"`
|
Visibility RepoVisibility `json:"visibility" xorm:"varchar(10) 'visibility'"`
|
||||||
IsSCMPrivate bool `json:"private" xorm:"private"`
|
IsSCMPrivate bool `json:"private" xorm:"private"`
|
||||||
IsTrusted bool `json:"trusted" xorm:"trusted"`
|
Trusted TrustedConfiguration `json:"trusted" xorm:"json 'trusted'"`
|
||||||
IsGated bool `json:"gated" xorm:"gated"`
|
IsGated bool `json:"gated" xorm:"gated"`
|
||||||
IsActive bool `json:"active" xorm:"active"`
|
IsActive bool `json:"active" xorm:"active"`
|
||||||
AllowPull bool `json:"allow_pr" xorm:"allow_pr"`
|
AllowPull bool `json:"allow_pr" xorm:"allow_pr"`
|
||||||
AllowDeploy bool `json:"allow_deploy" xorm:"allow_deploy"`
|
AllowDeploy bool `json:"allow_deploy" xorm:"allow_deploy"`
|
||||||
Config string `json:"config_file" xorm:"varchar(500) 'config_path'"`
|
Config string `json:"config_file" xorm:"varchar(500) 'config_path'"`
|
||||||
Hash string `json:"-" xorm:"varchar(500) 'hash'"`
|
Hash string `json:"-" xorm:"varchar(500) 'hash'"`
|
||||||
Perm *Perm `json:"-" xorm:"-"`
|
Perm *Perm `json:"-" xorm:"-"`
|
||||||
CancelPreviousPipelineEvents []WebhookEvent `json:"cancel_previous_pipeline_events" xorm:"json 'cancel_previous_pipeline_events'"`
|
CancelPreviousPipelineEvents []WebhookEvent `json:"cancel_previous_pipeline_events" xorm:"json 'cancel_previous_pipeline_events'"`
|
||||||
NetrcOnlyTrusted bool `json:"netrc_only_trusted" xorm:"NOT NULL DEFAULT true 'netrc_only_trusted'"`
|
NetrcOnlyTrusted bool `json:"netrc_only_trusted" xorm:"NOT NULL DEFAULT true 'netrc_only_trusted'"`
|
||||||
} // @name Repo
|
} // @name Repo
|
||||||
|
|
||||||
// TableName return database table name for xorm.
|
// TableName return database table name for xorm.
|
||||||
|
@ -108,15 +108,15 @@ func (r *Repo) Update(from *Repo) {
|
||||||
|
|
||||||
// RepoPatch represents a repository patch object.
|
// RepoPatch represents a repository patch object.
|
||||||
type RepoPatch struct {
|
type RepoPatch struct {
|
||||||
Config *string `json:"config_file,omitempty"`
|
Config *string `json:"config_file,omitempty"`
|
||||||
IsTrusted *bool `json:"trusted,omitempty"`
|
IsGated *bool `json:"gated,omitempty"`
|
||||||
IsGated *bool `json:"gated,omitempty"`
|
Timeout *int64 `json:"timeout,omitempty"`
|
||||||
Timeout *int64 `json:"timeout,omitempty"`
|
Visibility *string `json:"visibility,omitempty"`
|
||||||
Visibility *string `json:"visibility,omitempty"`
|
AllowPull *bool `json:"allow_pr,omitempty"`
|
||||||
AllowPull *bool `json:"allow_pr,omitempty"`
|
AllowDeploy *bool `json:"allow_deploy,omitempty"`
|
||||||
AllowDeploy *bool `json:"allow_deploy,omitempty"`
|
CancelPreviousPipelineEvents *[]WebhookEvent `json:"cancel_previous_pipeline_events"`
|
||||||
CancelPreviousPipelineEvents *[]WebhookEvent `json:"cancel_previous_pipeline_events"`
|
NetrcOnlyTrusted *bool `json:"netrc_only_trusted"`
|
||||||
NetrcOnlyTrusted *bool `json:"netrc_only_trusted"`
|
Trusted *TrustedConfigurationPatch `json:"trusted"`
|
||||||
} // @name RepoPatch
|
} // @name RepoPatch
|
||||||
|
|
||||||
type ForgeRemoteID string
|
type ForgeRemoteID string
|
||||||
|
@ -124,3 +124,15 @@ type ForgeRemoteID string
|
||||||
func (r ForgeRemoteID) IsValid() bool {
|
func (r ForgeRemoteID) IsValid() bool {
|
||||||
return r != "" && r != "0"
|
return r != "" && r != "0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TrustedConfiguration struct {
|
||||||
|
Network bool `json:"network"`
|
||||||
|
Volumes bool `json:"volumes"`
|
||||||
|
Security bool `json:"security"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrustedConfigurationPatch struct {
|
||||||
|
Network *bool `json:"network"`
|
||||||
|
Volumes *bool `json:"volumes"`
|
||||||
|
Security *bool `json:"security"`
|
||||||
|
}
|
||||||
|
|
|
@ -53,7 +53,11 @@ func MetadataFromStruct(forge metadata.ServerForge, repo *model.Repo, pipeline,
|
||||||
CloneSSHURL: repo.CloneSSH,
|
CloneSSHURL: repo.CloneSSH,
|
||||||
Private: repo.IsSCMPrivate,
|
Private: repo.IsSCMPrivate,
|
||||||
Branch: repo.Branch,
|
Branch: repo.Branch,
|
||||||
Trusted: repo.IsTrusted,
|
Trusted: metadata.TrustedConfiguration{
|
||||||
|
Network: repo.Trusted.Network,
|
||||||
|
Volumes: repo.Trusted.Volumes,
|
||||||
|
Security: repo.Trusted.Security,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if idx := strings.LastIndex(repo.FullName, "/"); idx != -1 {
|
if idx := strings.LastIndex(repo.FullName, "/"); idx != -1 {
|
||||||
|
|
|
@ -53,7 +53,8 @@ func TestMetadataFromStruct(t *testing.T) {
|
||||||
"CI_PREV_COMMIT_MESSAGE": "", "CI_PREV_COMMIT_REF": "", "CI_PREV_COMMIT_REFSPEC": "", "CI_PREV_COMMIT_SHA": "", "CI_PREV_COMMIT_URL": "", "CI_PREV_PIPELINE_CREATED": "0",
|
"CI_PREV_COMMIT_MESSAGE": "", "CI_PREV_COMMIT_REF": "", "CI_PREV_COMMIT_REFSPEC": "", "CI_PREV_COMMIT_SHA": "", "CI_PREV_COMMIT_URL": "", "CI_PREV_PIPELINE_CREATED": "0",
|
||||||
"CI_PREV_PIPELINE_DEPLOY_TARGET": "", "CI_PREV_PIPELINE_DEPLOY_TASK": "", "CI_PREV_PIPELINE_EVENT": "", "CI_PREV_PIPELINE_FINISHED": "0", "CI_PREV_PIPELINE_NUMBER": "0", "CI_PREV_PIPELINE_PARENT": "0",
|
"CI_PREV_PIPELINE_DEPLOY_TARGET": "", "CI_PREV_PIPELINE_DEPLOY_TASK": "", "CI_PREV_PIPELINE_EVENT": "", "CI_PREV_PIPELINE_FINISHED": "0", "CI_PREV_PIPELINE_NUMBER": "0", "CI_PREV_PIPELINE_PARENT": "0",
|
||||||
"CI_PREV_PIPELINE_STARTED": "0", "CI_PREV_PIPELINE_STATUS": "", "CI_PREV_PIPELINE_URL": "/repos/0/pipeline/0", "CI_PREV_PIPELINE_FORGE_URL": "", "CI_REPO": "", "CI_REPO_CLONE_URL": "", "CI_REPO_CLONE_SSH_URL": "", "CI_REPO_DEFAULT_BRANCH": "", "CI_REPO_REMOTE_ID": "",
|
"CI_PREV_PIPELINE_STARTED": "0", "CI_PREV_PIPELINE_STATUS": "", "CI_PREV_PIPELINE_URL": "/repos/0/pipeline/0", "CI_PREV_PIPELINE_FORGE_URL": "", "CI_REPO": "", "CI_REPO_CLONE_URL": "", "CI_REPO_CLONE_SSH_URL": "", "CI_REPO_DEFAULT_BRANCH": "", "CI_REPO_REMOTE_ID": "",
|
||||||
"CI_REPO_NAME": "", "CI_REPO_OWNER": "", "CI_REPO_PRIVATE": "false", "CI_REPO_SCM": "", "CI_REPO_TRUSTED": "false", "CI_REPO_URL": "",
|
"CI_REPO_NAME": "", "CI_REPO_OWNER": "", "CI_REPO_PRIVATE": "false", "CI_REPO_SCM": "", "CI_REPO_TRUSTED": "false", "CI_REPO_TRUSTED_NETWORK": "false",
|
||||||
|
"CI_REPO_TRUSTED_VOLUMES": "false", "CI_REPO_TRUSTED_SECURITY": "false", "CI_REPO_URL": "",
|
||||||
"CI_STEP_NAME": "", "CI_STEP_NUMBER": "0", "CI_STEP_STARTED": "", "CI_STEP_URL": "/repos/0/pipeline/0", "CI_SYSTEM_HOST": "", "CI_SYSTEM_NAME": "woodpecker",
|
"CI_STEP_NAME": "", "CI_STEP_NUMBER": "0", "CI_STEP_STARTED": "", "CI_STEP_URL": "/repos/0/pipeline/0", "CI_SYSTEM_HOST": "", "CI_SYSTEM_NAME": "woodpecker",
|
||||||
"CI_SYSTEM_PLATFORM": "", "CI_SYSTEM_URL": "", "CI_SYSTEM_VERSION": "", "CI_WORKFLOW_NAME": "", "CI_WORKFLOW_NUMBER": "0",
|
"CI_SYSTEM_PLATFORM": "", "CI_SYSTEM_URL": "", "CI_SYSTEM_VERSION": "", "CI_WORKFLOW_NAME": "", "CI_WORKFLOW_NUMBER": "0",
|
||||||
},
|
},
|
||||||
|
@ -89,7 +90,9 @@ func TestMetadataFromStruct(t *testing.T) {
|
||||||
"CI_PREV_PIPELINE_DEPLOY_TARGET": "", "CI_PREV_PIPELINE_DEPLOY_TASK": "", "CI_PREV_PIPELINE_EVENT": "", "CI_PREV_PIPELINE_FINISHED": "0", "CI_PREV_PIPELINE_NUMBER": "2", "CI_PREV_PIPELINE_PARENT": "0",
|
"CI_PREV_PIPELINE_DEPLOY_TARGET": "", "CI_PREV_PIPELINE_DEPLOY_TASK": "", "CI_PREV_PIPELINE_EVENT": "", "CI_PREV_PIPELINE_FINISHED": "0", "CI_PREV_PIPELINE_NUMBER": "2", "CI_PREV_PIPELINE_PARENT": "0",
|
||||||
"CI_PREV_PIPELINE_STARTED": "0", "CI_PREV_PIPELINE_STATUS": "", "CI_PREV_PIPELINE_URL": "https://example.com/repos/0/pipeline/2", "CI_PREV_PIPELINE_FORGE_URL": "", "CI_REPO": "testUser/testRepo", "CI_REPO_CLONE_URL": "https://gitea.com/testUser/testRepo.git", "CI_REPO_CLONE_SSH_URL": "git@gitea.com:testUser/testRepo.git",
|
"CI_PREV_PIPELINE_STARTED": "0", "CI_PREV_PIPELINE_STATUS": "", "CI_PREV_PIPELINE_URL": "https://example.com/repos/0/pipeline/2", "CI_PREV_PIPELINE_FORGE_URL": "", "CI_REPO": "testUser/testRepo", "CI_REPO_CLONE_URL": "https://gitea.com/testUser/testRepo.git", "CI_REPO_CLONE_SSH_URL": "git@gitea.com:testUser/testRepo.git",
|
||||||
"CI_REPO_DEFAULT_BRANCH": "main", "CI_REPO_NAME": "testRepo", "CI_REPO_OWNER": "testUser", "CI_REPO_PRIVATE": "true", "CI_REPO_REMOTE_ID": "",
|
"CI_REPO_DEFAULT_BRANCH": "main", "CI_REPO_NAME": "testRepo", "CI_REPO_OWNER": "testUser", "CI_REPO_PRIVATE": "true", "CI_REPO_REMOTE_ID": "",
|
||||||
"CI_REPO_SCM": "git", "CI_REPO_TRUSTED": "false", "CI_REPO_URL": "https://gitea.com/testUser/testRepo",
|
"CI_REPO_SCM": "git", "CI_REPO_TRUSTED": "false", "CI_REPO_TRUSTED_NETWORK": "false",
|
||||||
|
"CI_REPO_TRUSTED_VOLUMES": "false", "CI_REPO_TRUSTED_SECURITY": "false",
|
||||||
|
"CI_REPO_URL": "https://gitea.com/testUser/testRepo",
|
||||||
"CI_STEP_NAME": "", "CI_STEP_NUMBER": "0", "CI_STEP_STARTED": "", "CI_STEP_URL": "https://example.com/repos/0/pipeline/3", "CI_SYSTEM_HOST": "example.com",
|
"CI_STEP_NAME": "", "CI_STEP_NUMBER": "0", "CI_STEP_STARTED": "", "CI_STEP_URL": "https://example.com/repos/0/pipeline/3", "CI_SYSTEM_HOST": "example.com",
|
||||||
"CI_SYSTEM_NAME": "woodpecker", "CI_SYSTEM_PLATFORM": "", "CI_SYSTEM_URL": "https://example.com", "CI_SYSTEM_VERSION": "", "CI_WORKFLOW_NAME": "hello", "CI_WORKFLOW_NUMBER": "0",
|
"CI_SYSTEM_NAME": "woodpecker", "CI_SYSTEM_PLATFORM": "", "CI_SYSTEM_URL": "https://example.com", "CI_SYSTEM_VERSION": "", "CI_WORKFLOW_NAME": "hello", "CI_WORKFLOW_NUMBER": "0",
|
||||||
},
|
},
|
||||||
|
|
|
@ -141,7 +141,11 @@ func (b *StepBuilder) genItemForWorkflow(workflow *model.Workflow, axis matrix.A
|
||||||
|
|
||||||
// lint pipeline
|
// lint pipeline
|
||||||
errorsAndWarnings = multierr.Append(errorsAndWarnings, linter.New(
|
errorsAndWarnings = multierr.Append(errorsAndWarnings, linter.New(
|
||||||
linter.WithTrusted(b.Repo.IsTrusted),
|
linter.WithTrusted(linter.TrustedConfiguration{
|
||||||
|
Network: b.Repo.Trusted.Network,
|
||||||
|
Volumes: b.Repo.Trusted.Volumes,
|
||||||
|
Security: b.Repo.Trusted.Security,
|
||||||
|
}),
|
||||||
linter.PrivilegedPlugins(server.Config.Pipeline.PrivilegedPlugins),
|
linter.PrivilegedPlugins(server.Config.Pipeline.PrivilegedPlugins),
|
||||||
linter.WithTrustedClonePlugins(server.Config.Pipeline.TrustedClonePlugins),
|
linter.WithTrustedClonePlugins(server.Config.Pipeline.TrustedClonePlugins),
|
||||||
).Lint([]*linter.WorkflowConfig{{
|
).Lint([]*linter.WorkflowConfig{{
|
||||||
|
@ -295,7 +299,7 @@ func (b *StepBuilder) toInternalRepresentation(parsed *yaml_types.Workflow, envi
|
||||||
compiler.WithProxy(b.ProxyOpts),
|
compiler.WithProxy(b.ProxyOpts),
|
||||||
compiler.WithWorkspaceFromURL(compiler.DefaultWorkspaceBase, b.Repo.ForgeURL),
|
compiler.WithWorkspaceFromURL(compiler.DefaultWorkspaceBase, b.Repo.ForgeURL),
|
||||||
compiler.WithMetadata(metadata),
|
compiler.WithMetadata(metadata),
|
||||||
compiler.WithTrusted(b.Repo.IsTrusted),
|
compiler.WithTrustedSecurity(b.Repo.Trusted.Security),
|
||||||
compiler.WithNetrcOnlyTrusted(b.Repo.NetrcOnlyTrusted),
|
compiler.WithNetrcOnlyTrusted(b.Repo.NetrcOnlyTrusted),
|
||||||
).Compile(parsed)
|
).Compile(parsed)
|
||||||
}
|
}
|
||||||
|
|
73
server/store/datastore/migration/017_split_trusted.go
Normal file
73
server/store/datastore/migration/017_split_trusted.go
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package migration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"src.techknowlogick.com/xormigrate"
|
||||||
|
"xorm.io/xorm"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type repoV035 struct {
|
||||||
|
ID int64 `xorm:"pk autoincr 'id'"`
|
||||||
|
IsTrusted bool `xorm:"'trusted'"`
|
||||||
|
Trusted model.TrustedConfiguration `xorm:"json 'trusted_conf'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repoV035) TableName() string {
|
||||||
|
return "repos"
|
||||||
|
}
|
||||||
|
|
||||||
|
var splitTrusted = xormigrate.Migration{
|
||||||
|
ID: "split-trusted",
|
||||||
|
MigrateSession: func(sess *xorm.Session) error {
|
||||||
|
if err := sess.Sync(new(repoV035)); err != nil {
|
||||||
|
return fmt.Errorf("sync new models failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := sess.Where("trusted = ?", false).Cols("trusted_conf").Update(&repoV035{
|
||||||
|
Trusted: model.TrustedConfiguration{
|
||||||
|
Network: false,
|
||||||
|
Security: false,
|
||||||
|
Volumes: false,
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := sess.Where("trusted = ?", true).Cols("trusted_conf").Update(&repoV035{
|
||||||
|
Trusted: model.TrustedConfiguration{
|
||||||
|
Network: true,
|
||||||
|
Security: true,
|
||||||
|
Volumes: true,
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dropTableColumns(sess, "repos", "trusted"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sess.Commit(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return renameColumn(sess, "repos", "trusted_conf", "trusted")
|
||||||
|
},
|
||||||
|
}
|
|
@ -76,66 +76,14 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here we need to get the columns from the original table
|
// Now drop the columns
|
||||||
sql := fmt.Sprintf("SELECT sql FROM sqlite_master WHERE tbl_name='%s' and type='table'", tableName)
|
for _, columnName := range columnNames {
|
||||||
res, err := sess.Query(sql)
|
_, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP COLUMN `%s`;", tableName, columnName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("table `%s`, drop column %v: %w", tableName, columnName, err)
|
||||||
}
|
|
||||||
tableSQL := normalizeSQLiteTableSchema(string(res[0]["sql"]))
|
|
||||||
|
|
||||||
// Separate out the column definitions
|
|
||||||
sqlIndex := strings.Index(tableSQL, "(")
|
|
||||||
if sqlIndex < 0 {
|
|
||||||
return fmt.Errorf("could not separate column definitions")
|
|
||||||
}
|
|
||||||
tableSQL = tableSQL[sqlIndex:]
|
|
||||||
|
|
||||||
// Remove the required columnNames
|
|
||||||
tableSQL = removeColumnFromSQLITETableSchema(tableSQL, columnNames...)
|
|
||||||
|
|
||||||
// Ensure the query is ended properly
|
|
||||||
tableSQL = strings.TrimSpace(tableSQL)
|
|
||||||
if tableSQL[len(tableSQL)-1] != ')' {
|
|
||||||
if tableSQL[len(tableSQL)-1] == ',' {
|
|
||||||
tableSQL = tableSQL[:len(tableSQL)-1]
|
|
||||||
}
|
}
|
||||||
tableSQL += ")"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find all the columns in the table
|
|
||||||
var columns []string
|
|
||||||
for _, rawColumn := range strings.Split(strings.ReplaceAll(tableSQL[1:len(tableSQL)-1], ", ", ",\n"), "\n") {
|
|
||||||
if strings.ContainsAny(rawColumn, "()") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
rawColumn = strings.TrimSpace(rawColumn)
|
|
||||||
columns = append(columns,
|
|
||||||
strings.ReplaceAll(rawColumn[0:strings.Index(rawColumn, " ")], "`", ""),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
tableSQL = fmt.Sprintf("CREATE TABLE `new_%s_new` ", tableName) + tableSQL
|
|
||||||
if _, err := sess.Exec(tableSQL); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now restore the data
|
|
||||||
columnsSeparated := strings.Join(columns, ",")
|
|
||||||
insertSQL := fmt.Sprintf("INSERT INTO `new_%s_new` (%s) SELECT %s FROM %s", tableName, columnsSeparated, columnsSeparated, tableName)
|
|
||||||
if _, err := sess.Exec(insertSQL); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now drop the old table
|
|
||||||
if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rename the table
|
|
||||||
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `new_%s_new` RENAME TO `%s`", tableName, tableName)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case schemas.POSTGRES:
|
case schemas.POSTGRES:
|
||||||
cols := ""
|
cols := ""
|
||||||
for _, col := range columnNames {
|
for _, col := range columnNames {
|
||||||
|
@ -145,7 +93,7 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
|
||||||
cols += "DROP COLUMN `" + col + "` CASCADE"
|
cols += "DROP COLUMN `" + col + "` CASCADE"
|
||||||
}
|
}
|
||||||
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil {
|
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil {
|
||||||
return fmt.Errorf("drop table `%s` columns %v: %w", tableName, columnNames, err)
|
return fmt.Errorf("table `%s`, drop columns %v: %w", tableName, columnNames, err)
|
||||||
}
|
}
|
||||||
case schemas.MYSQL:
|
case schemas.MYSQL:
|
||||||
// Drop indexes on columns first
|
// Drop indexes on columns first
|
||||||
|
@ -173,7 +121,7 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
|
||||||
cols += "DROP COLUMN `" + col + "`"
|
cols += "DROP COLUMN `" + col + "`"
|
||||||
}
|
}
|
||||||
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil {
|
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil {
|
||||||
return fmt.Errorf("drop table `%s` columns %v: %w", tableName, columnNames, err)
|
return fmt.Errorf("table `%s`, drop columns %v: %w", tableName, columnNames, err)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("dialect '%s' not supported", dialect)
|
return fmt.Errorf("dialect '%s' not supported", dialect)
|
||||||
|
|
|
@ -45,6 +45,7 @@ var migrationTasks = []*xormigrate.Migration{
|
||||||
&removeOldMigrationsOfV1,
|
&removeOldMigrationsOfV1,
|
||||||
&addOrgAgents,
|
&addOrgAgents,
|
||||||
&addCustomLabelsToAgent,
|
&addCustomLabelsToAgent,
|
||||||
|
&splitTrusted,
|
||||||
}
|
}
|
||||||
|
|
||||||
var allBeans = []any{
|
var allBeans = []any{
|
||||||
|
|
|
@ -98,7 +98,18 @@
|
||||||
},
|
},
|
||||||
"trusted": {
|
"trusted": {
|
||||||
"trusted": "Trusted",
|
"trusted": "Trusted",
|
||||||
"desc": "Underlying pipeline containers get access to escalated capabilities (like mounting volumes)."
|
"network": {
|
||||||
|
"network": "Network",
|
||||||
|
"desc": "Underlying pipeline containers get access to network privileges like changing DNS."
|
||||||
|
},
|
||||||
|
"volumes": {
|
||||||
|
"volumes": "Volumes",
|
||||||
|
"desc": "Underlying pipeline containers get access to volume privileges."
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"security": "Security",
|
||||||
|
"desc": "Underlying pipeline containers get access to security privileges."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"visibility": {
|
"visibility": {
|
||||||
"visibility": "Project visibility",
|
"visibility": "Project visibility",
|
||||||
|
|
|
@ -45,11 +45,27 @@
|
||||||
:label="$t('repo.settings.general.netrc_only_trusted.netrc_only_trusted')"
|
:label="$t('repo.settings.general.netrc_only_trusted.netrc_only_trusted')"
|
||||||
:description="$t('repo.settings.general.netrc_only_trusted.desc')"
|
:description="$t('repo.settings.general.netrc_only_trusted.desc')"
|
||||||
/>
|
/>
|
||||||
|
</InputField>
|
||||||
|
|
||||||
|
<InputField
|
||||||
|
v-if="user?.admin"
|
||||||
|
docs-url="docs/usage/project-settings#project-settings-1"
|
||||||
|
:label="$t('repo.settings.general.trusted.trusted')"
|
||||||
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-if="user?.admin"
|
v-model="repoSettings.trusted.network"
|
||||||
v-model="repoSettings.trusted"
|
:label="$t('repo.settings.general.trusted.network.network')"
|
||||||
:label="$t('repo.settings.general.trusted.trusted')"
|
:description="$t('repo.settings.general.trusted.network.desc')"
|
||||||
:description="$t('repo.settings.general.trusted.desc')"
|
/>
|
||||||
|
<Checkbox
|
||||||
|
v-model="repoSettings.trusted.volumes"
|
||||||
|
:label="$t('repo.settings.general.trusted.volumes.volumes')"
|
||||||
|
:description="$t('repo.settings.general.trusted.volumes.desc')"
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
v-model="repoSettings.trusted.security"
|
||||||
|
:label="$t('repo.settings.general.trusted.security.security')"
|
||||||
|
:description="$t('repo.settings.general.trusted.security.desc')"
|
||||||
/>
|
/>
|
||||||
</InputField>
|
</InputField>
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ export interface Repo {
|
||||||
// Whether the repository has trusted access for pipelines.
|
// Whether the repository has trusted access for pipelines.
|
||||||
// If the repository is trusted then the host network can be used and
|
// If the repository is trusted then the host network can be used and
|
||||||
// volumes can be created.
|
// volumes can be created.
|
||||||
trusted: boolean;
|
trusted: RepoTrusted;
|
||||||
|
|
||||||
// x-dart-type: Duration
|
// x-dart-type: Duration
|
||||||
// The amount of time in minutes before the pipeline is killed.
|
// The amount of time in minutes before the pipeline is killed.
|
||||||
|
@ -102,3 +102,9 @@ export interface RepoPermissions {
|
||||||
admin: boolean;
|
admin: boolean;
|
||||||
synced: number;
|
synced: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RepoTrusted {
|
||||||
|
network: boolean;
|
||||||
|
volumes: boolean;
|
||||||
|
security: boolean;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue