open-nomad/client/testutil/rpc.go
Mahmood Ali 4b2ba62e35 acl: check ACL against object namespace
Fix a bug where a millicious user can access or manipulate an alloc in a
namespace they don't have access to.  The allocation endpoints perform
ACL checks against the request namespace, not the allocation namespace,
and performs the allocation lookup independently from namespaces.

Here, we check that the requested can access the alloc namespace
regardless of the declared request namespace.

Ideally, we'd enforce that the declared request namespace matches
the actual allocation namespace.  Unfortunately, we haven't documented
alloc endpoints as namespaced functions; we suspect starting to enforce
this will be very disruptive and inappropriate for a nomad point
release.  As such, we maintain current behavior that doesn't require
passing the proper namespace in request.  A future major release may
start enforcing checking declared namespace.
2019-10-08 12:59:22 -04:00

87 lines
1.9 KiB
Go

package testutil
import (
"fmt"
"io"
"net"
"strings"
"testing"
"time"
cstructs "github.com/hashicorp/nomad/client/structs"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/stretchr/testify/require"
"github.com/ugorji/go/codec"
)
// StreamingRPC may be satisfied by client.Client or server.Server.
type StreamingRPC interface {
StreamingRpcHandler(method string) (structs.StreamingRpcHandler, error)
}
// StreamingRPCErrorTestCase is a test case to be passed to the
// assertStreamingRPCError func.
type StreamingRPCErrorTestCase struct {
Name string
RPC string
Req interface{}
Assert func(error) bool
}
// AssertStreamingRPCError asserts a streaming RPC's error matches the given
// assertion in the test case.
func AssertStreamingRPCError(t *testing.T, s StreamingRPC, tc StreamingRPCErrorTestCase) {
handler, err := s.StreamingRpcHandler(tc.RPC)
require.NoError(t, err)
// Create a pipe
p1, p2 := net.Pipe()
defer p1.Close()
defer p2.Close()
errCh := make(chan error, 1)
streamMsg := make(chan *cstructs.StreamErrWrapper, 1)
// Start the handler
go handler(p2)
// Start the decoder
go func() {
decoder := codec.NewDecoder(p1, structs.MsgpackHandle)
for {
var msg cstructs.StreamErrWrapper
if err := decoder.Decode(&msg); err != nil {
if err == io.EOF || strings.Contains(err.Error(), "closed") {
return
}
errCh <- fmt.Errorf("error decoding: %v", err)
}
streamMsg <- &msg
}
}()
// Send the request
encoder := codec.NewEncoder(p1, structs.MsgpackHandle)
require.NoError(t, encoder.Encode(tc.Req))
timeout := time.After(5 * time.Second)
for {
select {
case <-timeout:
t.Fatal("timeout")
case err := <-errCh:
require.NoError(t, err)
case msg := <-streamMsg:
// Convert RpcError to error
var err error
if msg.Error != nil {
err = msg.Error
}
require.True(t, tc.Assert(err), "(%T) %s", msg.Error, msg.Error)
return
}
}
}