mirror of
https://github.com/kemko/nomad.git
synced 2026-01-10 12:25:42 +03:00
Merge pull request #3350 from hashicorp/f-acl-status-members
Status.Members ACL enforcement
This commit is contained in:
@@ -119,28 +119,11 @@ func (s *HTTPServer) AgentMembersRequest(resp http.ResponseWriter, req *http.Req
|
||||
return nil, CodedError(405, ErrInvalidMethod)
|
||||
}
|
||||
|
||||
var secret string
|
||||
s.parseToken(req, &secret)
|
||||
|
||||
var aclObj *acl.ACL
|
||||
var err error
|
||||
|
||||
if client := s.agent.Client(); client != nil {
|
||||
aclObj, err = client.ResolveToken(secret)
|
||||
} else {
|
||||
aclObj, err = s.agent.Server().ResolveToken(secret)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check node read permissions
|
||||
if aclObj != nil && !aclObj.AllowNodeRead() {
|
||||
return nil, structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
args := &structs.GenericRequest{}
|
||||
if s.parse(resp, req, &args.Region, &args.QueryOptions) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var out structs.ServerMembersResponse
|
||||
if err := s.agent.RPC("Status.Members", args, &out); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package nomad
|
||||
|
||||
import "github.com/hashicorp/nomad/nomad/structs"
|
||||
import (
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
)
|
||||
|
||||
// Status endpoint is used to check on server status
|
||||
type Status struct {
|
||||
@@ -70,6 +72,13 @@ func (s *Status) Peers(args *structs.GenericRequest, reply *[]string) error {
|
||||
// Members return the list of servers in a cluster that a particular server is
|
||||
// aware of
|
||||
func (s *Status) Members(args *structs.GenericRequest, reply *structs.ServerMembersResponse) error {
|
||||
// Check node read permissions
|
||||
if aclObj, err := s.srv.ResolveToken(args.SecretID); err != nil {
|
||||
return err
|
||||
} else if aclObj != nil && !aclObj.AllowNodeRead() {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
serfMembers := s.srv.Members()
|
||||
members := make([]*structs.ServerMember, len(serfMembers))
|
||||
for i, mem := range serfMembers {
|
||||
|
||||
@@ -4,8 +4,11 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/net-rpc-msgpackrpc"
|
||||
"github.com/hashicorp/nomad/acl"
|
||||
"github.com/hashicorp/nomad/nomad/mock"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
"github.com/hashicorp/nomad/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStatusVersion(t *testing.T) {
|
||||
@@ -94,3 +97,75 @@ func TestStatusPeers(t *testing.T) {
|
||||
t.Fatalf("no peers: %v", peers)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusMembers(t *testing.T) {
|
||||
t.Parallel()
|
||||
s1 := testServer(t, nil)
|
||||
defer s1.Shutdown()
|
||||
codec := rpcClient(t, s1)
|
||||
assert := assert.New(t)
|
||||
|
||||
arg := &structs.GenericRequest{
|
||||
QueryOptions: structs.QueryOptions{
|
||||
Region: "global",
|
||||
AllowStale: true,
|
||||
},
|
||||
}
|
||||
|
||||
var out structs.ServerMembersResponse
|
||||
assert.Nil(msgpackrpc.CallWithCodec(codec, "Status.Members", arg, &out))
|
||||
assert.Len(out.Members, 1)
|
||||
}
|
||||
|
||||
func TestStatusMembers_ACL(t *testing.T) {
|
||||
t.Parallel()
|
||||
s1, root := testACLServer(t, nil)
|
||||
defer s1.Shutdown()
|
||||
codec := rpcClient(t, s1)
|
||||
assert := assert.New(t)
|
||||
state := s1.fsm.State()
|
||||
|
||||
// Create the namespace policy and tokens
|
||||
validToken := mock.CreatePolicyAndToken(t, state, 1001, "test-valid", mock.NodePolicy(acl.PolicyRead))
|
||||
invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid", mock.AgentPolicy(acl.PolicyRead))
|
||||
|
||||
arg := &structs.GenericRequest{
|
||||
QueryOptions: structs.QueryOptions{
|
||||
Region: "global",
|
||||
AllowStale: true,
|
||||
},
|
||||
}
|
||||
|
||||
// Try without a token and expect failure
|
||||
{
|
||||
var out structs.ServerMembersResponse
|
||||
err := msgpackrpc.CallWithCodec(codec, "Status.Members", arg, &out)
|
||||
assert.NotNil(err)
|
||||
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
|
||||
}
|
||||
|
||||
// Try with an invalid token and expect failure
|
||||
{
|
||||
arg.SecretID = invalidToken.SecretID
|
||||
var out structs.ServerMembersResponse
|
||||
err := msgpackrpc.CallWithCodec(codec, "Status.Members", arg, &out)
|
||||
assert.NotNil(err)
|
||||
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
|
||||
}
|
||||
|
||||
// Try with a valid token
|
||||
{
|
||||
arg.SecretID = validToken.SecretID
|
||||
var out structs.ServerMembersResponse
|
||||
assert.Nil(msgpackrpc.CallWithCodec(codec, "Status.Members", arg, &out))
|
||||
assert.Len(out.Members, 1)
|
||||
}
|
||||
|
||||
// Try with a management token
|
||||
{
|
||||
arg.SecretID = root.SecretID
|
||||
var out structs.ServerMembersResponse
|
||||
assert.Nil(msgpackrpc.CallWithCodec(codec, "Status.Members", arg, &out))
|
||||
assert.Len(out.Members, 1)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user