| // Copyright Istio Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package v1alpha3 |
| |
| import ( |
| "reflect" |
| "sort" |
| "testing" |
| "time" |
| ) |
| |
| import ( |
| core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" |
| route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" |
| hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" |
| auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" |
| "github.com/google/go-cmp/cmp" |
| "github.com/google/uuid" |
| "google.golang.org/protobuf/testing/protocmp" |
| "google.golang.org/protobuf/types/known/durationpb" |
| meshconfig "istio.io/api/mesh/v1alpha1" |
| networking "istio.io/api/networking/v1alpha3" |
| ) |
| |
| import ( |
| "github.com/apache/dubbo-go-pixiu/pilot/pkg/features" |
| pilot_model "github.com/apache/dubbo-go-pixiu/pilot/pkg/model" |
| istionetworking "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking" |
| "github.com/apache/dubbo-go-pixiu/pilot/pkg/networking/util" |
| "github.com/apache/dubbo-go-pixiu/pilot/pkg/security/model" |
| "github.com/apache/dubbo-go-pixiu/pilot/test/xdstest" |
| "github.com/apache/dubbo-go-pixiu/pkg/config" |
| "github.com/apache/dubbo-go-pixiu/pkg/config/host" |
| "github.com/apache/dubbo-go-pixiu/pkg/config/protocol" |
| "github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk" |
| "github.com/apache/dubbo-go-pixiu/pkg/config/visibility" |
| "github.com/apache/dubbo-go-pixiu/pkg/proto" |
| "github.com/apache/dubbo-go-pixiu/pkg/test" |
| ) |
| |
| func TestBuildGatewayListenerTlsContext(t *testing.T) { |
| testCases := []struct { |
| name string |
| server *networking.Server |
| result *auth.DownstreamTlsContext |
| transportProtocol istionetworking.TransportProtocol |
| }{ |
| { |
| name: "mesh SDS enabled, tls mode ISTIO_MUTUAL", |
| server: &networking.Server{ |
| Hosts: []string{"httpbin.example.com"}, |
| Port: &networking.Port{ |
| Protocol: string(protocol.HTTPS), |
| }, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, |
| }, |
| }, |
| result: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "default", |
| SdsConfig: &core.ConfigSource{ |
| InitialFetchTimeout: durationpb.New(time.Second * 0), |
| ResourceApiVersion: core.ApiVersion_V3, |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ |
| CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ |
| DefaultValidationContext: &auth.CertificateValidationContext{}, |
| ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ |
| Name: "ROOTCA", |
| SdsConfig: &core.ConfigSource{ |
| InitialFetchTimeout: durationpb.New(time.Second * 0), |
| ResourceApiVersion: core.ApiVersion_V3, |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolTrue, |
| }, |
| }, |
| { |
| // regression test for having both fields set. This is rejected in validation. |
| name: "tls mode ISTIO_MUTUAL, with credentialName", |
| server: &networking.Server{ |
| Hosts: []string{"httpbin.example.com"}, |
| Port: &networking.Port{ |
| Protocol: string(protocol.HTTPS), |
| }, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, |
| CredentialName: "ignored", |
| }, |
| }, |
| result: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "default", |
| SdsConfig: &core.ConfigSource{ |
| InitialFetchTimeout: durationpb.New(time.Second * 0), |
| ResourceApiVersion: core.ApiVersion_V3, |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ |
| CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ |
| DefaultValidationContext: &auth.CertificateValidationContext{}, |
| ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ |
| Name: "ROOTCA", |
| SdsConfig: &core.ConfigSource{ |
| InitialFetchTimeout: durationpb.New(time.Second * 0), |
| ResourceApiVersion: core.ApiVersion_V3, |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolTrue, |
| }, |
| }, |
| { // No credential name is specified, generate file paths for key/cert. |
| name: "no credential name no key no cert tls SIMPLE", |
| server: &networking.Server{ |
| Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, |
| Port: &networking.Port{ |
| Protocol: string(protocol.HTTPS), |
| }, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_SIMPLE, |
| }, |
| }, |
| result: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "default", |
| SdsConfig: &core.ConfigSource{ |
| InitialFetchTimeout: durationpb.New(time.Second * 0), |
| ResourceApiVersion: core.ApiVersion_V3, |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolFalse, |
| }, |
| }, |
| { // Credential name is specified, SDS config is generated for fetching key/cert. |
| name: "credential name no key no cert tls SIMPLE", |
| server: &networking.Server{ |
| Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, |
| Port: &networking.Port{ |
| Protocol: string(protocol.HTTPS), |
| }, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_SIMPLE, |
| CredentialName: "ingress-sds-resource-name", |
| }, |
| }, |
| result: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "kubernetes://ingress-sds-resource-name", |
| SdsConfig: model.SDSAdsConfig, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolFalse, |
| }, |
| }, |
| { // Credential name and subject alternative names are specified, generate SDS configs for |
| // key/cert and static validation context config. |
| name: "credential name subject alternative name no key no cert tls SIMPLE", |
| server: &networking.Server{ |
| Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, |
| Port: &networking.Port{ |
| Protocol: string(protocol.HTTPS), |
| }, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_SIMPLE, |
| CredentialName: "ingress-sds-resource-name", |
| SubjectAltNames: []string{"subject.name.a.com", "subject.name.b.com"}, |
| }, |
| }, |
| result: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "kubernetes://ingress-sds-resource-name", |
| SdsConfig: model.SDSAdsConfig, |
| }, |
| }, |
| ValidationContextType: &auth.CommonTlsContext_ValidationContext{ |
| ValidationContext: &auth.CertificateValidationContext{ |
| MatchSubjectAltNames: util.StringToExactMatch([]string{"subject.name.a.com", "subject.name.b.com"}), |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolFalse, |
| }, |
| }, |
| { |
| name: "no credential name key and cert tls SIMPLE", |
| server: &networking.Server{ |
| Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, |
| Port: &networking.Port{ |
| Protocol: string(protocol.HTTPS), |
| }, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_SIMPLE, |
| ServerCertificate: "server-cert.crt", |
| PrivateKey: "private-key.key", |
| }, |
| }, |
| result: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "file-cert:server-cert.crt~private-key.key", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolFalse, |
| }, |
| }, |
| { |
| name: "no credential name key and cert tls MUTUAL", |
| server: &networking.Server{ |
| Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, |
| Port: &networking.Port{ |
| Protocol: string(protocol.HTTPS), |
| }, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_MUTUAL, |
| ServerCertificate: "server-cert.crt", |
| PrivateKey: "private-key.key", |
| CaCertificates: "ca-cert.crt", |
| }, |
| }, |
| result: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "file-cert:server-cert.crt~private-key.key", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ |
| CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ |
| DefaultValidationContext: &auth.CertificateValidationContext{}, |
| ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ |
| Name: "file-root:ca-cert.crt", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolTrue, |
| }, |
| }, |
| { |
| name: "no credential name key and cert subject alt names tls MUTUAL", |
| server: &networking.Server{ |
| Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, |
| Port: &networking.Port{ |
| Protocol: string(protocol.HTTPS), |
| }, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_MUTUAL, |
| ServerCertificate: "server-cert.crt", |
| PrivateKey: "private-key.key", |
| CaCertificates: "ca-cert.crt", |
| SubjectAltNames: []string{"subject.name.a.com", "subject.name.b.com"}, |
| }, |
| }, |
| result: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "file-cert:server-cert.crt~private-key.key", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ |
| CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ |
| DefaultValidationContext: &auth.CertificateValidationContext{ |
| MatchSubjectAltNames: util.StringToExactMatch([]string{"subject.name.a.com", "subject.name.b.com"}), |
| }, |
| ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ |
| Name: "file-root:ca-cert.crt", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolTrue, |
| }, |
| }, |
| { |
| // Credential name and subject names are specified, SDS configs are generated for fetching |
| // key/cert and root cert. |
| name: "credential name subject alternative name key and cert tls MUTUAL", |
| server: &networking.Server{ |
| Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, |
| Port: &networking.Port{ |
| Protocol: string(protocol.HTTPS), |
| }, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_MUTUAL, |
| CredentialName: "ingress-sds-resource-name", |
| ServerCertificate: "server-cert.crt", |
| PrivateKey: "private-key.key", |
| SubjectAltNames: []string{"subject.name.a.com", "subject.name.b.com"}, |
| }, |
| }, |
| result: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "kubernetes://ingress-sds-resource-name", |
| SdsConfig: model.SDSAdsConfig, |
| }, |
| }, |
| ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ |
| CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ |
| DefaultValidationContext: &auth.CertificateValidationContext{ |
| MatchSubjectAltNames: util.StringToExactMatch([]string{"subject.name.a.com", "subject.name.b.com"}), |
| }, |
| ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ |
| Name: "kubernetes://ingress-sds-resource-name-cacert", |
| SdsConfig: model.SDSAdsConfig, |
| }, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolTrue, |
| }, |
| }, |
| { |
| // Credential name and VerifyCertificateSpki options are specified, SDS configs are generated for fetching |
| // key/cert and root cert |
| name: "credential name verify spki key and cert tls MUTUAL", |
| server: &networking.Server{ |
| Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, |
| Port: &networking.Port{ |
| Protocol: string(protocol.HTTPS), |
| }, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_MUTUAL, |
| CredentialName: "ingress-sds-resource-name", |
| VerifyCertificateSpki: []string{"abcdef"}, |
| }, |
| }, |
| result: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "kubernetes://ingress-sds-resource-name", |
| SdsConfig: model.SDSAdsConfig, |
| }, |
| }, |
| ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ |
| CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ |
| DefaultValidationContext: &auth.CertificateValidationContext{ |
| VerifyCertificateSpki: []string{"abcdef"}, |
| }, |
| ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ |
| Name: "kubernetes://ingress-sds-resource-name-cacert", |
| SdsConfig: model.SDSAdsConfig, |
| }, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolTrue, |
| }, |
| }, |
| { |
| // Credential name and VerifyCertificateHash options are specified, SDS configs are generated for fetching |
| // key/cert and root cert |
| name: "credential name verify hash key and cert tls MUTUAL", |
| server: &networking.Server{ |
| Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, |
| Port: &networking.Port{ |
| Protocol: string(protocol.HTTPS), |
| }, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_MUTUAL, |
| CredentialName: "ingress-sds-resource-name", |
| VerifyCertificateHash: []string{"fedcba"}, |
| }, |
| }, |
| result: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "kubernetes://ingress-sds-resource-name", |
| SdsConfig: model.SDSAdsConfig, |
| }, |
| }, |
| ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ |
| CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ |
| DefaultValidationContext: &auth.CertificateValidationContext{ |
| VerifyCertificateHash: []string{"fedcba"}, |
| }, |
| ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ |
| Name: "kubernetes://ingress-sds-resource-name-cacert", |
| SdsConfig: model.SDSAdsConfig, |
| }, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolTrue, |
| }, |
| }, |
| { |
| name: "no credential name key and cert tls PASSTHROUGH", |
| server: &networking.Server{ |
| Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, |
| Port: &networking.Port{ |
| Protocol: string(protocol.HTTPS), |
| }, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_PASSTHROUGH, |
| ServerCertificate: "server-cert.crt", |
| PrivateKey: "private-key.key", |
| }, |
| }, |
| result: nil, |
| }, |
| { |
| name: "Downstream TLS settings for QUIC transport", |
| server: &networking.Server{ |
| Hosts: []string{"httpbin.example.com"}, |
| Port: &networking.Port{ |
| Protocol: string(protocol.HTTPS), |
| }, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_SIMPLE, |
| CredentialName: "httpbin-cred", |
| }, |
| }, |
| transportProtocol: istionetworking.TransportProtocolQUIC, |
| result: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp3OverQUIC, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "kubernetes://httpbin-cred", |
| SdsConfig: model.SDSAdsConfig, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolFalse, |
| }, |
| }, |
| { |
| name: "duplicated cipher suites with tls SIMPLE", |
| server: &networking.Server{ |
| Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, |
| Port: &networking.Port{ |
| Protocol: string(protocol.HTTPS), |
| }, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_SIMPLE, |
| ServerCertificate: "server-cert.crt", |
| PrivateKey: "private-key.key", |
| CipherSuites: []string{"ECDHE-ECDSA-AES128-SHA", "ECDHE-ECDSA-AES128-SHA"}, |
| }, |
| }, |
| result: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsParams: &auth.TlsParameters{ |
| CipherSuites: []string{"ECDHE-ECDSA-AES128-SHA"}, |
| }, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "file-cert:server-cert.crt~private-key.key", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolFalse, |
| }, |
| }, |
| { |
| // tcp server is simple tls, no istio-peer-exchange in the alpns |
| name: "tcp server, tls SIMPLE", |
| server: &networking.Server{ |
| Hosts: []string{"httpbin.example.com", "bookinfo.example.com"}, |
| Port: &networking.Port{ |
| Protocol: string(protocol.TLS), |
| }, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_SIMPLE, |
| CredentialName: "ingress-sds-resource-name", |
| }, |
| }, |
| result: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "kubernetes://ingress-sds-resource-name", |
| SdsConfig: model.SDSAdsConfig, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolFalse, |
| }, |
| }, |
| } |
| |
| for _, tc := range testCases { |
| t.Run(tc.name, func(t *testing.T) { |
| ret := buildGatewayListenerTLSContext(tc.server, &pilot_model.Proxy{ |
| Metadata: &pilot_model.NodeMetadata{}, |
| }, tc.transportProtocol) |
| if diff := cmp.Diff(tc.result, ret, protocmp.Transform()); diff != "" { |
| t.Errorf("got diff: %v", diff) |
| } |
| }) |
| } |
| } |
| |
| func TestCreateGatewayHTTPFilterChainOpts(t *testing.T) { |
| var stripPortMode *hcm.HttpConnectionManager_StripAnyHostPort |
| testCases := []struct { |
| name string |
| node *pilot_model.Proxy |
| server *networking.Server |
| routeName string |
| proxyConfig *meshconfig.ProxyConfig |
| result *filterChainOpts |
| transportProtocol istionetworking.TransportProtocol |
| }{ |
| { |
| name: "HTTP1.0 mode enabled", |
| node: &pilot_model.Proxy{ |
| Metadata: &pilot_model.NodeMetadata{HTTP10: "1"}, |
| }, |
| server: &networking.Server{ |
| Port: &networking.Port{ |
| Protocol: protocol.HTTP.String(), |
| }, |
| }, |
| routeName: "some-route", |
| proxyConfig: nil, |
| result: &filterChainOpts{ |
| sniHosts: nil, |
| tlsContext: nil, |
| httpOpts: &httpListenerOpts{ |
| rds: "some-route", |
| useRemoteAddress: true, |
| connectionManager: &hcm.HttpConnectionManager{ |
| XffNumTrustedHops: 0, |
| ForwardClientCertDetails: hcm.HttpConnectionManager_SANITIZE_SET, |
| SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ |
| Subject: proto.BoolTrue, |
| Cert: true, |
| Uri: true, |
| Dns: true, |
| }, |
| ServerName: EnvoyServerName, |
| HttpProtocolOptions: &core.Http1ProtocolOptions{ |
| AcceptHttp_10: true, |
| }, |
| StripPortMode: stripPortMode, |
| }, |
| class: istionetworking.ListenerClassGateway, |
| protocol: protocol.HTTP, |
| }, |
| }, |
| }, |
| { |
| name: "Duplicate hosts in TLS filterChain", |
| node: &pilot_model.Proxy{Metadata: &pilot_model.NodeMetadata{}}, |
| server: &networking.Server{ |
| Port: &networking.Port{ |
| Protocol: "HTTPS", |
| }, |
| Hosts: []string{"example.org", "example.org"}, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, |
| }, |
| }, |
| routeName: "some-route", |
| proxyConfig: nil, |
| result: &filterChainOpts{ |
| sniHosts: []string{"example.org"}, |
| tlsContext: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "default", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| InitialFetchTimeout: durationpb.New(time.Second * 0), |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ |
| CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ |
| DefaultValidationContext: &auth.CertificateValidationContext{}, |
| ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ |
| Name: "ROOTCA", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| InitialFetchTimeout: durationpb.New(time.Second * 0), |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolTrue, |
| }, |
| httpOpts: &httpListenerOpts{ |
| rds: "some-route", |
| useRemoteAddress: true, |
| connectionManager: &hcm.HttpConnectionManager{ |
| XffNumTrustedHops: 0, |
| ForwardClientCertDetails: hcm.HttpConnectionManager_SANITIZE_SET, |
| SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ |
| Subject: proto.BoolTrue, |
| Cert: true, |
| Uri: true, |
| Dns: true, |
| }, |
| ServerName: EnvoyServerName, |
| HttpProtocolOptions: &core.Http1ProtocolOptions{}, |
| StripPortMode: stripPortMode, |
| }, |
| class: istionetworking.ListenerClassGateway, |
| protocol: protocol.HTTPS, |
| }, |
| }, |
| }, |
| { |
| name: "Unique hosts in TLS filterChain", |
| node: &pilot_model.Proxy{Metadata: &pilot_model.NodeMetadata{}}, |
| server: &networking.Server{ |
| Port: &networking.Port{ |
| Protocol: "HTTPS", |
| }, |
| Hosts: []string{"example.org", "test.org"}, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, |
| }, |
| }, |
| routeName: "some-route", |
| proxyConfig: nil, |
| result: &filterChainOpts{ |
| sniHosts: []string{"example.org", "test.org"}, |
| tlsContext: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "default", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| InitialFetchTimeout: durationpb.New(time.Second * 0), |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ |
| CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ |
| DefaultValidationContext: &auth.CertificateValidationContext{}, |
| ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ |
| Name: "ROOTCA", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| InitialFetchTimeout: durationpb.New(time.Second * 0), |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolTrue, |
| }, |
| httpOpts: &httpListenerOpts{ |
| rds: "some-route", |
| useRemoteAddress: true, |
| connectionManager: &hcm.HttpConnectionManager{ |
| XffNumTrustedHops: 0, |
| ForwardClientCertDetails: hcm.HttpConnectionManager_SANITIZE_SET, |
| SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ |
| Subject: proto.BoolTrue, |
| Cert: true, |
| Uri: true, |
| Dns: true, |
| }, |
| ServerName: EnvoyServerName, |
| HttpProtocolOptions: &core.Http1ProtocolOptions{}, |
| StripPortMode: stripPortMode, |
| }, |
| class: istionetworking.ListenerClassGateway, |
| protocol: protocol.HTTPS, |
| }, |
| }, |
| }, |
| { |
| name: "Wildcard hosts in TLS filterChain are not duplicates", |
| node: &pilot_model.Proxy{Metadata: &pilot_model.NodeMetadata{}}, |
| server: &networking.Server{ |
| Port: &networking.Port{ |
| Protocol: "HTTPS", |
| }, |
| Hosts: []string{"*.example.org", "example.org"}, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, |
| }, |
| }, |
| routeName: "some-route", |
| proxyConfig: nil, |
| result: &filterChainOpts{ |
| sniHosts: []string{"*.example.org", "example.org"}, |
| tlsContext: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "default", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| InitialFetchTimeout: durationpb.New(time.Second * 0), |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ |
| CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ |
| DefaultValidationContext: &auth.CertificateValidationContext{}, |
| ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ |
| Name: "ROOTCA", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| InitialFetchTimeout: durationpb.New(time.Second * 0), |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolTrue, |
| }, |
| httpOpts: &httpListenerOpts{ |
| rds: "some-route", |
| useRemoteAddress: true, |
| connectionManager: &hcm.HttpConnectionManager{ |
| XffNumTrustedHops: 0, |
| ForwardClientCertDetails: hcm.HttpConnectionManager_SANITIZE_SET, |
| SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ |
| Subject: proto.BoolTrue, |
| Cert: true, |
| Uri: true, |
| Dns: true, |
| }, |
| ServerName: EnvoyServerName, |
| HttpProtocolOptions: &core.Http1ProtocolOptions{}, |
| StripPortMode: stripPortMode, |
| }, |
| class: istionetworking.ListenerClassGateway, |
| protocol: protocol.HTTPS, |
| }, |
| }, |
| }, |
| { |
| name: "Topology HTTP Protocol", |
| node: &pilot_model.Proxy{Metadata: &pilot_model.NodeMetadata{}}, |
| server: &networking.Server{ |
| Port: &networking.Port{ |
| Protocol: protocol.HTTP.String(), |
| }, |
| }, |
| routeName: "some-route", |
| proxyConfig: &meshconfig.ProxyConfig{ |
| GatewayTopology: &meshconfig.Topology{ |
| NumTrustedProxies: 2, |
| ForwardClientCertDetails: meshconfig.Topology_APPEND_FORWARD, |
| }, |
| }, |
| result: &filterChainOpts{ |
| sniHosts: nil, |
| tlsContext: nil, |
| httpOpts: &httpListenerOpts{ |
| rds: "some-route", |
| useRemoteAddress: true, |
| connectionManager: &hcm.HttpConnectionManager{ |
| XffNumTrustedHops: 2, |
| ForwardClientCertDetails: hcm.HttpConnectionManager_APPEND_FORWARD, |
| SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ |
| Subject: proto.BoolTrue, |
| Cert: true, |
| Uri: true, |
| Dns: true, |
| }, |
| ServerName: EnvoyServerName, |
| HttpProtocolOptions: &core.Http1ProtocolOptions{}, |
| StripPortMode: stripPortMode, |
| }, |
| class: istionetworking.ListenerClassGateway, |
| protocol: protocol.HTTP, |
| }, |
| }, |
| }, |
| { |
| name: "Topology HTTPS Protocol", |
| node: &pilot_model.Proxy{Metadata: &pilot_model.NodeMetadata{}}, |
| server: &networking.Server{ |
| Port: &networking.Port{ |
| Protocol: "HTTPS", |
| }, |
| Hosts: []string{"example.org"}, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, |
| }, |
| }, |
| routeName: "some-route", |
| proxyConfig: &meshconfig.ProxyConfig{ |
| GatewayTopology: &meshconfig.Topology{ |
| NumTrustedProxies: 3, |
| ForwardClientCertDetails: meshconfig.Topology_FORWARD_ONLY, |
| }, |
| }, |
| result: &filterChainOpts{ |
| sniHosts: []string{"example.org"}, |
| tlsContext: &auth.DownstreamTlsContext{ |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "default", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| InitialFetchTimeout: durationpb.New(time.Second * 0), |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ |
| CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ |
| DefaultValidationContext: &auth.CertificateValidationContext{}, |
| ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ |
| Name: "ROOTCA", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| InitialFetchTimeout: durationpb.New(time.Second * 0), |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| RequireClientCertificate: proto.BoolTrue, |
| }, |
| httpOpts: &httpListenerOpts{ |
| rds: "some-route", |
| useRemoteAddress: true, |
| connectionManager: &hcm.HttpConnectionManager{ |
| XffNumTrustedHops: 3, |
| ForwardClientCertDetails: hcm.HttpConnectionManager_FORWARD_ONLY, |
| SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ |
| Subject: proto.BoolTrue, |
| Cert: true, |
| Uri: true, |
| Dns: true, |
| }, |
| ServerName: EnvoyServerName, |
| HttpProtocolOptions: &core.Http1ProtocolOptions{}, |
| StripPortMode: stripPortMode, |
| }, |
| class: istionetworking.ListenerClassGateway, |
| protocol: protocol.HTTPS, |
| }, |
| }, |
| }, |
| { |
| name: "HTTPS Protocol with server name", |
| node: &pilot_model.Proxy{Metadata: &pilot_model.NodeMetadata{}}, |
| server: &networking.Server{ |
| Name: "server1", |
| Port: &networking.Port{ |
| Protocol: "HTTPS", |
| }, |
| Hosts: []string{"example.org"}, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, |
| }, |
| }, |
| routeName: "some-route", |
| proxyConfig: &meshconfig.ProxyConfig{ |
| GatewayTopology: &meshconfig.Topology{ |
| NumTrustedProxies: 3, |
| ForwardClientCertDetails: meshconfig.Topology_FORWARD_ONLY, |
| }, |
| }, |
| result: &filterChainOpts{ |
| sniHosts: []string{"example.org"}, |
| tlsContext: &auth.DownstreamTlsContext{ |
| RequireClientCertificate: proto.BoolTrue, |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "default", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| InitialFetchTimeout: durationpb.New(time.Second * 0), |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| ValidationContextType: &auth.CommonTlsContext_CombinedValidationContext{ |
| CombinedValidationContext: &auth.CommonTlsContext_CombinedCertificateValidationContext{ |
| DefaultValidationContext: &auth.CertificateValidationContext{}, |
| ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{ |
| Name: "ROOTCA", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| InitialFetchTimeout: durationpb.New(time.Second * 0), |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: model.SDSClusterName}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| httpOpts: &httpListenerOpts{ |
| rds: "some-route", |
| useRemoteAddress: true, |
| connectionManager: &hcm.HttpConnectionManager{ |
| XffNumTrustedHops: 3, |
| ForwardClientCertDetails: hcm.HttpConnectionManager_FORWARD_ONLY, |
| SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ |
| Subject: proto.BoolTrue, |
| Cert: true, |
| Uri: true, |
| Dns: true, |
| }, |
| ServerName: EnvoyServerName, |
| HttpProtocolOptions: &core.Http1ProtocolOptions{}, |
| StripPortMode: stripPortMode, |
| }, |
| statPrefix: "server1", |
| class: istionetworking.ListenerClassGateway, |
| protocol: protocol.HTTPS, |
| }, |
| }, |
| }, |
| { |
| name: "QUIC protocol with server name", |
| node: &pilot_model.Proxy{Metadata: &pilot_model.NodeMetadata{}}, |
| transportProtocol: istionetworking.TransportProtocolQUIC, |
| server: &networking.Server{ |
| Name: "server1", |
| Port: &networking.Port{ |
| Name: "https-app", |
| Number: 443, |
| TargetPort: 8443, |
| Protocol: "HTTPS", |
| }, |
| Hosts: []string{"example.org"}, |
| Tls: &networking.ServerTLSSettings{ |
| Mode: networking.ServerTLSSettings_SIMPLE, |
| ServerCertificate: "/etc/cert/example.crt", |
| PrivateKey: "/etc/cert/example.key", |
| }, |
| }, |
| routeName: "some-route", |
| proxyConfig: &meshconfig.ProxyConfig{ |
| GatewayTopology: &meshconfig.Topology{ |
| NumTrustedProxies: 3, |
| ForwardClientCertDetails: meshconfig.Topology_FORWARD_ONLY, |
| }, |
| }, |
| result: &filterChainOpts{ |
| sniHosts: []string{"example.org"}, |
| tlsContext: &auth.DownstreamTlsContext{ |
| RequireClientCertificate: proto.BoolFalse, |
| CommonTlsContext: &auth.CommonTlsContext{ |
| AlpnProtocols: util.ALPNHttp3OverQUIC, |
| TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{ |
| { |
| Name: "file-cert:/etc/cert/example.crt~/etc/cert/example.key", |
| SdsConfig: &core.ConfigSource{ |
| ResourceApiVersion: core.ApiVersion_V3, |
| ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{ |
| ApiConfigSource: &core.ApiConfigSource{ |
| ApiType: core.ApiConfigSource_GRPC, |
| GrpcServices: []*core.GrpcService{ |
| { |
| TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ |
| EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ |
| ClusterName: "sds-grpc", |
| }, |
| }, |
| }, |
| }, |
| SetNodeOnFirstMessageOnly: true, |
| TransportApiVersion: core.ApiVersion_V3, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| httpOpts: &httpListenerOpts{ |
| rds: "some-route", |
| http3Only: true, |
| connectionManager: &hcm.HttpConnectionManager{ |
| XffNumTrustedHops: 3, |
| ForwardClientCertDetails: hcm.HttpConnectionManager_FORWARD_ONLY, |
| SetCurrentClientCertDetails: &hcm.HttpConnectionManager_SetCurrentClientCertDetails{ |
| Subject: proto.BoolTrue, |
| Cert: true, |
| Uri: true, |
| Dns: true, |
| }, |
| ServerName: EnvoyServerName, |
| HttpProtocolOptions: &core.Http1ProtocolOptions{}, |
| Http3ProtocolOptions: &core.Http3ProtocolOptions{}, |
| CodecType: hcm.HttpConnectionManager_HTTP3, |
| StripPortMode: stripPortMode, |
| }, |
| useRemoteAddress: true, |
| statPrefix: "server1", |
| class: istionetworking.ListenerClassGateway, |
| protocol: protocol.HTTPS, |
| }, |
| }, |
| }, |
| } |
| |
| for _, tc := range testCases { |
| t.Run(tc.name, func(t *testing.T) { |
| cgi := NewConfigGenerator(&pilot_model.DisabledCache{}) |
| tc.node.MergedGateway = &pilot_model.MergedGateway{TLSServerInfo: map[*networking.Server]*pilot_model.TLSServerInfo{ |
| tc.server: {SNIHosts: pilot_model.GetSNIHostsForServer(tc.server)}, |
| }} |
| ret := cgi.createGatewayHTTPFilterChainOpts(tc.node, tc.server.Port, tc.server, |
| tc.routeName, tc.proxyConfig, tc.transportProtocol) |
| if diff := cmp.Diff(tc.result.tlsContext, ret.tlsContext, protocmp.Transform()); diff != "" { |
| t.Errorf("got diff in tls context: %v", diff) |
| } |
| if !reflect.DeepEqual(tc.result.httpOpts, ret.httpOpts) { |
| t.Errorf("expecting httpopts:\n %+v \nbut got:\n %+v", tc.result.httpOpts, ret.httpOpts) |
| } |
| if !reflect.DeepEqual(tc.result.sniHosts, ret.sniHosts) { |
| t.Errorf("expecting snihosts %+v but got %+v", tc.result.sniHosts, ret.sniHosts) |
| } |
| }) |
| } |
| } |
| |
| func TestGatewayHTTPRouteConfig(t *testing.T) { |
| httpRedirectGateway := config.Config{ |
| Meta: config.Meta{ |
| Name: "gateway-redirect", |
| Namespace: "default", |
| GroupVersionKind: gvk.Gateway, |
| }, |
| Spec: &networking.Gateway{ |
| Selector: map[string]string{"istio": "ingressgateway"}, |
| Servers: []*networking.Server{ |
| { |
| Hosts: []string{"example.org"}, |
| Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, |
| Tls: &networking.ServerTLSSettings{HttpsRedirect: true}, |
| }, |
| }, |
| }, |
| } |
| httpRedirectGatewayWithoutVS := config.Config{ |
| Meta: config.Meta{ |
| Name: "gateway-redirect-noroutes", |
| Namespace: "default", |
| GroupVersionKind: gvk.Gateway, |
| }, |
| Spec: &networking.Gateway{ |
| Selector: map[string]string{"istio": "ingressgateway"}, |
| Servers: []*networking.Server{ |
| { |
| Hosts: []string{"example.org"}, |
| Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, |
| Tls: &networking.ServerTLSSettings{HttpsRedirect: true}, |
| }, |
| }, |
| }, |
| } |
| httpGateway := config.Config{ |
| Meta: config.Meta{ |
| Name: "gateway", |
| Namespace: "default", |
| GroupVersionKind: gvk.Gateway, |
| }, |
| Spec: &networking.Gateway{ |
| Selector: map[string]string{"istio": "ingressgateway"}, |
| Servers: []*networking.Server{ |
| { |
| Hosts: []string{"example.org"}, |
| Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, |
| }, |
| }, |
| }, |
| } |
| httpsGateway := config.Config{ |
| Meta: config.Meta{ |
| Name: "gateway-https", |
| Namespace: "default", |
| GroupVersionKind: gvk.Gateway, |
| }, |
| Spec: &networking.Gateway{ |
| Selector: map[string]string{"istio": "ingressgateway"}, |
| Servers: []*networking.Server{ |
| { |
| Hosts: []string{"example.org"}, |
| Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, |
| Tls: &networking.ServerTLSSettings{HttpsRedirect: true}, |
| }, |
| { |
| Hosts: []string{"example.org"}, |
| Port: &networking.Port{Name: "https", Number: 443, Protocol: "HTTPS"}, |
| Tls: &networking.ServerTLSSettings{Mode: networking.ServerTLSSettings_TLSmode(networking.ClientTLSSettings_SIMPLE)}, |
| }, |
| }, |
| }, |
| } |
| httpsGatewayRedirect := config.Config{ |
| Meta: config.Meta{ |
| Name: "gateway-https", |
| Namespace: "default", |
| GroupVersionKind: gvk.Gateway, |
| }, |
| Spec: &networking.Gateway{ |
| Selector: map[string]string{"istio": "ingressgateway"}, |
| Servers: []*networking.Server{ |
| { |
| Hosts: []string{"example.org"}, |
| Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, |
| Tls: &networking.ServerTLSSettings{HttpsRedirect: true}, |
| }, |
| { |
| Hosts: []string{"example.org"}, |
| Port: &networking.Port{Name: "https", Number: 443, Protocol: "HTTPS"}, |
| Tls: &networking.ServerTLSSettings{HttpsRedirect: true, Mode: networking.ServerTLSSettings_TLSmode(networking.ClientTLSSettings_SIMPLE)}, |
| }, |
| }, |
| }, |
| } |
| httpGatewayWildcard := config.Config{ |
| Meta: config.Meta{ |
| Name: "gateway", |
| Namespace: "default", |
| GroupVersionKind: gvk.Gateway, |
| }, |
| Spec: &networking.Gateway{ |
| Selector: map[string]string{"istio": "ingressgateway"}, |
| Servers: []*networking.Server{ |
| { |
| Hosts: []string{"*"}, |
| Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, |
| }, |
| }, |
| }, |
| } |
| virtualServiceSpec := &networking.VirtualService{ |
| Hosts: []string{"example.org"}, |
| Gateways: []string{"gateway", "gateway-redirect"}, |
| Http: []*networking.HTTPRoute{ |
| { |
| Route: []*networking.HTTPRouteDestination{ |
| { |
| Destination: &networking.Destination{ |
| Host: "example.org", |
| Port: &networking.PortSelector{ |
| Number: 80, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| } |
| virtualServiceHTTPSMatchSpec := &networking.VirtualService{ |
| Hosts: []string{"example.org"}, |
| Gateways: []string{"gateway-https"}, |
| Http: []*networking.HTTPRoute{ |
| { |
| Match: []*networking.HTTPMatchRequest{ |
| { |
| Port: 443, |
| }, |
| }, |
| Route: []*networking.HTTPRouteDestination{ |
| { |
| Destination: &networking.Destination{ |
| Host: "example.default.svc.cluster.local", |
| Port: &networking.PortSelector{ |
| Number: 8080, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| } |
| virtualService := config.Config{ |
| Meta: config.Meta{ |
| GroupVersionKind: gvk.VirtualService, |
| Name: "virtual-service", |
| Namespace: "default", |
| }, |
| Spec: virtualServiceSpec, |
| } |
| virtualServiceHTTPS := config.Config{ |
| Meta: config.Meta{ |
| GroupVersionKind: gvk.VirtualService, |
| Name: "virtual-service-https", |
| Namespace: "default", |
| }, |
| Spec: virtualServiceHTTPSMatchSpec, |
| } |
| virtualServiceCopy := config.Config{ |
| Meta: config.Meta{ |
| GroupVersionKind: gvk.VirtualService, |
| Name: "virtual-service-copy", |
| Namespace: "default", |
| }, |
| Spec: virtualServiceSpec, |
| } |
| virtualServiceWildcard := config.Config{ |
| Meta: config.Meta{ |
| GroupVersionKind: gvk.VirtualService, |
| Name: "virtual-service-wildcard", |
| Namespace: "default", |
| }, |
| Spec: &networking.VirtualService{ |
| Hosts: []string{"*.org"}, |
| Gateways: []string{"gateway", "gateway-redirect"}, |
| Http: []*networking.HTTPRoute{ |
| { |
| Route: []*networking.HTTPRouteDestination{ |
| { |
| Destination: &networking.Destination{ |
| Host: "example.org", |
| Port: &networking.PortSelector{ |
| Number: 80, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| } |
| cases := []struct { |
| name string |
| virtualServices []config.Config |
| gateways []config.Config |
| routeName string |
| expectedVirtualHosts map[string][]string |
| expectedVirtualHostsHostPortStrip map[string][]string |
| expectedHTTPRoutes map[string]int |
| redirect bool |
| }{ |
| { |
| "404 when no services", |
| []config.Config{}, |
| []config.Config{httpGateway}, |
| "http.80", |
| map[string][]string{ |
| "blackhole:80": { |
| "*", |
| }, |
| }, |
| map[string][]string{ |
| "blackhole:80": { |
| "*", |
| }, |
| }, |
| map[string]int{"blackhole:80": 0}, |
| false, |
| }, |
| { |
| "tls redirect without virtual services", |
| []config.Config{virtualService}, |
| []config.Config{httpRedirectGatewayWithoutVS}, |
| "http.80", |
| map[string][]string{ |
| "example.org:80": { |
| "example.org", "example.org:*", |
| }, |
| }, |
| map[string][]string{ |
| "example.org:80": {"example.org"}, |
| }, |
| // We will setup a VHost which just redirects; no routes |
| map[string]int{"example.org:80": 0}, |
| true, |
| }, |
| { |
| "virtual services with tls redirect", |
| []config.Config{virtualService}, |
| []config.Config{httpRedirectGateway}, |
| "http.80", |
| map[string][]string{ |
| "example.org:80": { |
| "example.org", "example.org:*", |
| }, |
| }, |
| map[string][]string{ |
| "example.org:80": {"example.org"}, |
| }, |
| map[string]int{"example.org:80": 1}, |
| true, |
| }, |
| { |
| "merging of virtual services when tls redirect is set", |
| []config.Config{virtualService, virtualServiceCopy}, |
| []config.Config{httpRedirectGateway, httpGateway}, |
| "http.80", |
| map[string][]string{ |
| "example.org:80": { |
| "example.org", "example.org:*", |
| }, |
| }, |
| map[string][]string{ |
| "example.org:80": {"example.org"}, |
| }, |
| map[string]int{"example.org:80": 4}, |
| true, |
| }, |
| { |
| "reverse merging of virtual services when tls redirect is set", |
| []config.Config{virtualService, virtualServiceCopy}, |
| []config.Config{httpGateway, httpRedirectGateway}, |
| "http.80", |
| map[string][]string{ |
| "example.org:80": { |
| "example.org", "example.org:*", |
| }, |
| }, |
| map[string][]string{ |
| "example.org:80": {"example.org"}, |
| }, |
| map[string]int{"example.org:80": 4}, |
| true, |
| }, |
| { |
| "merging of virtual services when tls redirect is set without VS", |
| []config.Config{virtualService, virtualServiceCopy}, |
| []config.Config{httpGateway, httpRedirectGatewayWithoutVS}, |
| "http.80", |
| map[string][]string{ |
| "example.org:80": { |
| "example.org", "example.org:*", |
| }, |
| }, |
| map[string][]string{ |
| "example.org:80": {"example.org"}, |
| }, |
| map[string]int{"example.org:80": 2}, |
| true, |
| }, |
| { |
| "reverse merging of virtual services when tls redirect is set without VS", |
| []config.Config{virtualService, virtualServiceCopy}, |
| []config.Config{httpRedirectGatewayWithoutVS, httpGateway}, |
| "http.80", |
| map[string][]string{ |
| "example.org:80": { |
| "example.org", "example.org:*", |
| }, |
| }, |
| map[string][]string{ |
| "example.org:80": {"example.org"}, |
| }, |
| map[string]int{"example.org:80": 2}, |
| true, |
| }, |
| { |
| "add a route for a virtual service", |
| []config.Config{virtualService}, |
| []config.Config{httpGateway}, |
| "http.80", |
| map[string][]string{ |
| "example.org:80": { |
| "example.org", "example.org:*", |
| }, |
| }, |
| map[string][]string{ |
| "example.org:80": {"example.org"}, |
| }, |
| map[string]int{"example.org:80": 1}, |
| false, |
| }, |
| { |
| "duplicate virtual service should merge", |
| []config.Config{virtualService, virtualServiceCopy}, |
| []config.Config{httpGateway}, |
| "http.80", |
| map[string][]string{ |
| "example.org:80": { |
| "example.org", "example.org:*", |
| }, |
| }, |
| map[string][]string{ |
| "example.org:80": {"example.org"}, |
| }, |
| map[string]int{"example.org:80": 2}, |
| false, |
| }, |
| { |
| "duplicate by wildcard should merge", |
| []config.Config{virtualService, virtualServiceWildcard}, |
| []config.Config{httpGateway}, |
| "http.80", |
| map[string][]string{ |
| "example.org:80": { |
| "example.org", "example.org:*", |
| }, |
| }, |
| map[string][]string{ |
| "example.org:80": {"example.org"}, |
| }, |
| map[string]int{"example.org:80": 2}, |
| false, |
| }, |
| { |
| "wildcard virtual service", |
| []config.Config{virtualServiceWildcard}, |
| []config.Config{httpGatewayWildcard}, |
| "http.80", |
| map[string][]string{ |
| "*.org:80": {"*.org", "*.org:80"}, |
| }, |
| map[string][]string{ |
| "*.org:80": {"*.org"}, |
| }, |
| map[string]int{"*.org:80": 1}, |
| false, |
| }, |
| { |
| "http redirection not working when virtualservice not match http port", |
| []config.Config{virtualServiceHTTPS}, |
| []config.Config{httpsGateway}, |
| "https.443.https.gateway-https.default", |
| map[string][]string{ |
| "example.org:443": {"example.org", "example.org:*"}, |
| }, |
| map[string][]string{ |
| "example.org:443": {"example.org"}, |
| }, |
| map[string]int{"example.org:443": 1}, |
| false, |
| }, |
| { |
| "http redirection not working when virtualservice not match http port", |
| []config.Config{virtualServiceHTTPS}, |
| []config.Config{httpsGateway}, |
| "http.80", |
| map[string][]string{ |
| "example.org:80": {"example.org", "example.org:*"}, |
| }, |
| map[string][]string{ |
| "example.org:80": {"example.org"}, |
| }, |
| // We will setup a VHost which just redirects; no routes |
| map[string]int{"example.org:80": 0}, |
| true, |
| }, |
| { |
| "http & https redirection not working when virtualservice not match http port", |
| []config.Config{virtualServiceHTTPS}, |
| []config.Config{httpsGatewayRedirect}, |
| "https.443.https.gateway-https.default", |
| map[string][]string{ |
| "example.org:443": {"example.org", "example.org:*"}, |
| }, |
| map[string][]string{ |
| "example.org:443": {"example.org"}, |
| }, |
| map[string]int{"example.org:443": 1}, |
| true, |
| }, |
| { |
| "http & https redirection not working when virtualservice not match http port", |
| []config.Config{virtualServiceHTTPS}, |
| []config.Config{httpsGatewayRedirect}, |
| "http.80", |
| map[string][]string{ |
| "example.org:80": {"example.org", "example.org:*"}, |
| }, |
| map[string][]string{ |
| "example.org:80": {"example.org"}, |
| }, |
| // We will setup a VHost which just redirects; no routes |
| map[string]int{"example.org:80": 0}, |
| true, |
| }, |
| } |
| |
| for _, value := range []bool{false, true} { |
| for _, tt := range cases { |
| t.Run(tt.name, func(t *testing.T) { |
| test.SetBoolForTest(t, &features.StripHostPort, value) |
| cfgs := tt.gateways |
| cfgs = append(cfgs, tt.virtualServices...) |
| cg := NewConfigGenTest(t, TestOptions{ |
| Configs: cfgs, |
| }) |
| r := cg.ConfigGen.buildGatewayHTTPRouteConfig(cg.SetupProxy(&proxyGateway), cg.PushContext(), tt.routeName) |
| if r == nil { |
| t.Fatal("got an empty route configuration") |
| } |
| vh := make(map[string][]string) |
| hr := make(map[string]int) |
| for _, h := range r.VirtualHosts { |
| vh[h.Name] = h.Domains |
| hr[h.Name] = len(h.Routes) |
| if h.Name != "blackhole:80" && !h.IncludeRequestAttemptCount { |
| t.Errorf("expected attempt count to be set in virtual host, but not found") |
| } |
| if tt.redirect != (h.RequireTls == route.VirtualHost_ALL) { |
| t.Errorf("expected redirect %v, got %v", tt.redirect, h.RequireTls) |
| } |
| } |
| |
| if features.StripHostPort { |
| if !reflect.DeepEqual(tt.expectedVirtualHostsHostPortStrip, vh) { |
| t.Errorf("got unexpected virtual hosts. Expected: %v, Got: %v", tt.expectedVirtualHostsHostPortStrip, vh) |
| } |
| } else { |
| if !reflect.DeepEqual(tt.expectedVirtualHosts, vh) { |
| t.Errorf("got unexpected virtual hosts. Expected: %v, Got: %v", tt.expectedVirtualHosts, vh) |
| } |
| } |
| if !reflect.DeepEqual(tt.expectedHTTPRoutes, hr) { |
| t.Errorf("got unexpected number of http routes. Expected: %v, Got: %v", tt.expectedHTTPRoutes, hr) |
| } |
| }) |
| } |
| } |
| } |
| |
| func TestBuildGatewayListeners(t *testing.T) { |
| cases := []struct { |
| name string |
| node *pilot_model.Proxy |
| gateways []config.Config |
| virtualServices []config.Config |
| expectedListeners []string |
| }{ |
| { |
| "targetPort overrides service port", |
| &pilot_model.Proxy{ |
| ServiceInstances: []*pilot_model.ServiceInstance{ |
| { |
| Service: &pilot_model.Service{ |
| Hostname: "test", |
| }, |
| ServicePort: &pilot_model.Port{ |
| Port: 80, |
| }, |
| Endpoint: &pilot_model.IstioEndpoint{ |
| EndpointPort: 8080, |
| }, |
| }, |
| }, |
| }, |
| []config.Config{ |
| { |
| Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.Gateway}, |
| Spec: &networking.Gateway{ |
| Servers: []*networking.Server{ |
| { |
| Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| nil, |
| []string{"0.0.0.0_8080"}, |
| }, |
| { |
| "multiple ports", |
| &pilot_model.Proxy{}, |
| []config.Config{ |
| { |
| Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.Gateway}, |
| Spec: &networking.Gateway{ |
| Servers: []*networking.Server{ |
| { |
| Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, |
| }, |
| { |
| Port: &networking.Port{Name: "http", Number: 801, Protocol: "HTTP"}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| nil, |
| []string{"0.0.0.0_80", "0.0.0.0_801"}, |
| }, |
| { |
| "privileged port on unprivileged pod", |
| &pilot_model.Proxy{ |
| Metadata: &pilot_model.NodeMetadata{ |
| UnprivilegedPod: "true", |
| }, |
| }, |
| []config.Config{ |
| { |
| Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.Gateway}, |
| Spec: &networking.Gateway{ |
| Servers: []*networking.Server{ |
| { |
| Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, |
| }, |
| { |
| Port: &networking.Port{Name: "http", Number: 8080, Protocol: "HTTP"}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| nil, |
| []string{"0.0.0.0_8080"}, |
| }, |
| { |
| "privileged port on privileged pod when empty env var is set", |
| &pilot_model.Proxy{ |
| Metadata: &pilot_model.NodeMetadata{ |
| UnprivilegedPod: "", |
| }, |
| }, |
| []config.Config{ |
| { |
| Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.Gateway}, |
| Spec: &networking.Gateway{ |
| Servers: []*networking.Server{ |
| { |
| Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, |
| }, |
| { |
| Port: &networking.Port{Name: "http", Number: 8080, Protocol: "HTTP"}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| nil, |
| []string{"0.0.0.0_80", "0.0.0.0_8080"}, |
| }, |
| { |
| "privileged port on privileged pod", |
| &pilot_model.Proxy{}, |
| []config.Config{ |
| { |
| Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.Gateway}, |
| Spec: &networking.Gateway{ |
| Servers: []*networking.Server{ |
| { |
| Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, |
| }, |
| { |
| Port: &networking.Port{Name: "http", Number: 8080, Protocol: "HTTP"}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| nil, |
| []string{"0.0.0.0_80", "0.0.0.0_8080"}, |
| }, |
| { |
| "gateway with bind", |
| &pilot_model.Proxy{}, |
| []config.Config{ |
| { |
| Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.Gateway}, |
| Spec: &networking.Gateway{ |
| Servers: []*networking.Server{ |
| { |
| Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, |
| }, |
| { |
| Port: &networking.Port{Name: "http", Number: 8080, Protocol: "HTTP"}, |
| Hosts: []string{"externalgatewayclient.com"}, |
| }, |
| { |
| Port: &networking.Port{Name: "http", Number: 8080, Protocol: "HTTP"}, |
| Bind: "127.0.0.1", |
| Hosts: []string{"internalmesh.svc.cluster.local"}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| nil, |
| []string{"0.0.0.0_80", "0.0.0.0_8080", "127.0.0.1_8080"}, |
| }, |
| { |
| "gateway with simple and passthrough", |
| &pilot_model.Proxy{}, |
| []config.Config{ |
| { |
| Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.Gateway}, |
| Spec: &networking.Gateway{ |
| Servers: []*networking.Server{ |
| { |
| Port: &networking.Port{Name: "http", Number: 443, Protocol: "HTTPS"}, |
| Hosts: []string{"*.example.com"}, |
| Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_SIMPLE}, |
| }, |
| { |
| Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, |
| Hosts: []string{"foo.example.com"}, |
| }, |
| }, |
| }, |
| }, |
| { |
| Meta: config.Meta{Name: "passthrough-gateway", Namespace: "testns", GroupVersionKind: gvk.Gateway}, |
| Spec: &networking.Gateway{ |
| Servers: []*networking.Server{ |
| { |
| Port: &networking.Port{Name: "http", Number: 443, Protocol: "HTTPS"}, |
| Hosts: []string{"*.example.com"}, |
| Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_SIMPLE}, |
| }, |
| { |
| Port: &networking.Port{Name: "tcp", Number: 9443, Protocol: "TLS"}, |
| Hosts: []string{"barone.example.com"}, |
| Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_PASSTHROUGH}, |
| }, |
| { |
| Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, |
| Hosts: []string{"bar.example.com"}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| []config.Config{ |
| { |
| Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.VirtualService}, |
| Spec: &networking.VirtualService{ |
| Gateways: []string{"testns/passthrough-gateway"}, |
| Hosts: []string{"barone.example.com"}, |
| Tls: []*networking.TLSRoute{ |
| { |
| Match: []*networking.TLSMatchAttributes{ |
| { |
| Port: 9443, |
| SniHosts: []string{"barone.example.com"}, |
| }, |
| }, |
| Route: []*networking.RouteDestination{ |
| { |
| Destination: &networking.Destination{ |
| Host: "foo.com", |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| []string{"0.0.0.0_443", "0.0.0.0_80", "0.0.0.0_9443"}, |
| }, |
| { |
| "gateway with multiple http servers", |
| &pilot_model.Proxy{}, |
| []config.Config{ |
| { |
| Meta: config.Meta{Name: "gateway1", Namespace: "testns", GroupVersionKind: gvk.Gateway}, |
| Spec: &networking.Gateway{ |
| Servers: []*networking.Server{ |
| { |
| Port: &networking.Port{Name: "http", Number: 443, Protocol: "HTTPS"}, |
| Hosts: []string{"*.example.com"}, |
| Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_SIMPLE}, |
| }, |
| { |
| Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, |
| Hosts: []string{"foo.example.com"}, |
| }, |
| }, |
| }, |
| }, |
| { |
| Meta: config.Meta{Name: "gateway2", Namespace: "testns", GroupVersionKind: gvk.Gateway}, |
| Spec: &networking.Gateway{ |
| Servers: []*networking.Server{ |
| { |
| Port: &networking.Port{Name: "http", Number: 443, Protocol: "HTTPS"}, |
| Hosts: []string{"*.exampleone.com"}, |
| Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_SIMPLE}, |
| }, |
| { |
| Port: &networking.Port{Name: "http", Number: 80, Protocol: "HTTP"}, |
| Hosts: []string{"bar.example.com"}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| nil, |
| []string{"0.0.0.0_443", "0.0.0.0_80"}, |
| }, |
| { |
| "gateway with multiple TLS HTTPS TCP servers", |
| &pilot_model.Proxy{}, |
| []config.Config{ |
| { |
| Meta: config.Meta{Name: "gateway1", Namespace: "testns", GroupVersionKind: gvk.Gateway}, |
| Spec: &networking.Gateway{ |
| Servers: []*networking.Server{ |
| { |
| Port: &networking.Port{Name: "tcp", Number: 443, Protocol: "TLS"}, |
| Hosts: []string{"*.example.com"}, |
| Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_SIMPLE}, |
| }, |
| { |
| Port: &networking.Port{Name: "tcp", Number: 443, Protocol: "HTTPS"}, |
| Hosts: []string{"https.example.com"}, |
| Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_SIMPLE}, |
| }, |
| { |
| Port: &networking.Port{Name: "tcp", Number: 9443, Protocol: "TCP"}, |
| Hosts: []string{"tcp.example.com"}, |
| }, |
| }, |
| }, |
| }, |
| { |
| Meta: config.Meta{Name: "gateway2", Namespace: "testns", GroupVersionKind: gvk.Gateway}, |
| Spec: &networking.Gateway{ |
| Servers: []*networking.Server{ |
| { |
| Port: &networking.Port{Name: "http", Number: 443, Protocol: "HTTPS"}, |
| Hosts: []string{"*.exampleone.com"}, |
| Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_SIMPLE}, |
| }, |
| { |
| Port: &networking.Port{Name: "tcp", Number: 443, Protocol: "TLS"}, |
| Hosts: []string{"*.exampleone.com"}, |
| Tls: &networking.ServerTLSSettings{CredentialName: "test", Mode: networking.ServerTLSSettings_SIMPLE}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| []config.Config{ |
| { |
| Meta: config.Meta{Name: uuid.NewString(), Namespace: uuid.NewString(), GroupVersionKind: gvk.VirtualService}, |
| Spec: &networking.VirtualService{ |
| Gateways: []string{"testns/gateway1"}, |
| Hosts: []string{"tcp.example.com"}, |
| Tcp: []*networking.TCPRoute{ |
| { |
| Match: []*networking.L4MatchAttributes{ |
| { |
| Port: 9443, |
| }, |
| }, |
| Route: []*networking.RouteDestination{ |
| { |
| Destination: &networking.Destination{ |
| Host: "foo.com", |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| []string{"0.0.0.0_443", "0.0.0.0_9443"}, |
| }, |
| } |
| |
| for _, tt := range cases { |
| t.Run(tt.name, func(t *testing.T) { |
| Configs := make([]config.Config, 0) |
| Configs = append(Configs, tt.gateways...) |
| Configs = append(Configs, tt.virtualServices...) |
| cg := NewConfigGenTest(t, TestOptions{ |
| Configs: Configs, |
| }) |
| cg.MemRegistry.WantGetProxyServiceInstances = tt.node.ServiceInstances |
| proxy := cg.SetupProxy(&proxyGateway) |
| if tt.node.Metadata != nil { |
| proxy.Metadata = tt.node.Metadata |
| } else { |
| proxy.Metadata = &proxyGatewayMetadata |
| } |
| |
| builder := cg.ConfigGen.buildGatewayListeners(NewListenerBuilder(proxy, cg.PushContext())) |
| listeners := xdstest.ExtractListenerNames(builder.gatewayListeners) |
| sort.Strings(listeners) |
| sort.Strings(tt.expectedListeners) |
| if !reflect.DeepEqual(listeners, tt.expectedListeners) { |
| t.Fatalf("Expected listeners: %v, got: %v\n%v", tt.expectedListeners, listeners, proxyGateway.MergedGateway.MergedServers) |
| } |
| xdstest.ValidateListeners(t, builder.gatewayListeners) |
| |
| // gateways bind to port, but exact_balance can still be used |
| for _, l := range builder.gatewayListeners { |
| if l.ConnectionBalanceConfig != nil { |
| t.Fatalf("expected connection balance config to be empty, found %v", l.ConnectionBalanceConfig) |
| } |
| } |
| }) |
| } |
| } |
| |
| func TestBuildNameToServiceMapForHttpRoutes(t *testing.T) { |
| virtualServiceSpec := &networking.VirtualService{ |
| Hosts: []string{"*.example.org"}, |
| Http: []*networking.HTTPRoute{ |
| { |
| Route: []*networking.HTTPRouteDestination{ |
| { |
| Destination: &networking.Destination{ |
| Host: "foo.example.org", |
| }, |
| }, |
| { |
| Destination: &networking.Destination{ |
| Host: "bar.example.org", |
| }, |
| }, |
| }, |
| Mirror: &networking.Destination{ |
| Host: "baz.example.org", |
| }, |
| }, |
| }, |
| } |
| virtualService := config.Config{ |
| Meta: config.Meta{ |
| GroupVersionKind: gvk.VirtualService, |
| Name: "virtual-service", |
| Namespace: "test", |
| }, |
| Spec: virtualServiceSpec, |
| } |
| |
| fooHostName := host.Name("foo.example.org") |
| fooServiceInTestNamespace := &pilot_model.Service{ |
| Hostname: fooHostName, |
| Ports: []*pilot_model.Port{{ |
| Name: "http", |
| Protocol: "HTTP", |
| Port: 80, |
| }}, |
| Attributes: pilot_model.ServiceAttributes{ |
| Namespace: "test", |
| ExportTo: map[visibility.Instance]bool{ |
| visibility.Private: true, |
| }, |
| }, |
| } |
| |
| barHostName := host.Name("bar.example.org") |
| barServiceInDefaultNamespace := &pilot_model.Service{ |
| Hostname: barHostName, |
| Ports: []*pilot_model.Port{{ |
| Name: "http", |
| Protocol: "HTTP", |
| Port: 8080, |
| }}, |
| Attributes: pilot_model.ServiceAttributes{ |
| Namespace: "default", |
| ExportTo: map[visibility.Instance]bool{ |
| visibility.Public: true, |
| }, |
| }, |
| } |
| |
| bazHostName := host.Name("baz.example.org") |
| bazServiceInDefaultNamespace := &pilot_model.Service{ |
| Hostname: bazHostName, |
| Ports: []*pilot_model.Port{{ |
| Name: "http", |
| Protocol: "HTTP", |
| Port: 8090, |
| }}, |
| Attributes: pilot_model.ServiceAttributes{ |
| Namespace: "default", |
| ExportTo: map[visibility.Instance]bool{ |
| visibility.Private: true, |
| }, |
| }, |
| } |
| |
| cg := NewConfigGenTest(t, TestOptions{ |
| Configs: []config.Config{virtualService}, |
| Services: []*pilot_model.Service{fooServiceInTestNamespace, barServiceInDefaultNamespace, bazServiceInDefaultNamespace}, |
| }) |
| proxy := &pilot_model.Proxy{ |
| Type: pilot_model.Router, |
| ConfigNamespace: "test", |
| } |
| proxy = cg.SetupProxy(proxy) |
| |
| nameToServiceMap := buildNameToServiceMapForHTTPRoutes(proxy, cg.env.PushContext, virtualService) |
| |
| if len(nameToServiceMap) != 3 { |
| t.Errorf("The length of nameToServiceMap is wrong.") |
| } |
| |
| if service, exist := nameToServiceMap[fooHostName]; !exist || service == nil { |
| t.Errorf("The service of %s not found or should be not nil.", fooHostName) |
| } else { |
| if service.Ports[0].Port != 80 { |
| t.Errorf("The port of %s is wrong.", fooHostName) |
| } |
| |
| if service.Attributes.Namespace != "test" { |
| t.Errorf("The namespace of %s is wrong.", fooHostName) |
| } |
| } |
| |
| if service, exist := nameToServiceMap[barHostName]; !exist || service == nil { |
| t.Errorf("The service of %s not found or should be not nil", barHostName) |
| } else { |
| if service.Ports[0].Port != 8080 { |
| t.Errorf("The port of %s is wrong.", barHostName) |
| } |
| |
| if service.Attributes.Namespace != "default" { |
| t.Errorf("The namespace of %s is wrong.", barHostName) |
| } |
| } |
| |
| if service, exist := nameToServiceMap[bazHostName]; !exist || service != nil { |
| t.Errorf("The value of hostname %s mapping must be exist and it should be nil.", bazHostName) |
| } |
| } |