Merge pull request #3339 from hashicorp/f-acl-force-periodic

Force Periodic ACL enforcement
This commit is contained in:
Michael Schurter 2017-10-11 16:26:29 -07:00 committed by GitHub
commit 8c1a97765e
3 changed files with 91 additions and 3 deletions

View file

@ -6,6 +6,7 @@ import (
"github.com/armon/go-metrics" "github.com/armon/go-metrics"
memdb "github.com/hashicorp/go-memdb" memdb "github.com/hashicorp/go-memdb"
"github.com/hashicorp/nomad/acl"
"github.com/hashicorp/nomad/nomad/structs" "github.com/hashicorp/nomad/nomad/structs"
) )
@ -21,6 +22,13 @@ func (p *Periodic) Force(args *structs.PeriodicForceRequest, reply *structs.Peri
} }
defer metrics.MeasureSince([]string{"nomad", "periodic", "force"}, time.Now()) defer metrics.MeasureSince([]string{"nomad", "periodic", "force"}, time.Now())
// Check for write-job permissions
if aclObj, err := p.srv.ResolveToken(args.SecretID); err != nil {
return err
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilitySubmitJob) {
return structs.ErrPermissionDenied
}
// Validate the arguments // Validate the arguments
if args.JobID == "" { if args.JobID == "" {
return fmt.Errorf("missing job ID for evaluation") return fmt.Errorf("missing job ID for evaluation")

View file

@ -5,9 +5,11 @@ import (
memdb "github.com/hashicorp/go-memdb" memdb "github.com/hashicorp/go-memdb"
"github.com/hashicorp/net-rpc-msgpackrpc" "github.com/hashicorp/net-rpc-msgpackrpc"
"github.com/hashicorp/nomad/acl"
"github.com/hashicorp/nomad/nomad/mock" "github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs" "github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/testutil" "github.com/hashicorp/nomad/testutil"
"github.com/stretchr/testify/assert"
) )
func TestPeriodicEndpoint_Force(t *testing.T) { func TestPeriodicEndpoint_Force(t *testing.T) {
@ -60,6 +62,84 @@ func TestPeriodicEndpoint_Force(t *testing.T) {
} }
} }
func TestPeriodicEndpoint_Force_ACL(t *testing.T) {
t.Parallel()
s1, root := testACLServer(t, func(c *Config) {
c.NumSchedulers = 0 // Prevent automatic dequeue
})
defer s1.Shutdown()
state := s1.fsm.State()
assert := assert.New(t)
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Create and insert a periodic job.
job := mock.PeriodicJob()
job.Periodic.ProhibitOverlap = true // Shouldn't affect anything.
assert.Nil(state.UpsertJob(100, job))
_, err := s1.periodicDispatcher.Add(job)
assert.Nil(err)
// Force launch it.
req := &structs.PeriodicForceRequest{
JobID: job.ID,
WriteRequest: structs.WriteRequest{
Region: "global",
Namespace: job.Namespace,
},
}
// Try with no token and expect permission denied
{
var resp structs.PeriodicForceResponse
err := msgpackrpc.CallWithCodec(codec, "Periodic.Force", req, &resp)
assert.NotNil(err)
assert.Contains(err.Error(), structs.ErrPermissionDenied.Error())
}
// Try with an invalid token and expect permission denied
{
invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "invalid", mock.NodePolicy(acl.PolicyWrite))
req.SecretID = invalidToken.SecretID
var resp structs.PeriodicForceResponse
err := msgpackrpc.CallWithCodec(codec, "Periodic.Force", req, &resp)
assert.NotNil(err)
assert.Contains(err.Error(), structs.ErrPermissionDenied.Error())
}
// Fetch the response with a valid token
{
policy := mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilitySubmitJob})
token := mock.CreatePolicyAndToken(t, state, 1005, "valid", policy)
req.SecretID = token.SecretID
var resp structs.PeriodicForceResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Periodic.Force", req, &resp))
assert.NotEqual(0, resp.Index)
// Lookup the evaluation
ws := memdb.NewWatchSet()
eval, err := state.EvalByID(ws, resp.EvalID)
assert.Nil(err)
assert.NotNil(eval)
assert.Equal(eval.CreateIndex, resp.EvalCreateIndex)
}
// Fetch the response with management token
{
req.SecretID = root.SecretID
var resp structs.PeriodicForceResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Periodic.Force", req, &resp))
assert.NotEqual(0, resp.Index)
// Lookup the evaluation
ws := memdb.NewWatchSet()
eval, err := state.EvalByID(ws, resp.EvalID)
assert.Nil(err)
assert.NotNil(eval)
assert.Equal(eval.CreateIndex, resp.EvalCreateIndex)
}
}
func TestPeriodicEndpoint_Force_NonPeriodic(t *testing.T) { func TestPeriodicEndpoint_Force_NonPeriodic(t *testing.T) {
t.Parallel() t.Parallel()
s1 := testServer(t, func(c *Config) { s1 := testServer(t, func(c *Config) {

View file

@ -1533,9 +1533,9 @@ The table below shows this endpoint's support for
[blocking queries](/api/index.html#blocking-queries) and [blocking queries](/api/index.html#blocking-queries) and
[required ACLs](/api/index.html#acls). [required ACLs](/api/index.html#acls).
| Blocking Queries | ACL Required | | Blocking Queries | ACL Required |
| ---------------- | ------------ | | ---------------- | ---------------------- |
| `NO` | `none` | | `NO` | `namespace:submit-job` |
### Parameters ### Parameters