nomad: enforce ACLs on job submit

This commit is contained in:
Armon Dadgar 2017-08-20 21:31:45 -07:00
parent 6f5150a227
commit ac6283c31f
3 changed files with 54 additions and 0 deletions

View File

@ -12,6 +12,7 @@ import (
"github.com/hashicorp/consul/lib" "github.com/hashicorp/consul/lib"
"github.com/hashicorp/go-memdb" "github.com/hashicorp/go-memdb"
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/hashicorp/nomad/acl"
"github.com/hashicorp/nomad/client/driver" "github.com/hashicorp/nomad/client/driver"
"github.com/hashicorp/nomad/helper" "github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/nomad/state" "github.com/hashicorp/nomad/nomad/state"
@ -71,6 +72,13 @@ func (j *Job) Register(args *structs.JobRegisterRequest, reply *structs.JobRegis
// Set the warning message // Set the warning message
reply.Warnings = structs.MergeMultierrorWarnings(warnings, canonicalizeWarnings) reply.Warnings = structs.MergeMultierrorWarnings(warnings, canonicalizeWarnings)
// Check job submission permissions
if aclObj, err := j.srv.resolveToken(args.SecretID); err != nil {
return err
} else if aclObj != nil && !aclObj.AllowNamespaceOperation(structs.DefaultNamespace, acl.NamespaceCapabilitySubmitJob) {
return structs.ErrPermissionDenied
}
// Lookup the job // Lookup the job
snap, err := j.srv.fsm.State().Snapshot() snap, err := j.srv.fsm.State().Snapshot()
if err != nil { if err != nil {

View File

@ -93,6 +93,49 @@ func TestJobEndpoint_Register(t *testing.T) {
} }
} }
func TestJobEndpoint_Register_ACL(t *testing.T) {
t.Parallel()
s1, root := testACLServer(t, func(c *Config) {
c.NumSchedulers = 0 // Prevent automatic dequeue
})
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Create the register request
job := mock.Job()
req := &structs.JobRegisterRequest{
Job: job,
WriteRequest: structs.WriteRequest{Region: "global"},
}
// Try without a token, expect failure
var resp structs.JobRegisterResponse
if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err == nil {
t.Fatalf("expected error")
}
// Try with a token
req.SecretID = root.SecretID
if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
t.Fatalf("err: %v", err)
}
if resp.Index == 0 {
t.Fatalf("bad index: %d", resp.Index)
}
// Check for the node in the FSM
state := s1.fsm.State()
ws := memdb.NewWatchSet()
out, err := state.JobByID(ws, job.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if out == nil {
t.Fatalf("expected job")
}
}
func TestJobEndpoint_Register_InvalidDriverConfig(t *testing.T) { func TestJobEndpoint_Register_InvalidDriverConfig(t *testing.T) {
t.Parallel() t.Parallel()
s1 := testServer(t, func(c *Config) { s1 := testServer(t, func(c *Config) {

View File

@ -108,6 +108,9 @@ const (
// ACLClientToken and ACLManagementToken are the only types of tokens // ACLClientToken and ACLManagementToken are the only types of tokens
ACLClientToken = "client" ACLClientToken = "client"
ACLManagementToken = "management" ACLManagementToken = "management"
// DefaultNamespace is the default namespace.
DefaultNamespace = "default"
) )
// Context defines the scope in which a search for Nomad object operates, and // Context defines the scope in which a search for Nomad object operates, and