FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix: Initialize auth managers in standalone lineage server by ntkathole · Pull Request #6873 · feast-dev/feast · GitHub

Repository navigation

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .baseline  (1) .go  (2) .py  (1) All 3 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
6 changes: 3 additions & 3 deletions .secrets.baseline

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ func (feast *FeastServices) getServiceRepoConfig() (RepoConfig, error) {
}

func (feast *FeastServices) getLineageFeatureStoreYamlBase64() (string, error) {
repoConfig, err := feast.getLineageRepoConfig()
odhCaBundleExists := feast.GetCustomCertificatesBundle().IsDefined
repoConfig, err := feast.getLineageRepoConfig(odhCaBundleExists)
if err != nil {
return "", err
}
Expand All @@ -63,7 +64,7 @@ func (feast *FeastServices) getLineageFeatureStoreYamlBase64() (string, error) {
return base64.StdEncoding.EncodeToString(yamlBytes), nil
}

func (feast *FeastServices) getLineageRepoConfig() (RepoConfig, error) {
func (feast *FeastServices) getLineageRepoConfig(odhCaBundleExists bool) (RepoConfig, error) {
cr := feast.Handler.FeatureStore
applied := cr.Status.Applied

Expand Down Expand Up @@ -108,6 +109,28 @@ func (feast *FeastServices) getLineageRepoConfig() (RepoConfig, error) {
}
}

// Apply auth configuration so the lineage server enforces the same
// authentication as the other Feast servers. Without this the
// standalone lineage endpoints would be unauthenticated even when
// Kubernetes or OIDC auth is configured on the FeatureStore CR.
if applied.AuthzConfig != nil && applied.AuthzConfig.OidcAuthz != nil {
authzConfig, err := resolveOidcServerAuthzConfig(applied.AuthzConfig.OidcAuthz, feast.extractConfigFromSecret, odhCaBundleExists)
if err != nil {
return repoConfig, err
}
repoConfig.AuthzConfig = authzConfig
} else if applied.AuthzConfig != nil {
if applied.AuthzConfig.NoAuth != nil && *applied.AuthzConfig.NoAuth {
repoConfig.AuthzConfig = AuthzConfig{Type: NoAuthAuthType}
} else if applied.AuthzConfig.KubernetesAuthz != nil {
repoConfig.AuthzConfig = AuthzConfig{Type: KubernetesAuthType}
} else {
return repoConfig, fmt.Errorf("authzConfig must specify OIDC, Kubernetes, or no-auth")
}
} else {
repoConfig.AuthzConfig = defaultAuthzConfig
}

return repoConfig, nil
}

Expand Down Expand Up @@ -190,61 +213,77 @@ func getBaseServiceRepoConfig(
}
appliedSpec := featureStore.Status.Applied
if appliedSpec.AuthzConfig != nil && appliedSpec.AuthzConfig.OidcAuthz != nil {
repoConfig.AuthzConfig = AuthzConfig{Type: OidcAuthType}
oidcAuthz := appliedSpec.AuthzConfig.OidcAuthz
oidcParameters := map[string]interface{}{}

var secretProperties map[string]interface{}
if oidcAuthz.SecretRef != nil {
var err error
secretProperties, err = secretExtractionFunc("", oidcAuthz.SecretRef.Name, oidcAuthz.SecretKeyName)
if err != nil {
return repoConfig, err
}
for _, prop := range OidcOptionalSecretProperties {
if val, exists := secretProperties[string(prop)]; exists {
// Secret values are YAML-parsed on extraction, so an
// all-digits audience or issuer arrives as an int and
// would render unquoted, which the SDK's OidcAuthConfig
// rejects (Optional[str]). Coerce the claim keys back to
// strings; the five original keys keep their historical
// typing.
if prop == OidcAudience || prop == OidcIssuer {
if _, isString := val.(string); !isString {
val = fmt.Sprintf("%v", val)
}
}
oidcParameters[string(prop)] = val
}
}
}

discoveryUrl, err := resolveAuthDiscoveryUrl(oidcAuthz, secretProperties)
authzConfig, err := resolveOidcServerAuthzConfig(appliedSpec.AuthzConfig.OidcAuthz, secretExtractionFunc, odhCaBundleExists)
if err != nil {
return repoConfig, err
}
oidcParameters[string(OidcAuthDiscoveryUrl)] = discoveryUrl

if oidcAuthz.VerifySSL != nil {
oidcParameters[string(OidcVerifySsl)] = *oidcAuthz.VerifySSL
}
if oidcAuthz.JwksCacheLifespanSeconds != nil {
oidcParameters[string(OidcJwksCacheLifespanSeconds)] = *oidcAuthz.JwksCacheLifespanSeconds
}
if oidcAuthz.JwksRequestTimeoutSeconds != nil {
oidcParameters[string(OidcJwksRequestTimeoutSeconds)] = *oidcAuthz.JwksRequestTimeoutSeconds
}
if caCertPath := resolveOidcCACertPath(oidcAuthz, odhCaBundleExists); caCertPath != "" {
oidcParameters[string(OidcCaCertPath)] = caCertPath
}
repoConfig.AuthzConfig.OidcParameters = oidcParameters
repoConfig.AuthzConfig = authzConfig
} else {
repoConfig.AuthzConfig = clientRepoConfig.AuthzConfig
}

return repoConfig, nil
}

// resolveOidcServerAuthzConfig builds the server-side OIDC AuthzConfig from the
// CR's OidcAuthz spec. Used by both getBaseServiceRepoConfig and
// getLineageRepoConfig to avoid duplicating the secret extraction, discovery
// URL resolution, and TLS/CA logic.
func resolveOidcServerAuthzConfig(
oidcAuthz *feastdevv1.OidcAuthz,
secretExtractionFunc func(storeType string, secretRef string, secretKeyName string) (map[string]interface{}, error),
odhCaBundleExists bool,
) (AuthzConfig, error) {
authzConfig := AuthzConfig{Type: OidcAuthType}
oidcParameters := map[string]interface{}{}

var secretProperties map[string]interface{}
if oidcAuthz.SecretRef != nil {
var err error
secretProperties, err = secretExtractionFunc("", oidcAuthz.SecretRef.Name, oidcAuthz.SecretKeyName)
if err != nil {
return authzConfig, err
}
for _, prop := range OidcOptionalSecretProperties {
if val, exists := secretProperties[string(prop)]; exists {
// Secret values are YAML-parsed on extraction, so an
// all-digits audience or issuer arrives as an int and
// would render unquoted, which the SDK's OidcAuthConfig
// rejects (Optional[str]). Coerce the claim keys back to
// strings; the five original keys keep their historical
// typing.
if prop == OidcAudience || prop == OidcIssuer {
if _, isString := val.(string); !isString {
val = fmt.Sprintf("%v", val)
}
}
oidcParameters[string(prop)] = val
}
}
}

discoveryUrl, err := resolveAuthDiscoveryUrl(oidcAuthz, secretProperties)
if err != nil {
return authzConfig, err
}
oidcParameters[string(OidcAuthDiscoveryUrl)] = discoveryUrl

if oidcAuthz.VerifySSL != nil {
oidcParameters[string(OidcVerifySsl)] = *oidcAuthz.VerifySSL
}
if oidcAuthz.JwksCacheLifespanSeconds != nil {
oidcParameters[string(OidcJwksCacheLifespanSeconds)] = *oidcAuthz.JwksCacheLifespanSeconds
}
if oidcAuthz.JwksRequestTimeoutSeconds != nil {
oidcParameters[string(OidcJwksRequestTimeoutSeconds)] = *oidcAuthz.JwksRequestTimeoutSeconds
}
if caCertPath := resolveOidcCACertPath(oidcAuthz, odhCaBundleExists); caCertPath != "" {
oidcParameters[string(OidcCaCertPath)] = caCertPath
}
authzConfig.OidcParameters = oidcParameters
return authzConfig, nil
}

