consul: Create anonymous and master tokens
This commit is contained in:
parent
bbde4beefd
commit
5da5df716d
|
@ -12,6 +12,10 @@ import (
|
|||
const (
|
||||
// aclNotFound indicates there is no matching ACL
|
||||
aclNotFound = "ACL not found"
|
||||
|
||||
// anonymousToken is the token ID we re-write to if there
|
||||
// is no token ID provided
|
||||
anonymousToken = "anonymous"
|
||||
)
|
||||
|
||||
// aclCacheEntry is used to cache non-authoritative ACL's
|
||||
|
@ -39,10 +43,15 @@ func (s *Server) aclFault(id string) (string, error) {
|
|||
func (s *Server) resolveToken(id string) (acl.ACL, error) {
|
||||
// Check if there is no ACL datacenter (ACL's disabled)
|
||||
authDC := s.config.ACLDatacenter
|
||||
if authDC == "" {
|
||||
if len(authDC) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Handle the anonymous token
|
||||
if len(id) == 0 {
|
||||
id = anonymousToken
|
||||
}
|
||||
|
||||
// Check if we are the ACL datacenter and the leader, use the
|
||||
// authoritative cache
|
||||
if s.config.Datacenter == authDC && s.IsLeader() {
|
||||
|
|
|
@ -93,6 +93,59 @@ func TestACL_Authority_Found(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestACL_Authority_Anonymous_Found(t *testing.T) {
|
||||
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
||||
c.ACLDatacenter = "dc1" // Enable ACLs!
|
||||
})
|
||||
defer os.RemoveAll(dir1)
|
||||
defer s1.Shutdown()
|
||||
client := rpcClient(t, s1)
|
||||
defer client.Close()
|
||||
|
||||
testutil.WaitForLeader(t, client.Call, "dc1")
|
||||
|
||||
// Resolve the token
|
||||
acl, err := s1.resolveToken("")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if acl == nil {
|
||||
t.Fatalf("missing acl")
|
||||
}
|
||||
|
||||
// Check the policy, should allow all
|
||||
if !acl.KeyRead("foo/test") {
|
||||
t.Fatalf("unexpected failed read")
|
||||
}
|
||||
}
|
||||
|
||||
func TestACL_Authority_Master_Found(t *testing.T) {
|
||||
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
||||
c.ACLDatacenter = "dc1" // Enable ACLs!
|
||||
c.ACLMasterToken = "foobar"
|
||||
})
|
||||
defer os.RemoveAll(dir1)
|
||||
defer s1.Shutdown()
|
||||
client := rpcClient(t, s1)
|
||||
defer client.Close()
|
||||
|
||||
testutil.WaitForLeader(t, client.Call, "dc1")
|
||||
|
||||
// Resolve the token
|
||||
acl, err := s1.resolveToken("foobar")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if acl == nil {
|
||||
t.Fatalf("missing acl")
|
||||
}
|
||||
|
||||
// Check the policy, should allow all
|
||||
if !acl.KeyRead("foo/test") {
|
||||
t.Fatalf("unexpected failed read")
|
||||
}
|
||||
}
|
||||
|
||||
func TestACL_NonAuthority_NotFound(t *testing.T) {
|
||||
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
||||
c.ACLDatacenter = "dc1"
|
||||
|
|
|
@ -205,7 +205,13 @@ func (c *consulFSM) applyACLOperation(buf []byte, index uint64) interface{} {
|
|||
}
|
||||
switch req.Op {
|
||||
case structs.ACLSet:
|
||||
if err := c.state.ACLSet(index, &req.ACL); err != nil {
|
||||
if err := c.state.ACLSet(index, &req.ACL, false); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return req.ACL.ID
|
||||
}
|
||||
case structs.ACLForceSet:
|
||||
if err := c.state.ACLSet(index, &req.ACL, true); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return req.ACL.ID
|
||||
|
|
|
@ -329,7 +329,7 @@ func TestFSM_SnapshotRestore(t *testing.T) {
|
|||
session := &structs.Session{Node: "foo"}
|
||||
fsm.state.SessionCreate(9, session)
|
||||
acl := &structs.ACL{Name: "User Token"}
|
||||
fsm.state.ACLSet(10, acl)
|
||||
fsm.state.ACLSet(10, acl, false)
|
||||
|
||||
// Snapshot
|
||||
snap, err := fsm.Snapshot()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package consul
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
@ -54,6 +55,11 @@ func (s *Server) leaderLoop(stopCh chan struct{}) {
|
|||
s.logger.Printf("[WARN] consul: failed to broadcast new leader event: %v", err)
|
||||
}
|
||||
|
||||
// Setup ACLs if we are the leader and need to
|
||||
if err := s.initializeACL(); err != nil {
|
||||
s.logger.Printf("[ERR] consul: ACL initialization failed: %v", err)
|
||||
}
|
||||
|
||||
// Reconcile channel is only used once initial reconcile
|
||||
// has succeeded
|
||||
var reconcileCh chan serf.Member
|
||||
|
@ -99,6 +105,73 @@ WAIT:
|
|||
}
|
||||
}
|
||||
|
||||
// initializeACL is used to setup the ACLs if we are the leader
|
||||
// and need to do this.
|
||||
func (s *Server) initializeACL() error {
|
||||
// Bail if not configured or we are not authoritative
|
||||
authDC := s.config.ACLDatacenter
|
||||
if len(authDC) == 0 || authDC != s.config.Datacenter {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Purge the cache, since it could've changed while we
|
||||
// were not the leader
|
||||
s.aclAuthCache.Purge()
|
||||
|
||||
// Look for the anonymous token
|
||||
state := s.fsm.State()
|
||||
_, acl, err := state.ACLGet(anonymousToken)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get anonymous token: %v", err)
|
||||
}
|
||||
|
||||
// Create anonymous token if missing
|
||||
if acl == nil {
|
||||
req := structs.ACLRequest{
|
||||
Datacenter: authDC,
|
||||
Op: structs.ACLForceSet,
|
||||
ACL: structs.ACL{
|
||||
ID: anonymousToken,
|
||||
Name: "Anonymous Token",
|
||||
Type: structs.ACLTypeClient,
|
||||
},
|
||||
}
|
||||
_, err := s.raftApply(structs.ACLRequestType, &req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create anonymous token: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Check for configured master token
|
||||
master := s.config.ACLMasterToken
|
||||
if len(master) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Look for the master token
|
||||
_, acl, err = state.ACLGet(master)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get master token: %v", err)
|
||||
}
|
||||
if acl == nil {
|
||||
req := structs.ACLRequest{
|
||||
Datacenter: authDC,
|
||||
Op: structs.ACLForceSet,
|
||||
ACL: structs.ACL{
|
||||
ID: master,
|
||||
Name: "Master Token",
|
||||
Type: structs.ACLTypeManagement,
|
||||
},
|
||||
}
|
||||
_, err := s.raftApply(structs.ACLRequestType, &req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create master token: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// reconcile is used to reconcile the differences between Serf
|
||||
// membership and what is reflected in our strongly consistent store.
|
||||
// Mainly we need to ensure all live nodes are registered, all failed
|
||||
|
|
|
@ -1504,7 +1504,9 @@ func (s *StateStore) invalidateLocks(index uint64, tx *MDBTxn,
|
|||
}
|
||||
|
||||
// ACLSet is used to create or update an ACL entry
|
||||
func (s *StateStore) ACLSet(index uint64, acl *structs.ACL) error {
|
||||
// allowCreate is used for initialization of the anonymous and master tokens,
|
||||
// since it permits them to be created with a specified ID that does not exist.
|
||||
func (s *StateStore) ACLSet(index uint64, acl *structs.ACL, allowCreate bool) error {
|
||||
// Start a new txn
|
||||
tx, err := s.tables.StartTxn(false)
|
||||
if err != nil {
|
||||
|
@ -1537,7 +1539,11 @@ func (s *StateStore) ACLSet(index uint64, acl *structs.ACL) error {
|
|||
|
||||
switch len(res) {
|
||||
case 0:
|
||||
return fmt.Errorf("Invalid ACL")
|
||||
if !allowCreate {
|
||||
return fmt.Errorf("Invalid ACL")
|
||||
}
|
||||
acl.CreateIndex = index
|
||||
acl.ModifyIndex = index
|
||||
case 1:
|
||||
exist := res[0].(*structs.ACL)
|
||||
acl.CreateIndex = exist.CreateIndex
|
||||
|
|
|
@ -656,7 +656,7 @@ func TestStoreSnapshot(t *testing.T) {
|
|||
Name: "User token",
|
||||
Type: structs.ACLTypeClient,
|
||||
}
|
||||
if err := store.ACLSet(19, a1); err != nil {
|
||||
if err := store.ACLSet(19, a1, false); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
|
@ -664,7 +664,7 @@ func TestStoreSnapshot(t *testing.T) {
|
|||
Name: "User token",
|
||||
Type: structs.ACLTypeClient,
|
||||
}
|
||||
if err := store.ACLSet(20, a2); err != nil {
|
||||
if err := store.ACLSet(20, a2, false); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
|
@ -2180,7 +2180,7 @@ func TestACLSet_Get(t *testing.T) {
|
|||
Type: structs.ACLTypeClient,
|
||||
Rules: "",
|
||||
}
|
||||
if err := store.ACLSet(50, a); err != nil {
|
||||
if err := store.ACLSet(50, a, false); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if a.CreateIndex != 50 {
|
||||
|
@ -2206,7 +2206,7 @@ func TestACLSet_Get(t *testing.T) {
|
|||
|
||||
// Update
|
||||
a.Rules = "foo bar baz"
|
||||
if err := store.ACLSet(52, a); err != nil {
|
||||
if err := store.ACLSet(52, a, false); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if a.CreateIndex != 50 {
|
||||
|
@ -2240,7 +2240,7 @@ func TestACLDelete(t *testing.T) {
|
|||
Type: structs.ACLTypeClient,
|
||||
Rules: "",
|
||||
}
|
||||
if err := store.ACLSet(50, a); err != nil {
|
||||
if err := store.ACLSet(50, a, false); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
|
@ -2274,7 +2274,7 @@ func TestACLList(t *testing.T) {
|
|||
Name: "User token",
|
||||
Type: structs.ACLTypeClient,
|
||||
}
|
||||
if err := store.ACLSet(50, a1); err != nil {
|
||||
if err := store.ACLSet(50, a1, false); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
|
@ -2282,7 +2282,7 @@ func TestACLList(t *testing.T) {
|
|||
Name: "User token",
|
||||
Type: structs.ACLTypeClient,
|
||||
}
|
||||
if err := store.ACLSet(51, a2); err != nil {
|
||||
if err := store.ACLSet(51, a2, false); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -439,8 +439,9 @@ type ACLs []*ACL
|
|||
type ACLOp string
|
||||
|
||||
const (
|
||||
ACLSet ACLOp = "set"
|
||||
ACLDelete = "delete"
|
||||
ACLSet ACLOp = "set"
|
||||
ACLForceSet = "force-set"
|
||||
ACLDelete = "delete"
|
||||
)
|
||||
|
||||
// ACLRequest is used to create, update or delete an ACL
|
||||
|
|
Loading…
Reference in New Issue