mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-23 00:46:30 +00:00
use new .drone.sig signature file
This commit is contained in:
parent
8684210241
commit
faf7ff675d
15 changed files with 301 additions and 64 deletions
28
api/build.go
28
api/build.go
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/drone/drone/shared/httputil"
|
"github.com/drone/drone/shared/httputil"
|
||||||
"github.com/drone/drone/store"
|
"github.com/drone/drone/store"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/square/go-jose"
|
||||||
|
|
||||||
"github.com/drone/drone/model"
|
"github.com/drone/drone/model"
|
||||||
"github.com/drone/drone/router/middleware/session"
|
"github.com/drone/drone/router/middleware/session"
|
||||||
|
@ -33,6 +34,9 @@ func init() {
|
||||||
droneYml = ".drone.yml"
|
droneYml = ".drone.yml"
|
||||||
}
|
}
|
||||||
droneSec = fmt.Sprintf("%s.sec", strings.TrimSuffix(droneYml, filepath.Ext(droneYml)))
|
droneSec = fmt.Sprintf("%s.sec", strings.TrimSuffix(droneYml, filepath.Ext(droneYml)))
|
||||||
|
if os.Getenv("CANARY") == "true" {
|
||||||
|
droneSec = fmt.Sprintf("%s.sig", strings.TrimSuffix(droneYml, filepath.Ext(droneYml)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetBuilds(c *gin.Context) {
|
func GetBuilds(c *gin.Context) {
|
||||||
|
@ -296,25 +300,33 @@ func PostBuild(c *gin.Context) {
|
||||||
// enabled using with the environment variable CANARY=true
|
// enabled using with the environment variable CANARY=true
|
||||||
|
|
||||||
if os.Getenv("CANARY") == "true" {
|
if os.Getenv("CANARY") == "true" {
|
||||||
|
|
||||||
|
var signed bool
|
||||||
|
var verified bool
|
||||||
|
|
||||||
|
signature, err := jose.ParseSigned(string(sec))
|
||||||
|
if err == nil && len(sec) != 0 {
|
||||||
|
signed = true
|
||||||
|
output, err := signature.Verify(repo.Hash)
|
||||||
|
if err == nil && string(output) == string(raw) {
|
||||||
|
verified = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bus.Publish(c, bus.NewBuildEvent(bus.Enqueued, repo, build))
|
bus.Publish(c, bus.NewBuildEvent(bus.Enqueued, repo, build))
|
||||||
for _, job := range jobs {
|
for _, job := range jobs {
|
||||||
queue.Publish(c, &queue.Work{
|
queue.Publish(c, &queue.Work{
|
||||||
|
Signed: signed,
|
||||||
|
Verified: verified,
|
||||||
User: user,
|
User: user,
|
||||||
Repo: repo,
|
Repo: repo,
|
||||||
Build: build,
|
Build: build,
|
||||||
BuildLast: last,
|
BuildLast: last,
|
||||||
Job: job,
|
Job: job,
|
||||||
Keys: key,
|
|
||||||
Netrc: netrc,
|
Netrc: netrc,
|
||||||
Yaml: string(raw),
|
Yaml: string(raw),
|
||||||
YamlEnc: string(sec),
|
|
||||||
Secrets: secs,
|
Secrets: secs,
|
||||||
System: &model.System{
|
System: &model.System{Link: httputil.GetURL(c.Request)},
|
||||||
Link: httputil.GetURL(c.Request),
|
|
||||||
Plugins: strings.Split(os.Getenv("PLUGIN_FILTER"), " "),
|
|
||||||
Globals: strings.Split(os.Getenv("PLUGIN_PARAMS"), " "),
|
|
||||||
Escalates: strings.Split(os.Getenv("ESCALATE_FILTER"), " "),
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return // EXIT NOT TO AVOID THE 0.4 ENGINE CODE BELOW
|
return // EXIT NOT TO AVOID THE 0.4 ENGINE CODE BELOW
|
||||||
|
|
|
@ -18,7 +18,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
pathPull = "%s/api/queue/pull"
|
pathPull = "%s/api/queue/pull/%s/%s"
|
||||||
pathWait = "%s/api/queue/wait/%d"
|
pathWait = "%s/api/queue/wait/%d"
|
||||||
pathStream = "%s/api/queue/stream/%d"
|
pathStream = "%s/api/queue/stream/%d"
|
||||||
pathPush = "%s/api/queue/status/%d"
|
pathPush = "%s/api/queue/status/%d"
|
||||||
|
@ -43,9 +43,9 @@ func NewClientToken(uri, token string) Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pull pulls work from the server queue.
|
// Pull pulls work from the server queue.
|
||||||
func (c *client) Pull() (*queue.Work, error) {
|
func (c *client) Pull(os, arch string) (*queue.Work, error) {
|
||||||
out := new(queue.Work)
|
out := new(queue.Work)
|
||||||
uri := fmt.Sprintf(pathPull, c.base)
|
uri := fmt.Sprintf(pathPull, c.base, os, arch)
|
||||||
err := c.post(uri, nil, out)
|
err := c.post(uri, nil, out)
|
||||||
return out, err
|
return out, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
// Client is used to communicate with a Drone server.
|
// Client is used to communicate with a Drone server.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
// Pull pulls work from the server queue.
|
// Pull pulls work from the server queue.
|
||||||
Pull() (*queue.Work, error)
|
Pull(os, arch string) (*queue.Work, error)
|
||||||
|
|
||||||
// Push pushes an update to the server.
|
// Push pushes an update to the server.
|
||||||
Push(*queue.Work) error
|
Push(*queue.Work) error
|
||||||
|
|
|
@ -40,6 +40,18 @@ var AgentCmd = cli.Command{
|
||||||
Usage: "limit number of running docker processes",
|
Usage: "limit number of running docker processes",
|
||||||
Value: 2,
|
Value: 2,
|
||||||
},
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
EnvVar: "DOCKER_OS",
|
||||||
|
Name: "docker-os",
|
||||||
|
Usage: "docker operating system",
|
||||||
|
Value: "linux",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
EnvVar: "DOCKER_ARCH",
|
||||||
|
Name: "docker-arch",
|
||||||
|
Usage: "docker architecture system",
|
||||||
|
Value: "amd64",
|
||||||
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
EnvVar: "DRONE_SERVER",
|
EnvVar: "DRONE_SERVER",
|
||||||
Name: "drone-server",
|
Name: "drone-server",
|
||||||
|
@ -68,16 +80,40 @@ var AgentCmd = cli.Command{
|
||||||
Usage: "start the agent with experimental features",
|
Usage: "start the agent with experimental features",
|
||||||
},
|
},
|
||||||
cli.StringSliceFlag{
|
cli.StringSliceFlag{
|
||||||
EnvVar: "DRONE_NETRC_PLUGIN",
|
EnvVar: "DRONE_PLUGIN_NETRC",
|
||||||
Name: "netrc-plugin",
|
Name: "netrc-plugin",
|
||||||
Usage: "plugins that receive the netrc file",
|
Usage: "plugins that receive the netrc file",
|
||||||
Value: &cli.StringSlice{"git", "hg"},
|
Value: &cli.StringSlice{"git", "hg"},
|
||||||
},
|
},
|
||||||
cli.StringSliceFlag{
|
cli.StringSliceFlag{
|
||||||
EnvVar: "DRONE_PRIVILEGED_PLUGIN",
|
EnvVar: "DRONE_PLUGIN_PRIVILEGED",
|
||||||
Name: "privileged-plugin",
|
Name: "privileged",
|
||||||
Usage: "plugins that require privileged mode",
|
Usage: "plugins that require privileged mode",
|
||||||
Value: &cli.StringSlice{"docker", "gcr", "ecr"},
|
Value: &cli.StringSlice{
|
||||||
|
"plugins/docker",
|
||||||
|
"plugins/docker:*",
|
||||||
|
"plguins/gcr",
|
||||||
|
"plguins/gcr:*",
|
||||||
|
"plugins/ecr",
|
||||||
|
"plugins/ecr:*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
EnvVar: "DRONE_PLUGIN_PULL",
|
||||||
|
Name: "pull",
|
||||||
|
Usage: "always pull latest plugin images",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
EnvVar: "DRONE_PLUGIN_NAMESPACE",
|
||||||
|
Name: "namespace",
|
||||||
|
Value: "plugins",
|
||||||
|
Usage: "default plugin image namespace",
|
||||||
|
},
|
||||||
|
cli.StringSliceFlag{
|
||||||
|
EnvVar: "DRONE_PLUGIN_WHITELIST",
|
||||||
|
Name: "whitelist",
|
||||||
|
Usage: "plugins that are permitted to run on the host",
|
||||||
|
Value: &cli.StringSlice{"plugins/*"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -109,10 +145,21 @@ func start(c *cli.Context) {
|
||||||
for i := 0; i < c.Int("docker-max-procs"); i++ {
|
for i := 0; i < c.Int("docker-max-procs"); i++ {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
r := pipeline{
|
||||||
|
drone: client,
|
||||||
|
docker: docker,
|
||||||
|
config: config{
|
||||||
|
whitelist: c.StringSlice("whitelist"),
|
||||||
|
namespace: c.String("namespace"),
|
||||||
|
privileged: c.StringSlice("privileged"),
|
||||||
|
netrc: c.StringSlice("netrc-plugin"),
|
||||||
|
pull: c.Bool("pull"),
|
||||||
|
},
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
if err := recoverExec(client, docker); err != nil {
|
if err := r.run(); err != nil {
|
||||||
dur := c.Duration("backoff")
|
dur := c.Duration("backoff")
|
||||||
logrus.Debugf("Attempting to reconnect in %v", dur)
|
logrus.Warnf("Attempting to reconnect in %v", dur)
|
||||||
time.Sleep(dur)
|
time.Sleep(dur)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"github.com/drone/drone/engine/compiler"
|
"github.com/drone/drone/engine/compiler"
|
||||||
"github.com/drone/drone/engine/compiler/builtin"
|
"github.com/drone/drone/engine/compiler/builtin"
|
||||||
"github.com/drone/drone/engine/runner"
|
"github.com/drone/drone/engine/runner"
|
||||||
engine "github.com/drone/drone/engine/runner/docker"
|
"github.com/drone/drone/engine/runner/docker"
|
||||||
"github.com/drone/drone/model"
|
"github.com/drone/drone/model"
|
||||||
"github.com/drone/drone/queue"
|
"github.com/drone/drone/queue"
|
||||||
"github.com/drone/drone/yaml/expander"
|
"github.com/drone/drone/yaml/expander"
|
||||||
|
@ -23,15 +23,23 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
func recoverExec(client client.Client, docker dockerclient.Client) error {
|
type config struct {
|
||||||
defer func() {
|
platform string
|
||||||
recover()
|
namespace string
|
||||||
}()
|
whitelist []string
|
||||||
return exec(client, docker)
|
privileged []string
|
||||||
|
netrc []string
|
||||||
|
pull bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func exec(client client.Client, docker dockerclient.Client) error {
|
type pipeline struct {
|
||||||
w, err := client.Pull()
|
drone client.Client
|
||||||
|
docker dockerclient.Client
|
||||||
|
config config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *pipeline) run() error {
|
||||||
|
w, err := r.drone.Pull("linux", "amd64")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -46,23 +54,34 @@ func exec(client client.Client, docker dockerclient.Client) error {
|
||||||
|
|
||||||
envs := toEnv(w)
|
envs := toEnv(w)
|
||||||
w.Yaml = expander.ExpandString(w.Yaml, envs)
|
w.Yaml = expander.ExpandString(w.Yaml, envs)
|
||||||
|
if w.Verified {
|
||||||
|
|
||||||
|
}
|
||||||
|
if w.Signed {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// inject the netrc file into the clone plugin if the repositroy is
|
// inject the netrc file into the clone plugin if the repositroy is
|
||||||
// private and requires authentication.
|
// private and requires authentication.
|
||||||
|
var secrets []*model.Secret
|
||||||
|
if w.Verified {
|
||||||
|
secrets = append(secrets, w.Secrets...)
|
||||||
|
}
|
||||||
|
|
||||||
if w.Repo.IsPrivate {
|
if w.Repo.IsPrivate {
|
||||||
w.Secrets = append(w.Secrets, &model.Secret{
|
secrets = append(secrets, &model.Secret{
|
||||||
Name: "DRONE_NETRC_USERNAME",
|
Name: "DRONE_NETRC_USERNAME",
|
||||||
Value: w.Netrc.Login,
|
Value: w.Netrc.Login,
|
||||||
Images: []string{"git", "hg"}, // TODO(bradrydzewski) use the command line parameters here
|
Images: []string{"git", "hg"}, // TODO(bradrydzewski) use the command line parameters here
|
||||||
Events: []string{model.EventDeploy, model.EventPull, model.EventPush, model.EventTag},
|
Events: []string{model.EventDeploy, model.EventPull, model.EventPush, model.EventTag},
|
||||||
})
|
})
|
||||||
w.Secrets = append(w.Secrets, &model.Secret{
|
secrets = append(secrets, &model.Secret{
|
||||||
Name: "DRONE_NETRC_PASSWORD",
|
Name: "DRONE_NETRC_PASSWORD",
|
||||||
Value: w.Netrc.Password,
|
Value: w.Netrc.Password,
|
||||||
Images: []string{w.Repo.Kind},
|
Images: []string{"git", "hg"},
|
||||||
Events: []string{model.EventDeploy, model.EventPull, model.EventPush, model.EventTag},
|
Events: []string{model.EventDeploy, model.EventPull, model.EventPush, model.EventTag},
|
||||||
})
|
})
|
||||||
w.Secrets = append(w.Secrets, &model.Secret{
|
secrets = append(secrets, &model.Secret{
|
||||||
Name: "DRONE_NETRC_MACHINE",
|
Name: "DRONE_NETRC_MACHINE",
|
||||||
Value: w.Netrc.Machine,
|
Value: w.Netrc.Machine,
|
||||||
Images: []string{"git", "hg"},
|
Images: []string{"git", "hg"},
|
||||||
|
@ -71,25 +90,26 @@ func exec(client client.Client, docker dockerclient.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
trans := []compiler.Transform{
|
trans := []compiler.Transform{
|
||||||
builtin.NewCloneOp("plugins/"+w.Repo.Kind+":latest", true),
|
builtin.NewCloneOp("plugins/git:latest", true),
|
||||||
builtin.NewCacheOp(
|
builtin.NewCacheOp(
|
||||||
"plugins/cache:latest",
|
"plugins/cache:latest",
|
||||||
"/var/lib/drone/cache/"+w.Repo.FullName,
|
"/var/lib/drone/cache/"+w.Repo.FullName,
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
builtin.NewSecretOp(w.Build.Event, w.Secrets),
|
builtin.NewSecretOp(w.Build.Event, secrets),
|
||||||
builtin.NewNormalizeOp("plugins"),
|
builtin.NewNormalizeOp(r.config.namespace),
|
||||||
builtin.NewWorkspaceOp("/drone", "drone/src/github.com/"+w.Repo.FullName),
|
builtin.NewWorkspaceOp("/drone", "/drone/src/github.com/"+w.Repo.FullName),
|
||||||
builtin.NewValidateOp(
|
builtin.NewValidateOp(
|
||||||
w.Repo.IsTrusted,
|
w.Repo.IsTrusted,
|
||||||
[]string{"plugins/*"},
|
r.config.whitelist,
|
||||||
),
|
),
|
||||||
builtin.NewEnvOp(envs),
|
builtin.NewEnvOp(envs),
|
||||||
builtin.NewShellOp(builtin.Linux_adm64),
|
builtin.NewShellOp(builtin.Linux_adm64),
|
||||||
builtin.NewArgsOp(),
|
builtin.NewArgsOp(),
|
||||||
|
builtin.NewEscalateOp(r.config.privileged),
|
||||||
builtin.NewPodOp(prefix),
|
builtin.NewPodOp(prefix),
|
||||||
builtin.NewAliasOp(prefix),
|
builtin.NewAliasOp(prefix),
|
||||||
builtin.NewPullOp(false),
|
builtin.NewPullOp(r.config.pull),
|
||||||
builtin.NewFilterOp(
|
builtin.NewFilterOp(
|
||||||
model.StatusSuccess, // TODO(bradrydzewski) please add the last build status here
|
model.StatusSuccess, // TODO(bradrydzewski) please add the last build status here
|
||||||
w.Build.Branch,
|
w.Build.Branch,
|
||||||
|
@ -109,14 +129,14 @@ func exec(client client.Client, docker dockerclient.Client) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := client.Push(w); err != nil {
|
if err := r.drone.Push(w); err != nil {
|
||||||
logrus.Errorf("Error persisting update %s/%s#%d.%d. %s",
|
logrus.Errorf("Error persisting update %s/%s#%d.%d. %s",
|
||||||
w.Repo.Owner, w.Repo.Name, w.Build.Number, w.Job.Number, err)
|
w.Repo.Owner, w.Repo.Name, w.Build.Number, w.Job.Number, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
conf := runner.Config{
|
conf := runner.Config{
|
||||||
Engine: engine.New(docker),
|
Engine: docker.New(r.docker),
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
|
@ -126,7 +146,7 @@ func exec(client client.Client, docker dockerclient.Client) error {
|
||||||
run.Run()
|
run.Run()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
wait := client.Wait(w.Job.ID)
|
wait := r.drone.Wait(w.Job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -142,7 +162,7 @@ func exec(client client.Client, docker dockerclient.Client) error {
|
||||||
|
|
||||||
rc, wc := io.Pipe()
|
rc, wc := io.Pipe()
|
||||||
go func() {
|
go func() {
|
||||||
err := client.Stream(w.Job.ID, rc)
|
err := r.drone.Stream(w.Job.ID, rc)
|
||||||
if err != nil && err != io.ErrClosedPipe {
|
if err != nil && err != io.ErrClosedPipe {
|
||||||
logrus.Errorf("Error streaming build logs. %s", err)
|
logrus.Errorf("Error streaming build logs. %s", err)
|
||||||
}
|
}
|
||||||
|
@ -187,7 +207,7 @@ func exec(client client.Client, docker dockerclient.Client) error {
|
||||||
logrus.Infof("Finished build %s/%s#%d.%d",
|
logrus.Infof("Finished build %s/%s#%d.%d",
|
||||||
w.Repo.Owner, w.Repo.Name, w.Build.Number, w.Job.Number)
|
w.Repo.Owner, w.Repo.Name, w.Build.Number, w.Job.Number)
|
||||||
|
|
||||||
return client.Push(w)
|
return r.drone.Push(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
func toEnv(w *queue.Work) map[string]string {
|
func toEnv(w *queue.Work) map[string]string {
|
||||||
|
@ -218,7 +238,8 @@ func toEnv(w *queue.Work) map[string]string {
|
||||||
"DRONE_BUILD_CREATED": fmt.Sprintf("%d", w.Build.Created),
|
"DRONE_BUILD_CREATED": fmt.Sprintf("%d", w.Build.Created),
|
||||||
"DRONE_BUILD_STARTED": fmt.Sprintf("%d", w.Build.Started),
|
"DRONE_BUILD_STARTED": fmt.Sprintf("%d", w.Build.Started),
|
||||||
"DRONE_BUILD_FINISHED": fmt.Sprintf("%d", w.Build.Finished),
|
"DRONE_BUILD_FINISHED": fmt.Sprintf("%d", w.Build.Finished),
|
||||||
"DRONE_BUILD_VERIFIED": fmt.Sprintf("%v", false),
|
"DRONE_YAML_VERIFIED": fmt.Sprintf("%v", w.Verified),
|
||||||
|
"DRONE_YAML_SIGNED": fmt.Sprintf("%v", w.Signed),
|
||||||
|
|
||||||
// SHORTER ALIASES
|
// SHORTER ALIASES
|
||||||
"DRONE_BRANCH": w.Build.Branch,
|
"DRONE_BRANCH": w.Build.Branch,
|
||||||
|
|
30
engine/compiler/builtin/escalate.go
Normal file
30
engine/compiler/builtin/escalate.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package builtin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/drone/drone/engine/compiler/parse"
|
||||||
|
)
|
||||||
|
|
||||||
|
type escalateOp struct {
|
||||||
|
visitor
|
||||||
|
plugins []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEscalateOp returns a transformer that configures plugins to automatically
|
||||||
|
// execute in privileged mode. This is intended for plugins running dind.
|
||||||
|
func NewEscalateOp(plugins []string) Visitor {
|
||||||
|
return &escalateOp{
|
||||||
|
plugins: plugins,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *escalateOp) VisitContainer(node *parse.ContainerNode) error {
|
||||||
|
for _, pattern := range v.plugins {
|
||||||
|
ok, _ := filepath.Match(pattern, node.Container.Image)
|
||||||
|
if ok {
|
||||||
|
node.Container.Privileged = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
54
engine/compiler/builtin/escalate_test.go
Normal file
54
engine/compiler/builtin/escalate_test.go
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package builtin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/drone/drone/engine/compiler/parse"
|
||||||
|
"github.com/drone/drone/engine/runner"
|
||||||
|
|
||||||
|
"github.com/franela/goblin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_escalate(t *testing.T) {
|
||||||
|
root := parse.NewRootNode()
|
||||||
|
|
||||||
|
g := goblin.Goblin(t)
|
||||||
|
g.Describe("privileged transform", func() {
|
||||||
|
|
||||||
|
g.It("should handle matches", func() {
|
||||||
|
c := root.NewPluginNode()
|
||||||
|
c.Container = runner.Container{Image: "plugins/docker"}
|
||||||
|
op := NewEscalateOp([]string{"plugins/docker"})
|
||||||
|
|
||||||
|
op.VisitContainer(c)
|
||||||
|
g.Assert(c.Container.Privileged).IsTrue()
|
||||||
|
})
|
||||||
|
|
||||||
|
g.It("should handle glob matches", func() {
|
||||||
|
c := root.NewPluginNode()
|
||||||
|
c.Container = runner.Container{Image: "plugins/docker"}
|
||||||
|
op := NewEscalateOp([]string{"plugins/*"})
|
||||||
|
|
||||||
|
op.VisitContainer(c)
|
||||||
|
g.Assert(c.Container.Privileged).IsTrue()
|
||||||
|
})
|
||||||
|
|
||||||
|
g.It("should handle non matches", func() {
|
||||||
|
c := root.NewPluginNode()
|
||||||
|
c.Container = runner.Container{Image: "plugins/git"}
|
||||||
|
op := NewEscalateOp([]string{"plugins/docker"})
|
||||||
|
|
||||||
|
op.VisitContainer(c)
|
||||||
|
g.Assert(c.Container.Privileged).IsFalse()
|
||||||
|
})
|
||||||
|
|
||||||
|
g.It("should handle non glob matches", func() {
|
||||||
|
c := root.NewPluginNode()
|
||||||
|
c.Container = runner.Container{Image: "plugins/docker:develop"}
|
||||||
|
op := NewEscalateOp([]string{"plugins/docker"})
|
||||||
|
|
||||||
|
op.VisitContainer(c)
|
||||||
|
g.Assert(c.Container.Privileged).IsFalse()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -43,6 +43,9 @@ func (v *normalizeOp) normalizePlugin(node *parse.ContainerNode) {
|
||||||
if strings.Contains(node.Container.Image, "/") {
|
if strings.Contains(node.Container.Image, "/") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if strings.Contains(node.Container.Image, "_") {
|
||||||
|
node.Container.Image = strings.Replace(node.Container.Image, "_", "-", -1)
|
||||||
|
}
|
||||||
node.Container.Image = filepath.Join(v.namespace, node.Container.Image)
|
node.Container.Image = filepath.Join(v.namespace, node.Container.Image)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,15 @@ func Test_normalize(t *testing.T) {
|
||||||
g.Assert(c.Container.Image).Equal("index.docker.io/drone/git:latest")
|
g.Assert(c.Container.Image).Equal("index.docker.io/drone/git:latest")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
g.It("should replace underscores with dashes", func() {
|
||||||
|
c := root.NewPluginNode()
|
||||||
|
c.Container = runner.Container{Image: "gh_pages"}
|
||||||
|
op := NewNormalizeOp("plugins")
|
||||||
|
|
||||||
|
op.VisitContainer(c)
|
||||||
|
g.Assert(c.Container.Image).Equal("plugins/gh-pages:latest")
|
||||||
|
})
|
||||||
|
|
||||||
g.It("should ignore shell or service types", func() {
|
g.It("should ignore shell or service types", func() {
|
||||||
c := root.NewShellNode()
|
c := root.NewShellNode()
|
||||||
c.Container = runner.Container{Image: "golang"}
|
c.Container = runner.Container{Image: "golang"}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import "github.com/drone/drone/model"
|
||||||
// Work represents an item for work to be
|
// Work represents an item for work to be
|
||||||
// processed by a worker.
|
// processed by a worker.
|
||||||
type Work struct {
|
type Work struct {
|
||||||
|
Signed bool `json:"signed"`
|
||||||
|
Verified bool `json:"verified"`
|
||||||
Yaml string `json:"config"`
|
Yaml string `json:"config"`
|
||||||
YamlEnc string `json:"secret"`
|
YamlEnc string `json:"secret"`
|
||||||
Repo *model.Repo `json:"repo"`
|
Repo *model.Repo `json:"repo"`
|
||||||
|
|
45
router/middleware/agent.go
Normal file
45
router/middleware/agent.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/drone/drone/shared/token"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/ianschenck/envflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
secret = envflag.String("AGENT_SECRET", "", "")
|
||||||
|
noauth = envflag.Bool("AGENT_NO_AUTH", false, "")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Agent is a middleware function that initializes the authorization middleware
|
||||||
|
// for agents to connect to the queue.
|
||||||
|
func AgentMust() gin.HandlerFunc {
|
||||||
|
|
||||||
|
if *secret == "" {
|
||||||
|
logrus.Fatalf("please provide the agent secret to authenticate agent requests")
|
||||||
|
}
|
||||||
|
|
||||||
|
t := token.New(token.AgentToken, "")
|
||||||
|
s, err := t.Sign(*secret)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatalf("invalid agent secret. %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Infof("using agent secret %s", *secret)
|
||||||
|
logrus.Warnf("agents can connect with token %s", s)
|
||||||
|
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
parsed, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) {
|
||||||
|
return *secret, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(403, err)
|
||||||
|
} else if parsed.Kind != token.AgentToken {
|
||||||
|
c.AbortWithStatus(403)
|
||||||
|
} else {
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -70,15 +70,14 @@ func MustAdmin() gin.HandlerFunc {
|
||||||
user := User(c)
|
user := User(c)
|
||||||
switch {
|
switch {
|
||||||
case user == nil:
|
case user == nil:
|
||||||
c.AbortWithStatus(http.StatusUnauthorized)
|
c.String(401, "User not authorized")
|
||||||
// c.HTML(http.StatusUnauthorized, "401.html", gin.H{})
|
c.Abort()
|
||||||
case user.Admin == false:
|
case user.Admin == false:
|
||||||
c.AbortWithStatus(http.StatusForbidden)
|
c.String(413, "User not authorized")
|
||||||
// c.HTML(http.StatusForbidden, "401.html", gin.H{})
|
c.Abort()
|
||||||
default:
|
default:
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,11 +86,10 @@ func MustUser() gin.HandlerFunc {
|
||||||
user := User(c)
|
user := User(c)
|
||||||
switch {
|
switch {
|
||||||
case user == nil:
|
case user == nil:
|
||||||
c.AbortWithStatus(http.StatusUnauthorized)
|
c.String(401, "User not authorized")
|
||||||
// c.HTML(http.StatusUnauthorized, "401.html", gin.H{})
|
c.Abort()
|
||||||
default:
|
default:
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/drone/drone/api"
|
"github.com/drone/drone/api"
|
||||||
|
"github.com/drone/drone/router/middleware"
|
||||||
"github.com/drone/drone/router/middleware/header"
|
"github.com/drone/drone/router/middleware/header"
|
||||||
"github.com/drone/drone/router/middleware/session"
|
"github.com/drone/drone/router/middleware/session"
|
||||||
"github.com/drone/drone/router/middleware/token"
|
"github.com/drone/drone/router/middleware/token"
|
||||||
|
@ -16,7 +17,7 @@ import (
|
||||||
"github.com/drone/drone/web"
|
"github.com/drone/drone/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Load(middleware ...gin.HandlerFunc) http.Handler {
|
func Load(middlewares ...gin.HandlerFunc) http.Handler {
|
||||||
e := gin.New()
|
e := gin.New()
|
||||||
e.Use(gin.Recovery())
|
e.Use(gin.Recovery())
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
|
||||||
e.Use(header.NoCache)
|
e.Use(header.NoCache)
|
||||||
e.Use(header.Options)
|
e.Use(header.Options)
|
||||||
e.Use(header.Secure)
|
e.Use(header.Secure)
|
||||||
e.Use(middleware...)
|
e.Use(middlewares...)
|
||||||
e.Use(session.SetUser())
|
e.Use(session.SetUser())
|
||||||
e.Use(token.Refresh)
|
e.Use(token.Refresh)
|
||||||
|
|
||||||
|
@ -163,7 +164,9 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
|
||||||
|
|
||||||
queue := e.Group("/api/queue")
|
queue := e.Group("/api/queue")
|
||||||
{
|
{
|
||||||
|
queue.Use(middleware.AgentMust())
|
||||||
queue.POST("/pull", api.Pull)
|
queue.POST("/pull", api.Pull)
|
||||||
|
queue.POST("/pull/:os/:arch", api.Pull)
|
||||||
queue.POST("/wait/:id", api.Wait)
|
queue.POST("/wait/:id", api.Wait)
|
||||||
queue.POST("/stream/:id", api.Stream)
|
queue.POST("/stream/:id", api.Stream)
|
||||||
queue.POST("/status/:id", api.Update)
|
queue.POST("/status/:id", api.Update)
|
||||||
|
|
|
@ -10,10 +10,11 @@ import (
|
||||||
type SecretFunc func(*Token) (string, error)
|
type SecretFunc func(*Token) (string, error)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
UserToken = "user"
|
UserToken = "user"
|
||||||
SessToken = "sess"
|
SessToken = "sess"
|
||||||
HookToken = "hook"
|
HookToken = "hook"
|
||||||
CsrfToken = "csrf"
|
CsrfToken = "csrf"
|
||||||
|
AgentToken = "agent"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Default algorithm used to sign JWT tokens.
|
// Default algorithm used to sign JWT tokens.
|
||||||
|
|
28
web/hook.go
28
web/hook.go
|
@ -8,6 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/square/go-jose"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/drone/drone/bus"
|
"github.com/drone/drone/bus"
|
||||||
|
@ -31,6 +32,9 @@ func init() {
|
||||||
droneYml = ".drone.yml"
|
droneYml = ".drone.yml"
|
||||||
}
|
}
|
||||||
droneSec = fmt.Sprintf("%s.sec", strings.TrimSuffix(droneYml, filepath.Ext(droneYml)))
|
droneSec = fmt.Sprintf("%s.sec", strings.TrimSuffix(droneYml, filepath.Ext(droneYml)))
|
||||||
|
if os.Getenv("CANARY") == "true" {
|
||||||
|
droneSec = fmt.Sprintf("%s.sig", strings.TrimSuffix(droneYml, filepath.Ext(droneYml)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var skipRe = regexp.MustCompile(`\[(?i:ci *skip|skip *ci)\]`)
|
var skipRe = regexp.MustCompile(`\[(?i:ci *skip|skip *ci)\]`)
|
||||||
|
@ -214,25 +218,33 @@ func PostHook(c *gin.Context) {
|
||||||
// enabled using with the environment variable CANARY=true
|
// enabled using with the environment variable CANARY=true
|
||||||
|
|
||||||
if os.Getenv("CANARY") == "true" {
|
if os.Getenv("CANARY") == "true" {
|
||||||
|
|
||||||
|
var signed bool
|
||||||
|
var verified bool
|
||||||
|
|
||||||
|
signature, err := jose.ParseSigned(string(sec))
|
||||||
|
if err == nil && len(sec) != 0 {
|
||||||
|
signed = true
|
||||||
|
output, err := signature.Verify(repo.Hash)
|
||||||
|
if err == nil && string(output) == string(raw) {
|
||||||
|
verified = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bus.Publish(c, bus.NewBuildEvent(bus.Enqueued, repo, build))
|
bus.Publish(c, bus.NewBuildEvent(bus.Enqueued, repo, build))
|
||||||
for _, job := range jobs {
|
for _, job := range jobs {
|
||||||
queue.Publish(c, &queue.Work{
|
queue.Publish(c, &queue.Work{
|
||||||
|
Signed: signed,
|
||||||
|
Verified: verified,
|
||||||
User: user,
|
User: user,
|
||||||
Repo: repo,
|
Repo: repo,
|
||||||
Build: build,
|
Build: build,
|
||||||
BuildLast: last,
|
BuildLast: last,
|
||||||
Job: job,
|
Job: job,
|
||||||
Keys: key,
|
|
||||||
Netrc: netrc,
|
Netrc: netrc,
|
||||||
Yaml: string(raw),
|
Yaml: string(raw),
|
||||||
YamlEnc: string(sec),
|
|
||||||
Secrets: secs,
|
Secrets: secs,
|
||||||
System: &model.System{
|
System: &model.System{Link: httputil.GetURL(c.Request)},
|
||||||
Link: httputil.GetURL(c.Request),
|
|
||||||
Plugins: strings.Split(os.Getenv("PLUGIN_FILTER"), " "),
|
|
||||||
Globals: strings.Split(os.Getenv("PLUGIN_PARAMS"), " "),
|
|
||||||
Escalates: strings.Split(os.Getenv("ESCALATE_FILTER"), " "),
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return // EXIT NOT TO AVOID THE 0.4 ENGINE CODE BELOW
|
return // EXIT NOT TO AVOID THE 0.4 ENGINE CODE BELOW
|
||||||
|
|
Loading…
Reference in a new issue