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) {
|
||||
t.Parallel()
|
||||
ctx := testTaskRunner(t, false)
|
||||
|
|
|
@ -18,6 +18,7 @@ func (tr *TaskRunner) initHooks() {
|
|||
// Create the task directory hook. This is run first to ensure the
|
||||
// directoy path exists for other hooks.
|
||||
tr.runnerHooks = []interfaces.TaskHook{
|
||||
newValidateHook(tr.clientConfig, hookLogger),
|
||||
newTaskDirHook(tr, hookLogger),
|
||||
newArtifactHook(tr, hookLogger),
|
||||
newShutdownDelayHook(task.ShutdownDelay, hookLogger),
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -247,7 +247,8 @@ func NewBuilder(node *structs.Node, alloc *structs.Allocation, task *structs.Tas
|
|||
// NewEmptyBuilder creates a new environment builder.
|
||||
func NewEmptyBuilder() *Builder {
|
||||
return &Builder{
|
||||
mu: &sync.RWMutex{},
|
||||
mu: &sync.RWMutex{},
|
||||
envvars: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue