Skip to content

Commit

Permalink
gateway-api: ALPN support
Browse files Browse the repository at this point in the history
This feature is hidden behind `enable-gateway-api-alpn` flag on the operator gwapi cell. The implementation will change envoy listener configuration to expose ALPN suggesting both HTTP/2 and HTTP/1.1.

Fixes: #30794

Signed-off-by: Rauan Mayemir <[email protected]>
  • Loading branch information
rauanmayemir committed May 11, 2024
1 parent 4d65594 commit 8717da9
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 24 deletions.
4 changes: 4 additions & 0 deletions operator/pkg/gateway-api/cell.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ var Cell = cell.Module(
EnableGatewayAPISecretsSync: true,
EnableGatewayAPIProxyProtocol: false,
EnableGatewayAPIAppProtocol: false,
EnableGatewayAPIAlpn: false,
GatewayAPISecretsNamespace: "cilium-secrets",
GatewayAPIXffNumTrustedHops: 0,

Expand All @@ -63,6 +64,7 @@ type gatewayApiConfig struct {
EnableGatewayAPISecretsSync bool
EnableGatewayAPIProxyProtocol bool
EnableGatewayAPIAppProtocol bool
EnableGatewayAPIAlpn bool
GatewayAPISecretsNamespace string
GatewayAPIXffNumTrustedHops uint32

Expand All @@ -77,6 +79,7 @@ func (r gatewayApiConfig) Flags(flags *pflag.FlagSet) {
flags.Bool("enable-gateway-api-secrets-sync", r.EnableGatewayAPISecretsSync, "Enables fan-in TLS secrets sync from multiple namespaces to singular namespace (specified by gateway-api-secrets-namespace flag)")
flags.Bool("enable-gateway-api-proxy-protocol", r.EnableGatewayAPIProxyProtocol, "Enable proxy protocol for all GatewayAPI listeners. Note that _only_ Proxy protocol traffic will be accepted once this is enabled.")
flags.Bool("enable-gateway-api-app-protocol", r.EnableGatewayAPIAppProtocol, "Enables Backend Protocol selection (GEP-1911) for Gateway API via appProtocol")
flags.Bool("enable-gateway-api-alpn", r.EnableGatewayAPIAlpn, "Enables exposing ALPN with HTTP2 and HTTP/1.1 support for Gateway API")
flags.Uint32("gateway-api-xff-num-trusted-hops", r.GatewayAPIXffNumTrustedHops, "The number of additional GatewayAPI proxy hops from the right side of the HTTP header to trust when determining the origin client's IP address.")
flags.String("gateway-api-secrets-namespace", r.GatewayAPISecretsNamespace, "Namespace having tls secrets used by CEC for Gateway API")
flags.Bool("gateway-api-hostnetwork-enabled", r.GatewayAPIHostnetworkEnabled, "Exposes Gateway listeners on the host network.")
Expand Down Expand Up @@ -125,6 +128,7 @@ func initGatewayAPIController(params gatewayAPIParams) error {
params.GatewayApiConfig.GatewayAPISecretsNamespace,
params.GatewayApiConfig.EnableGatewayAPIProxyProtocol,
params.GatewayApiConfig.EnableGatewayAPIAppProtocol,
params.GatewayApiConfig.EnableGatewayAPIAlpn,
true, // hostNameSuffixMatch
params.OperatorConfig.ProxyIdleTimeoutSeconds,
params.GatewayApiConfig.GatewayAPIHostnetworkEnabled,
Expand Down
2 changes: 1 addition & 1 deletion operator/pkg/gateway-api/gateway_reconcile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ func Test_gatewayReconciler_Reconcile(t *testing.T) {
WithStatusSubresource(&gatewayv1.Gateway{}).
Build()

cecTranslator := translation.NewCECTranslator("", false, false, true, 60, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator("", false, false, false, true, 60, false, nil, false, false, 0)
gatewayAPITranslator := gatewayApiTranslation.NewTranslator(cecTranslator, false)

r := &gatewayReconciler{
Expand Down
1 change: 1 addition & 0 deletions operator/pkg/ingress/cell.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ func registerReconciler(params ingressParams) error {
params.IngressConfig.IngressSecretsNamespace,
params.IngressConfig.EnableIngressProxyProtocol,
false,
false,
false, // hostNameSuffixMatch
params.OperatorConfig.ProxyIdleTimeoutSeconds,
params.IngressConfig.IngressHostnetworkEnabled,
Expand Down
32 changes: 16 additions & 16 deletions operator/pkg/ingress/ingress_reconcile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func TestReconcile(t *testing.T) {
).
Build()

cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, testDefaultTimeout, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, false, testDefaultTimeout, false, nil, false, false, 0)
dedicatedIngressTranslator := ingressTranslation.NewDedicatedIngressTranslator(cecTranslator, false)

reconciler := newIngressReconciler(logger, fakeClient, cecTranslator, dedicatedIngressTranslator, testCiliumNamespace, []string{}, testDefaultLoadbalancingServiceName, "dedicated", testDefaultSecretNamespace, testDefaultSecretName, false, testIngressDefaultRequestTimeout, false, 0)
Expand Down Expand Up @@ -118,7 +118,7 @@ func TestReconcile(t *testing.T) {
).
Build()

cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, testDefaultTimeout, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, false, testDefaultTimeout, false, nil, false, false, 0)
dedicatedIngressTranslator := ingressTranslation.NewDedicatedIngressTranslator(cecTranslator, false)

reconciler := newIngressReconciler(logger, fakeClient, cecTranslator, dedicatedIngressTranslator, testCiliumNamespace, []string{}, testDefaultLoadbalancingServiceName, "dedicated", testDefaultSecretNamespace, testDefaultSecretName, false, testIngressDefaultRequestTimeout, false, 0)
Expand Down Expand Up @@ -174,7 +174,7 @@ func TestReconcile(t *testing.T) {
).
Build()

cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, testDefaultTimeout, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, false, testDefaultTimeout, false, nil, false, false, 0)
dedicatedIngressTranslator := ingressTranslation.NewDedicatedIngressTranslator(cecTranslator, false)

reconciler := newIngressReconciler(logger, fakeClient, cecTranslator, dedicatedIngressTranslator, testCiliumNamespace, []string{}, testDefaultLoadbalancingServiceName, "dedicated", testDefaultSecretNamespace, testDefaultSecretName, false, testIngressDefaultRequestTimeout, false, 0)
Expand Down Expand Up @@ -218,7 +218,7 @@ func TestReconcile(t *testing.T) {
).
Build()

cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, testDefaultTimeout, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, false, testDefaultTimeout, false, nil, false, false, 0)
dedicatedIngressTranslator := ingressTranslation.NewDedicatedIngressTranslator(cecTranslator, false)

reconciler := newIngressReconciler(logger, fakeClient, cecTranslator, dedicatedIngressTranslator, testCiliumNamespace, []string{}, testDefaultLoadbalancingServiceName, "dedicated", testDefaultSecretNamespace, testDefaultSecretName, false, testIngressDefaultRequestTimeout, false, 0)
Expand Down Expand Up @@ -288,7 +288,7 @@ func TestReconcile(t *testing.T) {
).
Build()

cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, testDefaultTimeout, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, false, testDefaultTimeout, false, nil, false, false, 0)
dedicatedIngressTranslator := ingressTranslation.NewDedicatedIngressTranslator(cecTranslator, false)

reconciler := newIngressReconciler(logger, fakeClient, cecTranslator, dedicatedIngressTranslator, testCiliumNamespace, []string{}, testDefaultLoadbalancingServiceName, "dedicated", testDefaultSecretNamespace, testDefaultSecretName, false, testIngressDefaultRequestTimeout, false, 0)
Expand Down Expand Up @@ -355,7 +355,7 @@ func TestReconcile(t *testing.T) {
).
Build()

cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, testDefaultTimeout, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, false, testDefaultTimeout, false, nil, false, false, 0)
dedicatedIngressTranslator := ingressTranslation.NewDedicatedIngressTranslator(cecTranslator, false)

reconciler := newIngressReconciler(logger, fakeClient, cecTranslator, dedicatedIngressTranslator, testCiliumNamespace, []string{}, testDefaultLoadbalancingServiceName, "dedicated", testDefaultSecretNamespace, testDefaultSecretName, false, testIngressDefaultRequestTimeout, false, 0)
Expand Down Expand Up @@ -409,7 +409,7 @@ func TestReconcile(t *testing.T) {
).
Build()

cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, testDefaultTimeout, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, false, testDefaultTimeout, false, nil, false, false, 0)
dedicatedIngressTranslator := ingressTranslation.NewDedicatedIngressTranslator(cecTranslator, false)

reconciler := newIngressReconciler(logger, fakeClient, cecTranslator, dedicatedIngressTranslator, testCiliumNamespace, []string{}, testDefaultLoadbalancingServiceName, "dedicated", testDefaultSecretNamespace, testDefaultSecretName, false, testIngressDefaultRequestTimeout, false, 0)
Expand Down Expand Up @@ -491,7 +491,7 @@ func TestReconcile(t *testing.T) {
).
Build()

cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, testDefaultTimeout, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, false, testDefaultTimeout, false, nil, false, false, 0)
dedicatedIngressTranslator := ingressTranslation.NewDedicatedIngressTranslator(cecTranslator, false)

reconciler := newIngressReconciler(logger, fakeClient, cecTranslator, dedicatedIngressTranslator, testCiliumNamespace, []string{}, testDefaultLoadbalancingServiceName, "dedicated", testDefaultSecretNamespace, testDefaultSecretName, false, testIngressDefaultRequestTimeout, false, 0)
Expand Down Expand Up @@ -563,7 +563,7 @@ func TestReconcile(t *testing.T) {
).
Build()

cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, testDefaultTimeout, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, false, testDefaultTimeout, false, nil, false, false, 0)
dedicatedIngressTranslator := ingressTranslation.NewDedicatedIngressTranslator(cecTranslator, false)

reconciler := newIngressReconciler(logger, fakeClient, cecTranslator, dedicatedIngressTranslator, testCiliumNamespace, []string{}, testDefaultLoadbalancingServiceName, "dedicated", testDefaultSecretNamespace, testDefaultSecretName, false, testIngressDefaultRequestTimeout, false, 0)
Expand Down Expand Up @@ -619,7 +619,7 @@ func TestReconcile(t *testing.T) {
).
Build()

cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, testDefaultTimeout, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, false, testDefaultTimeout, false, nil, false, false, 0)
dedicatedIngressTranslator := ingressTranslation.NewDedicatedIngressTranslator(cecTranslator, false)

reconciler := newIngressReconciler(logger, fakeClient, cecTranslator, dedicatedIngressTranslator, testCiliumNamespace, []string{}, testDefaultLoadbalancingServiceName, "dedicated", testDefaultSecretNamespace, testDefaultSecretName, false, testIngressDefaultRequestTimeout, false, 0)
Expand Down Expand Up @@ -659,7 +659,7 @@ func TestReconcile(t *testing.T) {
).
Build()

cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, testDefaultTimeout, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, false, testDefaultTimeout, false, nil, false, false, 0)
dedicatedIngressTranslator := ingressTranslation.NewDedicatedIngressTranslator(cecTranslator, false)

reconciler := newIngressReconciler(logger, fakeClient, cecTranslator, dedicatedIngressTranslator, testCiliumNamespace, []string{}, testDefaultLoadbalancingServiceName, "dedicated", testDefaultSecretNamespace, testDefaultSecretName, false, testIngressDefaultRequestTimeout, false, 0)
Expand Down Expand Up @@ -700,7 +700,7 @@ func TestReconcile(t *testing.T) {
).
Build()

cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, testDefaultTimeout, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, false, testDefaultTimeout, false, nil, false, false, 0)
dedicatedIngressTranslator := ingressTranslation.NewDedicatedIngressTranslator(cecTranslator, false)

reconciler := newIngressReconciler(logger, fakeClient, cecTranslator, dedicatedIngressTranslator, testCiliumNamespace, []string{"test.acme.io/"}, testDefaultLoadbalancingServiceName, "dedicated", testDefaultSecretNamespace, testDefaultSecretName, false, testIngressDefaultRequestTimeout, false, 0)
Expand Down Expand Up @@ -785,7 +785,7 @@ func TestReconcile(t *testing.T) {
).
Build()

cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, testDefaultTimeout, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, false, testDefaultTimeout, false, nil, false, false, 0)
dedicatedIngressTranslator := ingressTranslation.NewDedicatedIngressTranslator(cecTranslator, false)

reconciler := newIngressReconciler(logger, fakeClient, cecTranslator, dedicatedIngressTranslator, testCiliumNamespace, []string{}, testDefaultLoadbalancingServiceName, "dedicated", testDefaultSecretNamespace, testDefaultSecretName, false, testIngressDefaultRequestTimeout, false, 0)
Expand Down Expand Up @@ -850,7 +850,7 @@ func TestReconcile(t *testing.T) {
).
Build()

cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, testDefaultTimeout, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, false, testDefaultTimeout, false, nil, false, false, 0)
dedicatedIngressTranslator := ingressTranslation.NewDedicatedIngressTranslator(cecTranslator, false)

reconciler := newIngressReconciler(logger, fakeClient, cecTranslator, dedicatedIngressTranslator, testCiliumNamespace, []string{}, testDefaultLoadbalancingServiceName, "dedicated", testDefaultSecretNamespace, testDefaultSecretName, false, testIngressDefaultRequestTimeout, false, 0)
Expand Down Expand Up @@ -892,7 +892,7 @@ func TestReconcile(t *testing.T) {
).
Build()

cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, testDefaultTimeout, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, false, testDefaultTimeout, false, nil, false, false, 0)
dedicatedIngressTranslator := ingressTranslation.NewDedicatedIngressTranslator(cecTranslator, false)

reconciler := newIngressReconciler(logger, fakeClient, cecTranslator, dedicatedIngressTranslator, testCiliumNamespace, []string{}, testDefaultLoadbalancingServiceName, "dedicated", testDefaultSecretNamespace, testDefaultSecretName, false, testIngressDefaultRequestTimeout, false, 0)
Expand Down Expand Up @@ -950,7 +950,7 @@ func TestReconcile(t *testing.T) {
}).
Build()

cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, testDefaultTimeout, false, nil, false, false, 0)
cecTranslator := translation.NewCECTranslator(testCiliumSecretsNamespace, testUseProxyProtocol, false, false, false, testDefaultTimeout, false, nil, false, false, 0)
dedicatedIngressTranslator := ingressTranslation.NewDedicatedIngressTranslator(cecTranslator, false)

