feat(workerAuth): Implement nodeenrollment lib workerauth storage

This commit is contained in:
irenarindos committed 2022-06-02 15:09:12 -04:00
1 parent 1ee2c0dde0
commit 642e41c9d7
10 files changed
+1066 -41

No files matched your search

@@ -44,7 +44,7 @@ create trigger worker_auth_ca_update_time_column
insert into worker_auth_ca(private_id) values('roots');
create trigger update_version_column after update on worker_auth_ca
for each row execute procedure update_version_column();
for each row execute procedure update_version_column(private_id);
comment on table worker_auth_ca is
'worker_auth_ca is a one-row versioned table used for locking for certificate rotation on the worker_auth_ca_certificate table.';
@@ -110,7 +110,7 @@ create table worker_auth_authorized(
references kms_data_key_version (private_id)
on delete restrict
on update cascade,
nonce bytea
nonce bytea -- encrypted value
);
create trigger
@@ -124,7 +124,7 @@ comment on table worker_auth_authorized is
'worker_auth_authorized is a table where each row represents key and cert data associated with an authorized worker.';
create table worker_auth_certificate_bundle(
certificate_public_key bytea not null
root_certificate_public_key bytea not null
constraint worker_auth_ca_certificate_fkey
references worker_auth_ca_certificate(public_key)
on delete cascade
@@ -137,7 +137,7 @@ create table worker_auth_certificate_bundle(
cert_bundle bytea
constraint current_cert_bundle_must_not_be_empty
check(length(cert_bundle) > 0),
primary key(certificate_public_key, worker_key_identifier)
primary key(root_certificate_public_key, worker_key_identifier)
);
comment on table worker_auth_certificate_bundle is
@@ -147,6 +147,6 @@ create trigger
immutable_columns
before
update on worker_auth_certificate_bundle
for each row execute procedure immutable_columns('certificate_public_key', 'worker_key_identifier', 'cert_bundle');
for each row execute procedure immutable_columns('root_certificate_public_key', 'worker_key_identifier', 'cert_bundle');
commit;
@@ -40,11 +40,11 @@ message WorkerAuth {
// WorkerCertBundle contains all fields related to a WorkerCertBundle resource
message WorkerCertBundle {
// The public key of the issuing root certificate
// @inject_tag: `gorm:"not_null"`
bytes certificate_public_key = 10;
// @inject_tag: `gorm:"primary_key"`
bytes root_certificate_public_key = 10;
// The WorkerAuth worker_key_identifier this cert bundle record is for
// @inject_tag: `gorm:"not_null"`
// @inject_tag: `gorm:"primary_key"`
string worker_key_identifier = 20;
// CertBundle is the marshaled protobuf certificate bundle for a WorkerAuth
+15 -1
View File
@@ -2,11 +2,25 @@ package servers
const (
deleteWhereCreateTimeSql = `create_time < ?`
deleteTagsByWorkerIdSql = `
deleteTagsByWorkerIdSql = `
delete
from server_worker_tag
where
source = ?
and
worker_id = ?`
deleteWorkerAuthQuery = `
delete from worker_auth_authorized
where worker_key_identifier = @worker_key_identifier;
`
deleteWorkerCertBundlesQuery = `
delete from worker_auth_certificate_bundle
where worker_key_identifier = @worker_key_identifier;
`
deleteRootCertificateQuery = `
delete from worker_auth_ca_certificate
where state = @state;
`
)
+755
View File
@@ -0,0 +1,755 @@
package servers
import (
"context"
"crypto/x509"
"database/sql"
"fmt"
"strconv"
"github.com/fatih/structs"
"google.golang.org/protobuf/types/known/structpb"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/db/timestamp"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/servers/store"
"github.com/hashicorp/boundary/internal/types/scope"
"github.com/hashicorp/go-dbw"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
nodee "github.com/hashicorp/nodeenrollment"
"github.com/hashicorp/nodeenrollment/types"
"github.com/mitchellh/mapstructure"
"google.golang.org/protobuf/proto"
)
// Ensure we implement the Storage interfaces
var (
_ nodee.Storage = (*WorkerAuthRepositoryStorage)(nil)
)
type rootCertificatesVersion struct {
Version uint32
}
// WorkerAuthRepositoryStorage is the Worker Auth database repository
type WorkerAuthRepositoryStorage struct {
reader db.Reader
writer db.Writer
kms *kms.Kms
}
// NewRepositoryStorage creates a new WorkerAuthRepositoryStorage that implements the Storage interface
func NewRepositoryStorage(ctx context.Context, r db.Reader, w db.Writer, kms *kms.Kms) (*WorkerAuthRepositoryStorage, error) {
const op = "servers.(WorkerAuthRepositoryStorage).NewRepositoryStorage"
switch {
case isNil(r):
return nil, errors.New(ctx, errors.InvalidParameter, op, "reader is nil")
case isNil(w):
return nil, errors.New(ctx, errors.InvalidParameter, op, "writer is nil")
case isNil(kms):
return nil, errors.New(ctx, errors.InvalidParameter, op, "kms is nil")
}
workerAuthRepoStorage := &WorkerAuthRepositoryStorage{
reader: r,
writer: w,
kms: kms,
}
return workerAuthRepoStorage, nil
}
// Store implements the Storage interface
func (r *WorkerAuthRepositoryStorage) Store(ctx context.Context, msg nodee.MessageWithId) error {
const op = "servers.(WorkerAuthRepositoryStorage).Store"
if err := types.ValidateMessage(msg); err != nil {
return errors.Wrap(ctx, err, op)
}
if msg.GetId() == "" {
return errors.New(ctx, errors.InvalidParameter, op, "given message cannot be stored as it has no ID")
}
// Determine type of message to store
switch t := msg.(type) {
case *types.NodeInformation:
err := r.storeNodeInformation(ctx, t)
if err != nil {
return errors.Wrap(ctx, err, op)
}
case *types.RootCertificates:
err := r.storeRootCertificates(ctx, t)
if err != nil {
return errors.Wrap(ctx, err, op)
}
default:
return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("message type %T not supported for Store", msg))
}
return nil
}
// Node information is stored in two parts:
// * the workerAuth record is stored with a reference to a worker
// * certificate bundles are stored with a reference to the workerAuth record and issuing root certificate
func (r *WorkerAuthRepositoryStorage) storeNodeInformation(ctx context.Context, node *types.NodeInformation) error {
const op = "servers.(WorkerAuthRepositoryStorage).storeNodeInformation"
if node == nil {
return errors.New(ctx, errors.InvalidParameter, op, "missing NodeInformation")
}
nodeAuth := allocWorkerAuth()
nodeAuth.WorkerKeyIdentifier = node.Id
nodeAuth.WorkerEncryptionPubKey = node.EncryptionPublicKeyBytes
nodeAuth.WorkerSigningPubKey = node.CertificatePublicKeyPkix
// Encrypt the private key
databaseWrapper, err := r.kms.GetWrapper(ctx, scope.Global.String(), kms.KeyPurposeDatabase)
if err != nil {
return errors.Wrap(ctx, err, op)
}
nodeAuth.KeyId, err = databaseWrapper.KeyId(ctx)
if err != nil {
return errors.Wrap(ctx, err, op)
}
nodeAuth.ControllerEncryptionPrivKey, err = encrypt(ctx, node.ServerEncryptionPrivateKeyBytes, databaseWrapper)
if err != nil {
return errors.Wrap(ctx, err, op)
}
nodeAuth.Nonce, err = encrypt(ctx, node.RegistrationNonce, databaseWrapper)
if err != nil {
return errors.Wrap(ctx, err, op)
}
// Get workerId from state passed in
var result workerAuthWorkerId
err = mapstructure.Decode(node.State.AsMap(), &result)
if err != nil {
return errors.Wrap(ctx, err, op)
}
nodeAuth.WorkerId = result.WorkerId
if err := nodeAuth.ValidateNewWorkerAuth(ctx); err != nil {
return errors.Wrap(ctx, err, op)
}
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, func(read db.Reader, w db.Writer) error {
// Store WorkerAuth
storeNodeAuth := nodeAuth.clone()
if err := w.Create(ctx, &storeNodeAuth); err != nil {
return errors.Wrap(ctx, err, op)
}
// Then store cert bundles associated with this WorkerAuth
for _, c := range node.CertificateBundles {
if err := r.storeWorkerCertBundle(ctx, c, nodeAuth.WorkerKeyIdentifier, w); err != nil {
return errors.Wrap(ctx, err, op)
}
}
return nil
},
)
if err != nil {
return errors.Wrap(ctx, err, op)
}
return nil
}
func (r *WorkerAuthRepositoryStorage) storeWorkerCertBundle(ctx context.Context, bundle *types.CertificateBundle,
workerKeyIdentifier string, writer db.Writer,
) error {
const op = "servers.(WorkerAuthRepositoryStorage).storeWorkerCertBundle"
if bundle == nil {
return errors.New(ctx, errors.InvalidParameter, op, "missing CertificateBundle")
}
if workerKeyIdentifier == "" {
return errors.New(ctx, errors.InvalidParameter, op, "empty workerKeyIdentifier")
}
if writer == nil {
return errors.New(ctx, errors.InvalidParameter, op, "missing writer")
}
workerCertBundle := allocWorkerCertBundle()
bundleBytes, err := proto.Marshal(bundle)
if err != nil {
return errors.New(ctx, errors.Encode, op, "error marshaling nodetypes.CertificateBundle", errors.WithWrap(err))
}
// Extract serial number from CA cert
caCert := bundle.CaCertificateDer
parsedCert, err := x509.ParseCertificate(caCert)
if err != nil {
return errors.Wrap(ctx, err, op)
}
workerCertBundle.RootCertificatePublicKey = parsedCert.AuthorityKeyId
workerCertBundle.CertBundle = bundleBytes
workerCertBundle.WorkerKeyIdentifier = workerKeyIdentifier
err = workerCertBundle.ValidateNewWorkerCertBundle(ctx)
if err != nil {
return errors.Wrap(ctx, err, op)
}
err = writer.Create(ctx, &workerCertBundle)
if err != nil {
return errors.Wrap(ctx, err, op)
}
return nil
}
func (r *WorkerAuthRepositoryStorage) convertRootCertificate(ctx context.Context, cert *types.RootCertificate) (*RootCertificate, error) {
const op = "servers.(WorkerAuthRepositoryStorage).convertRootCertificate"
if cert == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing RootCertificate")
}
rootCert := allocRootCertificate()
parsedCert, err := x509.ParseCertificate(cert.CertificateDer)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
rootCert.SerialNumber = parsedCert.SerialNumber.Uint64()
rootCert.Certificate = cert.CertificateDer
rootCert.NotValidAfter = timestamp.New(cert.NotAfter.AsTime())
rootCert.NotValidBefore = timestamp.New(cert.NotBefore.AsTime())
rootCert.PublicKey = cert.PublicKeyPkix
rootCert.State = cert.Id
rootCert.IssuingCa = ca_id
// Encrypt the private key
databaseWrapper, err := r.kms.GetWrapper(ctx, scope.Global.String(), kms.KeyPurposeDatabase)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
rootCert.KeyId, err = databaseWrapper.KeyId(ctx)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
rootCert.PrivateKey, err = encrypt(ctx, cert.PrivateKeyPkcs8, databaseWrapper)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
err = rootCert.ValidateNewRootCertificate(ctx)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
return rootCert, nil
}
func (r *WorkerAuthRepositoryStorage) storeRootCertificates(ctx context.Context, cert *types.RootCertificates) error {
const op = "servers.(WorkerAuthRepositoryStorage).storeRootCertificates"
if cert == nil {
return errors.New(ctx, errors.InvalidParameter, op, "missing RootCertificate")
}
nextCert, err := r.convertRootCertificate(ctx, cert.Next)
currentCert, err := r.convertRootCertificate(ctx, cert.Current)
// Use passed version
var result rootCertificatesVersion
err = mapstructure.Decode(cert.State.AsMap(), &result)
if err != nil {
return errors.Wrap(ctx, err, op)
}
version := uint32(1)
if result.Version != 0 {
version = result.Version
}
certAuthority := &CertificateAuthority{
CertificateAuthority: &store.CertificateAuthority{
PrivateId: ca_id,
Version: version + 1,
},
}
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(read db.Reader, w db.Writer) error {
// Delete the old certs
if err = r.removeRootCertificateWithWriter(ctx, string(CurrentState), w); err != nil {
return errors.Wrap(ctx, err, op)
}
if err = r.removeRootCertificateWithWriter(ctx, string(NextState), w); err != nil {
return errors.Wrap(ctx, err, op)
}
// Update the certAuthority's version column
rowsUpdated, err := w.Update(ctx, certAuthority, []string{"Version"}, nil, db.WithVersion(&version))
if err != nil {
return errors.Wrap(ctx, err, op)
}
if rowsUpdated != 1 {
return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("updated CertificateAuthority and %d rows updated", rowsUpdated))
}
// Then insert the new certs
if nextCert != nil {
if err = w.Create(ctx, &nextCert); err != nil {
return errors.Wrap(ctx, err, op)
}
}
if currentCert != nil {
if err = w.Create(ctx, &currentCert); err != nil {
return errors.Wrap(ctx, err, op)
}
}
return nil
},
)
if err != nil {
return errors.Wrap(ctx, err, op)
}
return nil
}
// Load implements the Storage interface.
// Load loads values into the given message. The message must be populated
// with the ID value. If not found, the returned error should be ErrNotFound.
func (r *WorkerAuthRepositoryStorage) Load(ctx context.Context, msg nodee.MessageWithId) error {
const op = "servers.(WorkerAuthRepositoryStorage).Load"
if err := types.ValidateMessage(msg); err != nil {
return errors.Wrap(ctx, err, op)
}
if msg.GetId() == "" {
return errors.New(ctx, errors.InvalidParameter, op, "given message cannot be loaded as it has no ID")
}
switch t := msg.(type) {
case *types.NodeInformation:
err := r.loadNodeInformation(ctx, t)
if err != nil {
return errors.Wrap(ctx, err, op)
}
case *types.RootCertificates:
err := r.loadRootCertificates(ctx, t)
if err != nil {
return errors.Wrap(ctx, err, op)
}
default:
return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("message type %T not supported for Load", t))
}
return nil
}
// Node information is loaded in two parts:
// * the workerAuth record
// * its certificate bundles
func (r *WorkerAuthRepositoryStorage) loadNodeInformation(ctx context.Context, node *types.NodeInformation) error {
const op = "servers.(WorkerAuthRepositoryStorage).loadNodeInformation"
if node == nil {
return errors.New(ctx, errors.InvalidParameter, op, "missing NodeInformation")
}
authorizedWorker, err := r.findWorkerAuth(ctx, node)
if err != nil {
return errors.Wrap(ctx, err, op)
}
if authorizedWorker == nil {
return nodee.ErrNotFound
}
node.EncryptionPublicKeyBytes = authorizedWorker.WorkerEncryptionPubKey
node.CertificatePublicKeyPkix = authorizedWorker.WorkerSigningPubKey
// Default values are used for key types
node.EncryptionPublicKeyType = types.KEYTYPE_X25519
node.CertificatePublicKeyType = types.KEYTYPE_ED25519
node.ServerEncryptionPrivateKeyType = types.KEYTYPE_X25519
// Decrypt private key
databaseWrapper, err := r.kms.GetWrapper(ctx, scope.Global.String(), kms.KeyPurposeDatabase, kms.WithKeyId(authorizedWorker.KeyId))
if err != nil {
return errors.Wrap(ctx, err, op)
}
node.ServerEncryptionPrivateKeyBytes, err = decrypt(ctx, authorizedWorker.ControllerEncryptionPrivKey, databaseWrapper)
if err != nil {
return errors.Wrap(ctx, err, op)
}
node.RegistrationNonce, err = decrypt(ctx, authorizedWorker.Nonce, databaseWrapper)
if err != nil {
return errors.Wrap(ctx, err, op)
}
// Get cert bundles from the other table
certBundles, err := r.findCertBundles(ctx, node.Id)
if err != nil {
return errors.Wrap(ctx, err, op)
}
node.CertificateBundles = certBundles
return nil
}
func (r *WorkerAuthRepositoryStorage) findCertBundles(ctx context.Context, workerKeyId string) ([]*types.CertificateBundle, error) {
const op = "servers.(WorkerAuthRepositoryStorage).findCertBundles"
if workerKeyId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "empty workerKeyId")
}
var bundles []*WorkerCertBundle
err := r.reader.SearchWhere(ctx, &bundles, "worker_key_identifier = ?", []interface{}{workerKeyId}, db.WithLimit(-1))
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
certBundle := []*types.CertificateBundle{}
for _, bundle := range bundles {
thisBundle := &types.CertificateBundle{}
if err := proto.Unmarshal(bundle.WorkerCertBundle.CertBundle, thisBundle); err != nil {
return nil, errors.New(ctx, errors.Decode, op, "error unmarshaling message", errors.WithWrap(err))
}
certBundle = append(certBundle, thisBundle)
}
return certBundle, nil
}
func (r *WorkerAuthRepositoryStorage) findWorkerAuth(ctx context.Context, node *types.NodeInformation) (*WorkerAuth, error) {
const op = "servers.(WorkerAuthRepositoryStorage).findWorkerAuth"
if node == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "node is nil")
}
worker := allocWorkerAuth()
worker.WorkerKeyIdentifier = node.Id
err := r.reader.LookupById(ctx, worker)
if err != nil {
if errors.Is(err, dbw.ErrRecordNotFound) {
return nil, nil
}
return nil, errors.Wrap(ctx, err, op)
}
return worker, nil
}
func (r *WorkerAuthRepositoryStorage) loadRootCertificates(ctx context.Context, cert *types.RootCertificates) error {
const op = "servers.(WorkerAuthRepositoryStorage).loadRootCertificates"
if cert == nil {
return errors.New(ctx, errors.InvalidParameter, op, "cert is nil")
}
// See if we have the CertificateAuthority first
certAuthority := &CertificateAuthority{
CertificateAuthority: &store.CertificateAuthority{
PrivateId: cert.Id,
},
}
err := r.reader.LookupById(ctx, certAuthority)
if err != nil {
return nodee.ErrNotFound
}
// Add version to state field of cert
versionMap := &rootCertificatesVersion{Version: certAuthority.Version}
stateOpt := structs.Map(versionMap)
state, err := structpb.NewStruct(stateOpt)
if err != nil {
return errors.Wrap(ctx, err, op)
}
cert.State = state
// Load current and next certificates
certsToFind := []string{string(NextState), string(CurrentState)}
for _, c := range certsToFind {
rootCertificate := allocRootCertificate()
rootCert := &types.RootCertificate{}
if err := r.reader.SearchWhere(ctx, &rootCertificate, "state = ?", []interface{}{c}, db.WithLimit(-1)); err != nil {
return errors.Wrap(ctx, err, op)
}
if rootCertificate.Certificate == nil {
return nodee.ErrNotFound
}
rootCert.CertificateDer = rootCertificate.Certificate
rootCert.NotAfter = rootCertificate.NotValidAfter.Timestamp
rootCert.NotBefore = rootCertificate.NotValidBefore.Timestamp
rootCert.PublicKeyPkix = rootCertificate.PublicKey
rootCert.PrivateKeyType = types.KEYTYPE_ED25519
// decrypt private key
databaseWrapper, err := r.kms.GetWrapper(ctx, scope.Global.String(), kms.KeyPurposeDatabase, kms.WithKeyId(rootCertificate.KeyId))
if err != nil {
return errors.Wrap(ctx, err, op)
}
rootCert.PrivateKeyPkcs8, err = decrypt(ctx, rootCertificate.PrivateKey, databaseWrapper)
if err != nil {
return errors.Wrap(ctx, err, op)
}
if c == string(NextState) {
cert.Next = rootCert
}
if c == string(CurrentState) {
cert.Current = rootCert
}
}
return nil
}
// Remove implements the Storage interface.
// Remove removes the given message. Only the ID field of the message is considered.
func (r *WorkerAuthRepositoryStorage) Remove(ctx context.Context, msg nodee.MessageWithId) error {
const op = "servers.(WorkerAuthRepositoryStorage).Remove"
if err := types.ValidateMessage(msg); err != nil {
return errors.New(ctx, errors.InvalidParameter, op, "given message cannot be removed as it has no ID")
}
// Determine type of message to remove
switch msg.(type) {
case *types.NodeInformation:
err := r.removeNodeInformation(ctx, msg.GetId())
if err != nil {
return errors.Wrap(ctx, err, op)
}
case *types.RootCertificates:
err := r.removeCertificateAuthority(ctx, msg.(*types.RootCertificates))
if err != nil {
return errors.Wrap(ctx, err, op)
}
default:
return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("message type %T not supported for Remove", msg))
}
return nil
}
func (r *WorkerAuthRepositoryStorage) removeNodeInformation(ctx context.Context, id string) error {
const op = "servers.(WorkerAuthRepositoryStorage).removeNodeInformation"
if id == "" {
return errors.New(ctx, errors.InvalidParameter, op, "empty id")
}
_, err := r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(reader db.Reader, w db.Writer) error {
var err error
_, err = w.Exec(ctx, deleteWorkerAuthQuery, []interface{}{sql.Named("worker_key_identifier", id)})
if err != nil {
return errors.Wrap(ctx, err, op)
}
_, err = w.Exec(ctx, deleteWorkerCertBundlesQuery, []interface{}{sql.Named("worker_key_identifier", id)})
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete workerAuth"))
}
return nil
},
)
if err != nil {
return errors.Wrap(ctx, err, op)
}
return nil
}
func (r *WorkerAuthRepositoryStorage) removeCertificateAuthority(ctx context.Context, cert *types.RootCertificates) error {
const op = "servers.(WorkerAuthRepositoryStorage).removeCertificateAuthority"
var result rootCertificatesVersion
err := mapstructure.Decode(cert.State.AsMap(), &result)
if err != nil {
return errors.Wrap(ctx, err, op)
}
version := uint32(1)
if result.Version != 0 {
version = result.Version
}
certAuthority := &CertificateAuthority{
CertificateAuthority: &store.CertificateAuthority{
PrivateId: ca_id,
Version: version + 1,
},
}
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(read db.Reader, w db.Writer) error {
// Update the certAuthority's version column first
rowsUpdated, err := w.Update(ctx, certAuthority, []string{"Version"}, nil, db.WithVersion(&version))
if err != nil {
return errors.Wrap(ctx, err, op)
}
if rowsUpdated != 1 {
return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("updated CertificateAuthority and %d rows updated", rowsUpdated))
}
if err := r.removeRootCertificateWithWriter(ctx, string(CurrentState), w); err != nil {
return errors.Wrap(ctx, err, op)
}
if err := r.removeRootCertificateWithWriter(ctx, string(NextState), w); err != nil {
return errors.Wrap(ctx, err, op)
}
return nil
},
)
if err != nil {
return errors.Wrap(ctx, err, op)
}
return nil
}
func (r *WorkerAuthRepositoryStorage) removeRootCertificateWithWriter(ctx context.Context, id string, writer db.Writer) error {
const op = "servers.(WorkerAuthRepositoryStorage).removeRootCertificateWithWriter"
if id == "" {
return errors.New(ctx, errors.InvalidParameter, op, "empty id")
}
if writer == nil {
return errors.New(ctx, errors.InvalidParameter, op, "missing writer")
}
rows, err := writer.Exec(ctx, deleteRootCertificateQuery, []interface{}{
sql.Named("state", id),
})
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete root certificate"))
}
if rows > 1 {
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 root certificate would have been deleted")
}
return nil
}
// List implements the Storage interface.
// List returns a list of IDs; the type of the message is used to disambiguate what to list.
func (r *WorkerAuthRepositoryStorage) List(ctx context.Context, msg proto.Message) ([]string, error) {
const op = "servers.(WorkerAuthRepositoryStorage).List"
var err error
var ids []string
// Determine type of message to store
switch msg.(type) {
case *types.NodeInformation:
ids, err = r.listNodeInformation(ctx)
case *types.RootCertificate:
ids, err = r.listRootCertificates(ctx)
case *types.RootCertificates:
ids, err = r.listCertificateAuthority(ctx)
default:
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("message type %T not supported for List", msg))
}
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
return ids, nil
}
// Returns a list of node auth IDs
func (r *WorkerAuthRepositoryStorage) listNodeInformation(ctx context.Context) ([]string, error) {
const op = "servers.(WorkerAuthRepositoryStorage).listNodeCertificates"
var where string
var nodeAuths []*WorkerAuth
err := r.reader.SearchWhere(ctx, &nodeAuths, where, []interface{}{}, db.WithLimit(-1))
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
var nodeIds []string
for _, auth := range nodeAuths {
nodeIds = append(nodeIds, auth.WorkerKeyIdentifier)
}
return nodeIds, nil
}
// Returns a list of root certificates
func (r *WorkerAuthRepositoryStorage) listRootCertificates(ctx context.Context) ([]string, error) {
const op = "servers.(WorkerAuthRepositoryStorage).listRootCertificates"
var where string
var rootCertificates []*RootCertificate
err := r.reader.SearchWhere(ctx, &rootCertificates, where, []interface{}{}, db.WithLimit(-1))
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
var certIds []string
for _, cert := range rootCertificates {
certIds = append(certIds, strconv.FormatUint(cert.SerialNumber, 10))
}
return certIds, nil
}
// Returns a list of certificate authorities
func (r *WorkerAuthRepositoryStorage) listCertificateAuthority(ctx context.Context) ([]string, error) {
const op = "servers.(WorkerAuthRepositoryStorage).listCertificateAuthority"
var where string
var rootCertificates []*CertificateAuthority
err := r.reader.SearchWhere(ctx, &rootCertificates, where, []interface{}{}, db.WithLimit(-1))
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
var certIds []string
for _, cert := range rootCertificates {
certIds = append(certIds, cert.PrivateId)
}
return certIds, nil
}
// encrypt value before writing it to the db
func encrypt(ctx context.Context, value []byte, wrapper wrapping.Wrapper) ([]byte, error) {
const op = "servers.(WorkerAuthRepositoryStorage).encrypt"
if value == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing value")
}
if wrapper == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing wrapper")
}
blobInfo, err := wrapper.Encrypt(ctx, value)
if err != nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "error encrypting recovery info", errors.WithWrap(err))
}
marshaledBlob, err := proto.Marshal(blobInfo)
if err != nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "error marshaling encrypted blob", errors.WithWrap(err))
}
return marshaledBlob, nil
}
func decrypt(ctx context.Context, value []byte, wrapper wrapping.Wrapper) ([]byte, error) {
const op = "servers.(WorkerAuthRepositoryStorage).decrypt"
if value == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing value")
}
if wrapper == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing wrapper")
}
blobInfo := new(wrapping.BlobInfo)
if err := proto.Unmarshal(value, blobInfo); err != nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "error decoding encrypted blob", errors.WithWrap(err))
}
marshaledInfo, err := wrapper.Decrypt(ctx, blobInfo)
if err != nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "error decrypting recovery info", errors.WithWrap(err))
}
return marshaledInfo, nil
}
@@ -0,0 +1,236 @@
package servers
import (
"context"
"crypto/rand"
"testing"
"github.com/fatih/structs"
"github.com/mitchellh/mapstructure"
"google.golang.org/protobuf/types/known/structpb"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/types/scope"
"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/assert"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/curve25519"
)
// Test RootCertificate storage
func TestStoreRootCertificates(t *testing.T) {
require, assert := require.New(t), assert.New(t)
ctx := context.Background()
wrapper := db.TestWrapper(t)
conn, _ := db.TestSetup(t, "postgres")
kmsCache := kms.TestKms(t, conn, wrapper)
// Ensures the global scope contains a valid root key
err := kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader))
require.NoError(err)
wrapper, err = kmsCache.GetWrapper(context.Background(), scope.Global.String(), kms.KeyPurposeDatabase)
require.NoError(err)
require.NotNil(wrapper)
rw := db.New(conn)
workerAuthRepo, err := NewRepositoryStorage(ctx, rw, rw, kmsCache)
require.NoError(err)
// Fail to find root certificates prior to rotation/ creation
noCertAuth := &types.RootCertificates{Id: ca_id}
err = workerAuthRepo.Load(ctx, noCertAuth)
require.Error(err)
rootIds, err := workerAuthRepo.List(ctx, (*types.RootCertificate)(nil))
require.NoError(err)
assert.Len(rootIds, 0)
// Rotate will generate and store next and current, as we have none
roots, err := rotation.RotateRootCertificates(ctx, workerAuthRepo)
require.NoError(err)
rootIds, err = workerAuthRepo.List(ctx, (*types.RootCertificate)(nil))
require.NoError(err)
assert.Len(rootIds, 2)
// Load stored roots
certAuthority := &types.RootCertificates{Id: ca_id}
err = workerAuthRepo.Load(ctx, certAuthority)
require.NoError(err)
require.NotNil(certAuthority.GetNext())
require.NotNil(certAuthority.GetCurrent())
// Read the next cert and validate the stored values are valid after encrypt and decrypt
assert.Equal(roots.Next.PrivateKeyPkcs8, certAuthority.Next.PrivateKeyPkcs8)
// Read the current cert and validate the stored values are valid after encrypt and decrypt
assert.Equal(roots.Current.PrivateKeyPkcs8, certAuthority.Current.PrivateKeyPkcs8)
// Fail to find root certificates under bogus id
bogusCertAuth := &types.RootCertificates{Id: "bogus"}
err = workerAuthRepo.Load(ctx, bogusCertAuth)
require.Error(err)
}
func TestStoreCertAuthorityVersioning(t *testing.T) {
require, assert := require.New(t), assert.New(t)
ctx := context.Background()
wrapper := db.TestWrapper(t)
conn, _ := db.TestSetup(t, "postgres")
kmsCache := kms.TestKms(t, conn, wrapper)
// Ensures the global scope contains a valid root key
err := kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader))
require.NoError(err)
wrapper, err = kmsCache.GetWrapper(context.Background(), scope.Global.String(), kms.KeyPurposeDatabase)
require.NoError(err)
require.NotNil(wrapper)
rw := db.New(conn)
workerAuthRepo, err := NewRepositoryStorage(ctx, rw, rw, kmsCache)
require.NoError(err)
// Store CA and check that initial version updates
roots, err := rotation.RotateRootCertificates(ctx, workerAuthRepo)
require.NoError(err)
cAuth2 := &types.RootCertificates{Id: ca_id}
err = workerAuthRepo.Load(ctx, cAuth2)
require.NoError(err)
var result2 rootCertificatesVersion
err = mapstructure.Decode(cAuth2.State.AsMap(), &result2)
require.NoError(err)
assert.Equal(uint32(2), result2.Version)
// Store CA with old version, expect error
badVersion := &rootCertificatesVersion{Version: uint32(1)}
badStateOpt := structs.Map(badVersion)
badState, err := structpb.NewStruct(badStateOpt)
require.NoError(err)
newRoots2 := &types.RootCertificates{
Id: ca_id,
Next: roots.Next,
Current: roots.Current,
}
newRoots2.State = badState
err = workerAuthRepo.Store(ctx, newRoots2)
require.Error(err)
// Remove CA, expect updated version
cAuthRemove := &types.RootCertificates{Id: ca_id}
removeVersion := &rootCertificatesVersion{Version: uint32(2)}
removeOpt := structs.Map(removeVersion)
removeState, err := structpb.NewStruct(removeOpt)
require.NoError(err)
cAuthRemove.State = removeState
err = workerAuthRepo.Remove(ctx, cAuthRemove)
require.NoError(err)
}
// Test WorkerAuth storage
func TestStoreWorkerAuth(t *testing.T) {
require, assert := require.New(t), assert.New(t)
ctx := context.Background()
wrapper := db.TestWrapper(t)
conn, _ := db.TestSetup(t, "postgres")
kmsCache := kms.TestKms(t, conn, wrapper)
// Ensures the global scope contains a valid root key
err := kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader))
require.NoError(err)
wrapper, err = kmsCache.GetWrapper(context.Background(), scope.Global.String(), kms.KeyPurposeDatabase)
require.NoError(err)
require.NotNil(wrapper)
worker := TestWorker(t, conn, wrapper)
rw := db.New(conn)
rootStorage, err := NewRepositoryStorage(ctx, rw, rw, kmsCache)
require.NoError(err)
_, err = rotation.RotateRootCertificates(ctx, rootStorage)
require.NoError(err)
// Create struct to pass in with workerId that will be passed along to storage
state, err := AttachWorkerIdToState(ctx, worker.PublicId)
require.NoError(err)
// This happens on the worker
fileStorage, err := file.NewFileStorage(ctx)
require.NoError(err)
nodeCreds, err := types.NewNodeCredentials(ctx, fileStorage)
require.NoError(err)
// Create request using worker id
fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(ctx)
require.NoError(err)
keyId, err := nodeenrollment.KeyIdFromPkix(nodeCreds.CertificatePublicKeyPkix)
require.NoError(err)
nodePubKey, err := curve25519.X25519(nodeCreds.EncryptionPrivateKeyBytes, curve25519.Basepoint)
require.NoError(err)
// Add in node information to storage so we have a key to use
nodeInfo := &types.NodeInformation{
Id: keyId,
CertificatePublicKeyPkix: nodeCreds.CertificatePublicKeyPkix,
CertificatePublicKeyType: nodeCreds.CertificatePrivateKeyType,
EncryptionPublicKeyBytes: nodePubKey,
EncryptionPublicKeyType: nodeCreds.EncryptionPrivateKeyType,
RegistrationNonce: nodeCreds.RegistrationNonce,
State: state,
}
// Create storage for authentication and pass it the worker
storage, err := NewRepositoryStorage(ctx, rw, rw, kmsCache)
require.NoError(err)
// The AuthorizeNode request will result in a WorkerAuth record being stored
require.NoError(registration.AuthorizeNode(ctx, storage, fetchReq, nodeenrollment.WithState(state)))
// We should now look for a node information value in storage and validate that it's populated
nodeInfos, err := storage.List(ctx, (*types.NodeInformation)(nil))
require.NoError(err)
require.NotEmpty(nodeInfos)
assert.Len(nodeInfos, 1)
// Validate the stored fields match those from the worker
nodeLookup := &types.NodeInformation{
Id: keyId,
}
err = storage.Load(ctx, nodeLookup)
require.NoError(err)
assert.NotEmpty(nodeLookup)
assert.Equal(nodeInfo.EncryptionPublicKeyBytes, nodeLookup.EncryptionPublicKeyBytes)
assert.Equal(nodeInfo.RegistrationNonce, nodeLookup.RegistrationNonce)
assert.Equal(nodeInfo.CertificatePublicKeyPkix, nodeLookup.CertificatePublicKeyPkix)
// Remove node
err = storage.Remove(ctx, nodeLookup)
require.NoError(err)
err = storage.Load(ctx, nodeLookup)
require.Error(err)
}
func TestUnsupportedMessages(t *testing.T) {
require := require.New(t)
ctx := context.Background()
wrapper := db.TestWrapper(t)
conn, _ := db.TestSetup(t, "postgres")
kmsCache := kms.TestKms(t, conn, wrapper)
rw := db.New(conn)
storage, err := NewRepositoryStorage(ctx, rw, rw, kmsCache)
require.NoError(err)
err = storage.Store(ctx, &types.NodeCredentials{})
require.Error(err)
err = storage.Load(ctx, &types.NodeCredentials{Id: "bogus"})
require.Error(err)
_, err = storage.List(ctx, (*types.NodeCredentials)(nil))
require.Error(err)
err = storage.Remove(ctx, &types.NodeCredentials{Id: "bogus"})
require.Error(err)
}
+21 -21
View File
@@ -140,11 +140,11 @@ type WorkerCertBundle struct {
unknownFields protoimpl.UnknownFields
// The public key of the issuing root certificate
// @inject_tag: `gorm:"not_null"`
CertificatePublicKey []byte `protobuf:"bytes,10,opt,name=certificate_public_key,json=certificatePublicKey,proto3" json:"certificate_public_key,omitempty" gorm:"not_null"`
// @inject_tag: `gorm:"primary_key"`
RootCertificatePublicKey []byte `protobuf:"bytes,10,opt,name=root_certificate_public_key,json=rootCertificatePublicKey,proto3" json:"root_certificate_public_key,omitempty" gorm:"primary_key"`
// The WorkerAuth worker_key_identifier this cert bundle record is for
// @inject_tag: `gorm:"not_null"`
WorkerKeyIdentifier string `protobuf:"bytes,20,opt,name=worker_key_identifier,json=workerKeyIdentifier,proto3" json:"worker_key_identifier,omitempty" gorm:"not_null"`
// @inject_tag: `gorm:"primary_key"`
WorkerKeyIdentifier string `protobuf:"bytes,20,opt,name=worker_key_identifier,json=workerKeyIdentifier,proto3" json:"worker_key_identifier,omitempty" gorm:"primary_key"`
// CertBundle is the marshaled protobuf certificate bundle for a WorkerAuth
// @inject_tag: `gorm:"not_null"`
CertBundle []byte `protobuf:"bytes,30,opt,name=cert_bundle,json=certBundle,proto3" json:"cert_bundle,omitempty" gorm:"not_null"`
@@ -182,9 +182,9 @@ func (*WorkerCertBundle) Descriptor() ([]byte, []int) {
return file_controller_storage_servers_store_v1_worker_auth_proto_rawDescGZIP(), []int{1}
}
func (x *WorkerCertBundle) GetCertificatePublicKey() []byte {
func (x *WorkerCertBundle) GetRootCertificatePublicKey() []byte {
if x != nil {
return x.CertificatePublicKey
return x.RootCertificatePublicKey
}
return nil
}
@@ -231,22 +231,22 @@ var file_controller_storage_servers_store_v1_worker_auth_proto_rawDesc = []byte{
0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x69, 0x76, 0x4b, 0x65,
0x79, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x3c, 0x20, 0x01, 0x28,
0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63,
0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x9d,
0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0xa6,
0x01, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x42, 0x75, 0x6e,
0x64, 0x6c, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
0x74, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0a, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x14, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72,
0x6b, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
0x65, 0x72, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72,
0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1f, 0x0a,
0x0b, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x1e, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x0a, 0x63, 0x65, 0x72, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x42, 0x3c,
0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73,
0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73,
0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
0x64, 0x6c, 0x65, 0x12, 0x3d, 0x0a, 0x1b, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74,
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b,
0x65, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x18, 0x72, 0x6f, 0x6f, 0x74, 0x43, 0x65,
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b,
0x65, 0x79, 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79,
0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x14, 0x20, 0x01, 0x28,
0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x62,
0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x65, 0x72,
0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f,
0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b,
0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
+1 -1
View File
@@ -26,7 +26,7 @@ func populateBytes(length int) []byte {
return fieldBytes
}
func TestKkmsKey(ctx context.Context, t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) string {
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)
+20
View File
@@ -4,12 +4,32 @@ import (
"context"
"strings"
"github.com/fatih/structs"
"github.com/hashicorp/boundary/internal/db/timestamp"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/servers/store"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
)
type workerAuthWorkerId struct {
WorkerId string
}
// AttachWorkerIdToState accepts a workerId and creates a struct for use with the Nodeenrollment lib
// This is intended for use in worker authorization; AuthorizeNode in the lib accepts the option WithState
// so that the workerId is passed through to storage and associated with a WorkerAuth record
func AttachWorkerIdToState(ctx context.Context, workerId string) (*structpb.Struct, error) {
const op = "servers.(Worker).AttachWorkerIdToState"
if workerId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing workerId")
}
workerMap := &workerAuthWorkerId{WorkerId: workerId}
stateOpt := structs.Map(workerMap)
return structpb.NewStruct(stateOpt)
}
// A Worker is a server that provides an address which can be used to proxy
// session connections. It can be tagged with custom tags and is used when
// authorizing and establishing a session. It is owned by a scope.
+6 -6
View File
@@ -56,7 +56,7 @@ func newWorkerAuth(ctx context.Context, workerKeyIdentifier, workerId string, op
return l, nil
}
func AllocWorkerAuth() *WorkerAuth {
func allocWorkerAuth() *WorkerAuth {
return &WorkerAuth{
WorkerAuth: &store.WorkerAuth{},
}
@@ -132,15 +132,15 @@ func newWorkerCertBundle(ctx context.Context, certificatePublicKey []byte, worke
l := &WorkerCertBundle{
WorkerCertBundle: &store.WorkerCertBundle{
CertificatePublicKey: certificatePublicKey,
WorkerKeyIdentifier: workerKeyIdentifier,
CertBundle: certBundle,
RootCertificatePublicKey: certificatePublicKey,
WorkerKeyIdentifier: workerKeyIdentifier,
CertBundle: certBundle,
},
}
return l, nil
}
func AllocWorkerCertBundle() *WorkerCertBundle {
func allocWorkerCertBundle() *WorkerCertBundle {
return &WorkerCertBundle{
WorkerCertBundle: &store.WorkerCertBundle{},
}
@@ -156,7 +156,7 @@ func (w *WorkerCertBundle) clone() *WorkerCertBundle {
// Validate is called before storing a WorkerCertBundle in the db
func (w *WorkerCertBundle) ValidateNewWorkerCertBundle(ctx context.Context) error {
const op = "servers.(WorkerAuth).validateNewWorkerCertBundle"
if w.CertificatePublicKey == nil {
if w.RootCertificatePublicKey == nil {
return errors.New(ctx, errors.InvalidParameter, op, "missing CertificatePublicKey")
}
if w.WorkerKeyIdentifier == "" {
+4 -4
View File
@@ -427,7 +427,7 @@ func TestWorkerCertBundle(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
testKey := TestKkmsKey(ctx, t, conn, wrapper)
testKey := TestKmsKey(ctx, t, conn, wrapper)
worker := TestWorker(t, conn, wrapper)
workerAuth := TestWorkerAuth(ctx, t, conn, worker, testKey)
@@ -454,9 +454,9 @@ func TestWorkerCertBundle(t *testing.T) {
certificateBundle: certBundle,
},
expected: &store.WorkerCertBundle{
CertificatePublicKey: rootCA.PublicKey,
WorkerKeyIdentifier: workerAuth.WorkerKeyIdentifier,
CertBundle: certBundle,
RootCertificatePublicKey: rootCA.PublicKey,
WorkerKeyIdentifier: workerAuth.WorkerKeyIdentifier,
CertBundle: certBundle,
},
wantErr: false,
},