mirror of https://github.com/status-im/consul.git
Adds support to ACL package for session policies.
This commit is contained in:
parent
99e810f9c7
commit
60d4322c49
60
acl/acl.go
60
acl/acl.go
|
@ -95,6 +95,13 @@ type ACL interface {
|
||||||
// service
|
// service
|
||||||
ServiceWrite(string) bool
|
ServiceWrite(string) bool
|
||||||
|
|
||||||
|
// SessionRead checks for permission to read sessions for a given node.
|
||||||
|
SessionRead(string) bool
|
||||||
|
|
||||||
|
// SessionWrite checks for permission to create sessions for a given
|
||||||
|
// node.
|
||||||
|
SessionWrite(string) bool
|
||||||
|
|
||||||
// Snapshot checks for permission to take and restore snapshots.
|
// Snapshot checks for permission to take and restore snapshots.
|
||||||
Snapshot() bool
|
Snapshot() bool
|
||||||
}
|
}
|
||||||
|
@ -175,6 +182,14 @@ func (s *StaticACL) ServiceWrite(string) bool {
|
||||||
return s.defaultAllow
|
return s.defaultAllow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StaticACL) SessionRead(string) bool {
|
||||||
|
return s.defaultAllow
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticACL) SessionWrite(string) bool {
|
||||||
|
return s.defaultAllow
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StaticACL) Snapshot() bool {
|
func (s *StaticACL) Snapshot() bool {
|
||||||
return s.allowManage
|
return s.allowManage
|
||||||
}
|
}
|
||||||
|
@ -224,6 +239,9 @@ type PolicyACL struct {
|
||||||
// serviceRules contains the service policies
|
// serviceRules contains the service policies
|
||||||
serviceRules *radix.Tree
|
serviceRules *radix.Tree
|
||||||
|
|
||||||
|
// sessionRules contains the session policies
|
||||||
|
sessionRules *radix.Tree
|
||||||
|
|
||||||
// eventRules contains the user event policies
|
// eventRules contains the user event policies
|
||||||
eventRules *radix.Tree
|
eventRules *radix.Tree
|
||||||
|
|
||||||
|
@ -247,6 +265,7 @@ func New(parent ACL, policy *Policy) (*PolicyACL, error) {
|
||||||
keyRules: radix.New(),
|
keyRules: radix.New(),
|
||||||
nodeRules: radix.New(),
|
nodeRules: radix.New(),
|
||||||
serviceRules: radix.New(),
|
serviceRules: radix.New(),
|
||||||
|
sessionRules: radix.New(),
|
||||||
eventRules: radix.New(),
|
eventRules: radix.New(),
|
||||||
preparedQueryRules: radix.New(),
|
preparedQueryRules: radix.New(),
|
||||||
}
|
}
|
||||||
|
@ -266,6 +285,11 @@ func New(parent ACL, policy *Policy) (*PolicyACL, error) {
|
||||||
p.serviceRules.Insert(sp.Name, sp.Policy)
|
p.serviceRules.Insert(sp.Name, sp.Policy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load the session policy
|
||||||
|
for _, sp := range policy.Sessions {
|
||||||
|
p.sessionRules.Insert(sp.Node, sp.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
// Load the event policy
|
// Load the event policy
|
||||||
for _, ep := range policy.Events {
|
for _, ep := range policy.Events {
|
||||||
p.eventRules.Insert(ep.Event, ep.Policy)
|
p.eventRules.Insert(ep.Event, ep.Policy)
|
||||||
|
@ -547,3 +571,39 @@ func (p *PolicyACL) ServiceWrite(name string) bool {
|
||||||
// No matching rule, use the parent.
|
// No matching rule, use the parent.
|
||||||
return p.parent.ServiceWrite(name)
|
return p.parent.ServiceWrite(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SessionRead checks for permission to read sessions for a given node.
|
||||||
|
func (p *PolicyACL) SessionRead(node string) bool {
|
||||||
|
// Check for an exact rule or catch-all
|
||||||
|
_, rule, ok := p.sessionRules.LongestPrefix(node)
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
switch rule {
|
||||||
|
case PolicyRead, PolicyWrite:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No matching rule, use the parent.
|
||||||
|
return p.parent.SessionRead(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionWrite checks for permission to create sessions for a given node.
|
||||||
|
func (p *PolicyACL) SessionWrite(node string) bool {
|
||||||
|
// Check for an exact rule or catch-all
|
||||||
|
_, rule, ok := p.sessionRules.LongestPrefix(node)
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
switch rule {
|
||||||
|
case PolicyWrite:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No matching rule, use the parent.
|
||||||
|
return p.parent.SessionWrite(node)
|
||||||
|
}
|
||||||
|
|
101
acl/acl_test.go
101
acl/acl_test.go
|
@ -83,6 +83,12 @@ func TestStaticACL(t *testing.T) {
|
||||||
if !all.ServiceWrite("foobar") {
|
if !all.ServiceWrite("foobar") {
|
||||||
t.Fatalf("should allow")
|
t.Fatalf("should allow")
|
||||||
}
|
}
|
||||||
|
if !all.SessionRead("foobar") {
|
||||||
|
t.Fatalf("should allow")
|
||||||
|
}
|
||||||
|
if !all.SessionWrite("foobar") {
|
||||||
|
t.Fatalf("should allow")
|
||||||
|
}
|
||||||
if all.Snapshot() {
|
if all.Snapshot() {
|
||||||
t.Fatalf("should not allow")
|
t.Fatalf("should not allow")
|
||||||
}
|
}
|
||||||
|
@ -141,6 +147,12 @@ func TestStaticACL(t *testing.T) {
|
||||||
if none.ServiceWrite("foobar") {
|
if none.ServiceWrite("foobar") {
|
||||||
t.Fatalf("should not allow")
|
t.Fatalf("should not allow")
|
||||||
}
|
}
|
||||||
|
if none.SessionRead("foobar") {
|
||||||
|
t.Fatalf("should not allow")
|
||||||
|
}
|
||||||
|
if none.SessionWrite("foobar") {
|
||||||
|
t.Fatalf("should not allow")
|
||||||
|
}
|
||||||
if none.Snapshot() {
|
if none.Snapshot() {
|
||||||
t.Fatalf("should not allow")
|
t.Fatalf("should not allow")
|
||||||
}
|
}
|
||||||
|
@ -193,6 +205,12 @@ func TestStaticACL(t *testing.T) {
|
||||||
if !manage.ServiceWrite("foobar") {
|
if !manage.ServiceWrite("foobar") {
|
||||||
t.Fatalf("should allow")
|
t.Fatalf("should allow")
|
||||||
}
|
}
|
||||||
|
if !manage.SessionRead("foobar") {
|
||||||
|
t.Fatalf("should allow")
|
||||||
|
}
|
||||||
|
if !manage.SessionWrite("foobar") {
|
||||||
|
t.Fatalf("should allow")
|
||||||
|
}
|
||||||
if !manage.Snapshot() {
|
if !manage.Snapshot() {
|
||||||
t.Fatalf("should allow")
|
t.Fatalf("should allow")
|
||||||
}
|
}
|
||||||
|
@ -661,3 +679,86 @@ func TestPolicyACL_Node(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPolicyACL_Session(t *testing.T) {
|
||||||
|
deny := DenyAll()
|
||||||
|
policyRoot := &Policy{
|
||||||
|
Sessions: []*SessionPolicy{
|
||||||
|
&SessionPolicy{
|
||||||
|
Node: "root-nope",
|
||||||
|
Policy: PolicyDeny,
|
||||||
|
},
|
||||||
|
&SessionPolicy{
|
||||||
|
Node: "root-ro",
|
||||||
|
Policy: PolicyRead,
|
||||||
|
},
|
||||||
|
&SessionPolicy{
|
||||||
|
Node: "root-rw",
|
||||||
|
Policy: PolicyWrite,
|
||||||
|
},
|
||||||
|
&SessionPolicy{
|
||||||
|
Node: "override",
|
||||||
|
Policy: PolicyDeny,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
root, err := New(deny, policyRoot)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
policy := &Policy{
|
||||||
|
Sessions: []*SessionPolicy{
|
||||||
|
&SessionPolicy{
|
||||||
|
Node: "child-nope",
|
||||||
|
Policy: PolicyDeny,
|
||||||
|
},
|
||||||
|
&SessionPolicy{
|
||||||
|
Node: "child-ro",
|
||||||
|
Policy: PolicyRead,
|
||||||
|
},
|
||||||
|
&SessionPolicy{
|
||||||
|
Node: "child-rw",
|
||||||
|
Policy: PolicyWrite,
|
||||||
|
},
|
||||||
|
&SessionPolicy{
|
||||||
|
Node: "override",
|
||||||
|
Policy: PolicyWrite,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
acl, err := New(root, policy)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type sessioncase struct {
|
||||||
|
inp string
|
||||||
|
read bool
|
||||||
|
write bool
|
||||||
|
}
|
||||||
|
cases := []sessioncase{
|
||||||
|
{"nope", false, false},
|
||||||
|
{"root-nope", false, false},
|
||||||
|
{"root-ro", true, false},
|
||||||
|
{"root-rw", true, true},
|
||||||
|
{"root-nope-prefix", false, false},
|
||||||
|
{"root-ro-prefix", true, false},
|
||||||
|
{"root-rw-prefix", true, true},
|
||||||
|
{"child-nope", false, false},
|
||||||
|
{"child-ro", true, false},
|
||||||
|
{"child-rw", true, true},
|
||||||
|
{"child-nope-prefix", false, false},
|
||||||
|
{"child-ro-prefix", true, false},
|
||||||
|
{"child-rw-prefix", true, true},
|
||||||
|
{"override", true, true},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
if c.read != acl.SessionRead(c.inp) {
|
||||||
|
t.Fatalf("Read fail: %#v", c)
|
||||||
|
}
|
||||||
|
if c.write != acl.SessionWrite(c.inp) {
|
||||||
|
t.Fatalf("Write fail: %#v", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ type Policy struct {
|
||||||
Keys []*KeyPolicy `hcl:"key,expand"`
|
Keys []*KeyPolicy `hcl:"key,expand"`
|
||||||
Nodes []*NodePolicy `hcl:"node,expand"`
|
Nodes []*NodePolicy `hcl:"node,expand"`
|
||||||
Services []*ServicePolicy `hcl:"service,expand"`
|
Services []*ServicePolicy `hcl:"service,expand"`
|
||||||
|
Sessions []*SessionPolicy `hcl:"session,expand"`
|
||||||
Events []*EventPolicy `hcl:"event,expand"`
|
Events []*EventPolicy `hcl:"event,expand"`
|
||||||
PreparedQueries []*PreparedQueryPolicy `hcl:"query,expand"`
|
PreparedQueries []*PreparedQueryPolicy `hcl:"query,expand"`
|
||||||
Keyring string `hcl:"keyring"`
|
Keyring string `hcl:"keyring"`
|
||||||
|
@ -55,6 +56,17 @@ func (s *ServicePolicy) GoString() string {
|
||||||
return fmt.Sprintf("%#v", *s)
|
return fmt.Sprintf("%#v", *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SessionPolicy represents a policy for making sessions tied to specific node
|
||||||
|
// name prefixes.
|
||||||
|
type SessionPolicy struct {
|
||||||
|
Node string `hcl:",key"`
|
||||||
|
Policy string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SessionPolicy) GoString() string {
|
||||||
|
return fmt.Sprintf("%#v", *s)
|
||||||
|
}
|
||||||
|
|
||||||
// EventPolicy represents a user event policy.
|
// EventPolicy represents a user event policy.
|
||||||
type EventPolicy struct {
|
type EventPolicy struct {
|
||||||
Event string `hcl:",key"`
|
Event string `hcl:",key"`
|
||||||
|
@ -125,6 +137,13 @@ func Parse(rules string) (*Policy, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate the session policies
|
||||||
|
for _, sp := range p.Sessions {
|
||||||
|
if !isPolicyValid(sp.Policy) {
|
||||||
|
return nil, fmt.Errorf("Invalid session policy: %#v", sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Validate the user event policies
|
// Validate the user event policies
|
||||||
for _, ep := range p.Events {
|
for _, ep := range p.Events {
|
||||||
if !isPolicyValid(ep.Policy) {
|
if !isPolicyValid(ep.Policy) {
|
||||||
|
|
|
@ -46,6 +46,12 @@ service "" {
|
||||||
service "foo" {
|
service "foo" {
|
||||||
policy = "read"
|
policy = "read"
|
||||||
}
|
}
|
||||||
|
session "foo" {
|
||||||
|
policy = "write"
|
||||||
|
}
|
||||||
|
session "bar" {
|
||||||
|
policy = "deny"
|
||||||
|
}
|
||||||
query "" {
|
query "" {
|
||||||
policy = "read"
|
policy = "read"
|
||||||
}
|
}
|
||||||
|
@ -129,6 +135,16 @@ query "bar" {
|
||||||
Policy: PolicyRead,
|
Policy: PolicyRead,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Sessions: []*SessionPolicy{
|
||||||
|
&SessionPolicy{
|
||||||
|
Node: "foo",
|
||||||
|
Policy: PolicyWrite,
|
||||||
|
},
|
||||||
|
&SessionPolicy{
|
||||||
|
Node: "bar",
|
||||||
|
Policy: PolicyDeny,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := Parse(inp)
|
out, err := Parse(inp)
|
||||||
|
@ -199,6 +215,14 @@ func TestACLPolicy_Parse_JSON(t *testing.T) {
|
||||||
"foo": {
|
"foo": {
|
||||||
"policy": "read"
|
"policy": "read"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"session": {
|
||||||
|
"foo": {
|
||||||
|
"policy": "write"
|
||||||
|
},
|
||||||
|
"bar": {
|
||||||
|
"policy": "deny"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
exp := &Policy{
|
exp := &Policy{
|
||||||
|
@ -274,6 +298,16 @@ func TestACLPolicy_Parse_JSON(t *testing.T) {
|
||||||
Policy: PolicyRead,
|
Policy: PolicyRead,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Sessions: []*SessionPolicy{
|
||||||
|
&SessionPolicy{
|
||||||
|
Node: "foo",
|
||||||
|
Policy: PolicyWrite,
|
||||||
|
},
|
||||||
|
&SessionPolicy{
|
||||||
|
Node: "bar",
|
||||||
|
Policy: PolicyDeny,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := Parse(inp)
|
out, err := Parse(inp)
|
||||||
|
@ -331,6 +365,7 @@ func TestACLPolicy_Bad_Policy(t *testing.T) {
|
||||||
`operator = "nope"`,
|
`operator = "nope"`,
|
||||||
`query "" { policy = "nope" }`,
|
`query "" { policy = "nope" }`,
|
||||||
`service "" { policy = "nope" }`,
|
`service "" { policy = "nope" }`,
|
||||||
|
`session "" { policy = "nope" }`,
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
_, err := Parse(c)
|
_, err := Parse(c)
|
||||||
|
|
Loading…
Reference in New Issue