diff --git a/cmd/spire-agent/cli/run/run.go b/cmd/spire-agent/cli/run/run.go index 7c66301f65a..1eca0c662fd 100644 --- a/cmd/spire-agent/cli/run/run.go +++ b/cmd/spire-agent/cli/run/run.go @@ -38,6 +38,7 @@ import ( "github.com/spiffe/spire/pkg/common/log" "github.com/spiffe/spire/pkg/common/pemutil" "github.com/spiffe/spire/pkg/common/telemetry" + "github.com/spiffe/spire/pkg/common/tlspolicy" ) const ( @@ -118,6 +119,7 @@ type experimentalConfig struct { NamedPipeName string `hcl:"named_pipe_name"` AdminNamedPipeName string `hcl:"admin_named_pipe_name"` UseSyncAuthorizedEntries bool `hcl:"use_sync_authorized_entries"` + PQKEMMode string `hcl:"pq_kem_mode"` Flags fflag.RawConfig `hcl:"feature_flags"` @@ -595,6 +597,11 @@ func NewAgentConfig(c *Config, logOptions []log.Option, allowUnknownConfig bool) ac.AvailabilityTarget = t } + ac.TLSPolicy.PQKEMMode, err = tlspolicy.ParsePQKEMMode(log.NewHCLogAdapter(logger, "tlspolicy"), c.Agent.Experimental.PQKEMMode) + if err != nil { + return nil, fmt.Errorf("pq_kem_mode config option %q is invalid: %w", c.Agent.Experimental.PQKEMMode, err) + } + if cmp.Diff(experimentalConfig{}, c.Agent.Experimental) != "" { logger.Warn("Experimental features have been enabled. Please see doc/upgrading.md for upgrade and compatibility considerations for experimental features.") } diff --git a/cmd/spire-server/cli/run/run.go b/cmd/spire-server/cli/run/run.go index 6734190c598..45abce8de70 100644 --- a/cmd/spire-server/cli/run/run.go +++ b/cmd/spire-server/cli/run/run.go @@ -35,6 +35,7 @@ import ( "github.com/spiffe/spire/pkg/common/health" "github.com/spiffe/spire/pkg/common/log" "github.com/spiffe/spire/pkg/common/telemetry" + "github.com/spiffe/spire/pkg/common/tlspolicy" "github.com/spiffe/spire/pkg/server" "github.com/spiffe/spire/pkg/server/authpolicy" bundleClient "github.com/spiffe/spire/pkg/server/bundle/client" @@ -108,6 +109,7 @@ type experimentalConfig struct { EventsBasedCache bool `hcl:"events_based_cache"` PruneEventsOlderThan string `hcl:"prune_events_older_than"` SQLTransactionTimeout string `hcl:"sql_transaction_timeout"` + PQKEMMode string `hcl:"pq_kem_mode"` Flags fflag.RawConfig `hcl:"feature_flags"` @@ -508,6 +510,11 @@ func NewServerConfig(c *Config, logOptions []log.Option, allowUnknownConfig bool sc.ProfilingFreq = c.Server.ProfilingFreq sc.ProfilingNames = c.Server.ProfilingNames + sc.TLSPolicy.PQKEMMode, err = tlspolicy.ParsePQKEMMode(log.NewHCLogAdapter(logger, "tlspolicy"), c.Server.Experimental.PQKEMMode) + if err != nil { + return nil, fmt.Errorf("invalid pq_kem_mode: %q: %w", c.Server.Experimental.PQKEMMode, err) + } + for _, adminID := range c.Server.AdminIDs { id, err := spiffeid.FromString(adminID) if err != nil { diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go index 433bad44173..615fa630082 100644 --- a/pkg/agent/agent.go +++ b/pkg/agent/agent.go @@ -258,6 +258,7 @@ func (a *Agent) attest(ctx context.Context, sto storage.Storage, cat catalog.Cat Log: a.c.Log.WithField(telemetry.SubsystemName, telemetry.Attestor), ServerAddress: a.c.ServerAddress, NodeAttestor: na, + TLSPolicy: a.c.TLSPolicy, } return node_attestor.New(&config).Attest(ctx) } @@ -282,6 +283,7 @@ func (a *Agent) newManager(ctx context.Context, sto storage.Storage, cat catalog SVIDStoreCache: cache, NodeAttestor: na, RotationStrategy: rotationutil.NewRotationStrategy(a.c.AvailabilityTarget), + TLSPolicy: a.c.TLSPolicy, } mgr := manager.New(config) diff --git a/pkg/agent/attestor/node/node.go b/pkg/agent/attestor/node/node.go index aa0cf63355c..a6129231b61 100644 --- a/pkg/agent/attestor/node/node.go +++ b/pkg/agent/attestor/node/node.go @@ -25,6 +25,7 @@ import ( "github.com/spiffe/spire/pkg/common/telemetry" telemetry_agent "github.com/spiffe/spire/pkg/common/telemetry/agent" telemetry_common "github.com/spiffe/spire/pkg/common/telemetry/common" + "github.com/spiffe/spire/pkg/common/tlspolicy" "github.com/spiffe/spire/pkg/common/util" "github.com/spiffe/spire/pkg/common/x509util" "github.com/zeebo/errs" @@ -58,6 +59,7 @@ type Config struct { Log logrus.FieldLogger ServerAddress string NodeAttestor nodeattestor.NodeAttestor + TLSPolicy tlspolicy.Policy } type attestor struct { @@ -256,6 +258,7 @@ func (a *attestor) serverConn(ctx context.Context, bundle *spiffebundle.Bundle) Address: a.c.ServerAddress, TrustDomain: a.c.TrustDomain, GetBundle: bundle.X509Authorities, + TLSPolicy: a.c.TLSPolicy, }) } diff --git a/pkg/agent/client/client.go b/pkg/agent/client/client.go index 1ec3522f58b..e4699b06637 100644 --- a/pkg/agent/client/client.go +++ b/pkg/agent/client/client.go @@ -20,6 +20,7 @@ import ( "github.com/spiffe/spire-api-sdk/proto/spire/api/types" "github.com/spiffe/spire/pkg/common/bundleutil" "github.com/spiffe/spire/pkg/common/telemetry" + "github.com/spiffe/spire/pkg/common/tlspolicy" "github.com/spiffe/spire/proto/spire/common" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -92,6 +93,9 @@ type Config struct { // RotMtx is used to prevent the creation of new connections during SVID rotations RotMtx *sync.RWMutex + + // TLSPolicy determines the post-quantum-safe policy to apply to all TLS connections. + TLSPolicy tlspolicy.Policy } type client struct { @@ -371,6 +375,7 @@ func (c *client) dial(ctx context.Context) (*grpc.ClientConn, error) { } return agentCert }, + TLSPolicy: c.c.TLSPolicy, dialContext: c.dialContext, }) } diff --git a/pkg/agent/client/dial.go b/pkg/agent/client/dial.go index 031572b833f..2b6689af28e 100644 --- a/pkg/agent/client/dial.go +++ b/pkg/agent/client/dial.go @@ -14,6 +14,7 @@ import ( "github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig" "github.com/spiffe/go-spiffe/v2/svid/x509svid" "github.com/spiffe/spire/pkg/common/idutil" + "github.com/spiffe/spire/pkg/common/tlspolicy" "github.com/spiffe/spire/pkg/common/x509util" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -38,6 +39,9 @@ type DialServerConfig struct { // certificate to present to the server during the TLS handshake. GetAgentCertificate func() *tls.Certificate + // TLSPolicy determines the post-quantum-safe policy to apply to all TLS connections. + TLSPolicy tlspolicy.Policy + // dialContext is an optional constructor for the grpc client connection. dialContext func(ctx context.Context, target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) } @@ -57,6 +61,11 @@ func DialServer(ctx context.Context, config DialServerConfig) (*grpc.ClientConn, tlsConfig = tlsconfig.MTLSClientConfig(newX509SVIDSource(config.GetAgentCertificate), bundleSource, authorizer) } + err = tlspolicy.ApplyPolicy(tlsConfig, config.TLSPolicy) + if err != nil { + return nil, err + } + ctx, cancel := context.WithTimeout(ctx, defaultDialTimeout) defer cancel() diff --git a/pkg/agent/config.go b/pkg/agent/config.go index 1d964be1d91..0766c3598cf 100644 --- a/pkg/agent/config.go +++ b/pkg/agent/config.go @@ -12,6 +12,7 @@ import ( "github.com/spiffe/spire/pkg/common/catalog" "github.com/spiffe/spire/pkg/common/health" "github.com/spiffe/spire/pkg/common/telemetry" + "github.com/spiffe/spire/pkg/common/tlspolicy" ) type Config struct { @@ -103,6 +104,9 @@ type Config struct { // AvailabilityTarget controls how frequently rotate SVIDs AvailabilityTarget time.Duration + + // TLSPolicy determines the post-quantum-safe TLS policy to apply to all TLS connections. + TLSPolicy tlspolicy.Policy } func New(c *Config) *Agent { diff --git a/pkg/agent/manager/config.go b/pkg/agent/manager/config.go index f5d71bbe12c..a0c77aa18f5 100644 --- a/pkg/agent/manager/config.go +++ b/pkg/agent/manager/config.go @@ -18,6 +18,7 @@ import ( "github.com/spiffe/spire/pkg/agent/workloadkey" "github.com/spiffe/spire/pkg/common/rotationutil" "github.com/spiffe/spire/pkg/common/telemetry" + "github.com/spiffe/spire/pkg/common/tlspolicy" ) // Config holds a cache manager configuration @@ -42,6 +43,7 @@ type Config struct { DisableLRUCache bool NodeAttestor nodeattestor.NodeAttestor RotationStrategy *rotationutil.RotationStrategy + TLSPolicy tlspolicy.Policy // Clk is the clock the manager will use to get time Clk clock.Clock @@ -89,6 +91,7 @@ func newManager(c *Config) *manager { NodeAttestor: c.NodeAttestor, Reattestable: c.Reattestable, RotationStrategy: c.RotationStrategy, + TLSPolicy: c.TLSPolicy, } svidRotator, client := svid.NewRotator(rotCfg) diff --git a/pkg/agent/svid/rotator.go b/pkg/agent/svid/rotator.go index fd532c44c89..9218c7edf92 100644 --- a/pkg/agent/svid/rotator.go +++ b/pkg/agent/svid/rotator.go @@ -313,6 +313,7 @@ func (r *rotator) serverConn(ctx context.Context, bundle *spiffebundle.Bundle) ( Address: r.c.ServerAddr, TrustDomain: r.c.TrustDomain, GetBundle: bundle.X509Authorities, + TLSPolicy: r.c.TLSPolicy, }) } diff --git a/pkg/agent/svid/rotator_config.go b/pkg/agent/svid/rotator_config.go index 6eb4b0538d0..3dce096bdab 100644 --- a/pkg/agent/svid/rotator_config.go +++ b/pkg/agent/svid/rotator_config.go @@ -17,6 +17,7 @@ import ( "github.com/spiffe/spire/pkg/agent/plugin/nodeattestor" "github.com/spiffe/spire/pkg/common/rotationutil" "github.com/spiffe/spire/pkg/common/telemetry" + "github.com/spiffe/spire/pkg/common/tlspolicy" ) const DefaultRotatorInterval = 5 * time.Second @@ -43,6 +44,9 @@ type RotatorConfig struct { Clk clock.Clock RotationStrategy *rotationutil.RotationStrategy + + // TLSPolicy determines the post-quantum-safe policy for TLS connections. + TLSPolicy tlspolicy.Policy } func NewRotator(c *RotatorConfig) (Rotator, client.Client) { @@ -85,6 +89,7 @@ func newRotator(c *RotatorConfig) (*rotator, client.Client) { } return s.SVID, s.Key, rootCAs }, + TLSPolicy: c.TLSPolicy, } client := client.New(cfg) diff --git a/pkg/common/tlspolicy/parse.go b/pkg/common/tlspolicy/parse.go new file mode 100644 index 00000000000..c03b1178590 --- /dev/null +++ b/pkg/common/tlspolicy/parse.go @@ -0,0 +1,145 @@ +// Package tlspolicy provides for configuration and enforcement of policies +// relating to TLS. +package tlspolicy + +import ( + "crypto/tls" + "errors" + "fmt" + + "github.com/hashicorp/go-hclog" +) + +// SupportsPQKEM is a constant indicating whether the version of Go used to +// build, and the build configuration, supports a post-quantum safe TLS key +// exchange method. +const SupportsPQKEM = supportsPQKEM + +// Post-quantum TLS KEM mode. Determines whether a post-quantum safe KEM should +// be used when establishing a TLS connection. +type PQKEMMode int + +const ( + // Do not require use of a post-quantum KEM when establishing a TLS + // connection. Whether a post-quantum KEM is attempted depends on + // environmental configuration (e.g. GODEBUG setting tlskyber) and the target + // Go version at build time. + PQKEMModeDefault PQKEMMode = iota + + // Attempt use of a post-quantum KEM as the most preferred key exchange + // method when establishing a TLS connection. + // Support for this requires Go 1.23 or later. + // Configuring this will cause connections to fail if support is not available. + PQKEMModeAttempt + + // Require use of a post-quantum KEM when establishing a TLS connection. + // Attempts to initiate a connection with a key exchange method which is not + // post-quantum safe will fail. Support for this requires Go 1.23 or later. + // Configuring this will cause connections to fail if support is not available. + PQKEMModeRequire +) + +// ParsePQKEMMode parses a string into a PQKEMMode value or returns +// an error. +func ParsePQKEMMode(logger hclog.Logger, value string) (mode PQKEMMode, err error) { + if value != "" { + logger.Warn("pq_kem_mode is experimental and may be changed or removed in a future release") + } + + switch value { + case "": + if SupportsPQKEM { + logger.Debug("pq_kem_mode supported in this build; post-quantum-safe TLS key exchange may or may not be used depending on system configuration") + } else { + logger.Debug("pq_kem_mode not supported in this build") + } + return + + case "default": + if SupportsPQKEM { + logger.Debug("pq_kem_mode supported and explicitly set to 'default'; post-quantum-safe TLS key exchange may or may not be used depending on system configuration") + } else { + logger.Debug("pq_kem_mode explicitly set to 'default'; post-quantum-safe TLS key exchange not supported in this build") + } + return + + case "attempt": + if !SupportsPQKEM { + logger.Warn("pq_kem_mode set to 'attempt' but no post-quantum-safe key exchange methods are supported in this build (requires Go 1.23); ignoring") + return + } + + logger.Debug("pq_kem_mode supported and configured in 'attempt' mode") + mode = PQKEMModeAttempt + return + + case "require": + if !SupportsPQKEM { + err = errors.New("pq_kem_mode set to 'require' but not supported in this build; requires Go 1.23") + logger.Error(err.Error()) + return + } + + logger.Debug("pq_kem_mode supported and configured in 'require' mode - will require post-quantum security for all TLS connections") + mode = PQKEMModeRequire + return + + default: + mode = PQKEMModeDefault + err = fmt.Errorf("pq_kem_mode of %q is invalid; must be one of ['', 'default', 'attempt', 'require']", value) + return + } +} + +// Policy describes policy options to be applied to a TLS configuration. +// +// A zero-initialised Policy provides reasonable defaults. +type Policy struct { + // PQKEMMode specifies the post-quantum KEM policy to use. + PQKEMMode PQKEMMode +} + +// Not exported by crypto/tls, so we define it here from the I-D. +const x25519Kyber768Draft00 tls.CurveID = 0x6399 + +// ApplyPolicy applies the policy options in policy to a given tls.Config, +// which is assumed to have already been obtained from the go-spiffe +// tlsconfig package. +func ApplyPolicy(config *tls.Config, policy Policy) error { + // Apply post-quantum KEM mode option. + switch policy.PQKEMMode { + case PQKEMModeDefault: + // Nothing to do - allow default curve preferences. + + case PQKEMModeAttempt: + if len(config.CurvePreferences) == 0 { + // This is copied from the crypto/tls default curve list. + config.CurvePreferences = []tls.CurveID{ + x25519Kyber768Draft00, + tls.X25519, + tls.CurveP256, + tls.CurveP384, + tls.CurveP521, + } + } else if config.CurvePreferences[0] != x25519Kyber768Draft00 { + // Prepend X25519Kyber768Draft00 to the list, making it most preferred. + curves := make([]tls.CurveID, 0, len(config.CurvePreferences)+1) + curves = append(curves, x25519Kyber768Draft00) + curves = append(curves, config.CurvePreferences...) + config.CurvePreferences = curves + } + + case PQKEMModeRequire: + // List only known PQ-safe KEMs as valid curves. + config.CurvePreferences = []tls.CurveID{ + x25519Kyber768Draft00, + } + + // Require TLS 1.3, as all PQ-safe KEMs require it anyway. + if config.MinVersion < tls.VersionTLS13 { + config.MinVersion = tls.VersionTLS13 + } + } + + return nil +} diff --git a/pkg/common/tlspolicy/parse_test.go b/pkg/common/tlspolicy/parse_test.go new file mode 100644 index 00000000000..16b348f0364 --- /dev/null +++ b/pkg/common/tlspolicy/parse_test.go @@ -0,0 +1,38 @@ +package tlspolicy + +import ( + "testing" + + "github.com/spiffe/spire/pkg/common/log" + "github.com/stretchr/testify/require" +) + +func TestParsePQKEMMode(t *testing.T) { + require := require.New(t) + logger, err := log.NewLogger(log.WithLevel("ERROR")) + require.NoError(err) + + for _, s := range []struct { + Name string + Value PQKEMMode + ExpectError bool + }{ + {"", PQKEMModeDefault, false}, + {"default", PQKEMModeDefault, false}, + {"attempt", PQKEMModeAttempt, false}, + {"require", PQKEMModeRequire, !SupportsPQKEM}, + {"foo", PQKEMModeDefault, true}, + } { + r, err := ParsePQKEMMode(log.NewHCLogAdapter(logger, "tlspolicy"), s.Name) + if s.ExpectError { + require.Error(err) + } else { + require.NoError(err) + if SupportsPQKEM { + require.Equal(r, s.Value) + } else { + require.Equal(r, PQKEMModeDefault) + } + } + } +} diff --git a/pkg/common/tlspolicy/pqkem_no.go b/pkg/common/tlspolicy/pqkem_no.go new file mode 100644 index 00000000000..6fb827dd345 --- /dev/null +++ b/pkg/common/tlspolicy/pqkem_no.go @@ -0,0 +1,5 @@ +//go:build !go1.23 + +package tlspolicy + +const supportsPQKEM = false diff --git a/pkg/common/tlspolicy/pqkem_yes.go b/pkg/common/tlspolicy/pqkem_yes.go new file mode 100644 index 00000000000..ae0845771eb --- /dev/null +++ b/pkg/common/tlspolicy/pqkem_yes.go @@ -0,0 +1,5 @@ +//go:build go1.23 + +package tlspolicy + +const supportsPQKEM = true diff --git a/pkg/server/bundle/client/client.go b/pkg/server/bundle/client/client.go index 8b1adbd35fe..424c4c58645 100644 --- a/pkg/server/bundle/client/client.go +++ b/pkg/server/bundle/client/client.go @@ -13,6 +13,7 @@ import ( "github.com/spiffe/go-spiffe/v2/spiffeid" "github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig" "github.com/spiffe/spire/pkg/common/bundleutil" + "github.com/spiffe/spire/pkg/common/tlspolicy" "github.com/zeebo/errs" ) @@ -41,6 +42,10 @@ type ClientConfig struct { //revive:disable-line:exported name stutter is intent // mutateTransportHook is a hook to influence the transport used during // tests. mutateTransportHook func(*http.Transport) + + // TLSPolicy specifies the post-quantum-security policy used for TLS + // connections. + TLSPolicy tlspolicy.Policy } // Client is used to fetch a bundle and metadata from a bundle endpoint @@ -66,6 +71,11 @@ func NewClient(config ClientConfig) (Client, error) { authorizer := tlsconfig.AuthorizeID(endpointID) transport.TLSClientConfig = tlsconfig.TLSClientConfig(bundle, authorizer) + + err := tlspolicy.ApplyPolicy(transport.TLSClientConfig, config.TLSPolicy) + if err != nil { + return nil, err + } } if config.mutateTransportHook != nil { config.mutateTransportHook(transport) diff --git a/pkg/server/config.go b/pkg/server/config.go index fdbef83671b..c25a76208bb 100644 --- a/pkg/server/config.go +++ b/pkg/server/config.go @@ -10,6 +10,7 @@ import ( common "github.com/spiffe/spire/pkg/common/catalog" "github.com/spiffe/spire/pkg/common/health" "github.com/spiffe/spire/pkg/common/telemetry" + "github.com/spiffe/spire/pkg/common/tlspolicy" loggerv1 "github.com/spiffe/spire/pkg/server/api/logger/v1" "github.com/spiffe/spire/pkg/server/authpolicy" bundle_client "github.com/spiffe/spire/pkg/server/bundle/client" @@ -120,6 +121,9 @@ type Config struct { // calculation (prefer the TTL passed by the downstream caller, then fall // back to the default X509 CA TTL). UseLegacyDownstreamX509CATTL bool + + // TLSPolicy determines the policy settings to apply to all TLS connections. + TLSPolicy tlspolicy.Policy } type ExperimentalConfig struct { diff --git a/pkg/server/endpoints/config.go b/pkg/server/endpoints/config.go index 33b4d747ebf..c3ce2cdb556 100644 --- a/pkg/server/endpoints/config.go +++ b/pkg/server/endpoints/config.go @@ -14,6 +14,7 @@ import ( "github.com/spiffe/go-spiffe/v2/spiffeid" "github.com/spiffe/spire/pkg/common/bundleutil" "github.com/spiffe/spire/pkg/common/telemetry" + "github.com/spiffe/spire/pkg/common/tlspolicy" "github.com/spiffe/spire/pkg/server/api" agentv1 "github.com/spiffe/spire/pkg/server/api/agent/v1" bundlev1 "github.com/spiffe/spire/pkg/server/api/bundle/v1" @@ -106,6 +107,10 @@ type Config struct { // calculation (prefer the TTL passed by the downstream caller, then fall // back to the default X509 CA TTL). UseLegacyDownstreamX509CATTL bool + + // TLSPolicy determines the post-quantum-safe policy used for all TLS + // connections. + TLSPolicy tlspolicy.Policy } func (c *Config) maybeMakeBundleEndpointServer() (Server, func(context.Context) error) { diff --git a/pkg/server/endpoints/endpoints.go b/pkg/server/endpoints/endpoints.go index caf6f017f16..6897f3e5203 100644 --- a/pkg/server/endpoints/endpoints.go +++ b/pkg/server/endpoints/endpoints.go @@ -31,6 +31,7 @@ import ( "github.com/spiffe/spire/pkg/common/auth" "github.com/spiffe/spire/pkg/common/peertracker" "github.com/spiffe/spire/pkg/common/telemetry" + "github.com/spiffe/spire/pkg/common/tlspolicy" "github.com/spiffe/spire/pkg/common/util" "github.com/spiffe/spire/pkg/server/api" "github.com/spiffe/spire/pkg/server/api/middleware" @@ -83,6 +84,7 @@ type Endpoints struct { AuditLogEnabled bool AuthPolicyEngine *authpolicy.Engine AdminIDs []spiffeid.ID + TLSPolicy tlspolicy.Policy } type APIServers struct { @@ -174,6 +176,7 @@ func New(ctx context.Context, c Config) (*Endpoints, error) { AuditLogEnabled: c.AuditLogEnabled, AuthPolicyEngine: c.AuthPolicyEngine, AdminIDs: c.AdminIDs, + TLSPolicy: c.TLSPolicy, }, nil } @@ -349,6 +352,11 @@ func (e *Endpoints) getTLSConfig(ctx context.Context) func(*tls.ClientHelloInfo) }) spiffeTLSConfig := tlsconfig.MTLSServerConfig(svidSrc, bundleSrc, nil) + err := tlspolicy.ApplyPolicy(spiffeTLSConfig, e.TLSPolicy) + if err != nil { + return nil, err + } + // provided client certificates will be validated using the custom VerifyPeerCertificate hook spiffeTLSConfig.ClientAuth = tls.RequestClientCert spiffeTLSConfig.MinVersion = tls.VersionTLS12 diff --git a/pkg/server/plugin/upstreamauthority/spire/spire.go b/pkg/server/plugin/upstreamauthority/spire/spire.go index 1df3d7513a7..0e42fa4baf7 100644 --- a/pkg/server/plugin/upstreamauthority/spire/spire.go +++ b/pkg/server/plugin/upstreamauthority/spire/spire.go @@ -19,6 +19,7 @@ import ( "github.com/spiffe/spire/pkg/common/coretypes/jwtkey" "github.com/spiffe/spire/pkg/common/coretypes/x509certificate" "github.com/spiffe/spire/pkg/common/idutil" + "github.com/spiffe/spire/pkg/common/tlspolicy" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" @@ -41,6 +42,7 @@ type Configuration struct { type experimentalConfig struct { WorkloadAPINamedPipeName string `hcl:"workload_api_named_pipe_name" json:"workload_api_named_pipe_name"` + PQKEMMode string `hcl:"pq_kem_mode" json:"pq_kem_mode"` } func BuiltIn() catalog.BuiltIn { @@ -123,7 +125,13 @@ func (p *Plugin) Configure(_ context.Context, req *configv1.ConfigureRequest) (* return nil, status.Errorf(codes.Internal, "unable to build server ID: %v", err) } - p.serverClient = newServerClient(serverID, serverAddr, workloadAPIAddr, p.log) + var tlsPolicy tlspolicy.Policy + tlsPolicy.PQKEMMode, err = tlspolicy.ParsePQKEMMode(p.log, p.config.Experimental.PQKEMMode) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid pq_kem_mode value: %v", err) + } + + p.serverClient = newServerClient(serverID, serverAddr, workloadAPIAddr, p.log, tlsPolicy) return &configv1.ConfigureResponse{}, nil } diff --git a/pkg/server/plugin/upstreamauthority/spire/spire_server_client.go b/pkg/server/plugin/upstreamauthority/spire/spire_server_client.go index 0ef93c43a90..e827ef9504a 100644 --- a/pkg/server/plugin/upstreamauthority/spire/spire_server_client.go +++ b/pkg/server/plugin/upstreamauthority/spire/spire_server_client.go @@ -15,6 +15,7 @@ import ( bundlev1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/bundle/v1" svidv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/svid/v1" "github.com/spiffe/spire-api-sdk/proto/spire/api/types" + "github.com/spiffe/spire/pkg/common/tlspolicy" "github.com/spiffe/spire/pkg/common/util" "github.com/spiffe/spire/pkg/common/x509util" "google.golang.org/grpc" @@ -24,12 +25,13 @@ import ( ) // newServerClient creates a new spire-server client -func newServerClient(serverID spiffeid.ID, serverAddr string, workloadAPIAddr net.Addr, log hclog.Logger) *serverClient { +func newServerClient(serverID spiffeid.ID, serverAddr string, workloadAPIAddr net.Addr, log hclog.Logger, tlsPolicy tlspolicy.Policy) *serverClient { return &serverClient{ serverID: serverID, serverAddr: serverAddr, workloadAPIAddr: workloadAPIAddr, log: &logAdapter{log: log}, + tlsPolicy: tlsPolicy, } } @@ -39,6 +41,7 @@ type serverClient struct { serverAddr string workloadAPIAddr net.Addr log logger.Logger + tlsPolicy tlspolicy.Policy mtx sync.RWMutex source *workloadapi.X509Source @@ -60,6 +63,12 @@ func (c *serverClient) start(ctx context.Context) error { } tlsConfig := tlsconfig.MTLSClientConfig(source, source, tlsconfig.AuthorizeID(c.serverID)) + err = tlspolicy.ApplyPolicy(tlsConfig, c.tlsPolicy) + if err != nil { + source.Close() + return status.Errorf(codes.Internal, "error applying TLS policy: %v", err) + } + conn, err := grpc.NewClient(c.serverAddr, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))) if err != nil { diff --git a/pkg/server/server.go b/pkg/server/server.go index b24c86e7543..c512239ac74 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -403,6 +403,7 @@ func (s *Server) newEndpointsServer(ctx context.Context, catalog catalog.Catalog BundleManager: bundleManager, AdminIDs: s.config.AdminIDs, UseLegacyDownstreamX509CATTL: s.config.UseLegacyDownstreamX509CATTL, + TLSPolicy: s.config.TLSPolicy, } if s.config.Federation.BundleEndpoint != nil { config.BundleEndpoint.Address = s.config.Federation.BundleEndpoint.Address