Merge pull request #3338 from hashicorp/f-acl-operator
Operator ACL enforcement
This commit is contained in:
commit
88df58da6e
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue