From bdc65ee31202ecfe2e8dd840bd5a3dfbdc0d6b74 Mon Sep 17 00:00:00 2001 From: Michael Schurter Date: Thu, 5 Oct 2017 15:43:20 -0700 Subject: [PATCH] /v1/client/stats ACL enforcement --- command/agent/stats_endpoint.go | 16 ++++++++- command/agent/stats_endpoint_test.go | 52 ++++++++++++++++++++++++++++ website/source/api/client.html.md | 2 +- 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/command/agent/stats_endpoint.go b/command/agent/stats_endpoint.go index ac9a05d00..ba18d9c28 100644 --- a/command/agent/stats_endpoint.go +++ b/command/agent/stats_endpoint.go @@ -1,12 +1,26 @@ package agent -import "net/http" +import ( + "net/http" + + "github.com/hashicorp/nomad/nomad/structs" +) func (s *HTTPServer) ClientStatsRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { if s.agent.client == nil { return nil, clientNotRunning } + var secret string + s.parseToken(req, &secret) + + // Check node read permissions + if aclObj, err := s.agent.Client().ResolveToken(secret); err != nil { + return nil, err + } else if aclObj != nil && !aclObj.AllowNodeRead() { + return nil, structs.ErrPermissionDenied + } + clientStats := s.agent.client.StatsReporter() return clientStats.LatestHostStats(), nil } diff --git a/command/agent/stats_endpoint_test.go b/command/agent/stats_endpoint_test.go index cc3c612e5..9661878c2 100644 --- a/command/agent/stats_endpoint_test.go +++ b/command/agent/stats_endpoint_test.go @@ -4,6 +4,11 @@ import ( "net/http" "net/http/httptest" "testing" + + "github.com/hashicorp/nomad/acl" + "github.com/hashicorp/nomad/nomad/mock" + "github.com/hashicorp/nomad/nomad/structs" + "github.com/stretchr/testify/assert" ) func TestClientStatsRequest(t *testing.T) { @@ -21,3 +26,50 @@ func TestClientStatsRequest(t *testing.T) { } }) } + +func TestClientStatsRequest_ACL(t *testing.T) { + t.Parallel() + assert := assert.New(t) + httpACLTest(t, nil, func(s *TestAgent) { + state := s.Agent.server.State() + req, err := http.NewRequest("GET", "/v1/client/stats/", nil) + assert.Nil(err) + + // Try request without a token and expect failure + { + respW := httptest.NewRecorder() + _, err := s.Server.ClientStatsRequest(respW, req) + assert.NotNil(err) + assert.Equal(err.Error(), structs.ErrPermissionDenied.Error()) + } + + // Try request with an invalid token and expect failure + { + respW := httptest.NewRecorder() + token := mock.CreatePolicyAndToken(t, state, 1005, "invalid", mock.NodePolicy(acl.PolicyDeny)) + setToken(req, token) + _, err := s.Server.ClientStatsRequest(respW, req) + assert.NotNil(err) + assert.Equal(err.Error(), structs.ErrPermissionDenied.Error()) + } + + // Try request with a valid token + { + respW := httptest.NewRecorder() + token := mock.CreatePolicyAndToken(t, state, 1007, "valid", mock.NodePolicy(acl.PolicyRead)) + setToken(req, token) + _, err := s.Server.ClientStatsRequest(respW, req) + assert.Nil(err) + assert.Equal(http.StatusOK, respW.Code) + } + + // Try request with a management token + { + respW := httptest.NewRecorder() + setToken(req, s.RootToken) + _, err := s.Server.ClientStatsRequest(respW, req) + assert.Nil(err) + assert.Equal(http.StatusOK, respW.Code) + } + }) +} diff --git a/website/source/api/client.html.md b/website/source/api/client.html.md index 0a1ec1b49..fb83b9d2b 100644 --- a/website/source/api/client.html.md +++ b/website/source/api/client.html.md @@ -29,7 +29,7 @@ The table below shows this endpoint's support for | Blocking Queries | ACL Required | | ---------------- | ------------ | -| `NO` | `none` | +| `NO` | `node:read` | ### Sample Request