consul: Starting token enforcement

This commit is contained in:
Armon Dadgar 2014-08-12 15:32:44 -07:00
parent 78580a733e
commit 84488ed1f0
4 changed files with 137 additions and 9 deletions

View File

@ -17,11 +17,22 @@ const (
// rootDenied is returned when attempting to resolve a root ACL // rootDenied is returned when attempting to resolve a root ACL
rootDenied = "Cannot resolve root ACL" rootDenied = "Cannot resolve root ACL"
// permissionDenied is returned when an ACL based rejection happens
permissionDenied = "Permission denied"
// aclDisabled is returned when ACL changes are not permitted
// since they are disabled.
aclDisabled = "ACL support disabled"
// anonymousToken is the token ID we re-write to if there // anonymousToken is the token ID we re-write to if there
// is no token ID provided // is no token ID provided
anonymousToken = "anonymous" anonymousToken = "anonymous"
) )
var (
permissionDeniedErr = errors.New(permissionDenied)
)
// aclCacheEntry is used to cache non-authoritative ACL's // aclCacheEntry is used to cache non-authoritative ACL's
// If non-authoritative, then we must respect a TTL // If non-authoritative, then we must respect a TTL
type aclCacheEntry struct { type aclCacheEntry struct {
@ -42,9 +53,10 @@ func (s *Server) aclFault(id string) (string, string, error) {
return "", "", errors.New(aclNotFound) return "", "", errors.New(aclNotFound)
} }
// Management tokens have no policy and inherit from allow // Management tokens have no policy and inherit from the
// 'manage' root policy
if acl.Type == structs.ACLTypeManagement { if acl.Type == structs.ACLTypeManagement {
return "allow", "", nil return "manage", "", nil
} }
// Otherwise use the base policy // Otherwise use the base policy

View File

@ -22,6 +22,18 @@ func (a *ACL) Apply(args *structs.ACLRequest, reply *string) error {
} }
defer metrics.MeasureSince([]string{"consul", "acl", "apply"}, time.Now()) defer metrics.MeasureSince([]string{"consul", "acl", "apply"}, time.Now())
// Verify we are allowed to serve this request
if a.srv.config.ACLDatacenter != a.srv.config.Datacenter {
return fmt.Errorf(aclDisabled)
}
// Verify token is permitted to list ACLs
if acl, err := a.srv.resolveToken(args.Token); err != nil {
return err
} else if acl == nil || !acl.ACLModify() {
return permissionDeniedErr
}
switch args.Op { switch args.Op {
case structs.ACLSet: case structs.ACLSet:
// Verify the ACL type // Verify the ACL type
@ -71,6 +83,11 @@ func (a *ACL) Get(args *structs.ACLSpecificRequest,
return err return err
} }
// Verify we are allowed to serve this request
if a.srv.config.ACLDatacenter != a.srv.config.Datacenter {
return fmt.Errorf(aclDisabled)
}
// Get the local state // Get the local state
state := a.srv.fsm.State() state := a.srv.fsm.State()
return a.srv.blockingRPC(&args.QueryOptions, return a.srv.blockingRPC(&args.QueryOptions,
@ -93,6 +110,11 @@ func (a *ACL) GetPolicy(args *structs.ACLPolicyRequest, reply *structs.ACLPolicy
return err return err
} }
// Verify we are allowed to serve this request
if a.srv.config.ACLDatacenter != a.srv.config.Datacenter {
return fmt.Errorf(aclDisabled)
}
// Get the policy via the cache // Get the policy via the cache
parent, policy, err := a.srv.aclAuthCache.GetACLPolicy(args.ACL) parent, policy, err := a.srv.aclAuthCache.GetACLPolicy(args.ACL)
if err != nil { if err != nil {
@ -123,6 +145,18 @@ func (a *ACL) List(args *structs.DCSpecificRequest,
return err return err
} }
// Verify we are allowed to serve this request
if a.srv.config.ACLDatacenter != a.srv.config.Datacenter {
return fmt.Errorf(aclDisabled)
}
// Verify token is permitted to list ACLs
if acl, err := a.srv.resolveToken(args.Token); err != nil {
return err
} else if acl == nil || !acl.ACLList() {
return permissionDeniedErr
}
// Get the local state // Get the local state
state := a.srv.fsm.State() state := a.srv.fsm.State()
return a.srv.blockingRPC(&args.QueryOptions, return a.srv.blockingRPC(&args.QueryOptions,

View File

@ -2,6 +2,7 @@ package consul
import ( import (
"os" "os"
"strings"
"testing" "testing"
"time" "time"
@ -10,7 +11,10 @@ import (
) )
func TestACLEndpoint_Apply(t *testing.T) { func TestACLEndpoint_Apply(t *testing.T) {
dir1, s1 := testServer(t) dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.ACLDatacenter = "dc1"
c.ACLMasterToken = "root"
})
defer os.RemoveAll(dir1) defer os.RemoveAll(dir1)
defer s1.Shutdown() defer s1.Shutdown()
client := rpcClient(t, s1) client := rpcClient(t, s1)
@ -25,6 +29,7 @@ func TestACLEndpoint_Apply(t *testing.T) {
Name: "User token", Name: "User token",
Type: structs.ACLTypeClient, Type: structs.ACLTypeClient,
}, },
WriteRequest: structs.WriteRequest{Token: "root"},
} }
var out string var out string
if err := client.Call("ACL.Apply", &arg, &out); err != nil { if err := client.Call("ACL.Apply", &arg, &out); err != nil {
@ -65,8 +70,10 @@ func TestACLEndpoint_Apply(t *testing.T) {
} }
} }
func TestACLEndpoint_Get(t *testing.T) { func TestACLEndpoint_Apply_Denied(t *testing.T) {
dir1, s1 := testServer(t) dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.ACLDatacenter = "dc1"
})
defer os.RemoveAll(dir1) defer os.RemoveAll(dir1)
defer s1.Shutdown() defer s1.Shutdown()
client := rpcClient(t, s1) client := rpcClient(t, s1)
@ -83,6 +90,34 @@ func TestACLEndpoint_Get(t *testing.T) {
}, },
} }
var out string var out string
err := client.Call("ACL.Apply", &arg, &out)
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
t.Fatalf("err: %v", err)
}
}
func TestACLEndpoint_Get(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.ACLDatacenter = "dc1"
c.ACLMasterToken = "root"
})
defer os.RemoveAll(dir1)
defer s1.Shutdown()
client := rpcClient(t, s1)
defer client.Close()
testutil.WaitForLeader(t, client.Call, "dc1")
arg := structs.ACLRequest{
Datacenter: "dc1",
Op: structs.ACLSet,
ACL: structs.ACL{
Name: "User token",
Type: structs.ACLTypeClient,
},
WriteRequest: structs.WriteRequest{Token: "root"},
}
var out string
if err := client.Call("ACL.Apply", &arg, &out); err != nil { if err := client.Call("ACL.Apply", &arg, &out); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -109,7 +144,10 @@ func TestACLEndpoint_Get(t *testing.T) {
} }
func TestACLEndpoint_GetPolicy(t *testing.T) { func TestACLEndpoint_GetPolicy(t *testing.T) {
dir1, s1 := testServer(t) dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.ACLDatacenter = "dc1"
c.ACLMasterToken = "root"
})
defer os.RemoveAll(dir1) defer os.RemoveAll(dir1)
defer s1.Shutdown() defer s1.Shutdown()
client := rpcClient(t, s1) client := rpcClient(t, s1)
@ -124,6 +162,7 @@ func TestACLEndpoint_GetPolicy(t *testing.T) {
Name: "User token", Name: "User token",
Type: structs.ACLTypeClient, Type: structs.ACLTypeClient,
}, },
WriteRequest: structs.WriteRequest{Token: "root"},
} }
var out string var out string
if err := client.Call("ACL.Apply", &arg, &out); err != nil { if err := client.Call("ACL.Apply", &arg, &out); err != nil {
@ -162,7 +201,10 @@ func TestACLEndpoint_GetPolicy(t *testing.T) {
} }
func TestACLEndpoint_List(t *testing.T) { func TestACLEndpoint_List(t *testing.T) {
dir1, s1 := testServer(t) dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.ACLDatacenter = "dc1"
c.ACLMasterToken = "root"
})
defer os.RemoveAll(dir1) defer os.RemoveAll(dir1)
defer s1.Shutdown() defer s1.Shutdown()
client := rpcClient(t, s1) client := rpcClient(t, s1)
@ -179,6 +221,7 @@ func TestACLEndpoint_List(t *testing.T) {
Name: "User token", Name: "User token",
Type: structs.ACLTypeClient, Type: structs.ACLTypeClient,
}, },
WriteRequest: structs.WriteRequest{Token: "root"},
} }
var out string var out string
if err := client.Call("ACL.Apply", &arg, &out); err != nil { if err := client.Call("ACL.Apply", &arg, &out); err != nil {
@ -188,7 +231,8 @@ func TestACLEndpoint_List(t *testing.T) {
} }
getR := structs.DCSpecificRequest{ getR := structs.DCSpecificRequest{
Datacenter: "dc1", Datacenter: "dc1",
QueryOptions: structs.QueryOptions{Token: "root"},
} }
var acls structs.IndexedACLs var acls structs.IndexedACLs
if err := client.Call("ACL.List", &getR, &acls); err != nil { if err := client.Call("ACL.List", &getR, &acls); err != nil {
@ -198,11 +242,16 @@ func TestACLEndpoint_List(t *testing.T) {
if acls.Index == 0 { if acls.Index == 0 {
t.Fatalf("Bad: %v", acls) t.Fatalf("Bad: %v", acls)
} }
if len(acls.ACLs) != 5 {
// 5 + anonymous + master
if len(acls.ACLs) != 7 {
t.Fatalf("Bad: %v", acls.ACLs) t.Fatalf("Bad: %v", acls.ACLs)
} }
for i := 0; i < len(acls.ACLs); i++ { for i := 0; i < len(acls.ACLs); i++ {
s := acls.ACLs[i] s := acls.ACLs[i]
if s.ID == anonymousToken || s.ID == "root" {
continue
}
if !strContains(ids, s.ID) { if !strContains(ids, s.ID) {
t.Fatalf("bad: %v", s) t.Fatalf("bad: %v", s)
} }
@ -211,3 +260,24 @@ func TestACLEndpoint_List(t *testing.T) {
} }
} }
} }
func TestACLEndpoint_List_Denied(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.ACLDatacenter = "dc1"
})
defer os.RemoveAll(dir1)
defer s1.Shutdown()
client := rpcClient(t, s1)
defer client.Close()
testutil.WaitForLeader(t, client.Call, "dc1")
getR := structs.DCSpecificRequest{
Datacenter: "dc1",
}
var acls structs.IndexedACLs
err := client.Call("ACL.List", &getR, &acls)
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
t.Fatalf("err: %v", err)
}
}

