Allow Node.SecretID for GetNode and GetAlloc

This commit is contained in:
Michael Schurter
2017-10-12 16:27:33 -07:00
parent 89ccbcd3c4
commit 6c5ac10db8
4 changed files with 91 additions and 23 deletions

View File

@@ -5,6 +5,7 @@ import (
"github.com/armon/go-metrics"
"github.com/hashicorp/go-memdb"
multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/nomad/acl"
"github.com/hashicorp/nomad/nomad/state"
"github.com/hashicorp/nomad/nomad/structs"
@@ -81,7 +82,25 @@ func (a *Alloc) GetAlloc(args *structs.AllocSpecificRequest,
// Check namespace read-job permissions
if aclObj, err := a.srv.ResolveToken(args.AuthToken); err != nil {
return err
// If ResolveToken had an unexpected error return that
if err != structs.ErrTokenNotFound {
return err
}
// Attempt to lookup AuthToken as a Node.SecretID since nodes
// call this endpoint and don't have an ACL token.
node, stateErr := a.srv.fsm.State().NodeBySecretID(nil, args.AuthToken)
if stateErr != nil {
// Return the original ResolveToken error with this err
var merr multierror.Error
merr.Errors = append(merr.Errors, err, stateErr)
return merr.ErrorOrNil()
}
// Not a node or a valid ACL token
if node == nil {
return structs.ErrTokenNotFound
}
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob) {
return structs.ErrPermissionDenied
}

View File

@@ -267,32 +267,55 @@ func TestAllocEndpoint_GetAlloc_ACL(t *testing.T) {
invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityListJobs}))
// Lookup the alloc without a token and expect failure
get := &structs.AllocSpecificRequest{
AllocID: alloc.ID,
QueryOptions: structs.QueryOptions{Region: "global"},
}
var resp structs.SingleAllocResponse
assert.NotNil(msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp), "RPC")
// Try with a valid token
get.AuthToken = validToken.SecretID
assert.Nil(msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp), "RPC")
assert.EqualValues(resp.Index, 1000, "resp.Index")
assert.Equal(alloc, resp.Alloc, "Returned alloc not equal")
// Lookup the alloc without a token and expect failure
{
var resp structs.SingleAllocResponse
err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp)
assert.Equal(structs.ErrPermissionDenied.Error(), err.Error())
}
// Try with a valid ACL token
{
get.AuthToken = validToken.SecretID
var resp structs.SingleAllocResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp), "RPC")
assert.EqualValues(resp.Index, 1000, "resp.Index")
assert.Equal(alloc, resp.Alloc, "Returned alloc not equal")
}
// Try with a valid Node.SecretID
{
node := mock.Node()
assert.Nil(state.UpsertNode(1005, node))
get.AuthToken = node.SecretID
var resp structs.SingleAllocResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp), "RPC")
assert.EqualValues(resp.Index, 1000, "resp.Index")
assert.Equal(alloc, resp.Alloc, "Returned alloc not equal")
}
// Try with a invalid token
get.AuthToken = invalidToken.SecretID
err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp)
assert.NotNil(err, "RPC")
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
{
get.AuthToken = invalidToken.SecretID
var resp structs.SingleAllocResponse
err := msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp)
assert.NotNil(err, "RPC")
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
}
// Try with a root token
get.AuthToken = root.SecretID
var resp2 structs.SingleAllocResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp2), "RPC")
assert.EqualValues(resp2.Index, 1000, "resp.Index")
assert.Equal(alloc, resp2.Alloc, "Returned alloc not equal")
{
get.AuthToken = root.SecretID
var resp structs.SingleAllocResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Alloc.GetAlloc", get, &resp), "RPC")
assert.EqualValues(resp.Index, 1000, "resp.Index")
assert.Equal(alloc, resp.Alloc, "Returned alloc not equal")
}
}
func TestAllocEndpoint_GetAlloc_Blocking(t *testing.T) {

View File

@@ -507,8 +507,26 @@ func (n *Node) GetNode(args *structs.NodeSpecificRequest,
defer metrics.MeasureSince([]string{"nomad", "client", "get_node"}, time.Now())
// Check node read permissions
if aclObj, err := n.srv.ResolveToken(args.SecretID); err != nil {
return err
if aclObj, err := n.srv.ResolveToken(args.AuthToken); err != nil {
// If ResolveToken had an unexpected error return that
if err != structs.ErrTokenNotFound {
return err
}
// Attempt to lookup AuthToken as a Node.SecretID since nodes
// call this endpoint and don't have an ACL token.
node, stateErr := n.srv.fsm.State().NodeBySecretID(nil, args.AuthToken)
if stateErr != nil {
// Return the original ResolveToken error with this err
var merr multierror.Error
merr.Errors = append(merr.Errors, err, stateErr)
return merr.ErrorOrNil()
}
// Not a node or a valid ACL token
if node == nil {
return structs.ErrTokenNotFound
}
} else if aclObj != nil && !aclObj.AllowNodeRead() {
return structs.ErrPermissionDenied
}

View File

@@ -941,7 +941,15 @@ func TestClientEndpoint_GetNode_ACL(t *testing.T) {
}
// Try with a valid token
req.SecretID = validToken.SecretID
req.AuthToken = validToken.SecretID
{
var resp structs.SingleNodeResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Node.GetNode", req, &resp), "RPC")
assert.Equal(node.ID, resp.Node.ID)
}
// Try with a Node.SecretID
req.AuthToken = node.SecretID
{
var resp structs.SingleNodeResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Node.GetNode", req, &resp), "RPC")
@@ -949,7 +957,7 @@ func TestClientEndpoint_GetNode_ACL(t *testing.T) {
}
// Try with a invalid token
req.SecretID = invalidToken.SecretID
req.AuthToken = invalidToken.SecretID
{
var resp structs.SingleNodeResponse
err := msgpackrpc.CallWithCodec(codec, "Node.GetNode", req, &resp)
@@ -958,7 +966,7 @@ func TestClientEndpoint_GetNode_ACL(t *testing.T) {
}
// Try with a root token
req.SecretID = root.SecretID
req.AuthToken = root.SecretID
{
var resp structs.SingleNodeResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Node.GetNode", req, &resp), "RPC")