istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/security/ca_custom_root/trust_domain_validation_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 cacustomroot 19 20 import ( 21 "fmt" 22 "os" 23 "path" 24 "testing" 25 26 "istio.io/istio/pkg/test/echo/common/scheme" 27 "istio.io/istio/pkg/test/env" 28 "istio.io/istio/pkg/test/framework" 29 "istio.io/istio/pkg/test/framework/components/echo" 30 "istio.io/istio/pkg/test/framework/components/echo/check" 31 "istio.io/istio/pkg/test/framework/components/echo/match" 32 ) 33 34 const ( 35 httpPlaintext = "http-plaintext" 36 httpMTLS = "http-mtls" 37 tcpPlaintext = "tcp-plaintext" 38 tcpMTLS = "tcp-mtls" 39 tcpWL = "tcp-wl" 40 passThrough = "tcp-mtls-pass-through" 41 42 // policy to enable mTLS in client and server: 43 // ports with plaintext: 8090 (http) and 8092 (tcp) 44 // ports with mTLS: 8091 (http), 8093 (tcp) and 9000 (tcp passthrough). 45 policy = ` 46 apiVersion: security.istio.io/v1beta1 47 kind: PeerAuthentication 48 metadata: 49 name: "mtls" 50 spec: 51 selector: 52 matchLabels: 53 app: server 54 mtls: 55 mode: STRICT 56 portLevelMtls: 57 8090: 58 mode: DISABLE 59 8092: 60 mode: DISABLE 61 --- 62 apiVersion: networking.istio.io/v1alpha3 63 kind: DestinationRule 64 metadata: 65 name: server 66 spec: 67 host: server.%s.svc.cluster.local 68 trafficPolicy: 69 tls: 70 mode: ISTIO_MUTUAL 71 portLevelSettings: 72 - port: 73 number: 8090 74 tls: 75 mode: DISABLE 76 - port: 77 number: 8092 78 tls: 79 mode: DISABLE 80 ` 81 ) 82 83 // TestTrustDomainValidation tests the trust domain validation when mTLS is enabled. 84 // The trust domain validation should reject a request if it's not from the trust domains configured in the mesh config. 85 // The test uses naked client (no sidecar) with custom certificates of different trust domains and covers the following: 86 // - plaintext requests are not affected 87 // - same trust domain (cluster.local) and aliases (trust-domain-foo and trust-domain-bar) 88 // - works for both HTTP and TCP protocol 89 // - works for pass through filter chains 90 func TestTrustDomainValidation(t *testing.T) { 91 framework.NewTest(t).Run( 92 func(ctx framework.TestContext) { 93 testNS := apps.EchoNamespace.Namespace 94 95 ctx.ConfigIstio().YAML(testNS.Name(), fmt.Sprintf(policy, testNS.Name())).ApplyOrFail(ctx) 96 97 trustDomains := map[string]struct { 98 cert string 99 key string 100 }{ 101 "foo": { 102 cert: readFile(ctx, "workload-foo-cert.pem"), 103 key: readFile(ctx, "workload-foo-key.pem"), 104 }, 105 "bar": { 106 cert: readFile(ctx, "workload-bar-cert.pem"), 107 key: readFile(ctx, "workload-bar-key.pem"), 108 }, 109 } 110 111 for _, cluster := range ctx.Clusters() { 112 ctx.NewSubTest(fmt.Sprintf("From %s", cluster.StableName())).Run(func(t framework.TestContext) { 113 // naked: only test app without sidecar, send requests from trust domain aliases 114 // client: app with sidecar, send request from cluster.local 115 // server: app with sidecar, verify requests from cluster.local or trust domain aliases 116 client := match.Cluster(cluster).FirstOrFail(t, client) 117 naked := match.Cluster(cluster).FirstOrFail(t, apps.Naked) 118 server := match.Cluster(cluster).FirstOrFail(t, server) 119 verify := func(ctx framework.TestContext, from echo.Instance, td, port string, s scheme.Instance, allow bool) { 120 ctx.Helper() 121 want := "allow" 122 if !allow { 123 want = "deny" 124 } 125 name := fmt.Sprintf("%s[%s]->server:%s[%s]", from.Config().Service, td, port, want) 126 ctx.NewSubTest(name).Run(func(t framework.TestContext) { 127 t.Helper() 128 opt := echo.CallOptions{ 129 To: server, 130 Count: 1, 131 Port: echo.Port{ 132 Name: port, 133 }, 134 Address: "server", 135 Scheme: s, 136 TLS: echo.TLS{ 137 Cert: trustDomains[td].cert, 138 Key: trustDomains[td].key, 139 }, 140 } 141 if port == passThrough { 142 // Manually make the request for pass through port. 143 opt = echo.CallOptions{ 144 ToWorkload: server, 145 Port: echo.Port{Name: tcpWL}, 146 TLS: echo.TLS{ 147 Cert: trustDomains[td].cert, 148 Key: trustDomains[td].key, 149 }, 150 Check: check.OK(), 151 } 152 } 153 if !allow { 154 opt.Check = check.ErrorContains("tls: unknown certificate") 155 } 156 from.CallOrFail(t, opt) 157 }) 158 } 159 160 // Request using plaintext should always allowed. 161 verify(t, client, "plaintext", httpPlaintext, scheme.HTTP, true) 162 verify(t, client, "plaintext", tcpPlaintext, scheme.TCP, true) 163 verify(t, naked, "plaintext", httpPlaintext, scheme.HTTP, true) 164 verify(t, naked, "plaintext", tcpPlaintext, scheme.TCP, true) 165 166 // Request from local trust domain should always allowed. 167 verify(t, client, "cluster.local", httpMTLS, scheme.HTTP, true) 168 verify(t, client, "cluster.local", tcpMTLS, scheme.TCP, true) 169 170 // Trust domain foo is added as trust domain alias. 171 // Request from trust domain bar should be denied. 172 // Request from trust domain foo should be allowed. 173 verify(t, naked, "bar", httpMTLS, scheme.HTTPS, false) 174 verify(t, naked, "bar", tcpMTLS, scheme.TCP, false) 175 verify(t, naked, "bar", passThrough, scheme.TCP, false) 176 verify(t, naked, "foo", httpMTLS, scheme.HTTPS, true) 177 verify(t, naked, "foo", tcpMTLS, scheme.TCP, true) 178 verify(t, naked, "foo", passThrough, scheme.TCP, true) 179 }) 180 } 181 }) 182 } 183 184 func readFile(ctx framework.TestContext, name string) string { 185 data, err := os.ReadFile(path.Join(env.IstioSrc, "samples/certs", name)) 186 if err != nil { 187 ctx.Fatal(err) 188 } 189 return string(data) 190 }