agent/consul: test for ConnectCA.Sign
This commit is contained in:
parent
a360c5cca4
commit
9a8653f45e
|
@ -17,6 +17,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
"github.com/hashicorp/go-uuid"
|
||||||
"github.com/mitchellh/go-testing-interface"
|
"github.com/mitchellh/go-testing-interface"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,14 +33,15 @@ const testClusterID = "11111111-2222-3333-4444-555555555555"
|
||||||
var testCACounter uint64 = 0
|
var testCACounter uint64 = 0
|
||||||
|
|
||||||
// TestCA creates a test CA certificate and signing key and returns it
|
// TestCA creates a test CA certificate and signing key and returns it
|
||||||
// in the CARoot structure format. The CARoot returned will NOT have an ID
|
// in the CARoot structure format. The returned CA will be set as Active = true.
|
||||||
// set.
|
|
||||||
//
|
//
|
||||||
// If xc is non-nil, then the returned certificate will have a signing cert
|
// If xc is non-nil, then the returned certificate will have a signing cert
|
||||||
// that is cross-signed with the previous cert, and this will be set as
|
// that is cross-signed with the previous cert, and this will be set as
|
||||||
// SigningCert.
|
// SigningCert.
|
||||||
func TestCA(t testing.T, xc *structs.CARoot) *structs.CARoot {
|
func TestCA(t testing.T, xc *structs.CARoot) *structs.CARoot {
|
||||||
var result structs.CARoot
|
var result structs.CARoot
|
||||||
|
result.ID = testUUID(t)
|
||||||
|
result.Active = true
|
||||||
result.Name = fmt.Sprintf("Test CA %d", atomic.AddUint64(&testCACounter, 1))
|
result.Name = fmt.Sprintf("Test CA %d", atomic.AddUint64(&testCACounter, 1))
|
||||||
|
|
||||||
// Create the private key we'll use for this CA cert.
|
// Create the private key we'll use for this CA cert.
|
||||||
|
@ -276,3 +278,13 @@ func testPrivateKey(t testing.T, ca *structs.CARoot) crypto.Signer {
|
||||||
func testSerialNumber() (*big.Int, error) {
|
func testSerialNumber() (*big.Int, error) {
|
||||||
return rand.Int(rand.Reader, (&big.Int{}).Exp(big.NewInt(2), big.NewInt(159), nil))
|
return rand.Int(rand.Reader, (&big.Int{}).Exp(big.NewInt(2), big.NewInt(159), nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// testUUID generates a UUID for testing.
|
||||||
|
func testUUID(t testing.T) string {
|
||||||
|
ret, err := uuid.GenerateUUID()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to generate a UUID, %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package connect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/go-testing-interface"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestSpiffeIDService returns a SPIFFE ID representing a service.
|
||||||
|
func TestSpiffeIDService(t testing.T, service string) *SpiffeIDService {
|
||||||
|
return &SpiffeIDService{
|
||||||
|
Host: testClusterID + ".consul",
|
||||||
|
Namespace: "default",
|
||||||
|
Datacenter: "dc01",
|
||||||
|
Service: service,
|
||||||
|
}
|
||||||
|
}
|
|
@ -88,7 +88,12 @@ func (s *ConnectCA) Sign(
|
||||||
return fmt.Errorf("SPIFFE ID in CSR must be a service ID")
|
return fmt.Errorf("SPIFFE ID in CSR must be a service ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
var root *structs.CARoot
|
// Get the currently active root
|
||||||
|
state := s.srv.fsm.State()
|
||||||
|
_, root, err := state.CARootActive(nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Determine the signing certificate. It is the set signing cert
|
// Determine the signing certificate. It is the set signing cert
|
||||||
// unless that is empty, in which case it is identically to the public
|
// unless that is empty, in which case it is identically to the public
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
package consul
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/agent/connect"
|
||||||
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
"github.com/hashicorp/consul/testrpc"
|
||||||
|
"github.com/hashicorp/net-rpc-msgpackrpc"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Test CA signing
|
||||||
|
//
|
||||||
|
// NOTE(mitchellh): Just testing the happy path and not all the other validation
|
||||||
|
// issues because the internals of this method will probably be gutted for the
|
||||||
|
// CA plugins then we can just test mocks.
|
||||||
|
func TestConnectCASign(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := assert.New(t)
|
||||||
|
dir1, s1 := testServer(t)
|
||||||
|
defer os.RemoveAll(dir1)
|
||||||
|
defer s1.Shutdown()
|
||||||
|
codec := rpcClient(t, s1)
|
||||||
|
defer codec.Close()
|
||||||
|
|
||||||
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
||||||
|
|
||||||
|
// Insert a CA
|
||||||
|
state := s1.fsm.State()
|
||||||
|
assert.Nil(state.CARootSet(1, connect.TestCA(t, nil)))
|
||||||
|
|
||||||
|
// Generate a CSR and request signing
|
||||||
|
args := &structs.CASignRequest{
|
||||||
|
Datacenter: "dc01",
|
||||||
|
CSR: connect.TestCSR(t, connect.TestSpiffeIDService(t, "web")),
|
||||||
|
}
|
||||||
|
var reply interface{}
|
||||||
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "ConnectCA.Sign", args, &reply))
|
||||||
|
}
|
|
@ -55,6 +55,24 @@ func (s *Store) CARoots(ws memdb.WatchSet) (uint64, structs.CARoots, error) {
|
||||||
return idx, results, nil
|
return idx, results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CARootActive returns the currently active CARoot.
|
||||||
|
func (s *Store) CARootActive(ws memdb.WatchSet) (uint64, *structs.CARoot, error) {
|
||||||
|
// Get all the roots since there should never be that many and just
|
||||||
|
// do the filtering in this method.
|
||||||
|
var result *structs.CARoot
|
||||||
|
idx, roots, err := s.CARoots(ws)
|
||||||
|
if err == nil {
|
||||||
|
for _, r := range roots {
|
||||||
|
if r.Active {
|
||||||
|
result = r
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx, result, err
|
||||||
|
}
|
||||||
|
|
||||||
// CARootSet creates or updates a CA root.
|
// CARootSet creates or updates a CA root.
|
||||||
//
|
//
|
||||||
// NOTE(mitchellh): I have a feeling we'll want a CARootMultiSetCAS to
|
// NOTE(mitchellh): I have a feeling we'll want a CARootMultiSetCAS to
|
||||||
|
|
|
@ -33,6 +33,12 @@ type CARoot struct {
|
||||||
SigningCert string
|
SigningCert string
|
||||||
SigningKey string
|
SigningKey string
|
||||||
|
|
||||||
|
// Active is true if this is the current active CA. This must only
|
||||||
|
// be true for exactly one CA. For any method that modifies roots in the
|
||||||
|
// state store, tests should be written to verify that multiple roots
|
||||||
|
// cannot be active.
|
||||||
|
Active bool
|
||||||
|
|
||||||
RaftIndex
|
RaftIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue