Reputation: 73
I'm trying to integrate Airflow Webserver authentication with the Flask-AppBuilder RBAC available in Airflow 1.10.0, but no matter the configuration settings I try, I get an AttributeError: 'AnonymousUserMixin' object has no attribute 'roles'
.
Previously I had gotten the Airflow LDAP auth backend to work with my org's LDAP Microsoft AD server, but I can't get the configuration settings right to enable it to work with FAB RBAC. The Airflow and FAB documentations have very little to say about LDAP or troubleshooting it.
In the ${AIRFLOW_HOME}/webserver_config.py
file I have
# The authentication type
AUTH_TYPE = AUTH_LDAP
AUTH_ROLE_PUBLIC = "Public"
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = "Public"
AUTH_LDAP_SERVER = "ldaps://ldaps.myorg.org:636"
AUTH_LDAP_BIND_USER = "CN=myuser,OU=Service Accounts,DC=myorg,DC=org"
AUTH_LDAP_BIND_PASSWORD = "relevant_password"
AUTH_LDAP_SEARCH = "DC=myorg,DC=org"
AUTH_LDAP_UID_FIELD = "sAMAccountName"
# AUTH_LDAP_ALLOW_SELF_SIGNED = True
AUTH_LDAP_USE_TLS = False
AUTH_LDAP_APPEND_DOMAIN = "myorg.org"
AUTH_ROLE_ADMIN = "Admin"
And in {AIRFLOW_HOME}/airflow.cfg
I have
[webserver]
authenticate = True
rbac = True
I have already upgraded the backend Postgres DB so that it has the ab_
tables.
When I deploy everything via Docker Swarm and go to the appropriate Webserver URL, I just get the error listed above with the stack trace. It doesn't ever give me the option to log in (and I tried clearing the cookie), so I don't understand how to get it to let an 'anonymous' user even try to authenticate against the LDAP AD.
Is the issue
webserver_config.py
LDAP settings?airflow.cfg
settings?Please let me know if I've left any information out. Thank you!
Upvotes: 1
Views: 3301
Reputation: 73
Well, luckily I figured it out and am able to answer my own question, the first one that has been answered on SO so far.
Part of the reason is that FAB uses the python-ldap
package for error handling, but there are also the ldap3
and ldap
packages, which if you installed in a particular order could interfere with the use of python-ldap
.
I also found that python-ldap
has certain build requirements.
Additionally, I discovered that FAB has another LDAP parameter, AUTH_LDAP_SEARCH_FILTER
, that lets you filter possible users to a certain group, which is exactly what I wanted, but it's not mentioned in the documentation; I found it in the package repo on GitHub.
Here's my webserver_config.py
file that works for me:
# -*- coding: utf-8 -*-
import os
from airflow import configuration as conf
from flask_appbuilder.security.manager import AUTH_LDAP
basedir = os.path.abspath(os.path.dirname(__file__))
# The SQLAlchemy connection string.
SQLALCHEMY_DATABASE_URI = conf.get('core', 'SQL_ALCHEMY_CONN')
# Flask-WTF flag for CSRF
CSRF_ENABLED = True
# ------------------------------------------------------------------------------
# AUTHENTICATION CONFIG
# ------------------------------------------------------------------------------
# For details on how to set up each of the following authentications, see
# http://flask-appbuilder.readthedocs.io/en/latest/security.html# authentication-methods
# The authentication type
AUTH_TYPE = AUTH_LDAP
AUTH_ROLE_PUBLIC = "Public"
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = "Viewer"
AUTH_LDAP_SERVER = "ldaps://ldaps.myorg.org:636"
AUTH_LDAP_BIND_USER = "CN=myuser,OU=Service Accounts,DC=myorg,DC=org"
AUTH_LDAP_BIND_PASSWORD = "relevant_password"
AUTH_LDAP_SEARCH = "DC=myorg,DC=org" # same as BASEDN
AUTH_LDAP_SEARCH_FILTER = "(memberOf=CN=My Team,OU=My Group,DC=myorg,DC=org)"
AUTH_LDAP_UID_FIELD = AUTH_LDAP_UID_FIELD = "sAMAccountName"
AUTH_LDAP_ALLOW_SELF_SIGNED = True
AUTH_LDAP_USE_TLS = False
AUTH_LDAP_TLS_DEMAND = True
AUTH_ROLE_ADMIN = "Admin"
I set the AUTH_USER_REGISTRATION_ROLE
to Viewer because by default Public has no permissions associated with it, so if someone in AD logs in and their role were defaulted to Public, they wouldn't be able to do anything until an Admin changed their role.
I learned from IT at my org that the domain controller of the LDAP server handles the TLS, so I think that's why I can set AUTH_LDAP_ALLOW_SELF_SIGNED
to True
, whereas I don't have a TLS cert in the Docker containers to point Airflow to in order to set AUTH_LDAP_USE_TLS
to True.
Upvotes: 2
Reputation: 327
AnonymousUserMixin
is a mixin class defined in the Flask-Login
python package. You're likely experiencing an issue where a codepath is assuming a logged in user. You can change that codepath to be as follows:
if flask_login.current_user.is_authenticated():
*code*
You may want to consider adding a custom class as your anonymous user class in your Flask app configuration/setup code.
class MyCustomAnonymousUser(flask_login.AnonymousUserMixin):
def __init__(self):
self.roles = []
flask_app = Flask(...) # some flask app being initialized
flask_app.login_manager.anonymous_user = MyCustomAnonymousUser
However, it's also possible the code that's throwing this error is expecting Flask-Security
to be configured. A peek under the hood of what Flask-Security
does:
from flask.ext.login import AnonymousUserMixin, UserMixin as BaseUserMixin, \
LoginManager, current_user
...
class AnonymousUser(AnonymousUserMixin):
"""AnonymousUser definition"""
def __init__(self):
self.roles = ImmutableList()
def has_role(self, *args):
"""Returns `False`"""
return False
To configure Flask-Security
for your app, check out the quick start on their documentation: https://pythonhosted.org/Flask-Security/quickstart.html
Upvotes: 0