GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — fix: Initialize auth managers in standalone lineage server by ntkathole · Pull Request #6873 · feast-dev/feast

fix: Initialize auth managers in standalone lineage server #6873

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Sign up for GitHub

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
jyejare merged 1 commit into feast-dev:master from ntkathole:lineage_auth_wired
Oct 7, 2026

Uh oh!

There was an error while loading. Please reload this page.

Merged

fix: Initialize auth managers in standalone lineage server #6873

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
1 commit
Select commit
7dcc01f
fix: Initialize auth managers in standalone lineage server
ntkathole Sep 26, 2026
File filter
Filter by extension

Clear filters
Conversations
Failed to load comments.
Loading

Uh oh!

There was an error while loading. Please reload this page.

Jump to
Jump to file
Failed to load files.
Loading

Uh oh!

There was an error while loading. Please reload this page.

Diff view
Diff view

There are no files selected for viewing

6 changes: 3 additions & 3 deletions 6 .secrets.baseline
Show comments
View file
Open in desktop

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

Oops, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

133 changes: 86 additions & 47 deletions 133 infra/feast-operator/internal/controller/services/repo_config.go
Show comments
View file
Open in desktop
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
123 changes: 121 additions & 2 deletions 123 infra/feast-operator/internal/controller/services/repo_config_test.go
Show comments
View file
Open in desktop
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.
Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread
ntkathole marked this conversation as resolved.
Resolved

Uh oh!

There was an error while loading. Please reload this page.


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
Oops, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Loading
Oops, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Add this suggestion to a batch that can be applied as a single commit. This suggestion is invalid because no changes were made to the code. Suggestions cannot be applied while the pull request is closed. Suggestions cannot be applied while viewing a subset of changes. Only one suggestion per line can be applied in a batch. Add this suggestion to a batch that can be applied as a single commit. Applying suggestions on deleted lines is not supported. You must change the existing code in this line in order to create a valid suggestion. Outdated suggestions cannot be applied. This suggestion has been applied or marked resolved. Suggestions cannot be applied from pending reviews. Suggestions cannot be applied on multi-line comments. Suggestions cannot be applied while the pull request is queued to merge. Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.