tr: add validate task hook
This commit is contained in:
parent
7f4ec50906
commit
9a63d6103d
|
@ -664,6 +664,7 @@ func TestTaskRunner_UnregisterConsul_Retries(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//XXX Ported to allocrunnerv2/task_runner/validate_hook_test.go
|
||||||
func TestTaskRunner_Validate_UserEnforcement(t *testing.T) {
|
func TestTaskRunner_Validate_UserEnforcement(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
ctx := testTaskRunner(t, false)
|
ctx := testTaskRunner(t, false)
|
||||||
|
|
|
@ -18,6 +18,7 @@ func (tr *TaskRunner) initHooks() {
|
||||||
// Create the task directory hook. This is run first to ensure the
|
// Create the task directory hook. This is run first to ensure the
|
||||||
// directoy path exists for other hooks.
|
// directoy path exists for other hooks.
|
||||||
tr.runnerHooks = []interfaces.TaskHook{
|
tr.runnerHooks = []interfaces.TaskHook{
|
||||||
|
newValidateHook(tr.clientConfig, hookLogger),
|
||||||
newTaskDirHook(tr, hookLogger),
|
newTaskDirHook(tr, hookLogger),
|
||||||
newArtifactHook(tr, hookLogger),
|
newArtifactHook(tr, hookLogger),
|
||||||
newShutdownDelayHook(task.ShutdownDelay, hookLogger),
|
newShutdownDelayHook(task.ShutdownDelay, hookLogger),
|
||||||
|
|
66
client/allocrunnerv2/taskrunner/validate_hook.go
Normal file
66
client/allocrunnerv2/taskrunner/validate_hook.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package taskrunner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
log "github.com/hashicorp/go-hclog"
|
||||||
|
multierror "github.com/hashicorp/go-multierror"
|
||||||
|
"github.com/hashicorp/nomad/client/allocrunnerv2/interfaces"
|
||||||
|
"github.com/hashicorp/nomad/client/config"
|
||||||
|
"github.com/hashicorp/nomad/client/driver/env"
|
||||||
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// validateHook validates the task is able to be run.
|
||||||
|
type validateHook struct {
|
||||||
|
config *config.Config
|
||||||
|
logger log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func newValidateHook(config *config.Config, logger log.Logger) *validateHook {
|
||||||
|
h := &validateHook{
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
h.logger = logger.Named(h.Name())
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*validateHook) Name() string {
|
||||||
|
return "validate"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *validateHook) Prestart(ctx context.Context, req *interfaces.TaskPrestartRequest, resp *interfaces.TaskPrestartResponse) error {
|
||||||
|
if err := validateTask(req.Task, req.TaskEnv, h.config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Done = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateTask(task *structs.Task, taskEnv *env.TaskEnv, conf *config.Config) error {
|
||||||
|
var mErr multierror.Error
|
||||||
|
|
||||||
|
// Validate the user
|
||||||
|
unallowedUsers := conf.ReadStringListToMapDefault("user.blacklist", config.DefaultUserBlacklist)
|
||||||
|
checkDrivers := conf.ReadStringListToMapDefault("user.checked_drivers", config.DefaultUserCheckedDrivers)
|
||||||
|
if _, driverMatch := checkDrivers[task.Driver]; driverMatch {
|
||||||
|
if _, unallowed := unallowedUsers[task.User]; unallowed {
|
||||||
|
mErr.Errors = append(mErr.Errors, fmt.Errorf("running as user %q is disallowed", task.User))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the Service names once they're interpolated
|
||||||
|
for i, service := range task.Services {
|
||||||
|
name := taskEnv.ReplaceEnv(service.Name)
|
||||||
|
if err := service.ValidateName(name); err != nil {
|
||||||
|
mErr.Errors = append(mErr.Errors, fmt.Errorf("service (%d) failed validation: %v", i, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(mErr.Errors) == 1 {
|
||||||
|
return mErr.Errors[0]
|
||||||
|
}
|
||||||
|
return mErr.ErrorOrNil()
|
||||||
|
}
|
63
client/allocrunnerv2/taskrunner/validate_hook_test.go
Normal file
63
client/allocrunnerv2/taskrunner/validate_hook_test.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package taskrunner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/nomad/client/config"
|
||||||
|
"github.com/hashicorp/nomad/client/driver/env"
|
||||||
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTaskRunner_Validate_UserEnforcement(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
taskEnv := env.NewEmptyBuilder().Build()
|
||||||
|
conf := config.DefaultConfig()
|
||||||
|
|
||||||
|
// Try to run as root with exec.
|
||||||
|
task := &structs.Task{
|
||||||
|
Driver: "exec",
|
||||||
|
User: "root",
|
||||||
|
}
|
||||||
|
if err := validateTask(task, taskEnv, conf); err == nil {
|
||||||
|
t.Fatalf("expected error running as root with exec")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to run a non-blacklisted user with exec.
|
||||||
|
task.User = "foobar"
|
||||||
|
require.NoError(t, validateTask(task, taskEnv, conf))
|
||||||
|
|
||||||
|
// Try to run as root with docker.
|
||||||
|
task.Driver = "docker"
|
||||||
|
task.User = "root"
|
||||||
|
require.NoError(t, validateTask(task, taskEnv, conf))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTaskRunner_Validate_ServiceName(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
builder := env.NewEmptyBuilder()
|
||||||
|
conf := config.DefaultConfig()
|
||||||
|
|
||||||
|
// Create a task with a service for validation
|
||||||
|
task := &structs.Task{
|
||||||
|
Services: []*structs.Service{
|
||||||
|
{
|
||||||
|
Name: "ok",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, validateTask(task, builder.Build(), conf))
|
||||||
|
|
||||||
|
// Add an env var that should validate
|
||||||
|
builder.SetGenericEnv(map[string]string{"FOO": "bar"})
|
||||||
|
task.Services[0].Name = "${FOO}"
|
||||||
|
require.NoError(t, validateTask(task, builder.Build(), conf))
|
||||||
|
|
||||||
|
// Add an env var that should *not* validate
|
||||||
|
builder.SetGenericEnv(map[string]string{"BAD": "invalid/in/consul"})
|
||||||
|
task.Services[0].Name = "${BAD}"
|
||||||
|
require.Error(t, validateTask(task, builder.Build(), conf))
|
||||||
|
}
|
3
client/driver/env/env.go
vendored
3
client/driver/env/env.go
vendored
|
@ -247,7 +247,8 @@ func NewBuilder(node *structs.Node, alloc *structs.Allocation, task *structs.Tas
|
||||||
// NewEmptyBuilder creates a new environment builder.
|
// NewEmptyBuilder creates a new environment builder.
|
||||||
func NewEmptyBuilder() *Builder {
|
func NewEmptyBuilder() *Builder {
|
||||||
return &Builder{
|
return &Builder{
|
||||||
mu: &sync.RWMutex{},
|
mu: &sync.RWMutex{},
|
||||||
|
envvars: make(map[string]string),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue