Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support configuration of post-quantum-safe KEM mode #5371

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cmd/spire-agent/cli/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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"`

Expand Down Expand Up @@ -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.")
}
Expand Down
7 changes: 7 additions & 0 deletions cmd/spire-server/cli/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"`

Expand Down Expand Up @@ -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 {
Expand Down
55 changes: 55 additions & 0 deletions cmd/spire-server/cli/run/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/spiffe/spire/pkg/common/catalog"
"github.com/spiffe/spire/pkg/common/log"
"github.com/spiffe/spire/pkg/common/tlspolicy"
"github.com/spiffe/spire/pkg/server"
bundleClient "github.com/spiffe/spire/pkg/server/bundle/client"
"github.com/spiffe/spire/pkg/server/credtemplate"
Expand Down Expand Up @@ -64,6 +65,7 @@ func TestParseConfigGood(t *testing.T) {
_, ok := trustDomainConfig.EndpointProfile.(bundleClient.HTTPSWebProfile)
assert.True(t, ok)
assert.True(t, c.Server.AuditLogEnabled)
assert.Equal(t, c.Server.Experimental.PQKEMMode, "require")
testParseConfigGoodOS(t, c)

// Parse/reprint cycle trims outer whitespace
Expand Down Expand Up @@ -455,6 +457,16 @@ func TestMergeInput(t *testing.T) {
require.True(t, c.Server.AuditLogEnabled)
},
},
{
msg: "pq_kem_mode should be configurable by file",
fileInput: func(c *Config) {
c.Server.Experimental.PQKEMMode = "attempt"
},
cliFlags: []string{},
test: func(t *testing.T, c *Config) {
require.Equal(t, c.Server.Experimental.PQKEMMode, "attempt")
},
},
}
cases = append(cases, mergeInputCasesOS(t)...)

Expand Down Expand Up @@ -1160,6 +1172,49 @@ func TestNewServerConfig(t *testing.T) {
}, c.AdminIDs)
},
},
{
msg: "post-quantum KEM mode is set (default)",
input: func(c *Config) {},
test: func(t *testing.T, c *server.Config) {
require.Equal(t, tlspolicy.PQKEMModeDefault, c.TLSPolicy.PQKEMMode)
},
},
{
msg: "post-quantum KEM mode is set (explicit default)",
input: func(c *Config) {
c.Server.Experimental.PQKEMMode = "default"
},
test: func(t *testing.T, c *server.Config) {
require.Equal(t, tlspolicy.PQKEMModeDefault, c.TLSPolicy.PQKEMMode)
},
},
{
msg: "post-quantum KEM mode is set (attempt)",
input: func(c *Config) {
c.Server.Experimental.PQKEMMode = "attempt"
},
test: func(t *testing.T, c *server.Config) {
if tlspolicy.SupportsPQKEM {
require.Equal(t, tlspolicy.PQKEMModeAttempt, c.TLSPolicy.PQKEMMode)
} else {
require.Equal(t, tlspolicy.PQKEMModeDefault, c.TLSPolicy.PQKEMMode)
}
},
},
{
msg: "post-quantum KEM mode is set (require)",
input: func(c *Config) {
c.Server.Experimental.PQKEMMode = "require"
},
expectError: !tlspolicy.SupportsPQKEM,
test: func(t *testing.T, c *server.Config) {
if tlspolicy.SupportsPQKEM {
require.Equal(t, tlspolicy.PQKEMModeRequire, c.TLSPolicy.PQKEMMode)
} else {
require.Nil(t, c)
}
},
},
}
cases = append(cases, newServerConfigCasesOS(t)...)

Expand Down
17 changes: 14 additions & 3 deletions doc/plugin_server_upstreamauthority_spire.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,20 @@ The plugin accepts the following configuration options:

These are the current experimental configurations:

| experimental | Description | Default |
|------------------------------|-----------------------------------------------------------------------------------------------------------|---------|
| workload_api_named_pipe_name | Pipe name of the Workload API named pipe (Windows only; e.g. pipe name of the SPIRE Agent API named pipe) |
| experimental | Description | Default |
|------------------------------|----------------------------------------------------------------------------------------------------------------|---------|
| workload_api_named_pipe_name | Pipe name of the Workload API named pipe (Windows only; e.g. pipe name of the SPIRE Agent API named pipe) | |
| pq_kem_mode | Whether to use a post-quantum key exchange method for TLS handshake. Set to "default", "attempt" or "require". | default |

The `pq_kem_mode` option supports the following options:

| `pq_kem_mode` Value | Description |
|:--------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| default | Inherit system default key exchange configuration. Whether a post-quantum-safe key exchange method is available may depend on environmental configuration (e.g. GODEBUG). |
| attempt | Opportunistically attempt to negotiate a post-quantum-safe key exchange method. |
| require | Require negotiation of a post-quantum-safe key exchange method. |

The `pq_kem_mode` option is currently experimental and may be changed or removed in a future release. Currently, use of this option requires Go 1.23 or later, as this is the first Go release supporting at least one post-quantum-safe key exchange method.

Sample configuration (Unix):

