agent: add ACL enforcement to the v1/agent/health/service/* endpoints
This adds acl enforcement to the two endpoints that were missing it. Note that in the case of getting a services health by its id, we still must first lookup the service so we still "leak" information about a service with that ID existing. There isn't really a way around it though as ACLs are meant to check service names.
This commit is contained in:
parent
94de5adb02
commit
4bf1c2e4f7
|
@ -737,10 +737,23 @@ func (s *HTTPServer) AgentHealthServiceByID(resp http.ResponseWriter, req *http.
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var token string
|
||||
s.parseToken(req, &token)
|
||||
|
||||
// need to resolve to default the meta
|
||||
var authzContext acl.AuthorizerContext
|
||||
authz, err := s.agent.resolveTokenAndDefaultMeta(token, &entMeta, &authzContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sid structs.ServiceID
|
||||
sid.Init(serviceID, &entMeta)
|
||||
|
||||
if service := s.agent.State.Service(sid); service != nil {
|
||||
if authz != nil && authz.ServiceRead(service.Service, &authzContext) != acl.Allow {
|
||||
return nil, acl.ErrPermissionDenied
|
||||
}
|
||||
code, status, healthChecks := agentHealthService(sid, s)
|
||||
if returnTextPlain(req) {
|
||||
return status, CodeWithPayloadError{StatusCode: code, Reason: status, ContentType: "text/plain"}
|
||||
|
@ -777,6 +790,20 @@ func (s *HTTPServer) AgentHealthServiceByName(resp http.ResponseWriter, req *htt
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var token string
|
||||
s.parseToken(req, &token)
|
||||
|
||||
// need to resolve to default the meta
|
||||
var authzContext acl.AuthorizerContext
|
||||
authz, err := s.agent.resolveTokenAndDefaultMeta(token, &entMeta, &authzContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if authz != nil && authz.ServiceRead(serviceName, &authzContext) != acl.Allow {
|
||||
return nil, acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
code := http.StatusNotFound
|
||||
status := fmt.Sprintf("ServiceName %s Not Found", serviceName)
|
||||
services := s.agent.State.Services(&entMeta)
|
||||
|
|
|
@ -1081,6 +1081,59 @@ func TestAgent_HealthServiceByName(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAgent_HealthServicesACLEnforcement(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := NewTestAgent(t, t.Name(), TestACLConfigWithParams(nil))
|
||||
defer a.Shutdown()
|
||||
|
||||
service := &structs.NodeService{
|
||||
ID: "mysql1",
|
||||
Service: "mysql",
|
||||
}
|
||||
require.NoError(t, a.AddService(service, nil, false, "", ConfigSourceLocal))
|
||||
|
||||
service = &structs.NodeService{
|
||||
ID: "foo1",
|
||||
Service: "foo",
|
||||
}
|
||||
require.NoError(t, a.AddService(service, nil, false, "", ConfigSourceLocal))
|
||||
|
||||
// no token
|
||||
t.Run("no-token-health-by-id", func(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "/v1/agent/health/service/id/mysql1", nil)
|
||||
require.NoError(t, err)
|
||||
resp := httptest.NewRecorder()
|
||||
_, err = a.srv.AgentHealthServiceByID(resp, req)
|
||||
require.Equal(t, acl.ErrPermissionDenied, err)
|
||||
})
|
||||
|
||||
t.Run("no-token-health-by-name", func(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "/v1/agent/health/service/name/mysql", nil)
|
||||
require.NoError(t, err)
|
||||
resp := httptest.NewRecorder()
|
||||
_, err = a.srv.AgentHealthServiceByName(resp, req)
|
||||
require.Equal(t, acl.ErrPermissionDenied, err)
|
||||
})
|
||||
|
||||
t.Run("root-token-health-by-id", func(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "/v1/agent/health/service/id/foo1", nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Add("X-Consul-Token", TestDefaultMasterToken)
|
||||
resp := httptest.NewRecorder()
|
||||
_, err = a.srv.AgentHealthServiceByID(resp, req)
|
||||
require.NotEqual(t, acl.ErrPermissionDenied, err)
|
||||
})
|
||||
|
||||
t.Run("root-token-health-by-name", func(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "/v1/agent/health/service/name/foo", nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Add("X-Consul-Token", TestDefaultMasterToken)
|
||||
resp := httptest.NewRecorder()
|
||||
_, err = a.srv.AgentHealthServiceByName(resp, req)
|
||||
require.NotEqual(t, acl.ErrPermissionDenied, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAgent_Checks_ACLFilter(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := NewTestAgent(t, t.Name(), TestACLConfig())
|
||||
|
|
Loading…
Reference in New Issue