mirror of
https://github.com/kemko/nomad.git
synced 2026-01-07 02:45:42 +03:00
Merge pull request #3331 from hashicorp/f-acl-client-alloc
/v1/client/allocation/./{stats,gc} ACL enforcement
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
"github.com/hashicorp/nomad/acl"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
)
|
||||
|
||||
@@ -118,6 +119,18 @@ func (s *HTTPServer) ClientGCRequest(resp http.ResponseWriter, req *http.Request
|
||||
}
|
||||
|
||||
func (s *HTTPServer) allocGC(allocID string, resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
var secret string
|
||||
s.parseToken(req, &secret)
|
||||
|
||||
var namespace string
|
||||
parseNamespace(req, &namespace)
|
||||
|
||||
// Check namespace submit-job permissions
|
||||
if aclObj, err := s.agent.Client().ResolveToken(secret); err != nil {
|
||||
return nil, err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(namespace, acl.NamespaceCapabilitySubmitJob) {
|
||||
return nil, structs.ErrPermissionDenied
|
||||
}
|
||||
return nil, s.agent.Client().CollectAllocation(allocID)
|
||||
}
|
||||
|
||||
@@ -133,6 +146,19 @@ func (s *HTTPServer) allocSnapshot(allocID string, resp http.ResponseWriter, req
|
||||
}
|
||||
|
||||
func (s *HTTPServer) allocStats(allocID string, resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
var secret string
|
||||
s.parseToken(req, &secret)
|
||||
|
||||
var namespace string
|
||||
parseNamespace(req, &namespace)
|
||||
|
||||
// Check namespace read-job permissions
|
||||
if aclObj, err := s.agent.Client().ResolveToken(secret); err != nil {
|
||||
return nil, err
|
||||
} else if aclObj != nil && !aclObj.AllowNsOp(namespace, acl.NamespaceCapabilityReadJob) {
|
||||
return nil, structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
clientStats := s.agent.client.StatsReporter()
|
||||
aStats, err := clientStats.GetAllocStats(allocID)
|
||||
if err != nil {
|
||||
|
||||
@@ -243,6 +243,61 @@ func TestHTTP_AllocStats(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTP_AllocStats_ACL(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := assert.New(t)
|
||||
|
||||
httpACLTest(t, nil, func(s *TestAgent) {
|
||||
state := s.Agent.server.State()
|
||||
|
||||
// Make the HTTP request
|
||||
req, err := http.NewRequest("GET", "/v1/client/allocation/123/stats", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Try request without a token and expect failure
|
||||
{
|
||||
respW := httptest.NewRecorder()
|
||||
_, err := s.Server.ClientAllocRequest(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.PolicyWrite))
|
||||
setToken(req, token)
|
||||
_, err := s.Server.ClientAllocRequest(respW, req)
|
||||
assert.NotNil(err)
|
||||
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
|
||||
}
|
||||
|
||||
// Try request with a valid token
|
||||
// Still returns an error because the alloc does not exist
|
||||
{
|
||||
respW := httptest.NewRecorder()
|
||||
policy := mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob})
|
||||
token := mock.CreatePolicyAndToken(t, state, 1007, "valid", policy)
|
||||
setToken(req, token)
|
||||
_, err := s.Server.ClientAllocRequest(respW, req)
|
||||
assert.NotNil(err)
|
||||
assert.Contains(err.Error(), "unknown allocation ID")
|
||||
}
|
||||
|
||||
// Try request with a management token
|
||||
// Still returns an error because the alloc does not exist
|
||||
{
|
||||
respW := httptest.NewRecorder()
|
||||
setToken(req, s.RootToken)
|
||||
_, err := s.Server.ClientAllocRequest(respW, req)
|
||||
assert.NotNil(err)
|
||||
assert.Contains(err.Error(), "unknown allocation ID")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTP_AllocSnapshot(t *testing.T) {
|
||||
t.Parallel()
|
||||
httpTest(t, nil, func(s *TestAgent) {
|
||||
@@ -279,6 +334,61 @@ func TestHTTP_AllocGC(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTP_AllocGC_ACL(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := assert.New(t)
|
||||
|
||||
httpACLTest(t, nil, func(s *TestAgent) {
|
||||
state := s.Agent.server.State()
|
||||
|
||||
// Make the HTTP request
|
||||
req, err := http.NewRequest("GET", "/v1/client/allocation/123/gc", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Try request without a token and expect failure
|
||||
{
|
||||
respW := httptest.NewRecorder()
|
||||
_, err := s.Server.ClientAllocRequest(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.PolicyWrite))
|
||||
setToken(req, token)
|
||||
_, err := s.Server.ClientAllocRequest(respW, req)
|
||||
assert.NotNil(err)
|
||||
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
|
||||
}
|
||||
|
||||
// Try request with a valid token
|
||||
// Still returns an error because the alloc does not exist
|
||||
{
|
||||
respW := httptest.NewRecorder()
|
||||
policy := mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilitySubmitJob})
|
||||
token := mock.CreatePolicyAndToken(t, state, 1007, "valid", policy)
|
||||
setToken(req, token)
|
||||
_, err := s.Server.ClientAllocRequest(respW, req)
|
||||
assert.NotNil(err)
|
||||
assert.Contains(err.Error(), "not present")
|
||||
}
|
||||
|
||||
// Try request with a management token
|
||||
// Still returns an error because the alloc does not exist
|
||||
{
|
||||
respW := httptest.NewRecorder()
|
||||
setToken(req, s.RootToken)
|
||||
_, err := s.Server.ClientAllocRequest(respW, req)
|
||||
assert.NotNil(err)
|
||||
assert.Contains(err.Error(), "not present")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTP_AllocAllGC(t *testing.T) {
|
||||
t.Parallel()
|
||||
httpTest(t, nil, func(s *TestAgent) {
|
||||
|
||||
@@ -147,9 +147,9 @@ The table below shows this endpoint's support for
|
||||
[blocking queries](/api/index.html#blocking-queries) and
|
||||
[required ACLs](/api/index.html#acls).
|
||||
|
||||
| Blocking Queries | ACL Required |
|
||||
| ---------------- | ------------ |
|
||||
| `NO` | `none` |
|
||||
| Blocking Queries | ACL Required |
|
||||
| ---------------- | -------------------- |
|
||||
| `NO` | `namespace:read-job` |
|
||||
|
||||
### Parameters
|
||||
|
||||
|
||||
Reference in New Issue
Block a user