配置OIDC登录
使用 OIDC 登录
参考文档:https://airflow.apache.org/docs/apache-airflow/stable/security/webserver.html
from airflow.auth.managers.fab.security_manager.override import FabAirflowSecurityManagerOverride
from flask_appbuilder.security.manager import AUTH_OAUTH
import os
AUTH_TYPE = AUTH_OAUTH
AUTH_ROLES_SYNC_AT_LOGIN = True # Checks roles on every login
AUTH_USER_REGISTRATION = True # allow users who are not already in the FAB DB to register
AUTH_ROLES_MAPPING = {
"Viewer": ["Viewer"],
"Admin": ["Admin"],
}
# If you wish, you can add multiple OAuth providers.
OAUTH_PROVIDERS = [
{
"name": "github",
"icon": "fa-github",
"token_key": "access_token",
"remote_app": {
"client_id": os.getenv("OAUTH_APP_ID"),
"client_secret": os.getenv("OAUTH_APP_SECRET"),
"api_base_url": "https://api.github.com",
"client_kwargs": {"scope": "read:user, read:org"},
"access_token_url": "https://github.com/login/oauth/access_token",
"authorize_url": "https://github.com/login/oauth/authorize",
"request_token_url": None,
},
},
]
class CustomSecurityManager(FabAirflowSecurityManagerOverride):
pass
# Make sure to replace this with your own implementation of AirflowSecurityManager class
SECURITY_MANAGER_CLASS = CustomSecurityManager
其他参考 https://github.com/apache/airflow/discussions/35939
import os
from flask_appbuilder.security.manager import AUTH_OAUTH
from oauth_authorizer import KeycloakRoleAuthorizer
basedir = os.path.abspath(os.path.dirname(__file__))
WTF_CSRF_ENABLED = True
WTF_CSRF_TIME_LIMIT = None
AUTH_TYPE = AUTH_OAUTH
AUTH_ROLES_SYNC_AT_LOGIN = True # Checks roles on every login
AUTH_USER_REGISTRATION = True # allow users who are not already in the FAB DB to register
SECURITY_MANAGER_CLASS = KeycloakRoleAuthorizer
AUTH_ROLES_MAPPING = {
"Admin": ["Admin"],
"User": ["User"],
"Viewer": ["Viewer"],
}
oauth_client_secret = os.getenv("OAUTH_CLIENT_SECRET")
# this allows mounting from k8s secret in helm config
oauth_client_secret_fn = os.getenv("OAUTH_CLIENT_SECRET_FILE", '/var/tmp/ocs')
if os.path.isfile(oauth_client_secret_fn):
with open(oauth_client_secret_fn, 'r') as ocs: oauth_client_secret = ocs.read()
OAUTH_PROVIDERS = [
{
"name": "kc2fa",
"icon": "fa-key",
"token_key": "access_token",
"remote_app": {
"client_id": os.getenv("OAUTH_CLIENT_ID"),
"client_secret": oauth_client_secret,
"api_base_url": os.getenv("OAUTH_CLIENT_API_BASE_URL"),
"access_token_url": os.getenv("OAUTH_ACCESS_TOKEN_URL"),
"authorize_url": os.getenv("OAUTH_AUTHORIZE_URL"),
"jwks_uri": os.getenv("OAUTH_JWKS_URL"),
"request_token_url": None,
"client_kwargs": {
"scope": "email profile"
},
},
},
]
# ----------------------------------------------------
# Theme CONFIG
# ----------------------------------------------------
# Flask App Builder comes up with a number of predefined themes
# that you can use for Apache Airflow.
# http://flask-appbuilder.readthedocs.io/en/latest/customizing.html#changing-themes
# Please make sure to remove "navbar_color" configuration from airflow.cfg
# in order to fully utilize the theme. (or use that property in conjunction with theme)
# APP_THEME = "bootstrap-theme.css" # default bootstrap
# APP_THEME = "amelia.css"
# APP_THEME = "cerulean.css"
# APP_THEME = "cosmo.css"
# APP_THEME = "cyborg.css"
APP_THEME = "darkly.css"
# APP_THEME = "flatly.css"
# APP_THEME = "journal.css"
---
from flask import redirect
from flask_appbuilder import expose
from flask_appbuilder.security.views import AuthOAuthView
from flask_login import logout_user
from airflow.auth.managers.fab.security_manager.override import FabAirflowSecurityManagerOverride
from typing import Any, List, Union
import logging
import os
import jwt
log = logging.getLogger(__name__)
log.setLevel(os.getenv('AIRFLOW__LOGGING__FAB_LOGGING_LEVEL', 'INFO'))
FAB_ADMIN_ROLE = 'Admin'
FAB_USER_ROLE = 'User'
FAB_VIEWER_ROLE = 'Viewer'
FAB_PUBLIC_ROLE = 'Public' # The 'Public' role is given no permissions
KC_ADMIN_ROLE = 'AirflowAdmin'
KC_USER_ROLE = 'AirflowUser'
KC_VIEWER_ROLE = 'AirflowViewer'
def map_roles(team_list: list) -> list:
team_role_map = {
KC_ADMIN_ROLE: FAB_ADMIN_ROLE,
KC_USER_ROLE: FAB_USER_ROLE,
KC_VIEWER_ROLE: FAB_VIEWER_ROLE,
}
return list(set(team_role_map.get(team, FAB_PUBLIC_ROLE) for team in team_list))
class KeycloakAuthRemoteUserView(AuthOAuthView):
@expose("/logout/")
def logout(self):
logout_user()
return redirect(os.getenv("OAUTH_LOGOUT_REDIRECT_URL"))
# OAUTH_LOGOUT_REDIRECT_URL = "https://kc.example.com/realms/myrealm/protocol/openid-connect/logout?client_id=airflow&post_logout_redirect_uri=https%3A%2F%2Fairflow.example.com%2F"
class KeycloakRoleAuthorizer(FabAirflowSecurityManagerOverride):
authoauthview = KeycloakAuthRemoteUserView
def get_oauth_user_info(self, provider: str, resp: Any) -> dict:
access_token = resp.get('access_token')
#log.info(f"Token: {access_token}")
token_data = jwt.decode(access_token, options={"verify_signature": False})
#log.info(f"Token data: {token_data}")
roles = map_roles(token_data['realm_access']['roles'])
return {'username': token_data.get('preferred_username'),
'first_name': token_data.get('given_name'),
'last_name': token_data.get('family_name'),
'email': token_data.get('email'),
'role_keys': roles}
用于调试,提高日志等级