istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/security/sds_ingress/ingress_test.go (about)

     1  //go:build integ
     2  // +build integ
     3  
     4  //  Copyright Istio Authors
     5  //
     6  //  Licensed under the Apache License, Version 2.0 (the "License");
     7  //  you may not use this file except in compliance with the License.
     8  //  You may obtain a copy of the License at
     9  //
    10  //      http://www.apache.org/licenses/LICENSE-2.0
    11  //
    12  //  Unless required by applicable law or agreed to in writing, software
    13  //  distributed under the License is distributed on an "AS IS" BASIS,
    14  //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  //  See the License for the specific language governing permissions and
    16  //  limitations under the License.
    17  
    18  package sdsingress
    19  
    20  import (
    21  	"net/http"
    22  	"testing"
    23  
    24  	"istio.io/istio/pkg/test/framework"
    25  	"istio.io/istio/pkg/test/framework/components/cluster"
    26  	"istio.io/istio/pkg/test/framework/components/echo"
    27  	"istio.io/istio/pkg/test/framework/components/echo/common/deployment"
    28  	"istio.io/istio/pkg/test/framework/components/echo/echotest"
    29  	"istio.io/istio/pkg/test/framework/components/istio"
    30  	"istio.io/istio/pkg/test/framework/components/namespace"
    31  	"istio.io/istio/pkg/test/framework/resource"
    32  	ingressutil "istio.io/istio/tests/integration/security/sds_ingress/util"
    33  )
    34  
    35  var (
    36  	inst         istio.Instance
    37  	apps         deployment.SingleNamespaceView
    38  	echo1NS      namespace.Instance
    39  	customConfig []echo.Config
    40  )
    41  
    42  func TestMain(m *testing.M) {
    43  	// Integration test for the ingress SDS Gateway flow.
    44  	framework.
    45  		NewSuite(m).
    46  		Setup(istio.Setup(&inst, nil)).
    47  		Setup(namespace.Setup(&echo1NS, namespace.Config{Prefix: "echo1", Inject: true})).
    48  		Setup(func(ctx resource.Context) error {
    49  			// TODO: due to issue https://github.com/istio/istio/issues/25286,
    50  			// currently VM does not work in this test
    51  			err := ingressutil.SetupTest(ctx, &customConfig, namespace.Future(&echo1NS))
    52  			if err != nil {
    53  				return err
    54  			}
    55  			return nil
    56  		}).
    57  		Setup(deployment.SetupSingleNamespace(&apps, deployment.Config{
    58  			Namespaces: []namespace.Getter{
    59  				namespace.Future(&echo1NS),
    60  			},
    61  			Configs: echo.ConfigFuture(&customConfig),
    62  		})).
    63  		Setup(func(ctx resource.Context) error {
    64  			return ingressutil.CreateCustomInstances(&apps)
    65  		}).
    66  		Run()
    67  }
    68  
    69  // TestSingleTlsGateway_SecretRotation tests a single TLS ingress gateway with SDS enabled.
    70  // Verifies behavior in these scenarios.
    71  // (1) create a kubernetes secret to provision server key/cert, and
    72  // verify that TLS connection could establish to deliver HTTPS request.
    73  // (2) Rotates key/cert by deleting the secret generated in (1) and
    74  // replacing it a new secret with a different server key/cert.
    75  // (3) verify that client using older CA cert gets a 404 response
    76  // (4) verify that client using the newer CA cert is able to establish TLS connection
    77  // to deliver the HTTPS request.
    78  func TestSingleTlsGateway_SecretRotation(t *testing.T) {
    79  	framework.
    80  		NewTest(t).
    81  		Run(func(t framework.TestContext) {
    82  			var (
    83  				credName = "testsingletlsgateway-secretrotation"
    84  				host     = "testsingletlsgateway-secretrotation.example.com"
    85  			)
    86  			allInstances := []echo.Instances{ingressutil.A, ingressutil.VM}
    87  			for _, instances := range allInstances {
    88  				echotest.New(t, instances).
    89  					SetupForDestination(func(t framework.TestContext, to echo.Target) error {
    90  						ingressutil.SetupConfig(t, echo1NS, ingressutil.TestConfig{
    91  							Mode:           "SIMPLE",
    92  							CredentialName: credName,
    93  							Host:           host,
    94  							ServiceName:    to.Config().Service,
    95  							GatewayLabel:   inst.Settings().IngressGatewayIstioLabel,
    96  						})
    97  						return nil
    98  					}).
    99  					To(echotest.SingleSimplePodServiceAndAllSpecial()).
   100  					RunFromClusters(func(t framework.TestContext, _ cluster.Cluster, _ echo.Target) {
   101  						// Add kubernetes secret to provision key/cert for ingress gateway.
   102  						ingressutil.CreateIngressKubeSecret(t, credName, ingressutil.TLS,
   103  							ingressutil.IngressCredentialA, false)
   104  
   105  						ing := inst.IngressFor(t.Clusters().Default())
   106  						if ing == nil {
   107  							t.Skip()
   108  						}
   109  
   110  						tlsContextA := ingressutil.TLSContext{CaCert: ingressutil.CaCertA}
   111  						tlsContextB := ingressutil.TLSContext{CaCert: ingressutil.CaCertB}
   112  
   113  						// Verify the call works
   114  						ingressutil.SendRequestOrFail(t, ing, host, credName, ingressutil.TLS, tlsContextA,
   115  							ingressutil.ExpectedResponse{StatusCode: http.StatusOK})
   116  
   117  						// Now rotate the key/cert
   118  						ingressutil.RotateSecrets(t, credName, ingressutil.TLS,
   119  							ingressutil.IngressCredentialB, false)
   120  
   121  						t.NewSubTest("old cert should fail").Run(func(t framework.TestContext) {
   122  							// Client use old server CA cert to set up SSL connection would fail.
   123  							ingressutil.SendRequestOrFail(t, ing, host, credName, ingressutil.TLS, tlsContextA,
   124  								ingressutil.ExpectedResponse{ErrorMessage: "certificate signed by unknown authority"})
   125  						})
   126  
   127  						t.NewSubTest("new cert should succeed").Run(func(t framework.TestContext) {
   128  							// Client use new server CA cert to set up SSL connection.
   129  							ingressutil.SendRequestOrFail(t, ing, host, credName, ingressutil.TLS, tlsContextB,
   130  								ingressutil.ExpectedResponse{StatusCode: http.StatusOK})
   131  						})
   132  					})
   133  			}
   134  		})
   135  }
   136  
   137  // TestSingleMTLSGateway_ServerKeyCertRotation tests a single mTLS ingress gateway with SDS enabled.
   138  // Verifies behavior in these scenarios.
   139  // (1) create two kubernetes secrets to provision server key/cert and client CA cert, and
   140  // verify that mTLS connection could establish to deliver HTTPS request.
   141  // (2) replace kubernetes secret to rotate server key/cert, and verify that mTLS connection could
   142  // not establish. This is because client is still using old server CA cert to validate server cert,
   143  // and the new server cert cannot pass validation at client side.
   144  // (3) do another key/cert rotation to use the correct server key/cert this time, and verify that
   145  // mTLS connection could establish to deliver HTTPS request.
   146  func TestSingleMTLSGateway_ServerKeyCertRotation(t *testing.T) {
   147  	framework.
   148  		NewTest(t).
   149  		Run(func(t framework.TestContext) {
   150  			var (
   151  				credName   = "testsinglemtlsgateway-serverkeycertrotation"
   152  				credCaName = "testsinglemtlsgateway-serverkeycertrotation-cacert"
   153  				host       = "testsinglemtlsgateway-serverkeycertrotation.example.com"
   154  			)
   155  			allInstances := []echo.Instances{ingressutil.A, ingressutil.VM}
   156  			for _, instances := range allInstances {
   157  				echotest.New(t, instances).
   158  					SetupForDestination(func(t framework.TestContext, to echo.Target) error {
   159  						ingressutil.SetupConfig(t, echo1NS, ingressutil.TestConfig{
   160  							Mode:           "MUTUAL",
   161  							CredentialName: credName,
   162  							Host:           host,
   163  							ServiceName:    to.Config().Service,
   164  							GatewayLabel:   inst.Settings().IngressGatewayIstioLabel,
   165  						})
   166  						return nil
   167  					}).
   168  					To(echotest.SingleSimplePodServiceAndAllSpecial()).
   169  					RunFromClusters(func(t framework.TestContext, _ cluster.Cluster, _ echo.Target) {
   170  						// Add two kubernetes secrets to provision server key/cert and client CA cert for ingress gateway.
   171  						ingressutil.CreateIngressKubeSecret(t, credCaName, ingressutil.Mtls,
   172  							ingressutil.IngressCredentialCaCertA, false)
   173  						ingressutil.CreateIngressKubeSecret(t, credName, ingressutil.Mtls,
   174  							ingressutil.IngressCredentialServerKeyCertA, false)
   175  
   176  						ing := inst.IngressFor(t.Clusters().Default())
   177  						if ing == nil {
   178  							t.Skip()
   179  						}
   180  						tlsContext := ingressutil.TLSContext{
   181  							CaCert:     ingressutil.CaCertA,
   182  							PrivateKey: ingressutil.TLSClientKeyA,
   183  							Cert:       ingressutil.TLSClientCertA,
   184  						}
   185  						ingressutil.SendRequestOrFail(t, ing, host, credName, ingressutil.Mtls, tlsContext,
   186  							ingressutil.ExpectedResponse{StatusCode: http.StatusOK})
   187  
   188  						t.NewSubTest("mismatched key/cert should fail").Run(func(t framework.TestContext) {
   189  							// key/cert rotation using mis-matched server key/cert. The server cert cannot pass validation
   190  							// at client side.
   191  							ingressutil.RotateSecrets(t, credName, ingressutil.Mtls,
   192  								ingressutil.IngressCredentialServerKeyCertB, false)
   193  							// Client uses old server CA cert to set up SSL connection would fail.
   194  							ingressutil.SendRequestOrFail(t, ing, host, credName, ingressutil.Mtls, tlsContext,
   195  								ingressutil.ExpectedResponse{ErrorMessage: "certificate signed by unknown authority"})
   196  						})
   197  
   198  						t.NewSubTest("matched key/cert should succeed").Run(func(t framework.TestContext) {
   199  							// key/cert rotation using matched server key/cert. This time the server cert is able to pass
   200  							// validation at client side.
   201  							ingressutil.RotateSecrets(t, credName, ingressutil.Mtls,
   202  								ingressutil.IngressCredentialServerKeyCertA, false)
   203  							// Use old CA cert to set up SSL connection would succeed this time.
   204  							ingressutil.SendRequestOrFail(t, ing, host, credName, ingressutil.Mtls, tlsContext,
   205  								ingressutil.ExpectedResponse{StatusCode: http.StatusOK})
   206  						})
   207  					})
   208  			}
   209  		})
   210  }
   211  
   212  // TestSingleOptionalMTLSGateway tests a single mTLS ingress gateway with SDS enabled.
   213  // Verifies behavior when client sends certificate and when client does not send certificate.
   214  func TestSingleOptionalMTLSGateway(t *testing.T) {
   215  	framework.
   216  		NewTest(t).
   217  		Run(func(t framework.TestContext) {
   218  			var (
   219  				credName   = "testsinglemtlsgateway-serverkeyoptionalmtls"
   220  				credCaName = "testsinglemtlsgateway-serverkeyoptionalmtls-cacert"
   221  				host       = "testsinglemtlsgateway-serverkeyoptionalmtls.example.com"
   222  			)
   223  			allInstances := []echo.Instances{ingressutil.A, ingressutil.VM}
   224  			for _, instances := range allInstances {
   225  				echotest.New(t, instances).
   226  					SetupForDestination(func(t framework.TestContext, to echo.Target) error {
   227  						ingressutil.SetupConfig(t, echo1NS, ingressutil.TestConfig{
   228  							Mode:           "OPTIONAL_MUTUAL",
   229  							CredentialName: credName,
   230  							Host:           host,
   231  							ServiceName:    to.Config().Service,
   232  							GatewayLabel:   inst.Settings().IngressGatewayIstioLabel,
   233  						})
   234  						return nil
   235  					}).
   236  					To(echotest.SingleSimplePodServiceAndAllSpecial()).
   237  					RunFromClusters(func(t framework.TestContext, _ cluster.Cluster, _ echo.Target) {
   238  						// Add two kubernetes secrets to provision server key/cert and client CA cert for ingress gateway.
   239  						ingressutil.CreateIngressKubeSecret(t, credCaName, ingressutil.Mtls,
   240  							ingressutil.IngressCredentialCaCertA, false)
   241  						ingressutil.CreateIngressKubeSecret(t, credName, ingressutil.Mtls,
   242  							ingressutil.IngressCredentialServerKeyCertA, false)
   243  
   244  						ing := inst.IngressFor(t.Clusters().Default())
   245  						if ing == nil {
   246  							t.Skip()
   247  						}
   248  						tlsContext := ingressutil.TLSContext{
   249  							CaCert:     ingressutil.CaCertA,
   250  							PrivateKey: ingressutil.TLSClientKeyA,
   251  							Cert:       ingressutil.TLSClientCertA,
   252  						}
   253  						t.NewSubTest("request without client certificates").Run(func(t framework.TestContext) {
   254  							// Send a SIMPLE TLS request without client certificates.
   255  							ingressutil.SendRequestOrFail(t, ing, host, credName, ingressutil.TLS, tlsContext,
   256  								ingressutil.ExpectedResponse{StatusCode: http.StatusOK})
   257  						})
   258  						t.NewSubTest("request with client certificates").Run(func(t framework.TestContext) {
   259  							// Send a TLS request with client certificates.
   260  							ingressutil.SendRequestOrFail(t, ing, host, credName, ingressutil.Mtls, tlsContext,
   261  								ingressutil.ExpectedResponse{StatusCode: http.StatusOK})
   262  						})
   263  					})
   264  			}
   265  		})
   266  }
   267  
   268  // TestSingleMTLSGateway_CompoundSecretRotation tests a single mTLS ingress gateway with SDS enabled.
   269  // Verifies behavior in these scenarios.
   270  // (1) A valid kubernetes secret with key/cert and client CA cert is added, verifies that SSL connection
   271  // termination is working properly. This secret is a compound secret.
   272  // (2) After key/cert rotation, client needs to pick new CA cert to complete SSL connection. Old CA
   273  // cert will cause the SSL connection fail.
   274  func TestSingleMTLSGateway_CompoundSecretRotation(t *testing.T) {
   275  	framework.
   276  		NewTest(t).
   277  		Run(func(t framework.TestContext) {
   278  			var (
   279  				credName = "testsinglemtlsgateway-generic-compoundrotation"
   280  				host     = "testsinglemtlsgateway-compoundsecretrotation.example.com"
   281  			)
   282  			allInstances := []echo.Instances{ingressutil.A, ingressutil.VM}
   283  			for _, instances := range allInstances {
   284  				echotest.New(t, instances).
   285  					SetupForDestination(func(t framework.TestContext, to echo.Target) error {
   286  						ingressutil.SetupConfig(t, echo1NS, ingressutil.TestConfig{
   287  							Mode:           "MUTUAL",
   288  							CredentialName: credName,
   289  							Host:           host,
   290  							ServiceName:    to.Config().Service,
   291  							GatewayLabel:   inst.Settings().IngressGatewayIstioLabel,
   292  						})
   293  						return nil
   294  					}).
   295  					To(echotest.SingleSimplePodServiceAndAllSpecial()).
   296  					RunFromClusters(func(t framework.TestContext, _ cluster.Cluster, to echo.Target) {
   297  						// Add kubernetes secret to provision key/cert for ingress gateway.
   298  						ingressutil.CreateIngressKubeSecret(t, credName, ingressutil.Mtls,
   299  							ingressutil.IngressCredentialA, false)
   300  
   301  						// Wait for ingress gateway to fetch key/cert from Gateway agent via SDS.
   302  						ing := inst.IngressFor(t.Clusters().Default())
   303  						tlsContext := ingressutil.TLSContext{
   304  							CaCert:     ingressutil.CaCertA,
   305  							PrivateKey: ingressutil.TLSClientKeyA,
   306  							Cert:       ingressutil.TLSClientCertA,
   307  						}
   308  						ingressutil.SendRequestOrFail(t, ing, host, credName, ingressutil.Mtls, tlsContext,
   309  							ingressutil.ExpectedResponse{StatusCode: http.StatusOK})
   310  
   311  						t.NewSubTest("old server CA should fail").Run(func(t framework.TestContext) {
   312  							// key/cert rotation
   313  							ingressutil.RotateSecrets(t, credName, ingressutil.Mtls,
   314  								ingressutil.IngressCredentialB, false)
   315  							// Use old server CA cert to set up SSL connection would fail.
   316  							ingressutil.SendRequestOrFail(t, ing, host, credName, ingressutil.Mtls, tlsContext,
   317  								ingressutil.ExpectedResponse{ErrorMessage: "certificate signed by unknown authority"})
   318  						})
   319  
   320  						t.NewSubTest("new server CA should succeed").Run(func(t framework.TestContext) {
   321  							// Use new server CA cert to set up SSL connection.
   322  							tlsContext = ingressutil.TLSContext{
   323  								CaCert:     ingressutil.CaCertB,
   324  								PrivateKey: ingressutil.TLSClientKeyB,
   325  								Cert:       ingressutil.TLSClientCertB,
   326  							}
   327  							ingressutil.SendRequestOrFail(t, ing, host, credName, ingressutil.Mtls, tlsContext,
   328  								ingressutil.ExpectedResponse{StatusCode: http.StatusOK})
   329  						})
   330  					})
   331  			}
   332  		})
   333  }
   334  
   335  // TestSingleMTLSGatewayAndNotGeneric_CompoundSecretRotation tests a single mTLS ingress gateway with SDS enabled
   336  // and use the tls cert instead of generic cert Verifies behavior in these scenarios.
   337  // (1) A valid kubernetes secret with key/cert and client CA cert is added, verifies that SSL connection
   338  // termination is working properly. This secret is a compound secret.
   339  // (2) After key/cert rotation, client needs to pick new CA cert to complete SSL connection. Old CA
   340  // cert will cause the SSL connection fail.
   341  func TestSingleMTLSGatewayAndNotGeneric_CompoundSecretRotation(t *testing.T) {
   342  	framework.
   343  		NewTest(t).
   344  		Run(func(t framework.TestContext) {
   345  			var (
   346  				credName = "testsinglemtlsgatewayandnotgeneric-compoundsecretrotation"
   347  				host     = "testsinglemtlsgatewayandnotgeneric-compoundsecretrotation.example.com"
   348  			)
   349  			allInstances := []echo.Instances{ingressutil.A, ingressutil.VM}
   350  			for _, instances := range allInstances {
   351  				echotest.New(t, instances).
   352  					SetupForDestination(func(t framework.TestContext, to echo.Target) error {
   353  						ingressutil.SetupConfig(t, echo1NS, ingressutil.TestConfig{
   354  							Mode:           "MUTUAL",
   355  							CredentialName: credName,
   356  							Host:           host,
   357  							ServiceName:    to.Config().Service,
   358  							GatewayLabel:   inst.Settings().IngressGatewayIstioLabel,
   359  						})
   360  						return nil
   361  					}).
   362  					To(echotest.SingleSimplePodServiceAndAllSpecial()).
   363  					RunFromClusters(func(t framework.TestContext, _ cluster.Cluster, _ echo.Target) {
   364  						// Add kubernetes secret to provision key/cert for ingress gateway.
   365  						ingressutil.CreateIngressKubeSecret(t, credName, ingressutil.Mtls,
   366  							ingressutil.IngressCredentialA, true)
   367  
   368  						// Wait for ingress gateway to fetch key/cert from Gateway agent via SDS.
   369  						ing := inst.IngressFor(t.Clusters().Default())
   370  						if ing == nil {
   371  							t.Skip()
   372  						}
   373  						tlsContext := ingressutil.TLSContext{
   374  							CaCert:     ingressutil.CaCertA,
   375  							PrivateKey: ingressutil.TLSClientKeyA,
   376  							Cert:       ingressutil.TLSClientCertA,
   377  						}
   378  						ingressutil.SendRequestOrFail(t, ing, host, credName, ingressutil.Mtls, tlsContext,
   379  							ingressutil.ExpectedResponse{StatusCode: http.StatusOK})
   380  
   381  						t.NewSubTest("old server CA should fail").Run(func(t framework.TestContext) {
   382  							// key/cert rotation
   383  							ingressutil.RotateSecrets(t, credName, ingressutil.Mtls,
   384  								ingressutil.IngressCredentialB, true)
   385  							// Use old server CA cert to set up SSL connection would fail.
   386  							ingressutil.SendRequestOrFail(t, ing, host, credName, ingressutil.Mtls, tlsContext,
   387  								ingressutil.ExpectedResponse{ErrorMessage: "certificate signed by unknown authority"})
   388  						})
   389  
   390  						t.NewSubTest("new server CA should succeed").Run(func(t framework.TestContext) {
   391  							// Use new server CA cert to set up SSL connection.
   392  							tlsContext = ingressutil.TLSContext{
   393  								CaCert:     ingressutil.CaCertB,
   394  								PrivateKey: ingressutil.TLSClientKeyB,
   395  								Cert:       ingressutil.TLSClientCertB,
   396  							}
   397  							ingressutil.SendRequestOrFail(t, ing, host, credName, ingressutil.Mtls, tlsContext,
   398  								ingressutil.ExpectedResponse{StatusCode: http.StatusOK})
   399  						})
   400  					})
   401  			}
   402  		})
   403  }
   404  
   405  // TestTlsGateways deploys multiple TLS gateways with SDS enabled, and creates kubernetes that store
   406  // private key and server certificate for each TLS gateway. Verifies that all gateways are able to terminate
   407  // SSL connections successfully.
   408  func TestTlsGateways(t *testing.T) {
   409  	framework.
   410  		NewTest(t).
   411  		Run(func(t framework.TestContext) {
   412  			ingressutil.RunTestMultiTLSGateways(t, inst, namespace.Future(&echo1NS))
   413  		})
   414  }
   415  
   416  // TestMtlsGateways deploys multiple mTLS gateways with SDS enabled, and creates kubernetes that store
   417  // private key, server certificate and CA certificate for each mTLS gateway. Verifies that all gateways
   418  // are able to terminate mTLS connections successfully.
   419  func TestMtlsGateways(t *testing.T) {
   420  	framework.
   421  		NewTest(t).
   422  		Run(func(t framework.TestContext) {
   423  			ingressutil.RunTestMultiMtlsGateways(t, inst, namespace.Future(&echo1NS))
   424  		})
   425  }
   426  
   427  // TestMultiTlsGateway_InvalidSecret tests a single TLS ingress gateway with SDS enabled. Creates kubernetes secret
   428  // with invalid key/cert and verify the behavior.
   429  func TestMultiTlsGateway_InvalidSecret(t *testing.T) {
   430  	framework.
   431  		NewTest(t).
   432  		Run(func(t framework.TestContext) {
   433  			testCase := []struct {
   434  				name                     string
   435  				secretName               string
   436  				ingressGatewayCredential ingressutil.IngressCredential
   437  				hostName                 string
   438  				expectedResponse         ingressutil.ExpectedResponse
   439  				callType                 ingressutil.CallType
   440  				tlsContext               ingressutil.TLSContext
   441  			}{
   442  				{
   443  					name:       "tls ingress gateway invalid private key",
   444  					secretName: "testmultitlsgateway-invalidsecret-1",
   445  					ingressGatewayCredential: ingressutil.IngressCredential{
   446  						PrivateKey:  "invalid",
   447  						Certificate: ingressutil.TLSServerCertA,
   448  					},
   449  					hostName: "testmultitlsgateway-invalidsecret1.example.com",
   450  					expectedResponse: ingressutil.ExpectedResponse{
   451  						ErrorMessage: "connection reset by peer",
   452  					},
   453  					callType: ingressutil.TLS,
   454  					tlsContext: ingressutil.TLSContext{
   455  						CaCert: ingressutil.CaCertA,
   456  					},
   457  				},
   458  				{
   459  					name:       "tls ingress gateway invalid server cert",
   460  					secretName: "testmultitlsgateway-invalidsecret-2",
   461  					ingressGatewayCredential: ingressutil.IngressCredential{
   462  						PrivateKey:  ingressutil.TLSServerKeyA,
   463  						Certificate: "invalid",
   464  					},
   465  					hostName: "testmultitlsgateway-invalidsecret2.example.com",
   466  					expectedResponse: ingressutil.ExpectedResponse{
   467  						ErrorMessage: "connection reset by peer",
   468  					},
   469  					callType: ingressutil.TLS,
   470  					tlsContext: ingressutil.TLSContext{
   471  						CaCert: ingressutil.CaCertA,
   472  					},
   473  				},
   474  				{
   475  					name:       "tls ingress gateway mis-matched key and cert",
   476  					secretName: "testmultitlsgateway-invalidsecret-3",
   477  					ingressGatewayCredential: ingressutil.IngressCredential{
   478  						PrivateKey:  ingressutil.TLSServerKeyA,
   479  						Certificate: ingressutil.TLSServerCertB,
   480  					},
   481  					hostName: "testmultitlsgateway-invalidsecret3.example.com",
   482  					expectedResponse: ingressutil.ExpectedResponse{
   483  						ErrorMessage: "connection reset by peer",
   484  					},
   485  					callType: ingressutil.TLS,
   486  					tlsContext: ingressutil.TLSContext{
   487  						CaCert: ingressutil.CaCertA,
   488  					},
   489  				},
   490  				{
   491  					name:       "tls ingress gateway no private key",
   492  					secretName: "testmultitlsgateway-invalidsecret-4",
   493  					ingressGatewayCredential: ingressutil.IngressCredential{
   494  						Certificate: ingressutil.TLSServerCertA,
   495  					},
   496  					hostName: "testmultitlsgateway-invalidsecret4.example.com",
   497  					expectedResponse: ingressutil.ExpectedResponse{
   498  						ErrorMessage: "connection reset by peer",
   499  					},
   500  					callType: ingressutil.TLS,
   501  					tlsContext: ingressutil.TLSContext{
   502  						CaCert: ingressutil.CaCertA,
   503  					},
   504  				},
   505  				{
   506  					name:       "tls ingress gateway no server cert",
   507  					secretName: "testmultitlsgateway-invalidsecret-5",
   508  					ingressGatewayCredential: ingressutil.IngressCredential{
   509  						PrivateKey: ingressutil.TLSServerKeyA,
   510  					},
   511  					hostName: "testmultitlsgateway-invalidsecret5.example.com",
   512  					expectedResponse: ingressutil.ExpectedResponse{
   513  						ErrorMessage: "connection reset by peer",
   514  					},
   515  					callType: ingressutil.TLS,
   516  					tlsContext: ingressutil.TLSContext{
   517  						CaCert: ingressutil.CaCertA,
   518  					},
   519  				},
   520  			}
   521  
   522  			for _, c := range testCase {
   523  				allInstances := []echo.Instances{ingressutil.A, ingressutil.VM}
   524  				for _, instances := range allInstances {
   525  					echotest.New(t, instances).
   526  						SetupForDestination(func(t framework.TestContext, to echo.Target) error {
   527  							ingressutil.SetupConfig(t, echo1NS, ingressutil.TestConfig{
   528  								Mode:           "SIMPLE",
   529  								CredentialName: c.secretName,
   530  								Host:           c.hostName,
   531  								ServiceName:    to.Config().Service,
   532  								GatewayLabel:   inst.Settings().IngressGatewayIstioLabel,
   533  							})
   534  							return nil
   535  						}).
   536  						To(echotest.SingleSimplePodServiceAndAllSpecial()).
   537  						RunFromClusters(func(t framework.TestContext, _ cluster.Cluster, _ echo.Target) {
   538  							ing := inst.IngressFor(t.Clusters().Default())
   539  							if ing == nil {
   540  								t.Skip()
   541  							}
   542  							t.NewSubTest(c.name).Run(func(t framework.TestContext) {
   543  								ingressutil.CreateIngressKubeSecret(t, c.secretName, ingressutil.TLS,
   544  									c.ingressGatewayCredential, false)
   545  
   546  								ingressutil.SendRequestOrFail(t, ing, c.hostName, c.secretName, c.callType, c.tlsContext,
   547  									c.expectedResponse)
   548  							})
   549  						})
   550  				}
   551  			}
   552  		})
   553  }
   554  
   555  // TestMultiMtlsGateway_InvalidSecret tests a single mTLS ingress gateway with SDS enabled. Creates kubernetes secret
   556  // with invalid key/cert and verify the behavior.
   557  func TestMultiMtlsGateway_InvalidSecret(t *testing.T) {
   558  	framework.
   559  		NewTest(t).
   560  		Run(func(t framework.TestContext) {
   561  			testCase := []struct {
   562  				name                     string
   563  				secretName               string
   564  				ingressGatewayCredential ingressutil.IngressCredential
   565  				hostName                 string
   566  				expectedResponse         ingressutil.ExpectedResponse
   567  				callType                 ingressutil.CallType
   568  				tlsContext               ingressutil.TLSContext
   569  			}{
   570  				{
   571  					name:       "mtls ingress gateway invalid CA cert",
   572  					secretName: "testmultimtlsgateway-invalidsecret-1",
   573  					ingressGatewayCredential: ingressutil.IngressCredential{
   574  						PrivateKey:  ingressutil.TLSServerKeyA,
   575  						Certificate: ingressutil.TLSServerCertA,
   576  						CaCert:      "invalid",
   577  					},
   578  					hostName: "testmultimtlsgateway-invalidsecret1.example.com",
   579  					expectedResponse: ingressutil.ExpectedResponse{
   580  						ErrorMessage: "connection reset by peer",
   581  					},
   582  					callType: ingressutil.Mtls,
   583  					tlsContext: ingressutil.TLSContext{
   584  						CaCert:     ingressutil.CaCertA,
   585  						PrivateKey: ingressutil.TLSClientKeyA,
   586  						Cert:       ingressutil.TLSClientCertA,
   587  					},
   588  				},
   589  				{
   590  					name:       "mtls ingress gateway no CA cert",
   591  					secretName: "testmultimtlsgateway-invalidsecret-2",
   592  					ingressGatewayCredential: ingressutil.IngressCredential{
   593  						PrivateKey:  ingressutil.TLSServerKeyA,
   594  						Certificate: ingressutil.TLSServerCertA,
   595  					},
   596  					hostName: "testmultimtlsgateway-invalidsecret2.example.com",
   597  					expectedResponse: ingressutil.ExpectedResponse{
   598  						ErrorMessage: "connection reset by peer",
   599  					},
   600  					callType: ingressutil.Mtls,
   601  					tlsContext: ingressutil.TLSContext{
   602  						CaCert:     ingressutil.CaCertA,
   603  						PrivateKey: ingressutil.TLSClientKeyA,
   604  						Cert:       ingressutil.TLSClientCertA,
   605  					},
   606  				},
   607  				{
   608  					name:       "mtls ingress gateway mismatched CA cert",
   609  					secretName: "testmultimtlsgateway-invalidsecret-3",
   610  					ingressGatewayCredential: ingressutil.IngressCredential{
   611  						PrivateKey:  ingressutil.TLSServerKeyA,
   612  						Certificate: ingressutil.TLSServerCertA,
   613  						CaCert:      ingressutil.CaCertB,
   614  					},
   615  					hostName: "testmultimtlsgateway-invalidsecret3.example.com",
   616  					expectedResponse: ingressutil.ExpectedResponse{
   617  						ErrorMessage: "error decrypting message",
   618  					},
   619  					callType: ingressutil.Mtls,
   620  					tlsContext: ingressutil.TLSContext{
   621  						CaCert:     ingressutil.CaCertA,
   622  						PrivateKey: ingressutil.TLSClientKeyA,
   623  						Cert:       ingressutil.TLSClientCertA,
   624  					},
   625  				},
   626  			}
   627  
   628  			for _, c := range testCase {
   629  				allInstances := []echo.Instances{ingressutil.A, ingressutil.VM}
   630  				for _, instances := range allInstances {
   631  					echotest.New(t, instances).
   632  						SetupForDestination(func(t framework.TestContext, to echo.Target) error {
   633  							ingressutil.SetupConfig(t, echo1NS, ingressutil.TestConfig{
   634  								Mode:           "MUTUAL",
   635  								CredentialName: c.secretName,
   636  								Host:           c.hostName,
   637  								ServiceName:    to.Config().Service,
   638  								GatewayLabel:   inst.Settings().IngressGatewayIstioLabel,
   639  							})
   640  							return nil
   641  						}).
   642  						To(echotest.SingleSimplePodServiceAndAllSpecial()).
   643  						RunFromClusters(func(t framework.TestContext, src cluster.Cluster, dest echo.Target) {
   644  							ing := inst.IngressFor(t.Clusters().Default())
   645  							if ing == nil {
   646  								t.Skip()
   647  							}
   648  							t.NewSubTest(c.name).Run(func(t framework.TestContext) {
   649  								ingressutil.CreateIngressKubeSecret(t, c.secretName, ingressutil.Mtls,
   650  									c.ingressGatewayCredential, false)
   651  
   652  								ingressutil.SendRequestOrFail(t, ing, c.hostName, c.secretName, c.callType, c.tlsContext,
   653  									c.expectedResponse)
   654  							})
   655  						})
   656  				}
   657  			}
   658  		})
   659  }
   660  
   661  // TestMtlsGateway_CRL tests the behavior of a single mTLS ingress gateway with SDS enabled. Creates kubernetes secret
   662  // with CRL enabled and verify the behavior.
   663  func TestMtlsGateway_CRL(t *testing.T) {
   664  	framework.
   665  		NewTest(t).
   666  		Run(func(t framework.TestContext) {
   667  			testCase := []struct {
   668  				name                     string
   669  				secretName               string
   670  				ingressGatewayCredential ingressutil.IngressCredential
   671  				hostName                 string
   672  				expectedResponse         ingressutil.ExpectedResponse
   673  				callType                 ingressutil.CallType
   674  				tlsContext               ingressutil.TLSContext
   675  			}{
   676  				{
   677  					// TC1: regular communication without CRL from client A works
   678  					name:       "mtls ingress gateway without CRL-client A",
   679  					secretName: "testmtlsgateway-secret-without-crl-a",
   680  					ingressGatewayCredential: ingressutil.IngressCredential{
   681  						PrivateKey:  ingressutil.TLSServerKeyA,
   682  						Certificate: ingressutil.TLSServerCertA,
   683  						CaCert:      ingressutil.CaCertA,
   684  					},
   685  					hostName: "testmtlsgateway-crl.example.com",
   686  					expectedResponse: ingressutil.ExpectedResponse{
   687  						StatusCode: http.StatusOK,
   688  					},
   689  					callType: ingressutil.Mtls,
   690  					tlsContext: ingressutil.TLSContext{
   691  						CaCert:     ingressutil.CaCertA,
   692  						PrivateKey: ingressutil.TLSClientKeyA,
   693  						Cert:       ingressutil.TLSClientCertA,
   694  					},
   695  				},
   696  				// TC2: regular communication without CRL from client B works
   697  				{
   698  					name:       "mtls ingress gateway without CRL-client B",
   699  					secretName: "testmtlsgateway-secret-without-crl-b",
   700  					ingressGatewayCredential: ingressutil.IngressCredential{
   701  						PrivateKey:  ingressutil.TLSServerKeyB,
   702  						Certificate: ingressutil.TLSServerCertB,
   703  						CaCert:      ingressutil.CaCertB,
   704  					},
   705  					hostName: "testmtlsgateway-crl.example.com",
   706  					expectedResponse: ingressutil.ExpectedResponse{
   707  						StatusCode: http.StatusOK,
   708  					},
   709  					callType: ingressutil.Mtls,
   710  					tlsContext: ingressutil.TLSContext{
   711  						CaCert:     ingressutil.CaCertB,
   712  						PrivateKey: ingressutil.TLSClientKeyB,
   713  						Cert:       ingressutil.TLSClientCertB,
   714  					},
   715  				},
   716  				// TC3: Add CRL with revoked client Certificate A to the configuration,
   717  				// and initiate communication from client A.
   718  				// Server should respond with the "revoked certificate" error message.
   719  				{
   720  					name:       "mtls ingress gateway with CRL-client A",
   721  					secretName: "testmtlsgateway-secret-with-crl-a",
   722  					ingressGatewayCredential: ingressutil.IngressCredential{
   723  						PrivateKey:  ingressutil.TLSServerKeyA,
   724  						Certificate: ingressutil.TLSServerCertA,
   725  						CaCert:      ingressutil.CaCertA,
   726  						Crl:         ingressutil.CaCrlA,
   727  					},
   728  					hostName: "testmtlsgateway-crl.example.com",
   729  					expectedResponse: ingressutil.ExpectedResponse{
   730  						StatusCode:   http.StatusBadGateway,
   731  						ErrorMessage: "tls: revoked certificate",
   732  					},
   733  					callType: ingressutil.Mtls,
   734  					tlsContext: ingressutil.TLSContext{
   735  						CaCert:     ingressutil.CaCertA,
   736  						PrivateKey: ingressutil.TLSClientKeyA,
   737  						Cert:       ingressutil.TLSClientCertA,
   738  					},
   739  				},
   740  				// TC4: Add CRL with a revoked dummy client certificate to the configuration,
   741  				// and initiate communication from client A. The communication should go through
   742  				// as long as the CRL does not have client A certificate as revoked.
   743  				{
   744  					name:       "mtls ingress gateway with dummy CRL",
   745  					secretName: "testmtlsgateway-secret-with-dummy-crl",
   746  					ingressGatewayCredential: ingressutil.IngressCredential{
   747  						PrivateKey:  ingressutil.TLSServerKeyA,
   748  						Certificate: ingressutil.TLSServerCertA,
   749  						CaCert:      ingressutil.CaCertA,
   750  						Crl:         ingressutil.DummyCaCrlA,
   751  					},
   752  					hostName: "testmtlsgateway-crl.example.com",
   753  					expectedResponse: ingressutil.ExpectedResponse{
   754  						StatusCode: http.StatusOK,
   755  					},
   756  					callType: ingressutil.Mtls,
   757  					tlsContext: ingressutil.TLSContext{
   758  						CaCert:     ingressutil.CaCertA,
   759  						PrivateKey: ingressutil.TLSClientKeyA,
   760  						Cert:       ingressutil.TLSClientCertA,
   761  					},
   762  				},
   763  				// TC5: Add CRL with revoked client Certificate A to the configuration,
   764  				// and initiate communication from client B.
   765  				// Server should respond with the "unknown CA" error message.
   766  				{
   767  					name:       "mtls ingress gateway with CRL-unknown CA",
   768  					secretName: "testmtlsgateway-secret-with-crl-unknown-ca",
   769  					ingressGatewayCredential: ingressutil.IngressCredential{
   770  						PrivateKey:  ingressutil.TLSServerKeyB,
   771  						Certificate: ingressutil.TLSServerCertB,
   772  						CaCert:      ingressutil.CaCertB,
   773  						Crl:         ingressutil.CaCrlA,
   774  					},
   775  					hostName: "testmtlsgateway-crl.example.com",
   776  					expectedResponse: ingressutil.ExpectedResponse{
   777  						StatusCode:   http.StatusBadGateway,
   778  						ErrorMessage: "tls: unknown certificate authority",
   779  					},
   780  					callType: ingressutil.Mtls,
   781  					tlsContext: ingressutil.TLSContext{
   782  						CaCert:     ingressutil.CaCertB,
   783  						PrivateKey: ingressutil.TLSClientKeyB,
   784  						Cert:       ingressutil.TLSClientCertB,
   785  					},
   786  				},
   787  			}
   788  
   789  			for _, c := range testCase {
   790  				allInstances := []echo.Instances{ingressutil.A, ingressutil.VM}
   791  				for _, instances := range allInstances {
   792  					echotest.New(t, instances).
   793  						SetupForDestination(func(t framework.TestContext, to echo.Target) error {
   794  							ingressutil.SetupConfig(t, echo1NS, ingressutil.TestConfig{
   795  								Mode:           "MUTUAL",
   796  								CredentialName: c.secretName,
   797  								Host:           c.hostName,
   798  								ServiceName:    to.Config().Service,
   799  								GatewayLabel:   inst.Settings().IngressGatewayIstioLabel,
   800  							})
   801  							return nil
   802  						}).
   803  						To(echotest.SingleSimplePodServiceAndAllSpecial()).
   804  						RunFromClusters(func(t framework.TestContext, src cluster.Cluster, dest echo.Target) {
   805  							ing := inst.IngressFor(t.Clusters().Default())
   806  							if ing == nil {
   807  								t.Skip()
   808  							}
   809  							t.NewSubTest(c.name).Run(func(t framework.TestContext) {
   810  								ingressutil.CreateIngressKubeSecret(t, c.secretName, ingressutil.Mtls,
   811  									c.ingressGatewayCredential, false)
   812  
   813  								ingressutil.SendRequestOrFail(t, ing, c.hostName, c.secretName, c.callType, c.tlsContext,
   814  									c.expectedResponse)
   815  							})
   816  						})
   817  				}
   818  			}
   819  		})
   820  }