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>
This commit is contained in:
1 parent
a1be125f5b
commit
01fb949d0b
55 files changed
+2728
-625
No files matched your search
@@ -2,6 +2,7 @@ package server_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -143,6 +144,7 @@ func TestLookupWorkerIdByKeyId(t *testing.T) {
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
require.NoError(t, kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)))
|
||||
repo, err := server.NewRepository(rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
ctx := context.Background()
|
||||
@@ -252,6 +254,7 @@ func TestUpsertWorkerStatus(t *testing.T) {
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
require.NoError(t, kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)))
|
||||
repo, err := server.NewRepository(rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -496,9 +499,11 @@ func TestListWorkers(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
require.NoError(t, kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)))
|
||||
|
||||
const testLimit = 10
|
||||
repo, err := server.NewRepository(rw, rw, kms, server.WithLimit(testLimit))
|
||||
repo, err := server.NewRepository(rw, rw, kmsCache, server.WithLimit(testLimit))
|
||||
require.NoError(t, err)
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -696,7 +701,6 @@ func TestRepository_CreateWorker(t *testing.T) {
|
||||
name string
|
||||
setup func() *server.Worker
|
||||
repo *server.Repository
|
||||
fetchReq *types.FetchNodeCredentialsRequest
|
||||
reader db.Reader
|
||||
opt []server.Option
|
||||
wantErr bool
|
||||
@@ -812,19 +816,21 @@ func TestRepository_CreateWorker(t *testing.T) {
|
||||
return w
|
||||
},
|
||||
reader: rw,
|
||||
fetchReq: func() *types.FetchNodeCredentialsRequest {
|
||||
// This happens on the worker
|
||||
fileStorage, err := file.New(testCtx)
|
||||
require.NoError(t, err)
|
||||
defer fileStorage.Cleanup()
|
||||
opt: []server.Option{server.WithFetchNodeCredentialsRequest(
|
||||
func() *types.FetchNodeCredentialsRequest {
|
||||
// This happens on the worker
|
||||
fileStorage, err := file.New(testCtx)
|
||||
require.NoError(t, err)
|
||||
defer fileStorage.Cleanup()
|
||||
|
||||
nodeCreds, err := types.NewNodeCredentials(testCtx, fileStorage)
|
||||
require.NoError(t, err)
|
||||
// Create request using worker id
|
||||
fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(testCtx)
|
||||
require.NoError(t, err)
|
||||
return fetchReq
|
||||
}(),
|
||||
nodeCreds, err := types.NewNodeCredentials(testCtx, fileStorage)
|
||||
require.NoError(t, err)
|
||||
// Create request using worker id
|
||||
fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(testCtx)
|
||||
require.NoError(t, err)
|
||||
return fetchReq
|
||||
}(),
|
||||
)},
|
||||
repo: func() *server.Repository {
|
||||
mockConn, mock := db.TestSetupWithMock(t)
|
||||
mock.ExpectQuery(`SELECT`).WillReturnRows(sqlmock.NewRows([]string{"version", "create_time"}).AddRow(migrations.Version, time.Now()))
|
||||
@@ -844,7 +850,7 @@ func TestRepository_CreateWorker(t *testing.T) {
|
||||
return w
|
||||
},
|
||||
reader: rw,
|
||||
fetchReq: &types.FetchNodeCredentialsRequest{},
|
||||
opt: []server.Option{server.WithFetchNodeCredentialsRequest(&types.FetchNodeCredentialsRequest{})},
|
||||
repo: testRepo,
|
||||
wantErr: true,
|
||||
wantErrContains: "unable to authorize node",
|
||||
@@ -862,7 +868,7 @@ func TestRepository_CreateWorker(t *testing.T) {
|
||||
wantErrIs: errors.NotUnique,
|
||||
},
|
||||
{
|
||||
name: "success",
|
||||
name: "success-no-options",
|
||||
setup: func() *server.Worker {
|
||||
w := server.NewWorker(scope.Global.String())
|
||||
w.Name = "success"
|
||||
@@ -892,29 +898,40 @@ func TestRepository_CreateWorker(t *testing.T) {
|
||||
return w
|
||||
},
|
||||
reader: rw,
|
||||
fetchReq: func() *types.FetchNodeCredentialsRequest {
|
||||
// This happens on the worker
|
||||
fileStorage, err := file.New(testCtx)
|
||||
require.NoError(t, err)
|
||||
defer fileStorage.Cleanup()
|
||||
opt: []server.Option{server.WithFetchNodeCredentialsRequest(
|
||||
func() *types.FetchNodeCredentialsRequest {
|
||||
// This happens on the worker
|
||||
fileStorage, err := file.New(testCtx)
|
||||
require.NoError(t, err)
|
||||
defer fileStorage.Cleanup()
|
||||
|
||||
nodeCreds, err := types.NewNodeCredentials(testCtx, fileStorage)
|
||||
require.NoError(t, err)
|
||||
// Create request using worker id
|
||||
fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(testCtx)
|
||||
require.NoError(t, err)
|
||||
return fetchReq
|
||||
}(),
|
||||
nodeCreds, err := types.NewNodeCredentials(testCtx, fileStorage)
|
||||
require.NoError(t, err)
|
||||
// Create request using worker id
|
||||
fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(testCtx)
|
||||
require.NoError(t, err)
|
||||
return fetchReq
|
||||
}(),
|
||||
)},
|
||||
repo: testRepo,
|
||||
},
|
||||
{
|
||||
name: "success-with-controller-activation-token",
|
||||
setup: func() *server.Worker {
|
||||
w := server.NewWorker(scope.Global.String())
|
||||
w.Name = "success-with-controller-activation-token"
|
||||
return w
|
||||
},
|
||||
reader: rw,
|
||||
opt: []server.Option{server.WithCreateControllerLedActivationToken(true)},
|
||||
repo: testRepo,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
testWorker := tc.setup()
|
||||
|
||||
tc.opt = append(tc.opt, server.WithFetchNodeCredentialsRequest(tc.fetchReq))
|
||||
|
||||
got, err := tc.repo.CreateWorker(testCtx, testWorker, tc.opt...)
|
||||
if tc.wantErr {
|
||||
require.Error(err)
|
||||
@@ -942,12 +959,19 @@ func TestRepository_CreateWorker(t *testing.T) {
|
||||
require.NoError(err)
|
||||
assert.Empty(cmp.Diff(got, found, protocmp.Transform()))
|
||||
|
||||
if tc.fetchReq != nil {
|
||||
opts := server.GetOpts(tc.opt...)
|
||||
if opts.WithFetchNodeCredentialsRequest != nil {
|
||||
worker := &server.WorkerAuth{
|
||||
WorkerAuth: &store.WorkerAuth{},
|
||||
}
|
||||
require.NoError(tc.reader.LookupWhere(testCtx, worker, "worker_id = ?", []any{found.PublicId}))
|
||||
}
|
||||
if opts.WithCreateControllerLedActivationToken {
|
||||
activationToken := &server.WorkerAuthServerLedActivationToken{
|
||||
WorkerAuthServerLedActivationToken: &store.WorkerAuthServerLedActivationToken{},
|
||||
}
|
||||
require.NoError(tc.reader.LookupWhere(testCtx, activationToken, "worker_id = ?", []any{found.PublicId}))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -959,6 +983,7 @@ func TestRepository_UpdateWorker(t *testing.T) {
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
require.NoError(t, kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)))
|
||||
|
||||
repo, err := server.NewRepository(rw, rw, kmsCache)
|
||||
require.NoError(t, err)
|
||||
|
||||
Reference in new issue
Block a user