Files
threshold/internal/server/testing.go
T
Jeff MitchellandIrena Rindos 01fb949d0b Add controller-led worker auth flow (#2413)
This adds an action to the API that allows pulling out an activation
token that can be given to a worker in its config (directly or via env
or file). The worker can pass this along with its normal request in
place of its normal nonce and the server will automatically accept it.

Co-authored-by: Irena Rindos <irenarindos@users.noreply.github.com>
2022-09-02 14:32:41 -04:00

205 lines
6.7 KiB
Go

package server
import (
"context"
"crypto/rand"
mathRand "math/rand"
"strings"
"testing"
"time"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/db/timestamp"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/server/store"
"github.com/hashicorp/boundary/internal/types/scope"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
"github.com/hashicorp/nodeenrollment"
"github.com/hashicorp/nodeenrollment/registration"
"github.com/hashicorp/nodeenrollment/rotation"
"github.com/hashicorp/nodeenrollment/storage/file"
"github.com/hashicorp/nodeenrollment/types"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/timestamppb"
)
const defaultLength = 20
// Generate random bytes for byte fields
func populateBytes(length int) []byte {
fieldBytes := make([]byte, length)
rand.Read(fieldBytes)
return fieldBytes
}
func TestKmsKey(ctx context.Context, t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) string {
t.Helper()
org, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
kmsCache := kms.TestKms(t, conn, wrapper)
databaseWrapper, err := kmsCache.GetWrapper(context.Background(), org.PublicId, kms.KeyPurposeDatabase)
require.NoError(t, err)
testKey, err := databaseWrapper.KeyId(ctx)
require.NoError(t, err)
return testKey
}
func TestRootCertificate(ctx context.Context, t *testing.T, conn *db.DB, kmsKey string) *RootCertificate {
t.Helper()
rw := db.New(conn)
beforeTimestamp := &timestamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(-1 * time.Hour))}
afterTimestamp := &timestamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(1 * time.Hour))}
rootCertKeys := RootCertificateKeys{
publicKey: populateBytes(defaultLength),
privateKey: populateBytes(defaultLength),
}
cert, err := newRootCertificate(ctx, mathRand.Uint64(), populateBytes(defaultLength), beforeTimestamp, afterTimestamp,
rootCertKeys, kmsKey, CurrentState)
err = rw.Create(ctx, cert)
require.NoError(t, err)
return cert
}
func TestWorkerAuth(t *testing.T, conn *db.DB, worker *Worker, kmsKey string) *WorkerAuth {
t.Helper()
ctx := context.Background()
rw := db.New(conn)
wSignPubKey := populateBytes(defaultLength)
wEncPubKey := populateBytes(defaultLength)
workerKeys := WorkerKeys{workerSigningPubKey: wSignPubKey, workerEncryptionPubKey: wEncPubKey}
controllerKey := populateBytes(defaultLength)
nonce := populateBytes(defaultLength)
opt := []Option{
WithKeyId(kmsKey),
WithWorkerKeys(workerKeys),
WithControllerEncryptionPrivateKey(controllerKey),
WithNonce(nonce),
}
workerAuth, err := newWorkerAuth(ctx, "worker-key-identifier", worker.PublicId, opt...)
require.NoError(t, err)
err = rw.Create(ctx, workerAuth)
require.NoError(t, err)
return workerAuth
}
// TestKmsWorker inserts a worker into the db to satisfy foreign key constraints.
// The worker provided fields are auto generated. if WithName is not present a
// random name will be generated and assigned to the worker.
func TestKmsWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *Worker {
t.Helper()
rw := db.New(conn)
kms := kms.TestKms(t, conn, wrapper)
serversRepo, err := NewRepository(rw, rw, kms)
require.NoError(t, err)
ctx := context.Background()
opts := GetOpts(opt...)
namePart, err := newWorkerId(ctx)
require.NoError(t, err)
name := "test-worker-" + strings.ToLower(namePart)
if opts.withName != "" {
name = opts.withName
}
address := "127.0.0.1"
if opts.withAddress != "" {
address = opts.withAddress
}
wrk := NewWorker(scope.Global.String(),
WithName(name),
WithAddress(address),
WithDescription(opts.withDescription))
wrk, err = serversRepo.UpsertWorkerStatus(ctx, wrk)
require.NoError(t, err)
require.NotNil(t, wrk)
require.Equal(t, "kms", wrk.Type)
if len(opts.withWorkerTags) > 0 {
var tags []interface{}
for _, t := range opts.withWorkerTags {
tags = append(tags, &store.WorkerTag{
WorkerId: wrk.GetPublicId(),
Key: t.Key,
Value: t.Value,
Source: "configuration",
})
}
require.NoError(t, rw.CreateItems(ctx, tags))
}
wrk, err = serversRepo.LookupWorker(ctx, wrk.GetPublicId())
require.NoError(t, err)
return wrk
}
// TestPkiWorker inserts a worker into the db to satisfy foreign key constraints.
// The worker provided fields are auto generated. WithName and WithDescription,
// are applied to the resource name, description if present. WithTestPkiWorkerAuthorizedKeyId
// can be used to make the PkiWorker authorized in which case the string pointer
// passed to WithTestPkiWorkerAuthorizedKeyId is set to the key id.
func TestPkiWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *Worker {
t.Helper()
rw := db.New(conn)
kmsCache := kms.TestKms(t, conn, wrapper)
serversRepo, err := NewRepository(rw, rw, kmsCache)
require.NoError(t, err)
ctx := context.Background()
opts := GetOpts(opt...)
require.NoError(t, err)
wrk := NewWorker(scope.Global.String(),
opt...)
wrk, err = serversRepo.CreateWorker(ctx, wrk, opt...)
require.NoError(t, err)
require.NotNil(t, wrk)
if len(opts.withWorkerTags) > 0 {
var tags []interface{}
for _, t := range opts.withWorkerTags {
tags = append(tags, &store.WorkerTag{
WorkerId: wrk.GetPublicId(),
Key: t.Key,
Value: t.Value,
Source: "configuration",
})
}
require.NoError(t, rw.CreateItems(ctx, tags))
}
if opts.withTestPkiWorkerAuthorized {
err = kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader))
// We always try to create root global scope keys, but if it already exists, just continue...
require.True(t, err == nil || errors.IsUniqueError(err), err)
rootStorage, err := NewRepositoryStorage(ctx, rw, rw, kmsCache)
require.NoError(t, err)
_, err = rotation.RotateRootCertificates(ctx, rootStorage)
require.NoError(t, err)
// Create struct to pass in with workerId that will be passed along to storage
state, err := AttachWorkerIdToState(ctx, wrk.PublicId)
require.NoError(t, err)
// This happens on the worker
fileStorage, err := file.New(ctx)
require.NoError(t, err)
nodeCreds, err := types.NewNodeCredentials(ctx, fileStorage)
require.NoError(t, err)
// Create request using worker id
fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(ctx)
require.NoError(t, err)
registeredNode, err := registration.AuthorizeNode(ctx, rootStorage, fetchReq, nodeenrollment.WithState(state))
require.NoError(t, err)
if opts.withTestPkiWorkerKeyId != nil {
*opts.withTestPkiWorkerKeyId = registeredNode.Id
}
}
wrk, err = serversRepo.LookupWorker(ctx, wrk.GetPublicId())
require.NoError(t, err)
return wrk
}