diff --git a/components/kserve/kserve.go b/components/kserve/kserve.go index 89cbe86c94f..48029dc549d 100644 --- a/components/kserve/kserve.go +++ b/components/kserve/kserve.go @@ -3,6 +3,7 @@ package kserve import ( "fmt" + "path" "path/filepath" "strings" @@ -10,6 +11,8 @@ import ( "github.com/opendatahub-io/opendatahub-operator/v2/components" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/deploy" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/feature" + "github.com/opendatahub-io/opendatahub-operator/v2/pkg/feature/servicemesh" operatorv1 "github.com/openshift/api/operator/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" @@ -124,7 +127,7 @@ func (k *Kserve) ReconcileComponent(cli client.Client, owner metav1.Object, dsci if err := cluster.UpdatePodSecurityRolebinding(cli, dscispec.ApplicationsNamespace, "odh-model-controller"); err != nil { return err } - // Update image parameters for odh-maodel-controller + // Update image parameters for odh-model-controller if dscispec.DevFlags.ManifestsUri == "" && len(k.DevFlags.Manifests) == 0 { if err := deploy.ApplyParams(DependentPath, k.SetImageParamsMap(dependentParamMap), false); err != nil { return err @@ -140,6 +143,12 @@ func (k *Kserve) ReconcileComponent(cli client.Client, owner metav1.Object, dsci } } + if enabled { + if err := k.configureServiceMesh(cli, dscispec); err != nil { + return err + } + } + return nil } @@ -147,3 +156,49 @@ func (k *Kserve) DeepCopyInto(target *Kserve) { *target = *k target.Component = k.Component } + +func (k *Kserve) configureServiceMesh(cli client.Client, dscispec *dsci.DSCInitializationSpec) error { + shouldConfigureServiceMesh, err := deploy.ShouldConfigureServiceMesh(cli, dscispec) + if err != nil { + return err + } + + if shouldConfigureServiceMesh { + serviceMeshInitializer := servicemesh.NewServiceMeshInitializer(dscispec, k.defineServiceMeshFeatures(dscispec)) + + if err := serviceMeshInitializer.Prepare(); err != nil { + return err + } + + if err := serviceMeshInitializer.Apply(); err != nil { + return err + } + } + + return nil +} + +func (k *Kserve) defineServiceMeshFeatures(dscispec *dsci.DSCInitializationSpec) servicemesh.DefineFeatures { + return func(s *servicemesh.ServiceMeshInitializer) error { + var rootDir = filepath.Join(feature.BaseOutputDir, dscispec.ApplicationsNamespace) + if err := feature.CopyEmbeddedFiles("templates", rootDir); err != nil { + return err + } + + kserve, err := feature.CreateFeature("configure-kserve-for-external-authz"). + For(dscispec). + Manifests( + path.Join(rootDir, feature.ControlPlaneDir, "components", k.GetComponentName()), + ). + WithData(servicemesh.ClusterDetails). + Load() + + if err != nil { + return err + } + + s.Features = append(s.Features, kserve) + + return nil + } +} diff --git a/pkg/feature/manifest.go b/pkg/feature/manifest.go index a821fa82cbc..9933991b7c0 100644 --- a/pkg/feature/manifest.go +++ b/pkg/feature/manifest.go @@ -2,17 +2,19 @@ package feature import ( "fmt" - "github.com/pkg/errors" "html/template" "os" "path/filepath" "strings" + + "github.com/pkg/errors" ) const ( BaseDir = "templates/servicemesh/" ControlPlaneDir = BaseDir + "control-plane" AuthDir = BaseDir + "authorino" + KServeDir = BaseDir + "kserve" BaseOutputDir = "/tmp/servicemesh-manifests/" ) diff --git a/pkg/feature/templates/servicemesh/kserve/activator-envoyfilter.tmpl.yaml b/pkg/feature/templates/servicemesh/kserve/activator-envoyfilter.tmpl.yaml new file mode 100644 index 00000000000..4350cf7155e --- /dev/null +++ b/pkg/feature/templates/servicemesh/kserve/activator-envoyfilter.tmpl.yaml @@ -0,0 +1,42 @@ +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + labels: + app: odh + name: activator-host-header + namespace: {{ .Mesh.Namespace }} +spec: + priority: 20 + workloadSelector: + labels: + component: predictor + configPatches: + - applyTo: HTTP_FILTER + match: + listener: + filterChain: + filter: + name: envoy.filters.network.http_connection_manager + patch: + operation: INSERT_BEFORE + value: + name: envoy.filters.http.lua + typed_config: + '@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua + inlineCode: | + function envoy_on_request(request_handle) + local headers = request_handle:headers() + if not headers then + return + end + + local original_host = headers:get("k-original-host") + if original_host then + + port_seperator = string.find(original_host, ":", 7) + if port_seperator then + original_host = string.sub(original_host, 0, port_seperator-1) + end + headers:replace('host', original_host) + end + end diff --git a/pkg/feature/templates/servicemesh/kserve/envoy-oauth-temp-fix.tmpl.yaml b/pkg/feature/templates/servicemesh/kserve/envoy-oauth-temp-fix.tmpl.yaml new file mode 100644 index 00000000000..456b2ce119e --- /dev/null +++ b/pkg/feature/templates/servicemesh/kserve/envoy-oauth-temp-fix.tmpl.yaml @@ -0,0 +1,79 @@ +# https://issues.redhat.com/browse/OSSM-4873 +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + name: envoy-oauth-temp-fix-before + namespace: {{ .Mesh.Namespace }} + labels: + app: odh + temp: hack +spec: + workloadSelector: + labels: + istio: ingressgateway + priority: 20 + configPatches: + - applyTo: HTTP_FILTER + match: + listener: + filterChain: + filter: + name: envoy.filters.network.http_connection_manager + patch: + operation: INSERT_BEFORE + value: + name: envoy.filters.http.lua + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua + inlineCode: | + function envoy_on_request(request_handle) + local headers = request_handle:headers() + if not headers then + return + end + + local auth = headers:get("authorization") + if auth then + headers:replace("x-authorization", auth) + end + end +--- +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + name: envoy-oauth-temp-fix-after + namespace: {{ .Mesh.Namespace }} + labels: + app: odh + temp: hack +spec: + workloadSelector: + labels: + istio: ingressgateway + priority: 5 + configPatches: + - applyTo: HTTP_FILTER + match: + listener: + filterChain: + filter: + name: envoy.filters.network.http_connection_manager + patch: + operation: INSERT_BEFORE + value: + name: envoy.filters.http.lua + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua + inlineCode: | + function envoy_on_request(request_handle) + local headers = request_handle:headers() + if not headers then + return + end + + local xauth = headers:get("x-authorization") + if xauth then + headers:replace("authorization", xauth) + headers.remove("x-authorization") + end + end diff --git a/pkg/feature/templates/servicemesh/kserve/grpc-authorizationpolicy.tmpl.yaml b/pkg/feature/templates/servicemesh/kserve/grpc-authorizationpolicy.tmpl.yaml new file mode 100644 index 00000000000..254efbca2a5 --- /dev/null +++ b/pkg/feature/templates/servicemesh/kserve/grpc-authorizationpolicy.tmpl.yaml @@ -0,0 +1,19 @@ +apiVersion: security.istio.io/v1beta1 +kind: AuthorizationPolicy +metadata: + name: kserve-predicator + namespace: {{ .Mesh.Namespace }} +spec: + action: CUSTOM + provider: + name: opendatahub-odh-auth-provider + rules: + - to: + - operation: + notPaths: + - /healthz* + ports: + - "8013" + selector: + matchLabels: + component: predictor