Skip to content
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.

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

OpenID Connect Support #423

Merged
merged 44 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
708139f
Support OIDC
siegfriedweber Oct 24, 2023
e36417b
Merge remote-tracking branch 'origin/main' into feat/oidc
sbernauer Nov 17, 2023
7b5bba3
Rework to use new structs from operator-rs
sbernauer Nov 17, 2023
b70524e
charts
sbernauer Nov 17, 2023
8a69005
handle tls settings
sbernauer Nov 17, 2023
db3fd1b
charts
sbernauer Nov 17, 2023
3412c6e
Merge remote-tracking branch 'origin/main' into feat/oidc
sbernauer Nov 20, 2023
ce3d512
new operator-rs
sbernauer Nov 20, 2023
133530d
update operator-rs
sbernauer Nov 23, 2023
0ea4d0a
fix: Move oidcApiPath to the correct location
sbernauer Nov 23, 2023
8686c4b
chore: update operator-rs
NickLarsenNZ Nov 24, 2023
b47a58d
feat: erorr out if superset is configured for oidc+keycloak with a pr…
NickLarsenNZ Nov 24, 2023
d137770
update operator-rs
sbernauer Nov 27, 2023
fbd82b6
bump
sbernauer Dec 4, 2023
76c01ca
Merge remote-tracking branch 'origin/main' into feat/oidc
sbernauer Jan 2, 2024
1d771b5
Update operator-rs
sbernauer Jan 3, 2024
8130ec7
fix tests
sbernauer Jan 3, 2024
c8e5712
Merge remote-tracking branch 'origin/main' into feat/oidc
sbernauer Jan 3, 2024
7fb873f
Make authentication configuration optional
siegfriedweber Jan 11, 2024
b87bbd3
Rename SupersetAuthenticationConfigResolved to SupersetClientAuthenti…
siegfriedweber Jan 12, 2024
e00535d
Improve the data type of the authentication configuration
siegfriedweber Jan 12, 2024
b8829d3
Regenerate charts
siegfriedweber Jan 12, 2024
e7bca8e
Revise the resolution of OIDC authentication details
siegfriedweber Jan 16, 2024
8f89ca7
Merge branch 'main' into feat/oidc
siegfriedweber Jan 17, 2024
44a9bb6
Add OIDC test
siegfriedweber Jan 23, 2024
f088ffe
Use TLS for Keycloak in the OIDC test
siegfriedweber Jan 24, 2024
03bdd3b
Check the user info in the OIDC test
siegfriedweber Jan 25, 2024
efe1ac0
Fix linter warnings
siegfriedweber Jan 25, 2024
d505309
Fix secret scope in the OIDC test
siegfriedweber Jan 25, 2024
0369b12
Refactor the check that TLS verification cannot be disabled
siegfriedweber Jan 25, 2024
054e580
Allow multiple OIDC providers
siegfriedweber Jan 26, 2024
c97ac70
Add documentation for OIDC in Superset
siegfriedweber Jan 29, 2024
cee7130
Use main branch of operator-rs
siegfriedweber Jan 29, 2024
b5f640f
Merge branch 'main' into feat/oidc
siegfriedweber Jan 29, 2024
34e6185
Fix spelling
siegfriedweber Jan 30, 2024
71007aa
Add comments
siegfriedweber Jan 30, 2024
4e622b4
Move the creation of EnvVars into a separate function
siegfriedweber Jan 31, 2024
9830a69
Update docs/modules/superset/pages/usage-guide/security.adoc
siegfriedweber Jan 31, 2024
bf1c875
Update docs/modules/superset/pages/usage-guide/security.adoc
siegfriedweber Jan 31, 2024
fdf8fbc
Update docs/modules/superset/pages/usage-guide/security.adoc
siegfriedweber Jan 31, 2024
24e54ef
Upgrade operator-rs to version 0.64.0
siegfriedweber Jan 31, 2024
171889a
Merge branch 'main' into feat/oidc
siegfriedweber Jan 31, 2024
73a21be
Update changelog
siegfriedweber Jan 31, 2024
1605b48
Fix external link icons in the documentation
siegfriedweber Jan 31, 2024
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
707 changes: 382 additions & 325 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ strum = { version = "0.25", features = ["derive"] }
tokio = { version = "1.29", features = ["full"] }
tracing = "0.1"

