Files
threshold/internal/daemon/controller/worker_tls_config.go
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

145 lines
4.9 KiB
Go
Raw Normal View History

2021-10-27 20:32:07 -04:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
2023-02-02 17:15:17 -08:00
package controller
2020-03-22 20:17:23 -04:00
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
2021-10-27 20:32:07 -04:00
"fmt"
2020-08-12 00:40:39 -04:00
"net"
2020-03-22 20:17:23 -04:00
"strings"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/event"
2022-06-20 18:20:17 -07:00
"github.com/hashicorp/boundary/internal/server"
2023-03-07 16:29:34 -05:00
"github.com/hashicorp/boundary/version"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
2020-03-22 20:17:23 -04:00
"google.golang.org/protobuf/proto"
)
2020-08-12 00:40:39 -04:00
type workerAuthEntry struct {
*base.WorkerAuthInfo
conn net.Conn
clientNextProtos []string
2020-08-12 00:40:39 -04:00
}
2021-10-27 20:32:07 -04:00
// validateWorkerTls is called by the Go TLS stack with client info. It calls
// v1WorkerAuthConfig to validate the encryption and ensure the nonce has not
// been replayed, then stores connection information into the auth cache, which
// is used by the intercepting listener to log information.
2020-09-03 13:24:34 -04:00
func (c Controller) validateWorkerTls(hello *tls.ClientHelloInfo) (*tls.Config, error) {
2022-04-27 16:25:05 -04:00
const op = "controller.(Controller).validateWorkerTls"
2020-03-22 20:17:23 -04:00
for _, p := range hello.SupportedProtos {
switch {
case strings.HasPrefix(p, "v1workerauth-"):
2020-08-12 00:40:39 -04:00
tlsConf, workerInfo, err := c.v1WorkerAuthConfig(hello.SupportedProtos)
if err == nil {
2021-10-27 20:32:07 -04:00
// Store info that can be retrieved in the intercepting
// listener.
//
// NOTE: This is not a load-or-store because we need to ensure
// replays can't happen across controllers, hence storing in the
// database in v1WorkerAuthConfig. Although this could serve as
// a secondary check, a load-or-store here keeps the test from
// ensuring that it is rejected at the database level via the
// nonce unique constraint, which is by far the more important
// thing to validate (although there are DB-level tests for that
// too).
authEntry := &workerAuthEntry{
2020-08-12 00:40:39 -04:00
WorkerAuthInfo: workerInfo,
}
if len(hello.SupportedProtos) > 0 {
authEntry.clientNextProtos = make([]string, len(hello.SupportedProtos))
copy(authEntry.clientNextProtos, hello.SupportedProtos)
}
c.workerAuthCache.Store(workerInfo.ConnectionNonce, authEntry)
2020-08-12 00:40:39 -04:00
}
return tlsConf, err
2020-03-22 20:17:23 -04:00
}
}
2022-04-27 16:25:05 -04:00
return nil, errors.New("no supported protos found in hello")
}
2020-03-22 20:17:23 -04:00
2021-10-27 20:32:07 -04:00
// v1WorkerAuthConfig:
//
// * Reads the information from ALPN, combining across protos
// * Combines them to form the encrypted auth information
// * Validates it by decrypting against the configured KMS
// * Ensures that the nonce is unique to prevent replay attacks
// * Returns the shared TLS configuration that is used to establish the connection
2020-08-12 00:40:39 -04:00
func (c Controller) v1WorkerAuthConfig(protos []string) (*tls.Config, *base.WorkerAuthInfo, error) {
2023-03-07 16:29:34 -05:00
const op = "controller.(Controller).v1WorkerAuthConfig"
2020-03-22 20:17:23 -04:00
var firstMatchProto string
var encString string
for _, p := range protos {
if strings.HasPrefix(p, "v1workerauth-") {
// Strip that and the number
encString += strings.TrimPrefix(p, "v1workerauth-")[3:]
if firstMatchProto == "" {
firstMatchProto = p
}
}
}
if firstMatchProto == "" {
2020-08-12 00:40:39 -04:00
return nil, nil, errors.New("no matching proto found")
2020-03-22 20:17:23 -04:00
}
marshaledEncInfo, err := base64.RawStdEncoding.DecodeString(encString)
if err != nil {
2020-08-12 00:40:39 -04:00
return nil, nil, err
2020-03-22 20:17:23 -04:00
}
encInfo := new(wrapping.BlobInfo)
2020-03-22 20:17:23 -04:00
if err := proto.Unmarshal(marshaledEncInfo, encInfo); err != nil {
2020-08-12 00:40:39 -04:00
return nil, nil, err
2020-03-22 20:17:23 -04:00
}
if isNil(c.conf.WorkerAuthKms) {
return nil, nil, errors.New("worker auth kms not set")
}
marshaledInfo, err := c.conf.WorkerAuthKms.Decrypt(context.Background(), encInfo)
2020-03-22 20:17:23 -04:00
if err != nil {
2020-08-12 00:40:39 -04:00
return nil, nil, err
2020-03-22 20:17:23 -04:00
}
2020-08-12 00:40:39 -04:00
info := new(base.WorkerAuthInfo)
2020-03-22 20:17:23 -04:00
if err := json.Unmarshal(marshaledInfo, info); err != nil {
2020-08-12 00:40:39 -04:00
return nil, nil, err
2020-03-22 20:17:23 -04:00
}
2023-03-07 16:29:34 -05:00
if version.Get().VersionMetadata != "" {
incomingVer := version.FromVersionString(info.BoundaryVersion)
if (incomingVer == nil || incomingVer.VersionMetadata == "") && version.SupportsFeature(version.Binary, version.RequireVersionInWorkerInfo) {
err := errors.New("build contains version with metadata, incoming worker version is nil or has mismatched metadata")
event.WriteError(c.baseContext, op, err)
return nil, nil, err
}
}
2020-03-22 20:17:23 -04:00
2021-10-27 20:32:07 -04:00
// Check for replays
serversRepo, err := c.ServersRepoFn()
if err != nil {
2022-06-20 18:20:17 -07:00
return nil, nil, fmt.Errorf("unable to fetch server repo: %w", err)
2021-10-27 20:32:07 -04:00
}
2022-06-20 18:20:17 -07:00
if err := serversRepo.AddNonce(c.baseContext, info.ConnectionNonce, server.NoncePurposeWorkerAuth); err != nil {
2021-10-27 20:32:07 -04:00
return nil, nil, fmt.Errorf("unable to add connection nonce to database: %w", err)
}
2020-03-22 20:17:23 -04:00
rootCAs := x509.NewCertPool()
if ok := rootCAs.AppendCertsFromPEM(info.CertPEM); !ok {
2020-08-12 00:40:39 -04:00
return nil, info, errors.New("unable to add ca cert to cert pool")
2020-03-22 20:17:23 -04:00
}
tlsCert, err := tls.X509KeyPair(info.CertPEM, info.KeyPEM)
if err != nil {
2020-08-12 00:40:39 -04:00
return nil, info, err
2020-03-22 20:17:23 -04:00
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{tlsCert},
ClientCAs: rootCAs,
ClientAuth: tls.RequireAndVerifyClientCert,
NextProtos: []string{firstMatchProto},
MinVersion: tls.VersionTLS13,
}
2020-08-12 00:40:39 -04:00
return tlsConfig, info, nil
2020-03-22 20:17:23 -04:00
}