istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/security/util/framework.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 util
    19  
    20  import (
    21  	"fmt"
    22  	"os"
    23  	"path"
    24  
    25  	"istio.io/api/annotation"
    26  	"istio.io/istio/pkg/config/protocol"
    27  	"istio.io/istio/pkg/test/echo/common"
    28  	"istio.io/istio/pkg/test/env"
    29  	"istio.io/istio/pkg/test/framework/components/echo"
    30  	"istio.io/istio/pkg/test/framework/components/echo/match"
    31  	"istio.io/istio/pkg/test/framework/components/namespace"
    32  	"istio.io/istio/pkg/test/framework/resource"
    33  )
    34  
    35  const (
    36  	ASvc             = "a"
    37  	BSvc             = "b"
    38  	CSvc             = "c"
    39  	DSvc             = "d"
    40  	ESvc             = "e"
    41  	MultiversionSvc  = "multiversion"
    42  	VMSvc            = "vm"
    43  	HeadlessSvc      = "headless"
    44  	NakedSvc         = "naked"
    45  	HeadlessNakedSvc = "headless-naked"
    46  	ExternalSvc      = "external"
    47  )
    48  
    49  type EchoDeployments struct {
    50  	// TODO: Consolidate the echo config and reduce/reuse echo instances (https://github.com/istio/istio/issues/28599)
    51  	// Namespace1 is used as the default namespace for reachability tests and other tests which can reuse the same config for echo instances
    52  	Namespace1 namespace.Instance
    53  	// Namespace2 is used by most authorization test cases within authorization_test.go
    54  	Namespace2 namespace.Instance
    55  	// Namespace3 is used by TestAuthorization_Conditions and there is one C echo instance deployed
    56  	Namespace3    namespace.Instance
    57  	A, B, C, D, E echo.Instances
    58  	Multiversion  echo.Instances
    59  	Headless      echo.Instances
    60  	Naked         echo.Instances
    61  	VM            echo.Instances
    62  	HeadlessNaked echo.Instances
    63  	All           echo.Instances
    64  	External      echo.Instances
    65  }
    66  
    67  func EchoConfig(name string, headless bool, annos map[string]string) echo.Config {
    68  	out := echo.Config{
    69  		Service:        name,
    70  		ServiceAccount: true,
    71  		Headless:       headless,
    72  		Subsets: []echo.SubsetConfig{
    73  			{
    74  				Version:     "v1",
    75  				Annotations: annos,
    76  			},
    77  		},
    78  		Ports: []echo.Port{
    79  			{
    80  				Name:     "http",
    81  				Protocol: protocol.HTTP,
    82  				// We use a port > 1024 to not require root
    83  				WorkloadPort: 8090,
    84  				ServicePort:  8095,
    85  			},
    86  			{
    87  				Name:     "tcp",
    88  				Protocol: protocol.TCP,
    89  			},
    90  			{
    91  				Name:     "grpc",
    92  				Protocol: protocol.GRPC,
    93  			},
    94  			{
    95  				Name:         "https",
    96  				Protocol:     protocol.HTTPS,
    97  				ServicePort:  443,
    98  				WorkloadPort: 8443,
    99  				TLS:          true,
   100  			},
   101  			{
   102  				Name:         "http-8091",
   103  				Protocol:     protocol.HTTP,
   104  				WorkloadPort: 8091,
   105  			},
   106  			{
   107  				Name:         "http-8092",
   108  				Protocol:     protocol.HTTP,
   109  				WorkloadPort: 8092,
   110  			},
   111  			{
   112  				Name:         "tcp-8093",
   113  				Protocol:     protocol.TCP,
   114  				WorkloadPort: 8093,
   115  			},
   116  			{
   117  				Name:         "tcp-8094",
   118  				Protocol:     protocol.TCP,
   119  				WorkloadPort: 8094,
   120  			},
   121  			// Workload Ports needed by TestPassThroughFilterChain
   122  			// The port 8084-8089 will be defined only in the workload and not in the k8s service.
   123  			{
   124  				Name:         "tcp-8085",
   125  				ServicePort:  echo.NoServicePort,
   126  				WorkloadPort: 8085,
   127  				Protocol:     protocol.HTTP,
   128  			},
   129  			{
   130  				Name:         "tcp-8086",
   131  				ServicePort:  echo.NoServicePort,
   132  				WorkloadPort: 8086,
   133  				Protocol:     protocol.HTTP,
   134  			},
   135  			{
   136  				Name:         "tcp-8087",
   137  				ServicePort:  echo.NoServicePort,
   138  				WorkloadPort: 8087,
   139  				Protocol:     protocol.TCP,
   140  			},
   141  			{
   142  				Name:         "tcp-8088",
   143  				ServicePort:  echo.NoServicePort,
   144  				WorkloadPort: 8088,
   145  				Protocol:     protocol.TCP,
   146  			},
   147  			{
   148  				Name:         "tcp-8089",
   149  				ServicePort:  echo.NoServicePort,
   150  				WorkloadPort: 8089,
   151  				Protocol:     protocol.HTTPS,
   152  				TLS:          true,
   153  			},
   154  			{
   155  				Name:         "tcp-8084",
   156  				ServicePort:  echo.NoServicePort,
   157  				WorkloadPort: 8084,
   158  				Protocol:     protocol.HTTPS,
   159  				TLS:          true,
   160  			},
   161  		},
   162  	}
   163  
   164  	// for headless service with selector, the port and target port must be equal
   165  	// Ref: https://kubernetes.io/docs/concepts/services-networking/service/#headless-services
   166  	if headless {
   167  		for i := range out.Ports {
   168  			out.Ports[i].ServicePort = out.Ports[i].WorkloadPort
   169  		}
   170  	}
   171  	return out
   172  }
   173  
   174  func mustReadCert(f string) string {
   175  	b, err := os.ReadFile(path.Join(env.IstioSrc, "tests/testdata/certs/dns", f))
   176  	if err != nil {
   177  		panic(fmt.Sprintf("failed to read %v: %v", f, err))
   178  	}
   179  	return string(b)
   180  }
   181  
   182  func SetupApps(ctx resource.Context, customCfg *[]echo.Config, buildVM bool) error {
   183  	if ctx.Settings().Skip(echo.VM) {
   184  		buildVM = false
   185  	}
   186  
   187  	var customConfig []echo.Config
   188  	a := EchoConfig(ASvc, false, nil)
   189  	b := EchoConfig(BSvc, false, nil)
   190  	c := EchoConfig(CSvc, false, nil)
   191  	d := EchoConfig(DSvc, false, nil)
   192  	e := EchoConfig(ESvc, false, nil)
   193  	multiversionCfg := func() echo.Config {
   194  		// Multi-version specific setup
   195  		multiVersionCfg := EchoConfig(MultiversionSvc, false, nil)
   196  		multiVersionCfg.Subsets = []echo.SubsetConfig{
   197  			// Istio deployment, with sidecar.
   198  			{
   199  				Version: "vistio",
   200  			},
   201  			// Legacy deployment subset, does not have sidecar injected.
   202  			{
   203  				Version:     "vlegacy",
   204  				Annotations: map[string]string{annotation.SidecarInject.Name: "false"},
   205  			},
   206  		}
   207  		return multiVersionCfg
   208  	}()
   209  
   210  	nakedSvc := EchoConfig(NakedSvc, false, map[string]string{annotation.SidecarInject.Name: "false"})
   211  
   212  	vmCfg := func() echo.Config {
   213  		// VM specific setup
   214  		vmCfg := EchoConfig(VMSvc, false, nil)
   215  		// for test cases that have `buildVM` off, vm will function like a regular pod
   216  		vmCfg.DeployAsVM = buildVM
   217  		return vmCfg
   218  	}()
   219  
   220  	externalSvc := echo.Config{
   221  		Service: ExternalSvc,
   222  		// Namespace: appsNamespace,
   223  		Ports: []echo.Port{
   224  			{
   225  				// Plain HTTP port only used to route request to egress gateway
   226  				Name:         "http",
   227  				Protocol:     protocol.HTTP,
   228  				ServicePort:  80,
   229  				WorkloadPort: 8080,
   230  			},
   231  			{
   232  				// HTTPS port
   233  				Name:         "https",
   234  				Protocol:     protocol.HTTPS,
   235  				ServicePort:  443,
   236  				WorkloadPort: 8443,
   237  				TLS:          true,
   238  			},
   239  		},
   240  		// Set up TLS certs on the server. This will make the server listen with these credentials.
   241  		TLSSettings: &common.TLSSettings{
   242  			// Echo has these test certs baked into the docker image
   243  			RootCert:   mustReadCert("root-cert.pem"),
   244  			ClientCert: mustReadCert("cert-chain.pem"),
   245  			Key:        mustReadCert("key.pem"),
   246  			// Override hostname to match the SAN in the cert we are using
   247  			Hostname: "server.default.svc",
   248  		},
   249  		Subsets: []echo.SubsetConfig{{
   250  			Version:     "v1",
   251  			Annotations: map[string]string{annotation.SidecarInject.Name: "false"},
   252  		}},
   253  	}
   254  
   255  	headlessSvc := EchoConfig(HeadlessSvc, true, nil)
   256  	headlessNakedSvc := EchoConfig(HeadlessNakedSvc, true, map[string]string{annotation.SidecarInject.Name: "false"})
   257  
   258  	customConfig = append(customConfig, a, b, c, d, e, multiversionCfg, nakedSvc, vmCfg, externalSvc, headlessSvc, headlessNakedSvc)
   259  	*customCfg = customConfig
   260  	return nil
   261  }
   262  
   263  // IsMultiversion matches instances that have Multi-version specific setup.
   264  var IsMultiversion match.Matcher = func(i echo.Instance) bool {
   265  	if len(i.Config().Subsets) != 2 {
   266  		return false
   267  	}
   268  	var matchIstio, matchLegacy bool
   269  	for _, s := range i.Config().Subsets {
   270  		if s.Version == "vistio" {
   271  			matchIstio = true
   272  		} else if s.Version == "vlegacy" {
   273  			if val, ok := s.Annotations[annotation.SidecarInject.Name]; ok && val == "false" {
   274  				matchLegacy = true
   275  			}
   276  		}
   277  	}
   278  	return matchIstio && matchLegacy
   279  }
   280  
   281  var IsNotMultiversion = match.Not(IsMultiversion)
   282  
   283  // SourceMatcher matches workload pod A with sidecar injected and VM
   284  func SourceMatcher(ns namespace.Instance, skipVM bool) match.Matcher {
   285  	m := match.ServiceName(echo.NamespacedName{
   286  		Name:      ASvc,
   287  		Namespace: ns,
   288  	})
   289  
   290  	if !skipVM {
   291  		m = match.Or(m, match.ServiceName(echo.NamespacedName{
   292  			Name:      VMSvc,
   293  			Namespace: ns,
   294  		}))
   295  	}
   296  
   297  	return m
   298  }
   299  
   300  // DestMatcher matches workload pod B with sidecar injected and VM
   301  func DestMatcher(ns namespace.Instance, skipVM bool) match.Matcher {
   302  	m := match.ServiceName(echo.NamespacedName{
   303  		Name:      BSvc,
   304  		Namespace: ns,
   305  	})
   306  
   307  	if !skipVM {
   308  		m = match.Or(m, match.ServiceName(echo.NamespacedName{
   309  			Name:      VMSvc,
   310  			Namespace: ns,
   311  		}))
   312  	}
   313  
   314  	return m
   315  }