# [patch."https://github.com/stackabletech/operator-rs.git"]
# stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "main" }
[patch."https://github.com/stackabletech/operator-rs.git"]
sbernauer marked this conversation as resolved.
Show resolved Hide resolved
stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "main" }
58 changes: 39 additions & 19 deletions deploy/helm/superset-operator/crds/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,31 @@ spec:
properties:
authentication:
default: []
description: The Superset [authentication](https://docs.stackable.tech/home/nightly/superset/usage-guide/security) settings. Currently the underlying Flask App Builder only supports one authentication mechanism at a time. This means the operator will error out if multiple references to an AuthenticationClass are provided.
description: List of AuthenticationClasses used to authenticate users.
items:
properties:
authenticationClass:
description: Name of the [AuthenticationClass](https://docs.stackable.tech/home/nightly/concepts/authentication.html#authenticationclass) used to authenticate the users. At the moment only LDAP is supported. If not specified the default authentication (AUTH_DB) will be used.
nullable: true
description: A name/key which references an authentication class. To get the concrete [`AuthenticationClass`], we must resolve it. This resolution can be achieved by using [`ClientAuthenticationDetails::resolve_class`].
type: string
oidc:
description: |-
This field contains authentication provider specific configuration.

Use [`ClientAuthenticationDetails::oidc_or_error`] to get the value or report an error to the user.
nullable: true
properties:
clientCredentialsSecret:
description: A reference to the OIDC client credentials secret. The secret contains the client id and secret.
type: string
extraScopes:
default: []
description: An optional list of extra scopes which get merged with the scopes defined in the [`AuthenticationClass`].
items:
type: string
type: array
required:
- clientCredentialsSecret
type: object
syncRolesAt:
default: Registration
description: If we should replace ALL the user's roles each login, or only on registration. Gets mapped to `AUTH_ROLES_SYNC_AT_LOGIN`
Expand All @@ -52,13 +70,15 @@ spec:
default: Public
description: This role will be given in addition to any AUTH_ROLES_MAPPING. Gets mapped to `AUTH_USER_REGISTRATION_ROLE`
type: string
required:
- authenticationClass
type: object
type: array
clusterOperation:
default:
stopped: false
reconciliationPaused: false
description: '[Cluster operations](https://docs.stackable.tech/home/nightly/concepts/operations/cluster_operations) properties, allow stopping the product instance as well as pausing reconciliation.'
stopped: false
description: Cluster operations like pause reconciliation or cluster stop.
properties:
reconciliationPaused:
default: false
Expand Down Expand Up @@ -160,10 +180,10 @@ spec:
properties:
affinity:
default:
podAffinity: null
podAntiAffinity: null
nodeAffinity: null
nodeSelector: null
podAffinity: null
podAntiAffinity: null
description: These configuration settings control [Pod placement](https://docs.stackable.tech/home/nightly/concepts/operations/pod_placement).
properties:
nodeAffinity:
Expand Down Expand Up @@ -636,8 +656,8 @@ spec:
type: string
logging:
default:
enableVectorAgent: null
containers: {}
enableVectorAgent: null
description: Logging configuration, learn more in the [logging concept documentation](https://docs.stackable.tech/home/nightly/concepts/logging).
properties:
containers:
Expand Down Expand Up @@ -720,19 +740,19 @@ spec:
type: object
resources:
default:
cpu:
max: null
min: null
memory:
limit: null
runtimeLimits: {}
cpu:
min: null
max: null
storage: {}
description: CPU and memory limits for Superset pods
properties:
cpu:
default:
min: null
max: null
min: null
properties:
max:
description: The maximum amount of CPU cores that can be requested by Pods. Equivalent to the `limit` for Pod resource configuration. Cores are specified either as a decimal point number or as milli units. For example:`1.5` will be 1.5 cores, also written as `1500m`.
Expand Down Expand Up @@ -3623,10 +3643,10 @@ spec:
properties:
affinity:
default:
podAffinity: null
podAntiAffinity: null
nodeAffinity: null
nodeSelector: null
podAffinity: null
podAntiAffinity: null
description: These configuration settings control [Pod placement](https://docs.stackable.tech/home/nightly/concepts/operations/pod_placement).
properties:
nodeAffinity:
Expand Down Expand Up @@ -4099,8 +4119,8 @@ spec:
type: string
logging:
default:
enableVectorAgent: null
containers: {}
enableVectorAgent: null
description: Logging configuration, learn more in the [logging concept documentation](https://docs.stackable.tech/home/nightly/concepts/logging).
properties:
containers:
Expand Down Expand Up @@ -4183,19 +4203,19 @@ spec:
type: object
resources:
default:
cpu:
max: null
min: null
memory:
limit: null
runtimeLimits: {}
cpu:
min: null
max: null
storage: {}
description: CPU and memory limits for Superset pods
properties:
cpu:
default:
min: null
max: null
min: null
properties:
max:
description: The maximum amount of CPU cores that can be requested by Pods. Equivalent to the `limit` for Pod resource configuration. Cores are specified either as a decimal point number or as milli units. For example:`1.5` will be 1.5 cores, also written as `1500m`.
Expand Down
161 changes: 81 additions & 80 deletions docs/modules/superset/pages/usage-guide/security.adoc
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
= Security

== [[authentication]]Authentication
Every user has to be authenticated before using Superset: there are several ways in which this can be set up.

=== Webinterface
The default setting is to manually set up users via the Webinterface.
Every user has to be authenticated before using Superset:
there are several ways in which this can be set up.

Only one authentication type is supported at a time.
For instance, it is not possible to add an LDAP and an OIDC authentication class.
But it is possible to add several OIDC authentication classes.
siegfriedweber marked this conversation as resolved.
Show resolved Hide resolved

=== Superset database

The default setting is to manually set up users via the web interface where they are stored in the database attached to Superset.

=== LDAP

Superset supports xref:home:concepts:authentication.adoc[authentication] of users against an LDAP server. This requires setting up an AuthenticationClass for the LDAP server.
Superset supports xref:home:concepts:authentication.adoc[authentication] of users against a single LDAP server.
This requires setting up an AuthenticationClass for the LDAP server.
The AuthenticationClass is then referenced in the SupersetCluster resource as follows:

[source,yaml]
Expand All @@ -31,81 +39,63 @@ spec:

Users that log in with LDAP are assigned to a default https://superset.apache.org/docs/security/#roles[Role] which is specified with the `userRegistrationRole` property.

You can follow the xref:home:tutorials:authentication_with_openldap.adoc[] tutorial to learn how to set up an AuthenticationClass for an LDAP server, as well as consulting the {crd-docs}/authentication.stackable.tech/authenticationclass/v1alpha1/[AuthenticationClass reference {external-link-icon}^].
You can follow the xref:home:tutorials:authentication_with_openldap.adoc[] tutorial to learn how to set up an AuthenticationClass for an LDAP server, as well as consulting the xref:home:concepts:authentication.adoc#_ldap[AuthenticationClass concepts page].

=== [[oauth]]OAuth
=== [[oidc]]OpenID Connect

Strictly speaking, OAuth is an authorization protocol but can be used for authentication if the
security implications are acceptable. In the Superset cluster CRD, authentication via OAuth is not
directly supported but can be configured by overriding properties in `superset_config.py`. The
following example uses https://www.keycloak.org/[Keycloak 21.1] as OAuth provider:
An OpenID Connect provider can be used for authentication.
Unfortunately, there is no generic support for OpenID Connect built into Superset.
This means that only specific OpenID Connect providers can be configured.
Currently, only https://www.keycloak.org/[Keycloak {external-link-icon}^] is supported by the Stackable Data Platform.
siegfriedweber marked this conversation as resolved.
Show resolved Hide resolved

[source,yaml]
----
apiVersion: superset.stackable.tech/v1alpha1
kind: SupersetCluster
metadata:
name: superset-with-oauth
name: superset-with-oidc
spec:
image:
productVersion: 3.0.1
[...]
nodes:
configOverrides:
superset_config.py:
AUTH_TYPE: AUTH_OAUTH # <1>
AUTH_USER_REGISTRATION: 'true' # <2>
AUTH_USER_REGISTRATION_ROLE: 'Gamma' # <3>
OAUTH_PROVIDERS: |-
[
{ 'name': 'keycloak', # <4>
'icon': 'fa-key', # <5>
'token_key': 'access_token', # <6>
'remote_app': {
'client_id': 'KEYCLOAK_CLIENT_ID',
'client_secret': 'KEYCLOAK_CLIENT_SECRET',
'api_base_url': 'https://KEYCLOAK_DOMAIN/realms/KEYCLOAK_REALM/protocol/openid-connect', # <7>
'client_kwargs': {
'scope': 'email profile openid' # <8>
},
'access_token_url': 'https://KEYCLOAK_DOMAIN/realms/KEYCLOAK_REALM/protocol/openid-connect/token', # <9>
'authorize_url': 'https://KEYCLOAK_DOMAIN/realms/KEYCLOAK_REALM/protocol/openid-connect/auth', # <10>
'request_token_url': None,
},
}
]
clusterConfig:
authentication:
- authenticationClass: keycloak # <1>
oidc:
clientCredentialsSecret: superset-keycloak-client # <2>
userRegistrationRole: Gamma # <3>
----

<1> The authentication type must be set to `AUTH_OAUTH`.
<2> Authenticated users are added to the Superset database if they do not exist yet. The user
information is fetched from the `/userinfo` endpoint of the OAuth provider which is only
available if the `openid` scope is requested. The admin user is already present in the Superset
database as defined in the credentials secret, but the authentication is performed with the
password stored in Keycloak. The property `clusterConfig.authenticationConfig.userRegistration`
cannot be used here because it is only taken into account when an authentication class is set.
<3> This role will be given in addition to any roles defined in `AUTH_ROLE_MAPPING`. The property
`clusterConfig.authenticationConfig.userRegistrationRole` cannot be used here because it is only
taken into account when an authentication class is set.
<4> The name of the OAuth provider; Superset has built-in logic for `keycloak` and some other
providers.
<5> The Font Awesome icon on the sign-in button
<6> The token key name the provider uses
<7> The base URL used for well-known endpoints like `/userinfo`. It must be reachable from the
Kubernetes cluster/Superset pod.
<8> The scopes `email` and `profile` return claims which contain the user's name and email address
respectively. The `openid` scope is required for the `/userinfo` endpoint.
<9> The access token URL must be reachable from the Kubernetes client/Superset pod.
<10> The authorize URL must be reachable from the user's browser.

A minimum client configuration in Keycloak looks like this:
<1> The reference to an AuthenticationClass called `keycloak`
<2> The reference to the Secret containing the Superset client credentials
<3> The default role to which all users are assigned

Users that log in with OpenID Connect are assigned to a default https://superset.apache.org/docs/security/#roles[Role] which is specified with the `userRegistrationRole` property.

The Secret containing the Superset client credentials:

[source,yaml]
----
apiVersion: v1
kind: Secret
metadata:
name: superset-keycloak-client
stringData:
clientId: superset # <1>
clientSecret: superset_client_secret # <2>
----

<1> The client ID of Superset as defined in Keycloak
<1> The client secret as defined in Keycloak

A minimum client configuration in Keycloak for this example looks like this:

[source,json]
----
{
"clientId": "KEYCLOAK_CLIENT_ID",
"clientId": "superset",
"enabled": true,
"clientAuthenticatorType": "client-secret", # <1>
"secret": "KEYCLOAK_CLIENT_SECRET",
"secret": "superset_client_secret",
"redirectUris": [
"*"
],
Expand All @@ -121,33 +111,19 @@ A minimum client configuration in Keycloak looks like this:
<2> Enables the OAuth2 "Authorization Code Flow".
<3> Enables OpenID Connect and OAuth2 support.

Superset configuration examples for other providers can be found at
https://flask-appbuilder.readthedocs.io/en/latest/security.html#authentication-oauth[].

=== [[oidc]]OpenID Connect

OpenID Connect (OIDC) is an authentication protocol based on the OAuth 2.0 framework. Unfortunately,
it is not supported by Superset out of the box. An adapted `SupersetSecurityManager` and the
https://github.com/puiterwijk/flask-oidc[`flask-oidc`] library would be required which are both not
included in the official Stackable product image. But as OpenID Connect is just an authentication
layer on top of the OAuth 2.0 authorization framework, the configuration described in the
xref:oauth[OAuth section] usually works for OpenID Connect providers too.

=== OpenID

OpenID Authentication 2.0 is an authentication protocol. It is deprecated in favor of
xref:oidc[OpenID Connect]. Superset provides the authentication type `AUTH_OID` for it but also
requires the https://github.com/pallets-eco/flask-openid[Flask-OpenID] library which is not included
in the official Stackable product image.
Further information for specifying an AuthenticationClass for an OIDC provider can be found at the xref:home:concepts:authentication.adoc#_oidc[concepts page].

== [[authorization]]Authorization

Superset has a concept called `Roles` which allows you to grant user permissions based on roles.
Have a look at the https://superset.apache.org/docs/security[Superset documentation on Security].

=== Webinterface
You can view all the available roles in the Webinterface of Superset and can also assign users to these roles.
=== Superset database

You can view all the available roles in the web interface of Superset and can also assign users to these roles.

=== LDAP

Superset supports assigning https://superset.apache.org/docs/security/#roles[Roles] to users based on their LDAP group membership, though this is not yet supported by the Stackable operator.
All the users logging in via LDAP get assigned to the same role which you can configure via the attribute `authentication[*].userRegistrationRole` on the `SupersetCluster` object:

Expand All @@ -165,3 +141,28 @@ spec:
----

<1> All users are assigned to the `Admin` role

=== OpenID Connect

The same as for LDAP also applies to OpenID Connect.
siegfriedweber marked this conversation as resolved.
Show resolved Hide resolved
Superset supports assigning https://superset.apache.org/docs/security/#roles[Roles] to users based on their OpenID Connect scopes, though this is not yet supported by the Stackable operator.
All the users logging in via OpenID Connect get assigned to the same role which you can configure via the attribute `authentication[*].userRegistrationRole` on the `SupersetCluster` object:

[source,yaml]
----
apiVersion: superset.stackable.tech/v1alpha1
kind: SupersetCluster
metadata:
name: superset-with-oidc
spec:
image:
productVersion: 3.0.1
clusterConfig:
authentication:
- authenticationClass: keycloak
oidc:
clientCredentialsSecret: superset-keycloak-client
userRegistrationRole: Gamma # <1>
----

<1> All users are assigned to the `Gamma` role
2 changes: 2 additions & 0 deletions rust/crd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ snafu.workspace = true
tracing.workspace = true

[dev-dependencies]
indoc.workspace = true
serde_yaml.workspace = true
tokio.workspace = true
2 changes: 1 addition & 1 deletion rust/crd/src/affinity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ mod tests {
image:
productVersion: 3.0.1
clusterConfig:
credentialsSecret: superset-credentials
credentialsSecret: superset-db-credentials
nodes:
roleGroups:
default:
Expand Down
Loading
Loading