// resolveAuthDiscoveryUrl determines the OIDC discovery URL from the first available source.
// Priority: CR issuerUrl > Secret auth_discovery_url > OIDC_ISSUER_URL env var.
func resolveAuthDiscoveryUrl(oidcAuthz *feastdevv1.OidcAuthz, secretProperties map[string]interface{}) (string, error) {
Expand Down
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,7 @@ var _ = Describe("Repo Config", func() {
feast := FeastServices{
Handler: handler.FeastHandler{FeatureStore: featureStore},
}
repoConfig, err := feast.getLineageRepoConfig()
repoConfig, err := feast.getLineageRepoConfig(false)
Expect(err).NotTo(HaveOccurred())
Expect(repoConfig.Registry.RegistryType).To(Equal(RegistryRemoteConfigType))
Expect(repoConfig.Registry.Path).To(Equal(remoteHost))
Expand Down Expand Up @@ -882,7 +882,7 @@ var _ = Describe("Repo Config", func() {
feast := FeastServices{
Handler: handler.FeastHandler{FeatureStore: featureStore},
}
repoConfig, err := feast.getLineageRepoConfig()
repoConfig, err := feast.getLineageRepoConfig(false)
Expect(err).NotTo(HaveOccurred())
Expect(repoConfig.Registry.RegistryType).To(Equal(RegistryRemoteConfigType))
Expect(repoConfig.Registry.Path).To(Equal(resolvedHost))
Expand Down Expand Up @@ -922,6 +922,125 @@ var _ = Describe("Repo Config", func() {
Expect(err).NotTo(HaveOccurred())
})

It("should include kubernetes auth config in lineage repo config", func() {
featureStore := minimalFeatureStore()
remoteHost := "feast-banking-registry.feast.svc.cluster.local:443"
featureStore.Spec.Services = &feastdevv1.FeatureStoreServices{
Registry: &feastdevv1.Registry{
Remote: &feastdevv1.RemoteRegistryConfig{
Hostname: &remoteHost,
},
},
}
featureStore.Spec.AuthzConfig = &feastdevv1.AuthzConfig{
KubernetesAuthz: &feastdevv1.KubernetesAuthz{},
}
featureStore.Spec.OpenLineage = &feastdevv1.OpenLineageConfig{
Enabled: true,
Consumer: &feastdevv1.OpenLineageConsumerConfig{
Enabled: true,
LineageServer: &feastdevv1.LineageServerConfig{
Replicas: ptr.To[int32](1),
},
},
}
ApplyDefaultsToStatus(featureStore)
featureStore.Status.ServiceHostnames.Registry = remoteHost

feast := FeastServices{
Handler: handler.FeastHandler{FeatureStore: featureStore},
}
repoConfig, err := feast.getLineageRepoConfig(false)
Expect(err).NotTo(HaveOccurred())
Expect(repoConfig.AuthzConfig.Type).To(Equal(KubernetesAuthType))
Expect(repoConfig.Registry.RegistryType).To(Equal(RegistryRemoteConfigType))
Expect(repoConfig.Registry.Path).To(Equal(remoteHost))
})

It("should default to kubernetes auth in lineage repo config when authzConfig is nil", func() {
featureStore := minimalFeatureStore()
featureStore.Spec.OpenLineage = &feastdevv1.OpenLineageConfig{
Enabled: true,
Consumer: &feastdevv1.OpenLineageConsumerConfig{
Enabled: true,
LineageServer: &feastdevv1.LineageServerConfig{
Replicas: ptr.To[int32](1),
},
},
}
ApplyDefaultsToStatus(featureStore)

feast := FeastServices{
Handler: handler.FeastHandler{FeatureStore: featureStore},
}
repoConfig, err := feast.getLineageRepoConfig(false)
Expect(err).NotTo(HaveOccurred())
Expect(repoConfig.AuthzConfig.Type).To(Equal(KubernetesAuthType))
})
Comment thread
ntkathole marked this conversation as resolved.
Comment thread
ntkathole marked this conversation as resolved.

It("should include no_auth config in lineage repo config when NoAuth is set", func() {
featureStore := minimalFeatureStore()
featureStore.Spec.AuthzConfig = &feastdevv1.AuthzConfig{
NoAuth: boolPtr(true),
}
featureStore.Spec.OpenLineage = &feastdevv1.OpenLineageConfig{
Enabled: true,
Consumer: &feastdevv1.OpenLineageConsumerConfig{
Enabled: true,
LineageServer: &feastdevv1.LineageServerConfig{
Replicas: ptr.To[int32](1),
},
},
}
ApplyDefaultsToStatus(featureStore)

feast := FeastServices{
Handler: handler.FeastHandler{FeatureStore: featureStore},
}
repoConfig, err := feast.getLineageRepoConfig(false)
Expect(err).NotTo(HaveOccurred())
Expect(repoConfig.AuthzConfig.Type).To(Equal(NoAuthAuthType))
})

It("should include OIDC auth config in lineage repo config with issuerUrl", func() {
featureStore := minimalFeatureStore()
remoteHost := "feast-banking-registry.feast.svc.cluster.local:443"
featureStore.Spec.Services = &feastdevv1.FeatureStoreServices{
Registry: &feastdevv1.Registry{
Remote: &feastdevv1.RemoteRegistryConfig{
Hostname: &remoteHost,
},
},
}
featureStore.Spec.AuthzConfig = &feastdevv1.AuthzConfig{
OidcAuthz: &feastdevv1.OidcAuthz{
IssuerUrl: "https://keycloak.example.com/realms/test",
},
}
featureStore.Spec.OpenLineage = &feastdevv1.OpenLineageConfig{
Enabled: true,
Consumer: &feastdevv1.OpenLineageConsumerConfig{
Enabled: true,
LineageServer: &feastdevv1.LineageServerConfig{
Replicas: ptr.To[int32](1),
},
},
}
ApplyDefaultsToStatus(featureStore)
featureStore.Status.ServiceHostnames.Registry = remoteHost

feast := FeastServices{
Handler: handler.FeastHandler{FeatureStore: featureStore},
}
repoConfig, err := feast.getLineageRepoConfig(false)
Expect(err).NotTo(HaveOccurred())
Expect(repoConfig.AuthzConfig.Type).To(Equal(OidcAuthType))
Expect(repoConfig.AuthzConfig.OidcParameters).To(HaveKeyWithValue(
string(OidcAuthDiscoveryUrl),
"https://keycloak.example.com/realms/test/.well-known/openid-configuration",
))
})

It("should reject lineageServer without any registry when authz is set", func() {
featureStore := minimalFeatureStore()
featureStore.Spec.AuthzConfig = &feastdevv1.AuthzConfig{
Expand Down
Loading
Loading

Back | FazBrowse Home | New Git URL