reconciler := newIngressReconciler(logger, fakeClient, cecTranslator, dedicatedIngressTranslator, testCiliumNamespace, []string{}, testDefaultLoadbalancingServiceName, "dedicated", testDefaultSecretNamespace, testDefaultSecretName, false, testIngressDefaultRequestTimeout, false, 0)
Expand Down
8 changes: 7 additions & 1 deletion operator/pkg/model/translation/cec_translator.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type cecTranslator struct {
secretsNamespace string
useProxyProtocol bool
useAppProtocol bool
useAlpn bool

hostNetworkEnabled bool
hostNetworkNodeLabelSelector *slim_metav1.LabelSelector
Expand All @@ -59,14 +60,15 @@ type cecTranslator struct {
}

// NewCECTranslator returns a new translator
func NewCECTranslator(secretsNamespace string, useProxyProtocol bool, useAppProtocol bool, hostNameSuffixMatch bool, idleTimeoutSeconds int,
func NewCECTranslator(secretsNamespace string, useProxyProtocol bool, useAppProtocol bool, useAlpn bool, hostNameSuffixMatch bool, idleTimeoutSeconds int,
hostNetworkEnabled bool, hostNetworkNodeLabelSelector *slim_metav1.LabelSelector, ipv4Enabled bool, ipv6Enabled bool,
xffNumTrustedHops uint32,
) CECTranslator {
return &cecTranslator{
secretsNamespace: secretsNamespace,
useProxyProtocol: useProxyProtocol,
useAppProtocol: useAppProtocol,
useAlpn: useAlpn,
hostNameSuffixMatch: hostNameSuffixMatch,
idleTimeoutSeconds: idleTimeoutSeconds,
xffNumTrustedHops: xffNumTrustedHops,
Expand Down Expand Up @@ -184,6 +186,10 @@ func (i *cecTranslator) getListener(m *model.Model) []ciliumv2.XDSResource {
mutatorFuncs = append(mutatorFuncs, WithProxyProtocol())
}

if i.useAlpn {
mutatorFuncs = append(mutatorFuncs, WithAlpn())
}

if i.hostNetworkEnabled {
mutatorFuncs = append(mutatorFuncs, WithHostNetworkPort(m, i.ipv4Enabled, i.ipv6Enabled))
}
Expand Down
25 changes: 25 additions & 0 deletions operator/pkg/model/translation/envoy_listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,31 @@ func WithProxyProtocol() ListenerMutator {
}
}

func WithAlpn() ListenerMutator {
return func(listener *envoy_config_listener.Listener) *envoy_config_listener.Listener {
for _, filterChain := range listener.FilterChains {
transportSocket := filterChain.GetTransportSocket()
if transportSocket == nil {
continue
}

downstreamContext := &envoy_extensions_transport_sockets_tls_v3.DownstreamTlsContext{}
err := proto.Unmarshal(transportSocket.ConfigType.(*envoy_config_core_v3.TransportSocket_TypedConfig).TypedConfig.Value, downstreamContext)
if err != nil {
continue
}

// Use `h2,http/1.1` to support both HTTP/2 and HTTP/1.1
downstreamContext.CommonTlsContext.AlpnProtocols = []string{"h2,http/1.1"}

transportSocket.ConfigType = &envoy_config_core_v3.TransportSocket_TypedConfig{
TypedConfig: toAny(downstreamContext),
}
}
return listener
}
}

func WithXffNumTrustedHops(xff uint32) ListenerMutator {
return func(listener *envoy_config_listener.Listener) *envoy_config_listener.Listener {
if xff == 0 {
Expand Down
40 changes: 40 additions & 0 deletions operator/pkg/model/translation/envoy_listener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,46 @@ func TestNewListener(t *testing.T) {
require.Equal(t, []string{"example.com", "example.org"}, listener.GetFilterChains()[4].FilterChainMatch.ServerNames)
require.Equal(t, []string{"foo.bar"}, listener.GetFilterChains()[5].FilterChainMatch.ServerNames)
})

t.Run("without TLS with ALPN", func(t *testing.T) {
res, err := newListener("dummy-name", "dummy-secret-namespace", true, nil, nil, WithAlpn())
require.Nil(t, err)

listener := &envoy_config_listener.Listener{}
err = proto.Unmarshal(res.Value, listener)
require.Nil(t, err)
require.Len(t, listener.GetListenerFilters(), 1)
require.Len(t, listener.GetFilterChains(), 1)
// without TLS, ALPN setup is skipped
require.Nil(t, listener.GetFilterChains()[0].GetTransportSocket())
})

t.Run("with TLS with ALPN", func(t *testing.T) {
res, err := newListener(
"dummy-name",
"dummy-secret-namespace",
true,
map[model.TLSSecret][]string{
{Name: "dummy-secret-1", Namespace: "dummy-namespace"}: {"dummy.server.com"},
},
nil,
WithAlpn(),
)
require.Nil(t, err)

listener := &envoy_config_listener.Listener{}
err = proto.Unmarshal(res.Value, listener)
require.Nil(t, err)
require.Len(t, listener.GetListenerFilters(), 1)
require.Len(t, listener.GetFilterChains(), 2)
require.Nil(t, listener.GetFilterChains()[0].GetTransportSocket())

downstreamContext := &envoy_extensions_transport_sockets_tls_v3.DownstreamTlsContext{}
err = proto.Unmarshal(listener.GetFilterChains()[1].GetTransportSocket().ConfigType.(*envoy_config_core_v3.TransportSocket_TypedConfig).TypedConfig.Value, downstreamContext)
require.Nil(t, err)

require.Equal(t, []string{"h2,http/1.1"}, downstreamContext.CommonTlsContext.AlpnProtocols)
})
}

func TestGetHostNetworkListenerAddresses(t *testing.T) {
Expand Down

0 comments on commit 8717da9

Please sign in to comment.