istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/security/ca_custom_root/main_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 // cacustomroot creates cluster with custom plugin root CA (samples/cert/ca-cert.pem) 19 // instead of using the auto-generated self-signed root CA. 20 package cacustomroot 21 22 import ( 23 "fmt" 24 "os" 25 "os/exec" 26 "path" 27 "testing" 28 29 "istio.io/api/annotation" 30 "istio.io/istio/pkg/config/protocol" 31 "istio.io/istio/pkg/test/echo/common" 32 "istio.io/istio/pkg/test/env" 33 "istio.io/istio/pkg/test/framework" 34 "istio.io/istio/pkg/test/framework/components/echo" 35 "istio.io/istio/pkg/test/framework/components/echo/common/deployment" 36 "istio.io/istio/pkg/test/framework/components/istio" 37 "istio.io/istio/pkg/test/framework/components/namespace" 38 "istio.io/istio/pkg/test/framework/label" 39 "istio.io/istio/pkg/test/framework/resource" 40 "istio.io/istio/pkg/test/util/tmpl" 41 "istio.io/istio/tests/integration/security/util/cert" 42 ) 43 44 var ( 45 inst istio.Instance 46 apps deployment.SingleNamespaceView 47 client echo.Instances 48 server echo.Instances 49 serverNakedFoo echo.Instances 50 serverNakedBar echo.Instances 51 serverNakedFooAlt echo.Instances 52 customConfig []echo.Config 53 echo1NS namespace.Instance 54 config deployment.Config 55 ) 56 57 func TestMain(m *testing.M) { 58 framework. 59 NewSuite(m). 60 // k8s is required because the plugin CA key and certificate are stored in a k8s secret. 61 Label(label.CustomSetup). 62 Setup(istio.Setup(&inst, setupConfig, cert.CreateCASecret)). 63 Setup(namespace.Setup(&echo1NS, namespace.Config{Prefix: "echo1", Inject: true})). 64 Setup(func(ctx resource.Context) error { 65 err := SetupApps(ctx, namespace.Future(&echo1NS), &customConfig) 66 if err != nil { 67 return err 68 } 69 return nil 70 }). 71 Setup(func(ctx resource.Context) error { 72 config = deployment.Config{ 73 Namespaces: []namespace.Getter{ 74 namespace.Future(&echo1NS), 75 }, 76 Configs: echo.ConfigFuture(&customConfig), 77 } 78 err := addDefaultConfig(ctx, config, &customConfig) 79 if err != nil { 80 return err 81 } 82 return nil 83 }). 84 Setup(deployment.SetupSingleNamespace(&apps, deployment.Config{ 85 Namespaces: []namespace.Getter{ 86 namespace.Future(&echo1NS), 87 }, 88 Configs: echo.ConfigFuture(&customConfig), 89 })). 90 Setup(func(ctx resource.Context) error { 91 return createCustomInstances(&apps) 92 }). 93 Run() 94 } 95 96 func setupConfig(_ resource.Context, cfg *istio.Config) { 97 if cfg == nil { 98 return 99 } 100 // Add alternate root certificate to list of trusted anchors 101 script := path.Join(env.IstioSrc, "samples/certs", "root-cert-alt.pem") 102 rootPEM, err := cert.LoadCert(script) 103 if err != nil { 104 return 105 } 106 107 cfgYaml := tmpl.MustEvaluate(` 108 values: 109 pilot: 110 env: 111 ISTIO_MULTIROOT_MESH: true 112 meshConfig: 113 defaultConfig: 114 proxyMetadata: 115 PROXY_CONFIG_XDS_AGENT: "true" 116 trustDomainAliases: [some-other, trust-domain-foo] 117 caCertificates: 118 - pem: | 119 {{.pem | indent 8}} 120 `, map[string]string{"pem": rootPEM}) 121 cfg.ControlPlaneValues = cfgYaml 122 } 123 124 func createCustomInstances(apps *deployment.SingleNamespaceView) error { 125 for index, namespacedName := range apps.EchoNamespace.All.NamespacedNames() { 126 switch { 127 case namespacedName.Name == "client": 128 client = apps.EchoNamespace.All[index] 129 case namespacedName.Name == "server": 130 server = apps.EchoNamespace.All[index] 131 case namespacedName.Name == "server-naked-foo": 132 serverNakedFoo = apps.EchoNamespace.All[index] 133 case namespacedName.Name == "server-naked-bar": 134 serverNakedBar = apps.EchoNamespace.All[index] 135 case namespacedName.Name == "server-naked-foo-alt": 136 serverNakedFooAlt = apps.EchoNamespace.All[index] 137 } 138 } 139 return nil 140 } 141 142 func SetupApps(ctx resource.Context, customNs namespace.Getter, customCfg *[]echo.Config) error { 143 tmpdir, err := ctx.CreateTmpDirectory("ca-custom-root") 144 if err != nil { 145 return err 146 } 147 148 // Create testing certs using runtime namespace. 149 err = generateCerts(tmpdir, customNs.Get().Name()) 150 if err != nil { 151 return err 152 } 153 rootCert, err := cert.LoadCert(path.Join(tmpdir, "root-cert.pem")) 154 if err != nil { 155 return err 156 } 157 clientCert, err := cert.LoadCert(path.Join(tmpdir, "workload-server-naked-foo-cert.pem")) 158 if err != nil { 159 return err 160 } 161 Key, err := cert.LoadCert(path.Join(tmpdir, "workload-server-naked-foo-key.pem")) 162 if err != nil { 163 return err 164 } 165 166 rootCertAlt, err := cert.LoadCert(path.Join(tmpdir, "root-cert-alt.pem")) 167 if err != nil { 168 return err 169 } 170 clientCertAlt, err := cert.LoadCert(path.Join(tmpdir, "workload-server-naked-foo-alt-cert.pem")) 171 if err != nil { 172 return err 173 } 174 keyAlt, err := cert.LoadCert(path.Join(tmpdir, "workload-server-naked-foo-alt-key.pem")) 175 if err != nil { 176 return err 177 } 178 179 var customConfig []echo.Config 180 181 clientConfig := echo.Config{ 182 Namespace: customNs.Get(), 183 Service: "client", 184 } 185 serverNakedFooConfig := echo.Config{ 186 Namespace: customNs.Get(), 187 Service: "server-naked-foo", 188 Subsets: []echo.SubsetConfig{ 189 { 190 Annotations: map[string]string{annotation.SidecarInject.Name: "false"}, 191 }, 192 }, 193 ServiceAccount: true, 194 Ports: []echo.Port{ 195 { 196 Name: "https", 197 Protocol: protocol.HTTPS, 198 ServicePort: 443, 199 WorkloadPort: 8443, 200 TLS: true, 201 }, 202 }, 203 TLSSettings: &common.TLSSettings{ 204 RootCert: rootCert, 205 ClientCert: clientCert, 206 Key: Key, 207 AcceptAnyALPN: true, 208 }, 209 } 210 211 serverNakedBarConfig := echo.Config{ 212 Namespace: customNs.Get(), 213 Service: "server-naked-bar", 214 Subsets: []echo.SubsetConfig{ 215 { 216 Annotations: map[string]string{annotation.SidecarInject.Name: "false"}, 217 }, 218 }, 219 ServiceAccount: true, 220 Ports: []echo.Port{ 221 { 222 Name: "https", 223 Protocol: protocol.HTTPS, 224 ServicePort: 443, 225 WorkloadPort: 8443, 226 TLS: true, 227 }, 228 }, 229 TLSSettings: &common.TLSSettings{ 230 RootCert: rootCert, 231 ClientCert: clientCert, 232 Key: Key, 233 AcceptAnyALPN: true, 234 }, 235 } 236 237 serverNakedFooAltConfig := echo.Config{ 238 // Adding echo server for multi-root tests 239 Namespace: customNs.Get(), 240 Service: "server-naked-foo-alt", 241 Subsets: []echo.SubsetConfig{ 242 { 243 Annotations: map[string]string{annotation.SidecarInject.Name: "false"}, 244 }, 245 }, 246 ServiceAccount: true, 247 Ports: []echo.Port{ 248 { 249 Name: "https", 250 Protocol: protocol.HTTPS, 251 ServicePort: 443, 252 WorkloadPort: 8443, 253 TLS: true, 254 }, 255 }, 256 TLSSettings: &common.TLSSettings{ 257 RootCert: rootCertAlt, 258 ClientCert: clientCertAlt, 259 Key: keyAlt, 260 AcceptAnyALPN: true, 261 }, 262 } 263 264 serverConfig := echo.Config{ 265 Subsets: []echo.SubsetConfig{{}}, 266 Namespace: customNs.Get(), 267 Service: "server", 268 ServiceAccount: true, 269 Ports: []echo.Port{ 270 { 271 Name: httpPlaintext, 272 Protocol: protocol.HTTP, 273 ServicePort: 8090, 274 WorkloadPort: 8090, 275 }, 276 { 277 Name: httpMTLS, 278 Protocol: protocol.HTTP, 279 ServicePort: 8091, 280 WorkloadPort: 8091, 281 }, 282 { 283 Name: tcpPlaintext, 284 Protocol: protocol.TCP, 285 ServicePort: 8092, 286 WorkloadPort: 8092, 287 }, 288 { 289 Name: tcpMTLS, 290 Protocol: protocol.TCP, 291 ServicePort: 8093, 292 WorkloadPort: 8093, 293 }, 294 { 295 Name: tcpWL, 296 WorkloadPort: 9000, 297 Protocol: protocol.TCP, 298 }, 299 }, 300 } 301 302 customConfig = append(customConfig, clientConfig, serverNakedFooConfig, serverNakedBarConfig, 303 serverNakedFooAltConfig, serverConfig) 304 305 *customCfg = customConfig 306 return nil 307 } 308 309 func generateCerts(tmpdir, ns string) error { 310 workDir := path.Join(env.IstioSrc, "samples/certs") 311 script := path.Join(workDir, "generate-workload.sh") 312 313 // Create certificates signed by the same plugin CA that signs Istiod certificates 314 crts := []struct { 315 td string 316 sa string 317 }{ 318 { 319 td: "foo", 320 sa: "server-naked-foo", 321 }, 322 { 323 td: "bar", 324 sa: "server-naked-bar", 325 }, 326 } 327 for _, crt := range crts { 328 command := exec.Cmd{ 329 Path: script, 330 Args: []string{script, crt.td, ns, crt.sa, tmpdir}, 331 Stdout: os.Stdout, 332 Stderr: os.Stdout, 333 } 334 if err := command.Run(); err != nil { 335 return fmt.Errorf("failed to create testing certificates: %s", err) 336 } 337 } 338 339 // Create certificates signed by a Different ca with a different root 340 command := exec.Cmd{ 341 Path: script, 342 Args: []string{script, "foo", ns, "server-naked-foo-alt", tmpdir, "use-alternative-root"}, 343 Stdout: os.Stdout, 344 Stderr: os.Stdout, 345 } 346 if err := command.Run(); err != nil { 347 return fmt.Errorf("failed to create testing certificates: %s", err) 348 } 349 return nil 350 } 351 352 func addDefaultConfig(ctx resource.Context, cfg deployment.Config, customCfg *[]echo.Config) error { 353 defaultConfigs := cfg.DefaultEchoConfigs(ctx) 354 355 if defaultConfigs == nil { 356 return fmt.Errorf("unable to create default config") 357 } 358 359 *customCfg = append(*customCfg, defaultConfigs...) 360 return nil 361 }