-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Ldap] LDAP authentication should return a meaningful error when the LDAP server is unavailable #44898
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
Conversation
|
||
throw $e; | ||
} | ||
$this->ldap->bind($this->searchDn, $this->searchPassword); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could opt to catch InvalidCredentialsException
here in order to throw something more-specific that would indicate it is the search bind credentials (used during enumeration) that are invalid, as opposed to the credentials that the user has provided.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same in CheckLdapCredentialsListener.php
line 77
Point being that throwing the InvalidCredentialsException
can be ambiguous in either meaning "user-provided credentials are invalid" or "configured search credentials are invalid"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
7ab0611 introduces an InvalidSearchCredentialsException
which is thrown when binding fails when using the configured search credentials.
@wouterj FWIW, I have the following docker-compose setup for a local LDAP: docker-compose.yml:services:
openldap:
image: osixia/openldap:1.5.0
restart: unless-stopped
environment:
LDAP_LOG_LEVEL: "256"
LDAP_ORGANISATION: ${LDAP_ORGANISATION:-Example Inc.}
LDAP_DOMAIN: ${LDAP_DOMAIN:-example.org}
LDAP_BASE_DN: ${LDAP_BASE_DN:-"dc=example,dc=org"}
LDAP_ADMIN_PASSWORD: ${LDAP_ADMIN_PASSWORD:-admin}
LDAP_CONFIG_PASSWORD: ${LDAP_CONFIG_PASSWORD:-config}
LDAP_RFC2307BIS_SCHEMA: "false"
LDAP_BACKEND: "mdb"
LDAP_TLS: "true"
LDAP_TLS_CRT_FILENAME: "ldap.crt"
LDAP_TLS_KEY_FILENAME: "ldap.key"
LDAP_TLS_DH_PARAM_FILENAME: "dhparam.pem"
LDAP_TLS_CA_CRT_FILENAME: "ca.crt"
LDAP_TLS_ENFORCE: "false"
LDAP_TLS_CIPHER_SUITE: "SECURE256:-VERS-SSL3.0"
LDAP_TLS_VERIFY_CLIENT: "demand"
LDAP_REPLICATION: "false"
KEEP_EXISTING_CONFIG: "false"
LDAP_REMOVE_CONFIG_AFTER_SETUP: "true"
LDAP_SSL_HELPER_PREFIX: "ldap"
tty: true
stdin_open: true
volumes:
- ldap_data:/var/lib/ldap
- ldap_config:/etc/ldap/slapd.d
- ldap_certs:/container/service/slapd/assets/certs/
ports:
- "389:389"
- "636:636"
phpldapadmin:
image: osixia/phpldapadmin:latest
environment:
PHPLDAPADMIN_LDAP_HOSTS: "openldap"
PHPLDAPADMIN_HTTPS: "false"
ports:
- "8080:80"
depends_on:
- openldap
volumes:
ldap_data:
ldap_config:
ldap_certs: And the following configuration in Symfony: services.yaml: Symfony\Component\Ldap\Ldap:
arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
tags: ['ldap']
Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
arguments:
- host: '%env(resolve:LDAP_HOST)%'
port: '%env(resolve:LDAP_PORT)%'
encryption: '%env(resolve:LDAP_ENCRYPTION)%'
options:
protocol_version: 3
referrals: false security.yaml: providers:
app_ldap:
ldap:
service: Symfony\Component\Ldap\Ldap
base_dn: '%env(resolve:LDAP_BASE_DN)%'
search_dn: '%env(resolve:LDAP_SEARCH_DN)%'
search_password: '%env(resolve:LDAP_SEARCH_PASSWORD)%'
default_roles: ROLE_USER
uid_key: uid
firewalls:
main:
provider: app_ldap .env:
You'd have to create the rest of the schema by hand, if needed I can try to provide an LDIF. |
Hey! I think @karlshea has recently worked with this code. Maybe they can help review this? Cheers! Carsonbot |
@wouterj if there's anything I can do to make reviewing easier, let me know |
CheckLdapCredentialsListener has 2 modes. Your PR description lets me think that you tested only one of them. |
ec7a0be
to
58716bb
Compare
@stof Thanks. It is very possible I might have missed something, as I'm not familiar with the two modes you mention. Could you expand on this or send some documentation my way? |
I'm not familiar with the ldap part either. But for those 2 modes, see the if/else inside the try/catch you changed |
@stof Thanks. If I understand correctly the difference is when
Basically allowing the LDAP component to search for a given user in multiple OUs instead of simply interpolating the username into the Expand default behavior
Expand query_string behavior
Having said all that, my changes should not impact either scenario apart from what I've already described. However, when I have some more time I will set up a configuration using |
58716bb
to
8452c7a
Compare
[`Jayfrown/ldap dev-ldap-server-unavailable`](https://github.com/Jayfrown/ldap/tree/ldap-server-unavailable) is `symfony/ldap 6.1.*` with symfony/symfony#44898 applied.
[`Jayfrown/ldap dev-ldap-server-unavailable`](https://github.com/Jayfrown/ldap/tree/ldap-server-unavailable) is `symfony/ldap 6.1.*` with symfony/symfony#44898 applied.
I made an example application to make it easier to test and verify behavior. The repository includes a docker-compose configuration that spins up an OpenLDAP container, seeds it with some users, and exposes the ports such that the local application can communicate with it. There are a couple of branches to test all permutations (with/without
Branches with this PR applied require Note that after switching to/from branches with this PR applied it is necessary to run @stof I've tested the same scenarios as mentioned in the OP with the |
65aaf0c
to
7ab0611
Compare
…LDAP server is unavailable
7ab0611
to
ad2cc0e
Compare
Thank you @Jayfrown. |
Return a 500 ISE if the LDAP server is unreachable.
I have tested the following scenarios:
LDAP server unreachable
Adapter/ExtLdap/Connection.php
throws aConnectionException
which is not caught, and this results in a 500 ISE shown to the user. In development mode, you see a relevant trace and the correct error hinting to an unreachable LDAP server, and in production mode this wouldn't be shown to the user, and would be logged.LDAP server reachable, with invalid search bind credentials
The
LdapUserProvider
tries to search the LDAP directory for the provided user. To search, it tries to authenticate using the (invalid) bind credentials, resulting inAdapter/ExtLdap/Connection.php
throwing anInvalidCredentialsException
which is not caught. As this is indicative of a misconfiguration within the application (ie. the app's search bind credentials are wrong, and searching for any user will always fail) I believe a 500 ISE is appropriate.We could opt to catch the
InvalidCredentialsException
inside theLdapUserProvider
and throw something more specific, to indicate it is the applications search bind credentials that are invalid, as opposed to the credentials the user provided during authentication.LDAP server reachable, with valid search bind credentials, authenticating as an unknown user
As the search bind credentials are valid, the
LdapUserProvider
searches the LDAP directory, finds no entries, and throws aUserNotFoundException
which results in the authenticator returning{"code":401,"message":"Invalid credentials."}
. I believe this is good / expected behavior.LDAP server reachable, with valid search bind credentials, using known user with invalid password
The
ldap/Adapter/ExtLdap/Connection.php
throws anInvalidCredentialsException
. Theldap/Security/CheckLdapCredentialsListener.php
now catches theInvalidCredentialsException
(instead of theConnectionException
) and throws aBadCredentialsException
such that the authenticator returns{"code":401,"message":"Invalid credentials."}
LDAP server reachable, with valid search bind credentials, using known user with valid password
The credentials are validated and the user is logged in, everything works as expected