istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/framework/components/authz/kube.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package authz 16 17 import ( 18 "fmt" 19 "os" 20 "strings" 21 "time" 22 23 "github.com/hashicorp/go-multierror" 24 25 meshconfig "istio.io/api/mesh/v1alpha1" 26 "istio.io/istio/pkg/config/protocol" 27 "istio.io/istio/pkg/test/env" 28 "istio.io/istio/pkg/test/framework/components/echo" 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 "istio.io/istio/pkg/test/framework/resource/config/apply" 33 "istio.io/istio/pkg/test/framework/resource/config/cleanup" 34 "istio.io/istio/pkg/test/kube" 35 "istio.io/istio/pkg/test/scopes" 36 "istio.io/istio/pkg/test/util/tmpl" 37 "istio.io/istio/pkg/test/util/yml" 38 "istio.io/istio/pkg/util/protomarshal" 39 "istio.io/istio/pkg/util/sets" 40 ) 41 42 const ( 43 httpName = "ext-authz-http" 44 grpcName = "ext-authz-grpc" 45 httpPort = 8000 46 grpcPort = 9000 47 48 providerTemplate = ` 49 extensionProviders: 50 - name: "{{ .httpName }}" 51 envoyExtAuthzHttp: 52 service: "{{ .fqdn }}" 53 port: {{ .httpPort }} 54 headersToUpstreamOnAllow: ["x-ext-authz-*"] 55 headersToDownstreamOnDeny: ["x-ext-authz-*"] 56 includeRequestHeadersInCheck: ["x-ext-authz"] 57 includeAdditionalHeadersInCheck: 58 x-ext-authz-additional-header-new: additional-header-new-value 59 x-ext-authz-additional-header-override: additional-header-override-value 60 - name: "{{ .grpcName }}" 61 envoyExtAuthzGrpc: 62 service: "{{ .fqdn }}" 63 port: {{ .grpcPort }}` 64 ) 65 66 var _ resource.Resource = &serverImpl{} 67 68 func newKubeServer(ctx resource.Context, ns namespace.Instance) (server *serverImpl, err error) { 69 start := time.Now() 70 scopes.Framework.Info("=== BEGIN: Deploy authz server ===") 71 defer func() { 72 if err != nil { 73 scopes.Framework.Error("=== FAILED: Deploy authz server ===") 74 scopes.Framework.Error(err) 75 } else { 76 scopes.Framework.Infof("=== SUCCEEDED: Deploy authz server in %v ===", time.Since(start)) 77 } 78 }() 79 80 // Create the namespace, if unspecified. 81 if ns == nil { 82 ns, err = namespace.New(ctx, namespace.Config{ 83 Prefix: "authz", 84 Inject: true, 85 }) 86 if err != nil { 87 return 88 } 89 } 90 91 server = &serverImpl{ 92 ns: ns, 93 providers: []Provider{ 94 &providerImpl{ 95 name: httpName, 96 api: HTTP, 97 protocolSupported: func(p protocol.Instance) bool { 98 // HTTP protocol doesn't support raw TCP requests. 99 return !p.IsTCP() 100 }, 101 targetSupported: func(echo.Target) bool { 102 return true 103 }, 104 check: checkHTTP, 105 }, 106 &providerImpl{ 107 name: grpcName, 108 api: GRPC, 109 protocolSupported: func(protocol.Instance) bool { 110 return true 111 }, 112 targetSupported: func(echo.Target) bool { 113 return true 114 }, 115 check: checkGRPC, 116 }, 117 }, 118 } 119 server.id = ctx.TrackResource(server) 120 121 // Deploy the authz server. 122 if err = server.deploy(ctx); err != nil { 123 return 124 } 125 126 // Patch MeshConfig to install the providers. 127 err = server.installProviders(ctx) 128 return 129 } 130 131 func readDeploymentYAML(ctx resource.Context) (string, error) { 132 // Read the samples file. 133 filePath := fmt.Sprintf("%s/samples/extauthz/ext-authz.yaml", env.IstioSrc) 134 data, err := os.ReadFile(filePath) 135 if err != nil { 136 return "", err 137 } 138 yamlText := string(data) 139 // Replace the image. 140 s := ctx.Settings().Image 141 if s.PullSecret != "" { 142 var imageSpec resource.ImageSettings 143 imageSpec.PullSecret = s.PullSecret 144 secretName, err := imageSpec.PullSecretName() 145 if err != nil { 146 return "", err 147 } 148 yamlText, err = addPullSecret(yamlText, secretName) 149 if err != nil { 150 return "", err 151 } 152 } 153 154 oldImage := "gcr.io/istio-testing/ext-authz:latest" 155 newImage := fmt.Sprintf("%s/ext-authz:%s", s.Hub, s.Tag) 156 yamlText = strings.ReplaceAll(yamlText, oldImage, newImage) 157 158 // Replace the image pull policy 159 oldPolicy := "IfNotPresent" 160 newPolicy := s.PullPolicy 161 yamlText = strings.ReplaceAll(yamlText, oldPolicy, newPolicy) 162 163 return yamlText, nil 164 } 165 166 func addPullSecret(resource string, pullSecret string) (string, error) { 167 res := yml.SplitString(resource) 168 updatedYaml, err := yml.ApplyPullSecret(res[1], pullSecret) 169 if err != nil { 170 return "", err 171 } 172 mergedYaml := yml.JoinString(res[0], updatedYaml) 173 return mergedYaml, nil 174 } 175 176 func (s *serverImpl) deploy(ctx resource.Context) error { 177 yamlText, err := readDeploymentYAML(ctx) 178 if err != nil { 179 return err 180 } 181 182 if err := ctx.ConfigKube(ctx.Clusters()...). 183 YAML(s.ns.Name(), yamlText). 184 Apply(apply.CleanupConditionally); err != nil { 185 return err 186 } 187 188 // Wait for the endpoints to be ready. 189 var g multierror.Group 190 for _, c := range ctx.Clusters() { 191 c := c 192 g.Go(func() error { 193 _, _, err := kube.WaitUntilServiceEndpointsAreReady(c.Kube(), s.ns.Name(), "ext-authz") 194 return err 195 }) 196 } 197 198 return g.Wait().ErrorOrNil() 199 } 200 201 func (s *serverImpl) installProviders(ctx resource.Context) error { 202 // Update the mesh config extension provider for the ext-authz service. 203 providerYAML, err := tmpl.Evaluate(providerTemplate, s.templateArgs()) 204 if err != nil { 205 return err 206 } 207 208 return installProviders(ctx, providerYAML) 209 } 210 211 type serverImpl struct { 212 id resource.ID 213 ns namespace.Instance 214 providers []Provider 215 } 216 217 func (s *serverImpl) ID() resource.ID { 218 return s.id 219 } 220 221 func (s *serverImpl) Namespace() namespace.Instance { 222 return s.ns 223 } 224 225 func (s *serverImpl) Providers() []Provider { 226 return append([]Provider{}, s.providers...) 227 } 228 229 func (s *serverImpl) templateArgs() map[string]any { 230 fqdn := fmt.Sprintf("ext-authz.%s.svc.cluster.local", s.ns.Name()) 231 return map[string]any{ 232 "fqdn": fqdn, 233 "httpName": httpName, 234 "grpcName": grpcName, 235 "httpPort": httpPort, 236 "grpcPort": grpcPort, 237 } 238 } 239 240 func installProviders(ctx resource.Context, providerYAML string) error { 241 var ist istio.Instance 242 ist, err := istio.Get(ctx) 243 if err != nil { 244 return err 245 } 246 247 // Now parse the provider YAML. 248 newMC := &meshconfig.MeshConfig{} 249 if err := protomarshal.ApplyYAML(providerYAML, newMC); err != nil { 250 return err 251 } 252 253 providerNames := sets.New[string]() 254 for _, p := range newMC.GetExtensionProviders() { 255 providerNames.Insert(p.Name) 256 } 257 258 return ist.UpdateMeshConfig(ctx, 259 func(mc *meshconfig.MeshConfig) error { 260 newProviders := []*meshconfig.MeshConfig_ExtensionProvider{} 261 // Merge the extension providers. 262 // If we are overwriting an existing one, keep the new one. 263 for _, o := range mc.ExtensionProviders { 264 if !providerNames.Contains(o.Name) { 265 newProviders = append(newProviders, o) 266 } 267 } 268 newProviders = append(newProviders, newMC.ExtensionProviders...) 269 mc.ExtensionProviders = newProviders 270 return nil 271 }, cleanup.Conditionally) 272 }