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>
205 lines
6.7 KiB
Go
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 := ×tamp.Timestamp{Timestamp: timestamppb.New(time.Now().Add(-1 * time.Hour))}
|
|
afterTimestamp := ×tamp.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
|
|
}
|