Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a5b7d91
wip - compiling
maltesander Mar 29, 2025
aa254e4
wip
maltesander Mar 31, 2025
6f38bb9
wip
maltesander Apr 4, 2025
fc7b35a
cleanup & test
maltesander Apr 13, 2025
261df99
remove comments
maltesander Apr 15, 2025
41d7eec
adapt entra name
maltesander Apr 15, 2025
d52c9aa
set correct default
maltesander Apr 15, 2025
108abe1
Merge remote-tracking branch 'origin/main' into feat/experimental-ent…
maltesander Apr 15, 2025
6f75fc7
merge main
maltesander Apr 15, 2025
04c3117
fmt
maltesander Apr 15, 2025
dc5130f
test fix entra endpoint
maltesander Apr 15, 2025
29202b9
fmt
maltesander Apr 15, 2025
5a4860e
split token and host endpoint
maltesander Apr 15, 2025
6b85d1b
fix group response
maltesander Apr 15, 2025
f438b63
clippy
maltesander Apr 15, 2025
4ed2a92
use url in entra endpoint
maltesander Apr 15, 2025
c26952f
regenerate charts
maltesander Apr 15, 2025
888076d
extend test, improve erros
maltesander Apr 15, 2025
b974eb6
use with_context
maltesander Apr 16, 2025
61baffb
use with context 2
maltesander Apr 16, 2025
c85e2b3
clippy
maltesander Apr 16, 2025
60d903b
Merge remote-tracking branch 'origin/main' into feat/experimental-ent…
maltesander Apr 16, 2025
b1913ca
change endpoint to hostname in CRD
maltesander Apr 17, 2025
cad745a
consolidate naming
maltesander Apr 17, 2025
8c07822
change entrabackend to use url consistently
maltesander Apr 17, 2025
2dc58ce
Merge remote-tracking branch 'origin/main' into feat/experimental-ent…
maltesander Apr 17, 2025
e6a531a
remove obsolete tests
maltesander Apr 17, 2025
45cf3cc
reduce tests further
maltesander Apr 17, 2025
d0c127a
add documentation
maltesander Apr 17, 2025
c93f239
adapted changelog
maltesander Apr 17, 2025
616b514
fix typo
maltesander Apr 17, 2025
4d10eaa
make port optional
maltesander Apr 22, 2025
cd15999
clippy
maltesander Apr 22, 2025
d7b088c
Merge remote-tracking branch 'origin/main' into feat/experimental-ent…
maltesander Apr 22, 2025
bf0b62e
Merge remote-tracking branch 'origin/main' into feat/experimental-ent…
maltesander May 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
- Use `--file-log-rotation-period` (or `FILE_LOG_ROTATION_PERIOD`) to configure the frequency of rotation.
- Use `--console-log-format` (or `CONSOLE_LOG_FORMAT`) to set the format to `plain` (default) or `json`.
- Log the startup event for bundle-builder and user-info-fetcher ([#703]).
- Support experimental user-info-fetcher Entra backend to fetch user groups ([#712]).

### Changed

Expand Down Expand Up @@ -42,6 +43,7 @@ All notable changes to this project will be documented in this file.
[#707]: https://github.com/stackabletech/opa-operator/pull/707
[#709]: https://github.com/stackabletech/opa-operator/pull/709
[#710]: https://github.com/stackabletech/opa-operator/pull/710
[#712]: https://github.com/stackabletech/opa-operator/pull/712
[#715]: https://github.com/stackabletech/opa-operator/pull/715

## [25.3.0] - 2025-03-21
Expand Down
77 changes: 77 additions & 0 deletions deploy/helm/opa-operator/crds/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ spec:
- experimentalXfscAas
- required:
- experimentalActiveDirectory
- required:
- experimentalEntra
properties:
experimentalActiveDirectory:
description: Backend that fetches user information from Active Directory
Expand Down Expand Up @@ -137,6 +139,81 @@ spec:
- kerberosSecretClassName
- ldapServer
type: object
experimentalEntra:
description: Backend that fetches user information from Microsoft Entra
properties:
clientCredentialsSecret:
description: |-
Name of a Secret that contains client credentials of an Entra account with permissions `User.ReadAll` and `GroupMemberShip.ReadAll`.

Must contain the fields `clientId` and `clientSecret`.
type: string
port:
description: Port of the identity provider. If TLS is used defaults to `443`, otherwise to `80`.
format: uint16
minimum: 0.0
nullable: true
type: integer
tenantId:
description: The Microsoft Entra tenant ID.
type: string
tls:
default:
verification:
server:
caCert:
webPki: {}
description: Use a TLS connection. Should usually be set to WebPki.
nullable: true
properties:
verification:
description: The verification method used to verify the certificates of the server and/or the client.
oneOf:
- required:
- none
- required:
- server
properties:
none:
description: Use TLS but don't verify certificates.
type: object
server:
description: Use TLS and a CA certificate to verify the server.
properties:
caCert:
description: CA cert to verify the server.
oneOf:
- required:
- webPki
- required:
- secretClass
properties:
secretClass:
description: Name of the [SecretClass](https://docs.stackable.tech/home/nightly/secret-operator/secretclass) which will provide the CA certificate. Note that a SecretClass does not need to have a key but can also work with just a CA certificate, so if you got provided with a CA cert but don't have access to the key you can still use this method.
type: string
webPki:
description: Use TLS and the CA certificates trusted by the common web browsers to verify the server. This can be useful when you e.g. use public AWS S3 or other public available services.
type: object
type: object
required:
- caCert
type: object
type: object
required:
- verification
type: object
tokenHostname:
default: login.microsoft.com
description: Hostname of the token provider, defaults to `login.microsoft.com`.
type: string
userInfoHostname:
default: graph.microsoft.com
description: Hostname of the user info provider, defaults to `graph.microsoft.com`.
type: string
required:
- clientCredentialsSecret
- tenantId
type: object
experimentalXfscAas:
description: Backend that fetches user information from the Gaia-X Cross Federation Services Components (XFSC) Authentication & Authorization Service.
properties:
Expand Down
24 changes: 24 additions & 0 deletions docs/modules/opa/pages/usage-guide/user-info-fetcher.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Currently the following backends are supported:

* xref:#backend-keycloak[]
* xref:#backend-activedirectory[]
* xref:#backend-entra[]

[#backends]
== Backends
Expand Down Expand Up @@ -109,6 +110,29 @@ spec:
<7> The name of the SecretClass that knows how to create Kerberos keytabs trusted by Active Directory
<8> The name of the SecretClass that contains the Active Directory's root CA certificate(s)

[#backend-entra]
=== Entra

WARNING: The Entra backend is experimental, and subject to change.

Fetch groups but not roles for a user from Entra.

NOTE: The client in Entra must use the `client_credentials` flow and requires the `User.ReadAll` and `GroupMemberShip.ReadAll` permissions.

[source,yaml]
----
spec:
clusterConfig:
userInfo:
backend:
experimentalEntra: # <1>
tenantId: 00000000-0000-0000-0000-000000000000 # <2>
clientCredentialsSecret: user-info-fetcher-client-credentials # <3>
----
<1> Enables the Entra backend
<2> The Entra tenant ID
<3> A secret containing the `clientId` and `clientSecret` keys

== User info fetcher API

User information can be retrieved from regorules using the functions `userInfoByUsername(username)` and `userInfoById(id)` in `data.stackable.opa.userinfo.v1`.
Expand Down
25 changes: 24 additions & 1 deletion rust/operator-binary/src/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use stackable_operator::{
product_image_selection::ResolvedProductImage,
rbac::build_rbac_resources,
secret_class::{SecretClassVolume, SecretClassVolumeScope},
tls_verification::TlsClientDetailsError,
tls_verification::{TlsClientDetails, TlsClientDetailsError},
},
k8s_openapi::{
DeepMerge,
Expand Down Expand Up @@ -1014,6 +1014,29 @@ fn build_server_rolegroup_daemonset(
.add_volumes_and_mounts(&mut pb, vec![&mut cb_user_info_fetcher])
.context(UserInfoFetcherTlsVolumeAndMountsSnafu)?;
}
user_info_fetcher::v1alpha1::Backend::Entra(entra) => {
pb.add_volume(
VolumeBuilder::new(USER_INFO_FETCHER_CREDENTIALS_VOLUME_NAME)
.secret(SecretVolumeSource {
secret_name: Some(entra.client_credentials_secret.clone()),
..Default::default()
})
.build(),
)
.context(AddVolumeSnafu)?;
cb_user_info_fetcher
.add_volume_mount(
USER_INFO_FETCHER_CREDENTIALS_VOLUME_NAME,
USER_INFO_FETCHER_CREDENTIALS_DIR,
)
.context(AddVolumeMountSnafu)?;

TlsClientDetails {
tls: entra.tls.clone(),
}
.add_volumes_and_mounts(&mut pb, vec![&mut cb_user_info_fetcher])
.context(UserInfoFetcherTlsVolumeAndMountsSnafu)?;
}
}

pb.add_container(cb_user_info_fetcher.build());
Expand Down
60 changes: 58 additions & 2 deletions rust/operator-binary/src/crd/user_info_fetcher.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use std::collections::BTreeMap;
use std::{collections::BTreeMap, str::FromStr};

use serde::{Deserialize, Serialize};
use stackable_operator::{
commons::{networking::HostName, tls_verification::TlsClientDetails},
commons::{
networking::HostName,
tls_verification::{CaCert, Tls, TlsClientDetails, TlsServerVerification, TlsVerification},
},
schemars::{self, JsonSchema},
time::Duration,
versioned::versioned,
Expand Down Expand Up @@ -38,6 +41,10 @@ pub mod versioned {
/// Backend that fetches user information from Active Directory
#[serde(rename = "experimentalActiveDirectory")]
ActiveDirectory(v1alpha1::ActiveDirectoryBackend),

/// Backend that fetches user information from Microsoft Entra
#[serde(rename = "experimentalEntra")]
Entra(v1alpha1::EntraBackend),
}

#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
Expand Down Expand Up @@ -110,6 +117,39 @@ pub mod versioned {
pub additional_group_attribute_filters: BTreeMap<String, String>,
}

#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EntraBackend {
/// Hostname of the token provider, defaults to `login.microsoft.com`.
#[serde(default = "entra_default_token_hostname")]
pub token_hostname: HostName,

/// Hostname of the user info provider, defaults to `graph.microsoft.com`.
#[serde(default = "entra_default_user_info_hostname")]
pub user_info_hostname: HostName,

/// Port of the identity provider. If TLS is used defaults to `443`, otherwise to `80`.
pub port: Option<u16>,

/// The Microsoft Entra tenant ID.
pub tenant_id: String,

/// Use a TLS connection. Should usually be set to WebPki.
// We do not use the flattened `TlsClientDetails` here since we cannot
// default to WebPki using a default and flatten
// https://github.com/serde-rs/serde/issues/1626
// This means we have to wrap `Tls` in `TlsClientDetails` to use its
// method like `uses_tls()`.
#[serde(default = "default_tls_web_pki")]
pub tls: Option<Tls>,

/// Name of a Secret that contains client credentials of an Entra account with
/// permissions `User.ReadAll` and `GroupMemberShip.ReadAll`.
///
/// Must contain the fields `clientId` and `clientSecret`.
pub client_credentials_secret: String,
}

#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Cache {
Expand All @@ -129,6 +169,22 @@ fn default_root_path() -> String {
"/".to_string()
}

fn entra_default_token_hostname() -> HostName {
HostName::from_str("login.microsoft.com").unwrap()
}

fn entra_default_user_info_hostname() -> HostName {
HostName::from_str("graph.microsoft.com").unwrap()
}

fn default_tls_web_pki() -> Option<Tls> {
Some(Tls {
verification: TlsVerification::Server(TlsServerVerification {
ca_cert: CaCert::WebPki {},
}),
})
}

fn aas_default_port() -> u16 {
5000
}
Expand Down
Loading