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

feat(translator): client tls session resumption #4293

Merged
merged 25 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
40 changes: 40 additions & 0 deletions api/v1alpha1/tls_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ type ClientTLSSettings struct {
// +optional
ClientValidation *ClientValidationContext `json:"clientValidation,omitempty"`
TLSSettings `json:",inline"`

zhaohuabing marked this conversation as resolved.
Show resolved Hide resolved
// SessionTimeout determines the maximum lifetime of a TLS session.
// https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_DEFAULT_SESSION_TIMEOUT
// Default: 7200s
// +optional
SessionTimeout *gwapiv1.Duration `json:"sessionTimeout,omitempty"`

// SessionResumptionSettings determine the proxy's supported TLS session resumption option.
// By default, Envoy Gateway does not enable session resumption. Use sessionResumption to
// enable stateful and stateless session resumption. Users should consider security impacts
// of different resumption methods. Performance gains from resumption are diminished when
// Envoy proxy is deployed with more than one replica.
// +optional
SessionResumptionSettings *SessionResumption `json:"sessionResumption,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use following structure?

session:
  timeout:
  resumption:

if there're more than 1 property with session prefix, prefer to move them into a struct.

Copy link
Member

@zhaohuabing zhaohuabing Oct 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it seems that different implementations have different meanings for session timeout.

BoringSSL seems to treat this as a hard timeout after which a new handshake is required:

This is how long we are willing to use the secret to encrypt traffic without fresh key material.

In envoy, this is also used as a hint on session tickets.

OpenSSL seems to treat this as a soft timeout after which resumption cannot occur, but an active session may still continue (?):

Whenever a new session is negotiated, it is assigned a timeout value, after which it will not be accepted for session reuse.

@arkodg requested that we leave sessionTimeout out of this PR. I will change the API to @zirain's proposal, to support for future addition of session-related settings that are not specific to resumption.

}

// +kubebuilder:validation:XValidation:rule="has(self.minVersion) && self.minVersion == '1.3' ? !has(self.ciphers) : true", message="setting ciphers has no effect if the minimum possible TLS version is 1.3"
Expand Down Expand Up @@ -133,3 +147,29 @@ type ClientValidationContext struct {
// +optional
CACertificateRefs []gwapiv1.SecretObjectReference `json:"caCertificateRefs,omitempty"`
}

// SessionResumption defines supported tls session resumption methods and their associated configuration.
type SessionResumption struct {
// StatelessSessionResumption defines setting for stateless (session-ticket based) session resumption
// +optional
StatelessSessionResumption *StatelessTLSSessionResumption `json:"statelessSessionResumption,omitempty"`

// StatefulSessionResumption defines setting for stateful (session-id based) session resumption
// +optional
StatefulSessionResumption *StatefulTLSSessionResumption `json:"statefulSessionResumption,omitempty"`
}

// StatefulTLSSessionResumption defines the stateful (session-id based) type of TLS session resumption.
// Note: When Envoy Proxy is deployed with more than one replica, session caches are not synchronized
// between instances, possibly leading to resumption failures.
// Envoy does not re-validate client certificates upon session resumption.
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#config-route-v3-routematch-tlscontextmatchoptions
type StatefulTLSSessionResumption struct{}

// StatelessTLSSessionResumption defines the stateless (session-ticket based) type of TLS session resumption.
// Note: When Envoy Proxy is deployed with more than one replica, session ticket encryption keys are not
// synchronized between instances, possibly leading to resumption failures.
// In-memory session ticket encryption keys are rotated every 48 hours.
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/transport_sockets/tls/v3/common.proto#extensions-transport-sockets-tls-v3-tlssessionticketkeys
// https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#Session-tickets
type StatelessTLSSessionResumption struct{}
65 changes: 65 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,30 @@ spec:
- "1.2"
- "1.3"
type: string
sessionResumption:
description: |-
SessionResumptionSettings determine the proxy's supported TLS session resumption option.
By default, Envoy Gateway does not enable session resumption. Use sessionResumption to
enable stateful and stateless session resumption. Users should consider security impacts
of different resumption methods. Performance gains from resumption are diminished when
Envoy proxy is deployed with more than one replica.
properties:
statefulSessionResumption:
description: StatefulSessionResumption defines setting for
stateful (session-id based) session resumption
type: object
statelessSessionResumption:
description: StatelessSessionResumption defines setting for
stateless (session-ticket based) session resumption
type: object
type: object
sessionTimeout:
description: |-
SessionTimeout determines the maximum lifetime of a TLS session.
https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_DEFAULT_SESSION_TIMEOUT
Default: 7200s
pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$
type: string
signatureAlgorithms:
description: |-
SignatureAlgorithms specifies which signature algorithms the listener should
Expand Down
17 changes: 17 additions & 0 deletions internal/gatewayapi/clienttrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,23 @@
}
}

if tlsParams.SessionResumptionSettings != nil {
if tlsParams.SessionResumptionSettings.StatelessSessionResumption != nil {
irTLSConfig.StatelessSessionResumption = true
}
if tlsParams.SessionResumptionSettings.StatefulSessionResumption != nil {
irTLSConfig.StatefulSessionResumption = true
}
}

if tlsParams.SessionTimeout != nil {
d, err := time.ParseDuration(string(*tlsParams.SessionTimeout))
if err != nil {
return nil, fmt.Errorf("invalid TLS SessionTimeout value %s", *tlsParams.SessionTimeout)

Check warning on line 879 in internal/gatewayapi/clienttrafficpolicy.go

View check run for this annotation

Codecov / codecov/patch

internal/gatewayapi/clienttrafficpolicy.go#L879

Added line #L879 was not covered by tests
}
irTLSConfig.SessionTimeout = ptr.To(metav1.Duration{Duration: d})
}

return irTLSConfig, nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ clientTrafficPolicies:
signatureAlgorithms:
- sig1
- sig2
sessionTimeout: 30s
sessionResumption:
statelessSessionResumption: {}
statefulSessionResumption: {}
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ clientTrafficPolicies:
- curve1
maxVersion: "1.3"
minVersion: "1.0"
sessionResumption:
statefulSessionResumption: {}
statelessSessionResumption: {}
sessionTimeout: 30s
signatureAlgorithms:
- sig1
- sig2
Expand Down Expand Up @@ -171,9 +175,12 @@ xdsIR:
- curve1
maxVersion: "1.3"
minVersion: "1.0"
sessionTimeout: 30s
signatureAlgorithms:
- sig1
- sig2
statefulSessionResumption: true
statelessSessionResumption: true
- address: 0.0.0.0
hostnames:
- '*'
Expand Down
6 changes: 6 additions & 0 deletions internal/ir/xds.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,12 @@ type TLSConfig struct {
SignatureAlgorithms []string `json:"signatureAlgorithms,omitempty" yaml:"signatureAlgorithms,omitempty"`
// ALPNProtocols exposed by this listener
ALPNProtocols []string `json:"alpnProtocols,omitempty" yaml:"alpnProtocols,omitempty"`
// SessionTimeout determines the maximum lifetime of a TLS session
SessionTimeout *metav1.Duration `json:"sessionTimeout,omitempty" yaml:"sessionTimeout,omitempty"`
// StatelessSessionResumption determines if stateless (session-ticket based) session resumption is enabled
StatelessSessionResumption bool `json:"statelessSessionResumption,omitempty" yaml:"statelessSessionResumption,omitempty"`
// StatefulSessionResumption determines if stateful (session-id based) session resumption is enabled
StatefulSessionResumption bool `json:"statefulSessionResumption,omitempty" yaml:"statefulSessionResumption,omitempty"`
}

// TLSCertificate holds a single certificate's details
Expand Down
5 changes: 5 additions & 0 deletions internal/ir/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions internal/xds/translator/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,8 @@ func buildDownstreamQUICTransportSocket(tlsConfig *ir.TLSConfig) (*corev3.Transp
}
}

setDownstreamTLSSessionSettings(tlsConfig, tlsCtx.DownstreamTlsContext)

tlsCtxAny, err := anypb.New(tlsCtx)
if err != nil {
return nil, err
Expand Down Expand Up @@ -664,6 +666,8 @@ func buildXdsDownstreamTLSSocket(tlsConfig *ir.TLSConfig) (*corev3.TransportSock
}
}

setDownstreamTLSSessionSettings(tlsConfig, tlsCtx)

tlsCtxAny, err := anypb.New(tlsCtx)
if err != nil {
return nil, err
Expand All @@ -677,6 +681,22 @@ func buildXdsDownstreamTLSSocket(tlsConfig *ir.TLSConfig) (*corev3.TransportSock
}, nil
}

func setDownstreamTLSSessionSettings(tlsConfig *ir.TLSConfig, tlsCtx *tlsv3.DownstreamTlsContext) {
if tlsConfig.SessionTimeout != nil {
tlsCtx.SessionTimeout = durationpb.New(tlsConfig.SessionTimeout.Duration)
}

if !tlsConfig.StatefulSessionResumption {
tlsCtx.DisableStatefulSessionResumption = true
}

if !tlsConfig.StatelessSessionResumption {
tlsCtx.SessionTicketKeysType = &tlsv3.DownstreamTlsContext_DisableStatelessSessionResumption{
DisableStatelessSessionResumption: true,
}
}
}

func buildTLSParams(tlsConfig *ir.TLSConfig) *tlsv3.TlsParameters {
p := &tlsv3.TlsParameters{}
isEmpty := true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ http:
minVersion: "1.0"
alpnProtocols:
- some-other-protocol
sessionTimeout: 30s
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sessionTimeout clean up needed here

statefulSessionResumption: true
certificates:
- name: secret-1
# byte slice representation of "key-data"
Expand Down Expand Up @@ -107,6 +109,8 @@ tcp:
minVersion: "1.0"
alpnProtocols:
- some-other-protocol
sessionTimeout: 60s
statelessSessionResumption: true
certificates:
- name: secret-3
# byte slice representation of "key-data"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
sdsConfig:
ads: {}
resourceApiVersion: V3
disableStatefulSessionResumption: true
disableStatelessSessionResumption: true
name: envoy-gateway/gateway-1/tls-quic
udpListenerConfig:
downstreamSocketConfig: {}
Expand Down Expand Up @@ -96,5 +98,7 @@
sdsConfig:
ads: {}
resourceApiVersion: V3
disableStatefulSessionResumption: true
disableStatelessSessionResumption: true
name: envoy-gateway/gateway-1/tls
perConnectionBufferLimitBytes: 32768
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,7 @@
sdsConfig:
ads: {}
resourceApiVersion: V3
disableStatefulSessionResumption: true
disableStatelessSessionResumption: true
name: first-listener
perConnectionBufferLimitBytes: 32768
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,7 @@
sdsConfig:
ads: {}
resourceApiVersion: V3
disableStatefulSessionResumption: true
disableStatelessSessionResumption: true
name: first-listener
perConnectionBufferLimitBytes: 32768
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
sdsConfig:
ads: {}
resourceApiVersion: V3
disableStatefulSessionResumption: true
disableStatelessSessionResumption: true
listenerFilters:
- name: envoy.filters.listener.proxy_protocol
typedConfig:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,7 @@
sdsConfig:
ads: {}
resourceApiVersion: V3
disableStatefulSessionResumption: true
disableStatelessSessionResumption: true
name: first-listener
perConnectionBufferLimitBytes: 32768
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@
sdsConfig:
ads: {}
resourceApiVersion: V3
disableStatefulSessionResumption: true
disableStatelessSessionResumption: true
- filterChainMatch:
serverNames:
- foo.net
Expand Down Expand Up @@ -117,6 +119,8 @@
sdsConfig:
ads: {}
resourceApiVersion: V3
disableStatefulSessionResumption: true
disableStatelessSessionResumption: true
- filterChainMatch:
serverNames:
- bar.com
Expand Down
Loading