Merge pull request #3338 from hashicorp/f-acl-operator

Operator ACL enforcement
This commit is contained in:
Michael Schurter 2017-10-10 17:06:28 -07:00 committed by GitHub
commit 88df58da6e
3 changed files with 129 additions and 2 deletions

View file

@ -20,6 +20,13 @@ func (op *Operator) RaftGetConfiguration(args *structs.GenericRequest, reply *st
return err
}
// Check management permissions
if aclObj, err := op.srv.ResolveToken(args.SecretID); err != nil {
return err
} else if aclObj != nil && !aclObj.IsManagement() {
return structs.ErrPermissionDenied
}
// We can't fetch the leader and the configuration atomically with
// the current Raft API.
future := op.srv.raft.GetConfiguration()
@ -69,6 +76,13 @@ func (op *Operator) RaftRemovePeerByAddress(args *structs.RaftPeerByAddressReque
return err
}
// Check management permissions
if aclObj, err := op.srv.ResolveToken(args.SecretID); err != nil {
return err
} else if aclObj != nil && !aclObj.IsManagement() {
return structs.ErrPermissionDenied
}
// Since this is an operation designed for humans to use, we will return
// an error if the supplied address isn't among the peers since it's
// likely they screwed up.

View file

@ -7,9 +7,12 @@ 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/hashicorp/raft"
"github.com/stretchr/testify/assert"
)
func TestOperator_RaftGetConfiguration(t *testing.T) {
@ -54,6 +57,68 @@ func TestOperator_RaftGetConfiguration(t *testing.T) {
}
}
func TestOperator_RaftGetConfiguration_ACL(t *testing.T) {
t.Parallel()
s1, root := testACLServer(t, nil)
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
assert := assert.New(t)
state := s1.fsm.State()
// Create ACL token
invalidToken := mock.CreatePolicyAndToken(t, state, 1001, "test-invalid", mock.NodePolicy(acl.PolicyWrite))
arg := structs.GenericRequest{
QueryOptions: structs.QueryOptions{
Region: s1.config.Region,
},
}
// Try with no token and expect permission denied
{
var reply structs.RaftConfigurationResponse
err := msgpackrpc.CallWithCodec(codec, "Operator.RaftGetConfiguration", &arg, &reply)
assert.NotNil(err)
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
}
// Try with an invalid token and expect permission denied
{
arg.SecretID = invalidToken.SecretID
var reply structs.RaftConfigurationResponse
err := msgpackrpc.CallWithCodec(codec, "Operator.RaftGetConfiguration", &arg, &reply)
assert.NotNil(err)
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
}
// Use management token
{
arg.SecretID = root.SecretID
var reply structs.RaftConfigurationResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Operator.RaftGetConfiguration", &arg, &reply))
future := s1.raft.GetConfiguration()
assert.Nil(future.Error())
assert.Len(future.Configuration().Servers, 1)
me := future.Configuration().Servers[0]
expected := structs.RaftConfigurationResponse{
Servers: []*structs.RaftServer{
{
ID: me.ID,
Node: fmt.Sprintf("%v.%v", s1.config.NodeName, s1.config.Region),
Address: me.Address,
Leader: true,
Voter: true,
},
},
Index: future.Index(),
}
assert.Equal(expected, reply)
}
}
func TestOperator_RaftRemovePeerByAddress(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
@ -109,3 +174,51 @@ func TestOperator_RaftRemovePeerByAddress(t *testing.T) {
}
}
}
func TestOperator_RaftRemovePeerByAddress_ACL(t *testing.T) {
t.Parallel()
s1, root := testACLServer(t, nil)
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
assert := assert.New(t)
state := s1.fsm.State()
// Create ACL token
invalidToken := mock.CreatePolicyAndToken(t, state, 1001, "test-invalid", mock.NodePolicy(acl.PolicyWrite))
arg := structs.RaftPeerByAddressRequest{
Address: raft.ServerAddress(fmt.Sprintf("127.0.0.1:%d", getPort())),
}
arg.Region = s1.config.Region
// Add peer manually to Raft.
{
future := s1.raft.AddPeer(arg.Address)
assert.Nil(future.Error())
}
var reply struct{}
// Try with no token and expect permission denied
{
err := msgpackrpc.CallWithCodec(codec, "Operator.RaftRemovePeerByAddress", &arg, &reply)
assert.NotNil(err)
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
}
// Try with an invalid token and expect permission denied
{
arg.SecretID = invalidToken.SecretID
err := msgpackrpc.CallWithCodec(codec, "Operator.RaftRemovePeerByAddress", &arg, &reply)
assert.NotNil(err)
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
}
// Try with a management token
{
arg.SecretID = root.SecretID
err := msgpackrpc.CallWithCodec(codec, "Operator.RaftRemovePeerByAddress", &arg, &reply)
assert.Nil(err)
}
}

View file

@ -34,7 +34,7 @@ The table below shows this endpoint's support for
| Blocking Queries | ACL Required |
| ---------------- | ------------ |
| `NO` | `none` |
| `NO` | `management` |
### Parameters
@ -104,7 +104,7 @@ The table below shows this endpoint's support for
| Blocking Queries | ACL Required |
| ---------------- | ------------ |
| `NO` | `none` |
| `NO` | `management` |
### Parameters