Files
threshold/internal/daemon/cluster/connstate.go
T
Jeff Mitchell 163ce184b8 KMS-PKI Workers (#3101)
This adds a new mechanism for worker authentication that uses the
third-party arbitration of the original KMS flow by leveraging
recently-added capabilities in nodeenrollment to support a registration
wrapper. This allows for workers to be registered to the system with
just a name and a matching wrapper, while at the same time allowing
these workers to be used for private Vault access and multi-hop as they
contain per-worker encryption keys and support rotation.

Nodes using this mode will generate and rotate credentials in-memory
(using another recently added nodeenrollment capability) so will have a
new set of keys on every startup. This has the side effect of ensuring
that there is never a conflict or re-use of these credentials.

Note that the normal test worker flow was using the now-deprecated KMS
method; there is still a test that uses this (explicitly) but all other
tests using test workers that did not specify a specific PKI flow now
actually use this mechanism. This means there is fairly decent test
coverage in a general sense, plus the specific tests updated/added for
this feature.

This also adds a new KMS type that can be used to have
distinct KMSes for upstream authentication vs. accepting from
downstream.
2023-03-30 16:20:28 -04:00

73 lines
2.6 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package cluster
import (
"context"
stderror "errors"
"net"
"github.com/fatih/structs"
"github.com/hashicorp/boundary/internal/errors"
"github.com/mitchellh/mapstructure"
"google.golang.org/protobuf/types/known/structpb"
)
var errStateNotFound = stderror.New("no state attached to the connection")
// WorkerConnectionInfo contains information about a worker that gets attached
// to outgoing connections to an upstream worker or the controller through the
// state of a *protocol.Conn.
type WorkerConnectionInfo struct {
// The address that the dialing worker is dialing
UpstreamAddress string `mapstructure:"upstream_address,omitempty"`
// The worker id of the worker that is dialing
WorkerId string `mapstructure:"worker_id,omitempty"`
// The version of the worker that is dialing
BoundaryVersion string `mapstructure:"boundary_version,omitempty"`
// The name, used for wrapping registration flow
Name string `mapstructure:"name,omitempty"`
// The description, used for wrapping registration flow
Description string `mapstructure:"description,omitempty"`
// The public address, used for wrapping registration flow
PublicAddr string `mapstructure:"public_addr,omitempty"`
// The operational state, used for wrapping registration flow
OperationalState string `mapstructure:"operational_state,omitempty"`
}
// AsConnectionStateStruct builds a structPb in the format that is expected for
// workers to attach their state to a *protocol.Conn through the withState option.
func (w *WorkerConnectionInfo) AsConnectionStateStruct() (*structpb.Struct, error) {
if w == nil {
return nil, nil
}
s := structs.New(w)
s.TagName = "mapstructure"
return structpb.NewStruct(s.Map())
}
type stateProvider interface {
ClientState() *structpb.Struct
}
// GetWorkerInfoFromStateMap returns the WorkerConnectionInfo for the provided
// connection if the connection is a stateProvider. If it is not a stateProvider
// or if the provided state is nil, an error is returned.
func GetWorkerInfoFromStateMap(ctx context.Context, c net.Conn) (*WorkerConnectionInfo, error) {
const op = "cluster.GetWorkerInfoFromStateMap"
var downstreamInfo WorkerConnectionInfo
pc, ok := c.(stateProvider)
if !ok {
return nil, errors.Wrap(ctx, errStateNotFound, op, errors.WithMsg("conn is not a state provider"))
}
st := pc.ClientState()
if st == nil {
return nil, errors.Wrap(ctx, errStateNotFound, op, errors.WithMsg("no state attached to connection"))
}
if err := mapstructure.Decode(st.AsMap(), &downstreamInfo); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
return &downstreamInfo, nil
}