Skip to content

Commit

Permalink
OpenID Connect Support (#524)
Browse files Browse the repository at this point in the history
* First parts of airflow OIDC

* Add integration test for OIDC

* Document OIDC support

* Update wip

* Adapt module tests in operator-binary/src/config.rs

* Adding unit tests

* WIP before merge

* WIP compiling operator

* Fixng auth tests

* Update the OIDC integration test

* Env vars oidc

* Cleaning up

* Adding changelog

* Remove reference

* Regenerate charts and Nix files

* making pre-commit happy

* fixing typo

* again pre-commit

* pre-commit nr3

* pre-commit nr4

* pre-commit nr5

* Env var set by operator rather then envoverrides

* Removing TODO, sort toml alphabetically

* Andrew comments

Co-authored-by: Andrew Kenworthy <1712947+adwk67@users.noreply.github.com>

* Specify every arm rather then just matching all

---------

Co-authored-by: Siegfried Weber <mail@siegfriedweber.net>
Co-authored-by: Andrew Kenworthy <1712947+adwk67@users.noreply.github.com>
  • Loading branch information
3 people authored Oct 31, 2024
1 parent 1dd21f6 commit 6529f52
Show file tree
Hide file tree
Showing 32 changed files with 2,100 additions and 231 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
Use the env var `KUBERNETES_CLUSTER_DOMAIN` or the operator Helm chart property `kubernetesClusterDomain` to set a non-default cluster domain ([#518]).
- Support for `2.9.3` ([#494]).
- Experimental Support for `2.10.2` ([#512]).
- Add support for OpenID Connect ([#524])

### Changed

Expand All @@ -30,6 +31,7 @@
[#494]: https://github.com/stackabletech/airflow-operator/pull/494
[#518]: https://github.com/stackabletech/airflow-operator/pull/518
[#520]: https://github.com/stackabletech/airflow-operator/pull/520
[#524]: https://github.com/stackabletech/airflow-operator/pull/524

## [24.7.0] - 2024-07-24

Expand Down
9 changes: 9 additions & 0 deletions Cargo.lock

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

24 changes: 24 additions & 0 deletions Cargo.nix

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ built = { version = "0.7", features = ["chrono", "git2"] }
clap = "4.5"
fnv = "1.0"
futures = { version = "0.3", features = ["compat"] }
indoc = "2.0"
product-config = { git = "https://github.com/stackabletech/product-config.git", tag = "0.7.0" }
rstest = "0.23"
semver = "1.0"
Expand Down
22 changes: 19 additions & 3 deletions deploy/helm/airflow-operator/crds/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -452,13 +452,27 @@ spec:
properties:
authentication:
default: []
description: The Airflow [authentication](https://docs.stackable.tech/home/nightly/airflow/usage-guide/security.html) 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.
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: Name of the [AuthenticationClass](https://docs.stackable.tech/home/nightly/concepts/authentication) used to authenticate users.
type: string
oidc:
description: This field contains OIDC-specific configuration. It is only required in case OIDC is used.
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 @@ -474,6 +488,8 @@ 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
credentialsSecret:
Expand Down
115 changes: 109 additions & 6 deletions docs/modules/airflow/pages/usage-guide/security.adoc
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
= Security
:description: Secure Apache Airflow by configuring user authentication and authorization, either with built-in methods or LDAP.
:description: Secure Apache Airflow by configuring user authentication and authorization.
:airflow-access-control-docs: https://airflow.apache.org/docs/apache-airflow/stable/security/access-control.html
:keycloak: https://www.keycloak.org/

Secure Apache Airflow by configuring user authentication and authorization.
Airflow provides built-in user and role management, but can also connect to a LDAP server to manage users centrally instead.
Airflow provides built-in user and role management, but can also connect to an LDAP server or an OIDC provider to manage users centrally instead.

== Authentication

Users need to authenticate themselves before using Airflow, and there are two ways to configure users:
The built-in user management or LDAP.
Users need to authenticate themselves before using Airflow, and there are several ways in which this can be set up.
[IMPORTANT]
.Multiple authentication methods
====
Only one authentication method is supported at a time, and in case of LDAP, only one authentication class is allowed.
This means, it is not possible to configure both LDAP and OIDC authentication methods at the same time, but *it is* possible to configure multiple OIDC classes *or* one LDAP authentication class.
====

=== Built-in user management

Expand All @@ -19,7 +26,7 @@ image::airflow_security.png[Airflow Security menu]

=== LDAP

Airflow supports xref:concepts:authentication.adoc[user authentication] via LDAP.
Airflow supports xref:concepts:authentication.adoc[user authentication] against a single LDAP server.
Set up an AuthenticationClass for the LDAP server and reference it in the Airflow Stacklet resource as shown:

[source,yaml]
Expand All @@ -30,7 +37,7 @@ metadata:
name: airflow-with-ldap
spec:
image:
productVersion: 2.9.3
productVersion: 2.10.2
clusterConfig:
authentication:
- authenticationClass: ldap # <1>
Expand All @@ -48,6 +55,79 @@ The users and roles can be viewed as before in the Webserver UI, but the blue "+

image::airflow_security_ldap.png[Airflow Security menu]

=== [[oidc]]OpenID Connect

An OpenID Connect provider can be used for authentication.
Unfortunately, there is no generic support for OpenID Connect built into Airflow.
This means that only specific OpenID Connect providers can be configured.

IMPORTANT: Airflow deployments on the Stackable Data Platform only support {keycloak}[Keycloak].

[source,yaml]
----
apiVersion: airflow.stackable.tech/v1alpha1
kind: AirflowCluster
metadata:
name: airflow-with-oidc
spec:
image:
productVersion: 2.10.2
clusterConfig:
authentication:
- authenticationClass: keycloak # <1>
oidc:
clientCredentialsSecret: airflow-keycloak-client # <2>
userRegistrationRole: User # <3>
----

<1> The reference to an AuthenticationClass called `keycloak`
<2> The reference to the Secret containing the Airflow client credentials
<3> The default role to which all users are assigned

Users that log in with OpenID Connect are assigned to a default {airflow-access-control-docs}[role] which is specified with the `userRegistrationRole` property.

The Secret containing the Airflow client credentials:

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

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

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

[source,json]
----
{
"clientId": "airflow",
"enabled": true,
"clientAuthenticatorType": "client-secret", # <1>
"secret": "airflow_client_secret",
"redirectUris": [
"*"
],
"webOrigins": [
"*"
],
"standardFlowEnabled": true, # <2>
"protocol": "openid-connect" # <3>
}
----

<1> Sets the OIDC type to confidential access type.
<2> Enables the OAuth2 "Authorization Code Flow".
<3> Enables OpenID Connect and OAuth2 support.

Further information for specifying an AuthenticationClass for an OIDC provider can be found at the xref:concepts:authentication.adoc#_oidc[concepts page].

== Authorization
The Airflow Webserver delegates the {airflow-access-control-docs}[handling of user access control] to https://flask-appbuilder.readthedocs.io/en/latest/security.html[Flask AppBuilder].

Expand All @@ -74,3 +154,26 @@ spec:

<1> The reference to an AuthenticationClass called `ldap`
<2> All users are assigned to the `Admin` role

=== OpenID Connect

The mechanism for assigning roles to users described in the LDAP section also applies to OpenID Connect.
Airflow supports assigning {airflow-access-control-docs}[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 `AirflowCluster` object:

[source,yaml]
----
apiVersion: airflow.stackable.tech/v1alpha1
kind: AirflowCluster
metadata:
name: airflow-with-oidc
spec:
clusterConfig:
authentication:
- authenticationClass: keycloak
oidc:
clientCredentialsSecret: airflow-keycloak-client
userRegistrationRole: Admin # <1>
----

<1> All users are assigned to the `Admin` role
4 changes: 3 additions & 1 deletion rust/crd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ repository.workspace = true
publish = false

[dependencies]
indoc.workspace = true
product-config.workspace = true
serde.workspace = true
serde_json.workspace = true
snafu.workspace = true
stackable-operator.workspace = true
product-config.workspace = true
strum.workspace = true
tokio.workspace = true
tracing.workspace = true

[dev-dependencies]
Expand Down
Loading

0 comments on commit 6529f52

Please sign in to comment.