peering-manager / docker

🐳 Docker Image of Peering Manager
Apache License 2.0
9 stars 13 forks source link

LDAP Configuration doesn't pick up AUTH_LDAP_USER_SEARCH correctly. #25

Open the-maldridge opened 1 year ago

the-maldridge commented 1 year ago

I am trying to debug LDAP configuration for allowing users to sign in with single sign on. My LDAP server is OpenLDAP with base schemas available for posixAccount and InetOrgPerson, which should be sufficient. SSO from netbox to ldap works, so I know it is possible to get django-auth-ldap to be happy with this setup at the least.

I added the following lines to my environment file to configure ldap, and changed the tag on the pulled image to be the ldap one:

AUTH_LDAP_ATTR_FIRSTNAME: "cn"
AUTH_LDAP_GROUP_SEARCH_BASEDN: "dc=example,dc=com"
AUTH_LDAP_GROUP_SEARCH_CLASS: "groupOfNames"
AUTH_LDAP_GROUP_TYPE: "GroupOfNamesType"
AUTH_LDAP_IS_ADMIN_DN: "cn=netbox_admin,ou=group,dc=example,dc=com"
AUTH_LDAP_IS_SUPERUSER_DN: "cn=netbox_admin,ou=group,dc=example,dc=com"
AUTH_LDAP_MIRROR_GROUPS: "true"
AUTH_LDAP_REQUIRE_GROUP_DN: "cn=netbox_ro,ou=group,dc=example,dc=com"
AUTH_LDAP_SERVER_URI: "ldaps://ldap.example.com"
AUTH_LDAP_START_TLS: "false"
AUTH_LDAP_USER_SEARCH_BASEDN: "ou=people,dc=example,dc=com"
AUTH_LDAP_USER_SEARCH_ATTR: "uid"
AUTH_LDAP_USER_DN_TEMPLATE: "uid=%(user)s,ou=people,dc=example,dc=com"
AUTH_LDAP_BIND_AS_AUTHENTICATING_USER: "true"

I reliably get the error message that AUTH_LDAP_USER_SEARCH is not an instance of LDAPSearch:

image

However when I check the type of that config attribute via a manage.py shell, the type is clearly LDAPSearch:

>>> type(_loaded_configurations[1].AUTH_LDAP_USER_SEARCH)
<class 'django_auth_ldap.config.LDAPSearch'>

I'm at my whit's end here for what could still be preventing this from working, is there something I've just missed?

gmazoyer commented 1 year ago

Did you try to enable django ldap logging to see if it gives something, with a config like (see also logging.py)?

LOGGING = {
    "version": 1,
    "formatters": {
        "simple": {
            "format": "%(asctime)s | %(levelname)s | %(message)s",
            "datefmt": "%Y-%m-%d %H:%M:%S",
        }
    },
    "handlers": {"console": {"class": "logging.StreamHandler", "formatter": "simple"}},
    "loggers": {
        "django_auth_ldap": {"handlers": ["console"], "level": "DEBUG"},
    },
}
the-maldridge commented 1 year ago

Hmm, I have not yet attempted to do this inside the container as I couldn't come up with a good way to configure logging inside. Is there some trick to setting up logging or should I just exec in and edit the file?

gmazoyer commented 1 year ago

The file should be mounted in the container if you use the docker compose base setup. Just editing it and restarting the containers should be enough.

the-maldridge commented 1 year ago

Ah, I am deploying the containers via another mechanism. I'll just exec in and make an edit, will likely be tomorrow though before I can mess with this again though.

the-maldridge commented 1 year ago

Doesn't look like that has made any change. I ported all my ldap config down to the docker-compose environment so I could debug more easily. This is what the log shows for my login attempt:

peering-manager-docker-peering-manager-1  | 2023/02/08 20:15:33 [info] 40#40 "peeringmanager" application started
peering-manager-docker-peering-manager-1  | 172.22.0.1 - - [08/Feb/2023:20:15:33 +0000] "GET /login/?next=/ HTTP/1.1" 200 3280 "http://localhost:8080/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"
peering-manager-docker-peering-manager-1  | 2023-02-08 20:15:40 | WARNING | AUTH_LDAP_USER_SEARCH must be an LDAPSearch instance. while authenticating maldridge
peering-manager-docker-peering-manager-1  | AUTH_LDAP_USER_SEARCH must be an LDAPSearch instance. while authenticating maldridge
peering-manager-docker-peering-manager-1  | 172.22.0.1 - - [08/Feb/2023:20:15:40 +0000] "POST /login/ HTTP/1.1" 500 1261 "http://localhost:8080/login/?next=/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"

Anything else I need to do to make logging work in the compose environment?

gmazoyer commented 1 year ago

Do you see some lines like Loaded config <path to file> in your docker logs? Did you mount all the configuration directory like it's done in the docker compose example ./configuration:/etc/peering-manager/config?

the-maldridge commented 1 year ago

Yes, I did see the relevant lines in the output. I am now running the example directly to make my debug environment closer to the provided example that is known working.

On Wed, Feb 8, 2023, 4:52 PM Guillaume Mazoyer @.***> wrote:

Do you see some lines like Loaded config in your docker logs? Did you mount all the configuration directory like it's done in the docker compose example ./configuration:/etc/peering-manager/config?