Expand Down
23 changes: 17 additions & 6 deletions doc/spire_agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,23 @@ This may be useful for templating configuration files, for example across differ
| `workload_x509_svid_key_type` | The workload X509 SVID key type <rsa-2048|ec-p256> | ec-p256 |
| `availability_target` | The minimum amount of time desired to gracefully handle SPIRE Server or Agent downtime. This configurable influences how aggressively X509 SVIDs should be rotated. If set, must be at least 24h. See [Availability Target](#availability-target) | |

| experimental | Description | Default |
|:---------------------------|------------------------------------------------------------------------------------|-------------------------|
| `named_pipe_name` | Pipe name to bind the SPIRE Agent API named pipe (Windows only) | \spire-agent\public\api |
| `sync_interval` | Sync interval with SPIRE server with exponential backoff | 5 sec |
| `x509_svid_cache_max_size` | Soft limit of max number of SVIDs that would be stored in LRU cache (deprecated) | 1000 |
| `disable_lru_cache` | Reverts back to use the SPIRE Agent non-LRU cache for storing SVIDs (deprecated) | false |
| experimental | Description | Default |
|:---------------------------|------------------------------------------------------------------------------------------------------------------|-------------------------|
| `named_pipe_name` | Pipe name to bind the SPIRE Agent API named pipe (Windows only) | \spire-agent\public\api |
| `sync_interval` | Sync interval with SPIRE server with exponential backoff | 5 sec |
| `x509_svid_cache_max_size` | Soft limit of max number of SVIDs that would be stored in LRU cache (deprecated) | 1000 |
| `disable_lru_cache` | Reverts back to use the SPIRE Agent non-LRU cache for storing SVIDs (deprecated) | false |
| `pq_kem_mode` | Whether to use a post-quantum key exchange method for TLS handshake. Set to "default", "attempt" or "require". | default |

The `pq_kem_mode` option supports the following options:

| `pq_kem_mode` Value | Description |
|:--------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| default | Inherit system default key exchange configuration. Whether a post-quantum-safe key exchange method is available may depend on environmental configuration (e.g. GODEBUG). |
| attempt | Opportunistically attempt to negotiate a post-quantum-safe key exchange method. |
| require | Require negotiation of a post-quantum-safe key exchange method. |

The `pq_kem_mode` option is currently experimental and may be changed or removed in a future release. Currently, use of this option requires Go 1.23 or later, as this is the first Go release supporting at least one post-quantum-safe key exchange method.

### Initial trust bundle configuration

Expand Down
11 changes: 11 additions & 0 deletions doc/spire_server.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ This may be useful for templating configuration files, for example across differ
| `prune_events_older_than`| How old an event can be before being deleted. Used with events based cache. Decreasing this will keep the events table smaller, but will increase risk of missing an event if connection to the database is down. | 12h |
| `auth_opa_policy_engine` | The [auth opa_policy engine](/doc/authorization_policy_engine.md) used for authorization decisions | default SPIRE authorization policy |
| `named_pipe_name` | Pipe name of the SPIRE Server API named pipe (Windows only) | \spire-server\private\api |
| `pq_kem_mode` | Whether to use a post-quantum key exchange method for TLS handshake. Set to "default", "attempt" or "require". | default |

| ratelimit | Description | Default |
|:--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|---------|
Expand All @@ -111,6 +112,16 @@ This may be useful for templating configuration files, for example across differ
| `rego_path` | File to retrieve OPA rego policy for authorization. | |
| `policy_data_path` | File to retrieve databindings for policy evaluation. | |

The `pq_kem_mode` option supports the following options:

| `pq_kem_mode` Value | Description |
|:--------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| default | Inherit system default key exchange configuration. Whether a post-quantum-safe key exchange method is available may depend on environmental configuration (e.g. GODEBUG). |
| attempt | Opportunistically attempt to negotiate a post-quantum-safe key exchange method. |
| require | Require negotiation of a post-quantum-safe key exchange method. |

The `pq_kem_mode` option is currently experimental and may be changed or removed in a future release. Currently, use of this option requires Go 1.23 or later, as this is the first Go release supporting at least one post-quantum-safe key exchange method.

### Profiling Names

These are the available profiles that can be set in the `profiling_names` configuration value:
Expand Down
2 changes: 2 additions & 0 deletions pkg/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions pkg/agent/attestor/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -58,6 +59,7 @@ type Config struct {
Log logrus.FieldLogger
ServerAddress string
NodeAttestor nodeattestor.NodeAttestor
TLSPolicy tlspolicy.Policy
}

type attestor struct {
Expand Down Expand Up @@ -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,
})
}

Expand Down
5 changes: 5 additions & 0 deletions pkg/agent/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -371,6 +375,7 @@ func (c *client) dial(ctx context.Context) (*grpc.ClientConn, error) {
}
return agentCert
},
TLSPolicy: c.c.TLSPolicy,
dialContext: c.dialContext,
})
}
Expand Down
9 changes: 9 additions & 0 deletions pkg/agent/client/dial.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
}
Expand All @@ -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()

Expand Down
4 changes: 4 additions & 0 deletions pkg/agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions pkg/agent/manager/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)

Expand Down
1 change: 1 addition & 0 deletions pkg/agent/svid/rotator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
}

Expand Down
Loading
Loading