agent: enforce event policy during event fire

This commit is contained in:
Ryan Uber 2015-06-17 18:58:27 -07:00
parent d777105c11
commit 6f309c355f
4 changed files with 103 additions and 30 deletions

View File

@ -36,6 +36,10 @@ func (s *HTTPServer) EventFire(resp http.ResponseWriter, req *http.Request) (int
return nil, nil return nil, nil
} }
// Get the ACL token
var token string
s.parseToken(req, &token)
// Get the filters // Get the filters
if filt := req.URL.Query().Get("node"); filt != "" { if filt := req.URL.Query().Get("node"); filt != "" {
event.NodeFilter = filt event.NodeFilter = filt
@ -57,7 +61,7 @@ func (s *HTTPServer) EventFire(resp http.ResponseWriter, req *http.Request) (int
} }
// Try to fire the event // Try to fire the event
if err := s.agent.UserEvent(dc, event); err != nil { if err := s.agent.UserEvent(dc, token, event); err != nil {
return nil, err return nil, err
} }

View File

@ -13,6 +13,8 @@ import (
func TestEventFire(t *testing.T) { func TestEventFire(t *testing.T) {
httpTest(t, func(srv *HTTPServer) { httpTest(t, func(srv *HTTPServer) {
testutil.WaitForLeader(t, srv.agent.RPC, "dc1")
body := bytes.NewBuffer([]byte("test")) body := bytes.NewBuffer([]byte("test"))
url := "/v1/event/fire/test?node=Node&service=foo&tag=bar" url := "/v1/event/fire/test?node=Node&service=foo&tag=bar"
req, err := http.NewRequest("PUT", url, body) req, err := http.NewRequest("PUT", url, body)
@ -53,8 +55,10 @@ func TestEventFire(t *testing.T) {
func TestEventList(t *testing.T) { func TestEventList(t *testing.T) {
httpTest(t, func(srv *HTTPServer) { httpTest(t, func(srv *HTTPServer) {
testutil.WaitForLeader(t, srv.agent.RPC, "dc1")
p := &UserEvent{Name: "test"} p := &UserEvent{Name: "test"}
if err := srv.agent.UserEvent("", p); err != nil { if err := srv.agent.UserEvent("dc1", "root", p); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -89,13 +93,15 @@ func TestEventList(t *testing.T) {
func TestEventList_Filter(t *testing.T) { func TestEventList_Filter(t *testing.T) {
httpTest(t, func(srv *HTTPServer) { httpTest(t, func(srv *HTTPServer) {
testutil.WaitForLeader(t, srv.agent.RPC, "dc1")
p := &UserEvent{Name: "test"} p := &UserEvent{Name: "test"}
if err := srv.agent.UserEvent("", p); err != nil { if err := srv.agent.UserEvent("dc1", "root", p); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
p = &UserEvent{Name: "foo"} p = &UserEvent{Name: "foo"}
if err := srv.agent.UserEvent("", p); err != nil { if err := srv.agent.UserEvent("dc1", "root", p); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -130,8 +136,10 @@ func TestEventList_Filter(t *testing.T) {
func TestEventList_Blocking(t *testing.T) { func TestEventList_Blocking(t *testing.T) {
httpTest(t, func(srv *HTTPServer) { httpTest(t, func(srv *HTTPServer) {
testutil.WaitForLeader(t, srv.agent.RPC, "dc1")
p := &UserEvent{Name: "test"} p := &UserEvent{Name: "test"}
if err := srv.agent.UserEvent("", p); err != nil { if err := srv.agent.UserEvent("dc1", "root", p); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -159,7 +167,7 @@ func TestEventList_Blocking(t *testing.T) {
go func() { go func() {
time.Sleep(50 * time.Millisecond) time.Sleep(50 * time.Millisecond)
p := &UserEvent{Name: "second"} p := &UserEvent{Name: "second"}
if err := srv.agent.UserEvent("", p); err != nil { if err := srv.agent.UserEvent("dc1", "root", p); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
}() }()
@ -192,6 +200,8 @@ func TestEventList_Blocking(t *testing.T) {
func TestEventList_EventBufOrder(t *testing.T) { func TestEventList_EventBufOrder(t *testing.T) {
httpTest(t, func(srv *HTTPServer) { httpTest(t, func(srv *HTTPServer) {
testutil.WaitForLeader(t, srv.agent.RPC, "dc1")
// Fire some events in a non-sequential order // Fire some events in a non-sequential order
expected := &UserEvent{Name: "foo"} expected := &UserEvent{Name: "foo"}
@ -202,7 +212,7 @@ func TestEventList_EventBufOrder(t *testing.T) {
expected, expected,
&UserEvent{Name: "bar"}, &UserEvent{Name: "bar"},
} { } {
if err := srv.agent.UserEvent("", e); err != nil { if err := srv.agent.UserEvent("dc1", "root", e); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
} }

View File

@ -71,7 +71,7 @@ func validateUserEventParams(params *UserEvent) error {
} }
// UserEvent is used to fire an event via the Serf layer on the LAN // UserEvent is used to fire an event via the Serf layer on the LAN
func (a *Agent) UserEvent(dc string, params *UserEvent) error { func (a *Agent) UserEvent(dc, token string, params *UserEvent) error {
// Validate the params // Validate the params
if err := validateUserEventParams(params); err != nil { if err := validateUserEventParams(params); err != nil {
return err return err
@ -85,28 +85,22 @@ func (a *Agent) UserEvent(dc string, params *UserEvent) error {
return fmt.Errorf("UserEvent encoding failed: %v", err) return fmt.Errorf("UserEvent encoding failed: %v", err)
} }
// Check if this is the local DC, fire locally // Send an RPC to service this
if dc == "" || dc == a.config.Datacenter {
if a.server != nil {
return a.server.UserEvent(params.Name, payload)
} else {
return a.client.UserEvent(params.Name, payload)
}
} else {
// Send an RPC to remote datacenter to service this
args := structs.EventFireRequest{ args := structs.EventFireRequest{
Datacenter: dc, Datacenter: dc,
Name: params.Name, Name: params.Name,
Payload: payload, Payload: payload,
} }
// Pass along the ACL token, if any
args.Token = token
// Any server can process in the remote DC, since the // Any server can process in the remote DC, since the
// gossip will take over anyways // gossip will take over anyways
args.AllowStale = true args.AllowStale = true
var out structs.EventFireResponse var out structs.EventFireResponse
return a.RPC("Internal.EventFire", &args, &out) return a.RPC("Internal.EventFire", &args, &out)
} }
}
// handleEvents is used to process incoming user events // handleEvents is used to process incoming user events
func (a *Agent) handleEvents() { func (a *Agent) handleEvents() {

View File

@ -153,6 +153,8 @@ func TestFireReceiveEvent(t *testing.T) {
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
defer agent.Shutdown() defer agent.Shutdown()
testutil.WaitForLeader(t, agent.RPC, "dc1")
srv1 := &structs.NodeService{ srv1 := &structs.NodeService{
ID: "mysql", ID: "mysql",
Service: "mysql", Service: "mysql",
@ -162,13 +164,13 @@ func TestFireReceiveEvent(t *testing.T) {
agent.state.AddService(srv1, "") agent.state.AddService(srv1, "")
p1 := &UserEvent{Name: "deploy", ServiceFilter: "web"} p1 := &UserEvent{Name: "deploy", ServiceFilter: "web"}
err := agent.UserEvent("", p1) err := agent.UserEvent("dc1", "root", p1)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
p2 := &UserEvent{Name: "deploy"} p2 := &UserEvent{Name: "deploy"}
err = agent.UserEvent("", p2) err = agent.UserEvent("dc1", "root", p2)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -186,3 +188,66 @@ func TestFireReceiveEvent(t *testing.T) {
t.Fatalf("bad: %#v", last) t.Fatalf("bad: %#v", last)
} }
} }
func TestUserEventToken(t *testing.T) {
conf := nextConfig()
// Set the default policies to deny
conf.ACLDefaultPolicy = "deny"
dir, agent := makeAgent(t, conf)
defer os.RemoveAll(dir)
defer agent.Shutdown()
testutil.WaitForLeader(t, agent.RPC, "dc1")
// Create an ACL token
args := structs.ACLRequest{
Datacenter: "dc1",
Op: structs.ACLSet,
ACL: structs.ACL{
Name: "User token",
Type: structs.ACLTypeClient,
Rules: testEventPolicy,
},
WriteRequest: structs.WriteRequest{Token: "root"},
}
var token string
if err := agent.RPC("ACL.Apply", &args, &token); err != nil {
t.Fatalf("err: %v", err)
}
type tcase struct {
name string
expect bool
}
cases := []tcase{
{"foo", false},
{"bar", false},
{"baz", true},
{"zip", false},
}
for _, c := range cases {
event := &UserEvent{Name: c.name}
err := agent.UserEvent("dc1", token, event)
allowed := false
if err == nil || err.Error() != permissionDenied {
allowed = true
}
if allowed != c.expect {
t.Fatalf("bad: %#v result: %v", c, allowed)
}
}
}
const testEventPolicy = `
event "foo" {
policy = "deny"
}
event "bar" {
policy = "read"
}
event "baz" {
policy = "write"
}
`