View File

@ -76,6 +76,7 @@ func TestACL_Authority_NotFound(t *testing.T) {
func TestACL_Authority_Found(t *testing.T) { func TestACL_Authority_Found(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) { dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.ACLDatacenter = "dc1" // Enable ACLs! c.ACLDatacenter = "dc1" // Enable ACLs!
c.ACLMasterToken = "root"
}) })
defer os.RemoveAll(dir1) defer os.RemoveAll(dir1)
defer s1.Shutdown() defer s1.Shutdown()
@ -93,6 +94,7 @@ func TestACL_Authority_Found(t *testing.T) {
Type: structs.ACLTypeClient, Type: structs.ACLTypeClient,
Rules: testACLPolicy, Rules: testACLPolicy,
}, },
WriteRequest: structs.WriteRequest{Token: "root"},
} }
var id string var id string
if err := client.Call("ACL.Apply", &arg, &id); err != nil { if err := client.Call("ACL.Apply", &arg, &id); err != nil {
@ -250,6 +252,7 @@ func TestACL_NonAuthority_NotFound(t *testing.T) {
func TestACL_NonAuthority_Found(t *testing.T) { func TestACL_NonAuthority_Found(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) { dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.ACLDatacenter = "dc1" c.ACLDatacenter = "dc1"
c.ACLMasterToken = "root"
}) })
defer os.RemoveAll(dir1) defer os.RemoveAll(dir1)
defer s1.Shutdown() defer s1.Shutdown()
@ -287,6 +290,7 @@ func TestACL_NonAuthority_Found(t *testing.T) {
Type: structs.ACLTypeClient, Type: structs.ACLTypeClient,
Rules: testACLPolicy, Rules: testACLPolicy,
}, },
WriteRequest: structs.WriteRequest{Token: "root"},
} }
var id string var id string
if err := client.Call("ACL.Apply", &arg, &id); err != nil { if err := client.Call("ACL.Apply", &arg, &id); err != nil {
@ -380,6 +384,7 @@ func TestACL_DownPolicy_Deny(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) { dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.ACLDatacenter = "dc1" c.ACLDatacenter = "dc1"
c.ACLDownPolicy = "deny" c.ACLDownPolicy = "deny"
c.ACLMasterToken = "root"
}) })
defer os.RemoveAll(dir1) defer os.RemoveAll(dir1)
defer s1.Shutdown() defer s1.Shutdown()
@ -418,6 +423,7 @@ func TestACL_DownPolicy_Deny(t *testing.T) {
Type: structs.ACLTypeClient, Type: structs.ACLTypeClient,
Rules: testACLPolicy, Rules: testACLPolicy,
}, },
WriteRequest: structs.WriteRequest{Token: "root"},
} }
var id string var id string
if err := client.Call("ACL.Apply", &arg, &id); err != nil { if err := client.Call("ACL.Apply", &arg, &id); err != nil {
@ -452,6 +458,7 @@ func TestACL_DownPolicy_Allow(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) { dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.ACLDatacenter = "dc1" c.ACLDatacenter = "dc1"
c.ACLDownPolicy = "allow" c.ACLDownPolicy = "allow"
c.ACLMasterToken = "root"
}) })
defer os.RemoveAll(dir1) defer os.RemoveAll(dir1)
defer s1.Shutdown() defer s1.Shutdown()
@ -490,6 +497,7 @@ func TestACL_DownPolicy_Allow(t *testing.T) {
Type: structs.ACLTypeClient, Type: structs.ACLTypeClient,
Rules: testACLPolicy, Rules: testACLPolicy,
}, },
WriteRequest: structs.WriteRequest{Token: "root"},
} }
var id string var id string
if err := client.Call("ACL.Apply", &arg, &id); err != nil { if err := client.Call("ACL.Apply", &arg, &id); err != nil {
@ -525,6 +533,7 @@ func TestACL_DownPolicy_ExtendCache(t *testing.T) {
c.ACLDatacenter = "dc1" c.ACLDatacenter = "dc1"
c.ACLTTL = 0 c.ACLTTL = 0
c.ACLDownPolicy = "extend-cache" c.ACLDownPolicy = "extend-cache"
c.ACLMasterToken = "root"
}) })
defer os.RemoveAll(dir1) defer os.RemoveAll(dir1)
defer s1.Shutdown() defer s1.Shutdown()
@ -564,6 +573,7 @@ func TestACL_DownPolicy_ExtendCache(t *testing.T) {
Type: structs.ACLTypeClient, Type: structs.ACLTypeClient,
Rules: testACLPolicy, Rules: testACLPolicy,
}, },
WriteRequest: structs.WriteRequest{Token: "root"},
} }
var id string var id string
if err := client.Call("ACL.Apply", &arg, &id); err != nil { if err := client.Call("ACL.Apply", &arg, &id); err != nil {
@ -606,6 +616,7 @@ func TestACL_DownPolicy_ExtendCache(t *testing.T) {
func TestACL_MultiDC_Found(t *testing.T) { func TestACL_MultiDC_Found(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) { dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.ACLDatacenter = "dc1" c.ACLDatacenter = "dc1"
c.ACLMasterToken = "root"
}) })
defer os.RemoveAll(dir1) defer os.RemoveAll(dir1)
defer s1.Shutdown() defer s1.Shutdown()
@ -638,6 +649,7 @@ func TestACL_MultiDC_Found(t *testing.T) {
Type: structs.ACLTypeClient, Type: structs.ACLTypeClient,
Rules: testACLPolicy, Rules: testACLPolicy,
}, },
WriteRequest: structs.WriteRequest{Token: "root"},
} }
var id string var id string
if err := client.Call("ACL.Apply", &arg, &id); err != nil { if err := client.Call("ACL.Apply", &arg, &id); err != nil {