— Reply to this email directly, view it on GitHub https://github.com/peering-manager/docker/issues/25#issuecomment-1423343035, or unsubscribe https://github.com/notifications/unsubscribe-auth/AARW3FTIPNTB57ZZ45QVFW3WWQPTFANCNFSM6AAAAAAUCA23OM . You are receiving this because you authored the thread.Message ID: @.***>

gmazoyer commented 1 year ago

Too bad it does not log anything then :/

Did you try to inspect via manage.py shell the actual variables that are in the settings module like the following?

>>> from django.conf import settings
>>> settings.VERSION
'v1.7.4'
>>> settings.LDAP_CONFIGURED
True
>>> settings.AUTH_LDAP_USER_SEARCH
...
the-maldridge commented 1 year ago

Well, that looks like a smoking gun to me:

$ docker compose exec -it peering-manager /bin/sh
/opt/peering-manager $ . venv/bin/activate
(venv) /opt/peering-manager $ ./manage.py shell
⚙️  Loaded config '/etc/peering-manager/config/configuration.py'
⚙️  Loaded config '/etc/peering-manager/config/extra.py'
⚙️  Loaded config '/etc/peering-manager/config/logging.py'
⚙️  Loaded config '/etc/peering-manager/config/ldap/ldap_config.py'
⚙️  Loaded config '/etc/peering-manager/config/ldap/extra.py'
Python 3.9.16 (main, Dec 10 2022, 13:47:19) 
[GCC 10.3.1 20210424] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.conf import settings
>>> settings.VERSION
'v1.7.4'
>>> settings.LDAP_CONFIGURED
True
>>> settings.AUTH_LDAP_USER_SEARCH
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/opt/peering-manager/venv/lib/python3.9/site-packages/django/conf/__init__.py", line 88, in __getattr__
    val = getattr(self._wrapped, name)
AttributeError: 'Settings' object has no attribute 'AUTH_LDAP_USER_SEARCH'

What's curious to me is that in configuration/ldap/ldap_config.py at the root of this repo is the following block:

AUTH_LDAP_USER_SEARCH = LDAPSearch(
    AUTH_LDAP_USER_SEARCH_BASEDN,
    ldap.SCOPE_SUBTREE,
    "(" + AUTH_LDAP_USER_SEARCH_ATTR + "=%(user)s)",
)

This gets installed to /etc/peering-manager/config/ldap/ldap_config.py which then gets read by /opt-peering-manager/peering_manager/ldap_config.py. The value looks pretty clearly defined, so I'm not really seeing why this shouldn't work. In fact all ldap attributes seem to behave this way even though they're set in the environment. This is really behaving like the environment parsing or the loading via this other file is just broken.

gmazoyer commented 1 year ago

OK so I tried to reproduce your issue and managed to so by using the default setup.

However that's actually weird behaviour. Peering Manager settings load the peering_manager.ldap_config module to know if LDAP is config and imports all variables defined in it. In the container CLI, I can see that the module is there and properly loaded.

(venv) /opt/peering-manager $ python manage.py shell
Python 3.9.16 (main, Dec 10 2022, 13:47:19) 
[GCC 10.3.1 20210424] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from peering_manager import ldap_config as l
>>> l.AUTH_LDAP_USER_SEARCH
<LDAPSearch: ou=people,dc=example,dc=com>
>>> 

But for some reason, the main settings module does not pick these settings up.

>>> from django.conf import settings
>>> settings.AUTH_LDAP_USER_SEARCH
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/opt/peering-manager/venv/lib/python3.9/site-packages/django/conf/__init__.py", line 88, in __getattr__
    val = getattr(self._wrapped, name)
AttributeError: 'Settings' object has no attribute 'AUTH_LDAP_USER_SEARCH'
>>>

I either don't know how this work anymore or there is an issue somewhere, well there's obviously an issue somewhere.

the-maldridge commented 1 year ago

At any rate I'm glad this isn't just me having missed something simple. Let me know if there's anything I can do to further assist in troubleshooting.

madsi1m commented 1 day ago

Hi, any fixes or workarounds for this? I seem to be having the same issues.

I've also tried volume mounting directly over /opt/peering-manager/peering_manager/ldap_config.py to see what would happen and it seems when i do this ldap settings are totally ignored as i now get "Please enter a correct username and password. Note that both fields may be case-sensitive."

madsi1m commented 1 day ago

some debug by volume mounting directly over /opt/peering-manager/peering_manager/ldap_config.py to see what would happen

bash-5.1# . venv/bin/activate
(venv) bash-5.1# ./manage.py shell
Python 3.9.17 (main, Jun  9 2023, 02:31:24)
[GCC 10.3.1 20210424] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.conf import settings
>>> settings.VERSION
'1.8.3'
>>> settings.LDAP_CONFIGURED
True
>>> settings.AUTH_LDAP_USER_SEARCH
<LDAPSearch: ou=people,dc=example,dc=com>

Watching access logs on LDAP I do not see peering-manager hitting it.

Happy to try things if you have suggestions

madsi1m commented 1 day ago

Progress Update + Final Success

Indeed volume mounting/overwriting ldap_config.py to /opt/peering-manager/peering_manager/ldap_config.py makes it take the configs.

Also blink and you miss it, the commented out logging file has 'filters': ['require_debug_false'], on console that blocked debug logs showing in the container logs...

With the above two fixed I was able to see what i needed to adjust in my ldap_config.py to make login work.

gmazoyer commented 1 day ago

Also blink and you miss it, the commented out logging file has 'filters': ['require_debug_false'], on console that blocked debug logs showing in the container logs...

Nice catch. I completely overlooked this one.