prod_settings_template: Move LDAP up into authentication, and merge part 1/2.

This commit is contained in:
Nikhil Maske 2021-02-05 12:45:56 +05:30 committed by Tim Abbott
parent 6ad79b4f85
commit 89b25556dc
2 changed files with 252 additions and 255 deletions

View File

@ -35,161 +35,6 @@ Each of these requires one to a handful of lines of configuration in
`settings.py`, as well as a secret in `zulip-secrets.conf`. Details `settings.py`, as well as a secret in `zulip-secrets.conf`. Details
are documented in your `settings.py`. are documented in your `settings.py`.
## SAML
Zulip 2.1 and later supports SAML authentication, used by Okta,
OneLogin, and many other IdPs (identity providers). You can configure
it as follows:
1. These instructions assume you have an installed Zulip server; if
you're using Zulip Cloud, see [this article][saml-help-center],
which also has IdP-side configuration advice for common IdPs.
You can have created a Zulip organization already using the default
EmailAuthBackend, or plan to create the organization using SAML
authentication.
1. Tell your IdP how to find your Zulip server:
* **SP Entity ID**: `https://yourzulipdomain.example.com`.
The `Entity ID` should match the value of
`SOCIAL_AUTH_SAML_SP_ENTITY_ID` computed in the Zulip settings.
You can get the correct value by running the following:
`/home/zulip/deployments/current/scripts/get-django-setting
SOCIAL_AUTH_SAML_SP_ENTITY_ID`.
* **SSO URL**:
`https://yourzulipdomain.example.com/complete/saml/`. This is
the "SAML ACS url" in SAML terminology.
If you're
[hosting multiple organizations](../production/multiple-organizations.html#authentication),
you need to use `SOCIAL_AUTH_SUBDOMAIN`. For example,
if `SOCIAL_AUTH_SUBDOMAIN="auth"` and `EXTERNAL_HOST=zulip.example.com`,
this should be `https://auth.zulip.example.com/complete/saml/`.
2. Tell Zulip how to connect to your SAML provider(s) by filling
out the section of `/etc/zulip/settings.py` on your Zulip server
with the heading "SAML Authentication".
* You will need to update `SOCIAL_AUTH_SAML_ORG_INFO` with your
organization name (`displayname` may appear in the IdP's
authentication flow; `name` won't be displayed to humans).
* Fill out `SOCIAL_AUTH_SAML_ENABLED_IDPS` with data provided by
your identity provider. You may find [the python-social-auth
SAML
docs](https://python-social-auth.readthedocs.io/en/latest/backends/saml.html)
helpful. You'll need to obtain several values from your IdP's
metadata and enter them on the right-hand side of this
Python dictionary:
1. Set the outer `idp_name` key to be an identifier for your IdP,
e.g. `testshib` or `okta`. This field appears in URLs for
parts of your Zulip server's SAML authentication flow.
2. The IdP should provide the `url` and `entity_id` values.
3. Save the `x509cert` value to a file; you'll use it in the
instructions below.
4. The values needed in the `attr_` fields are often configurable
in your IdP's interface when setting up SAML authentication
(referred to as "Attribute Statements" with Okta, or
"Attribute Mapping" with GSuite). You'll want to connect
these so that Zulip gets the email address (used as a unique
user ID) and name for the user.
5. The `display_name` and `display_icon` fields are used to
display the login/registration buttons for the IdP.
3. Install the certificate(s) required for SAML authentication. You
will definitely need the public certificate of your IdP. Some IdP
providers also support the Zulip server (Service Provider) having
a certificate used for encryption and signing. We detail these
steps as optional below, because they aren't required for basic
setup, and some IdPs like Okta don't fully support Service
Provider certificates. You should install them as follows:
1. On your Zulip server, `mkdir -p /etc/zulip/saml/idps/`
2. Put the IDP public certificate in `/etc/zulip/saml/idps/{idp_name}.crt`
3. (Optional) Put the Zulip server public certificate in `/etc/zulip/saml/zulip-cert.crt`
4. (Optional) Put the Zulip server private key in `/etc/zulip/saml/zulip-private-key.key`
5. Set the proper permissions on these files and directories:
```
chown -R zulip.zulip /etc/zulip/saml/
find /etc/zulip/saml/ -type f -exec chmod 644 -- {} +
chmod 640 /etc/zulip/saml/zulip-private-key.key
```
4. (Optional) If you configured the optional public and private server
certificates above, you can enable the additional setting
`"authnRequestsSigned": True` in `SOCIAL_AUTH_SAML_SECURITY_CONFIG`
to have the SAMLRequests the server will be issuing to the IdP
signed using those certificates. Additionally, if the IdP supports
it, you can upload the public certificate to enable encryption of
assertions in the SAMLResponses the IdP will send about
authenticated users.
5. Enable the `zproject.backends.SAMLAuthBackend` auth backend, in
`AUTHENTICATION_BACKENDS` in `/etc/zulip/settings.py`.
6. [Restart the Zulip server](../production/settings.md) to ensure
your settings changes take effect. The Zulip login page should now
have a button for SAML authentication that you can use to log in or
create an account (including when creating a new organization).
7. If the configuration was successful, the server's metadata can be
found at `https://yourzulipdomain.example.com/saml/metadata.xml`. You
can use this for verifying your configuration or provide it to your
IdP.
[saml-help-center]: https://zulip.com/help/saml-authentication
### IdP-initiated SSO
The above configuration is sufficient for Service Provider initialized
SSO, i.e. you can visit the Zulip webapp and click "Sign in with
{IdP}" and it'll correctly start the authentication flow. If you are
not hosting multiple organizations, with Zulip 3.0+, the above
configuration is also sufficient for Identity Provider initiated SSO,
i.e. clicking a "Sign in to Zulip" button on the IdP's website can
correctly authenticate the user to Zulip.
If you're hosting multiple organizations and thus using the
`SOCIAL_AUTH_SUBDOMAIN` setting, you'll need to configure a custom
`RelayState` in your IdP of the form `{"subdomain":
"yourzuliporganization"}` to let Zulip know which organization to
authenticate the user to when they visit your SSO URL from the IdP.
(If the organization is on the root domain, use the empty string:
`{"subdomain": ""}`.).
```eval_rst
.. _ldap:
```
### Restricting access to specific organizations
If you're hosting multiple Zulip organizations, you can restrict which
organizations can use a given IdP by setting `limit_to_subdomains`.
For example, `limit_to_subdomains = ["", "engineering"]` would
restrict an IdP the root domain and the `engineering` subdomain.
You can achieve the same goal with a SAML attribute; just declare
which attribute using `attr_org_membership` in the IdP configuration.
For the root subdomain, `www` in the list will work, or any other of
`settings.ROOT_SUBDOMAIN_ALIASES`.
For example, with `attr_org_membership` set to `member`, a user with
the following attribute in their `AttributeStatement` will have access
to the root and `engineering` subdomains:
```
<saml2:Attribute Name="member" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
www
</saml2:AttributeValue>
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
engineering
</saml2:AttributeValue>
</saml2:Attribute>
```
## LDAP (including Active Directory) ## LDAP (including Active Directory)
Zulip supports retrieving information about users via LDAP, and Zulip supports retrieving information about users via LDAP, and
@ -459,6 +304,162 @@ the bottom of the problem:
this file (feel free to anonymize any email addresses to this file (feel free to anonymize any email addresses to
`username@example.com`) in your report. `username@example.com`) in your report.
## SAML
Zulip 2.1 and later supports SAML authentication, used by Okta,
OneLogin, and many other IdPs (identity providers). You can configure
it as follows:
1. These instructions assume you have an installed Zulip server; if
you're using Zulip Cloud, see [this article][saml-help-center],
which also has IdP-side configuration advice for common IdPs.
You can have created a Zulip organization already using the default
EmailAuthBackend, or plan to create the organization using SAML
authentication.
1. Tell your IdP how to find your Zulip server:
* **SP Entity ID**: `https://yourzulipdomain.example.com`.
The `Entity ID` should match the value of
`SOCIAL_AUTH_SAML_SP_ENTITY_ID` computed in the Zulip settings.
You can get the correct value by running the following:
`/home/zulip/deployments/current/scripts/get-django-setting
SOCIAL_AUTH_SAML_SP_ENTITY_ID`.
* **SSO URL**:
`https://yourzulipdomain.example.com/complete/saml/`. This is
the "SAML ACS url" in SAML terminology.
If you're
[hosting multiple organizations](../production/multiple-organizations.html#authentication),
you need to use `SOCIAL_AUTH_SUBDOMAIN`. For example,
if `SOCIAL_AUTH_SUBDOMAIN="auth"` and `EXTERNAL_HOST=zulip.example.com`,
this should be `https://auth.zulip.example.com/complete/saml/`.
2. Tell Zulip how to connect to your SAML provider(s) by filling
out the section of `/etc/zulip/settings.py` on your Zulip server
with the heading "SAML Authentication".
* You will need to update `SOCIAL_AUTH_SAML_ORG_INFO` with your
organization name (`displayname` may appear in the IdP's
authentication flow; `name` won't be displayed to humans).
* Fill out `SOCIAL_AUTH_SAML_ENABLED_IDPS` with data provided by
your identity provider. You may find [the python-social-auth
SAML
docs](https://python-social-auth.readthedocs.io/en/latest/backends/saml.html)
helpful. You'll need to obtain several values from your IdP's
metadata and enter them on the right-hand side of this
Python dictionary:
1. Set the outer `idp_name` key to be an identifier for your IdP,
e.g. `testshib` or `okta`. This field appears in URLs for
parts of your Zulip server's SAML authentication flow.
2. The IdP should provide the `url` and `entity_id` values.
3. Save the `x509cert` value to a file; you'll use it in the
instructions below.
4. The values needed in the `attr_` fields are often configurable
in your IdP's interface when setting up SAML authentication
(referred to as "Attribute Statements" with Okta, or
"Attribute Mapping" with GSuite). You'll want to connect
these so that Zulip gets the email address (used as a unique
user ID) and name for the user.
5. The `display_name` and `display_icon` fields are used to
display the login/registration buttons for the IdP.
3. Install the certificate(s) required for SAML authentication. You
will definitely need the public certificate of your IdP. Some IdP
providers also support the Zulip server (Service Provider) having
a certificate used for encryption and signing. We detail these
steps as optional below, because they aren't required for basic
setup, and some IdPs like Okta don't fully support Service
Provider certificates. You should install them as follows:
1. On your Zulip server, `mkdir -p /etc/zulip/saml/idps/`
2. Put the IDP public certificate in `/etc/zulip/saml/idps/{idp_name}.crt`
3. (Optional) Put the Zulip server public certificate in `/etc/zulip/saml/zulip-cert.crt`
4. (Optional) Put the Zulip server private key in `/etc/zulip/saml/zulip-private-key.key`
5. Set the proper permissions on these files and directories:
```
chown -R zulip.zulip /etc/zulip/saml/
find /etc/zulip/saml/ -type f -exec chmod 644 -- {} +
chmod 640 /etc/zulip/saml/zulip-private-key.key
```
4. (Optional) If you configured the optional public and private server
certificates above, you can enable the additional setting
`"authnRequestsSigned": True` in `SOCIAL_AUTH_SAML_SECURITY_CONFIG`
to have the SAMLRequests the server will be issuing to the IdP
signed using those certificates. Additionally, if the IdP supports
it, you can upload the public certificate to enable encryption of
assertions in the SAMLResponses the IdP will send about
authenticated users.
5. Enable the `zproject.backends.SAMLAuthBackend` auth backend, in
`AUTHENTICATION_BACKENDS` in `/etc/zulip/settings.py`.
6. [Restart the Zulip server](../production/settings.md) to ensure
your settings changes take effect. The Zulip login page should now
have a button for SAML authentication that you can use to log in or
create an account (including when creating a new organization).
7. If the configuration was successful, the server's metadata can be
found at `https://yourzulipdomain.example.com/saml/metadata.xml`. You
can use this for verifying your configuration or provide it to your
IdP.
[saml-help-center]: https://zulip.com/help/saml-authentication
### IdP-initiated SSO
The above configuration is sufficient for Service Provider initialized
SSO, i.e. you can visit the Zulip webapp and click "Sign in with
{IdP}" and it'll correctly start the authentication flow. If you are
not hosting multiple organizations, with Zulip 3.0+, the above
configuration is also sufficient for Identity Provider initiated SSO,
i.e. clicking a "Sign in to Zulip" button on the IdP's website can
correctly authenticate the user to Zulip.
If you're hosting multiple organizations and thus using the
`SOCIAL_AUTH_SUBDOMAIN` setting, you'll need to configure a custom
`RelayState` in your IdP of the form `{"subdomain":
"yourzuliporganization"}` to let Zulip know which organization to
authenticate the user to when they visit your SSO URL from the IdP.
(If the organization is on the root domain, use the empty string:
`{"subdomain": ""}`.).
```eval_rst
.. _ldap:
```
### Restricting access to specific organizations
If you're hosting multiple Zulip organizations, you can restrict which
organizations can use a given IdP by setting `limit_to_subdomains`.
For example, `limit_to_subdomains = ["", "engineering"]` would
restrict an IdP the root domain and the `engineering` subdomain.
You can achieve the same goal with a SAML attribute; just declare
which attribute using `attr_org_membership` in the IdP configuration.
For the root subdomain, `www` in the list will work, or any other of
`settings.ROOT_SUBDOMAIN_ALIASES`.
For example, with `attr_org_membership` set to `member`, a user with
the following attribute in their `AttributeStatement` will have access
to the root and `engineering` subdomains:
```
<saml2:Attribute Name="member" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
www
</saml2:AttributeValue>
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
engineering
</saml2:AttributeValue>
</saml2:Attribute>
```
## Apache-based SSO with `REMOTE_USER` ## Apache-based SSO with `REMOTE_USER`
If you have any existing SSO solution where a preferred way to deploy If you have any existing SSO solution where a preferred way to deploy

View File

@ -127,6 +127,102 @@ AUTHENTICATION_BACKENDS: Tuple[str, ...] = (
# 'zproject.backends.ZulipRemoteUserBackend', # Local SSO, setup docs on readthedocs # 'zproject.backends.ZulipRemoteUserBackend', # Local SSO, setup docs on readthedocs
) )
# LDAP integration.
#
# Zulip supports retrieving information about users via LDAP, and
# optionally using LDAP as an authentication mechanism.
import ldap
from django_auth_ldap.config import LDAPSearch
# Connecting to the LDAP server.
#
# For detailed instructions, see the Zulip documentation:
# https://zulip.readthedocs.io/en/latest/production/authentication-methods.html#ldap
# The LDAP server to connect to. Setting this enables Zulip
# automatically fetching each new user's name from LDAP.
# Example: "ldaps://ldap.example.com"
AUTH_LDAP_SERVER_URI = ""
# The DN of the user to bind as (i.e., authenticate as) in order to
# query LDAP. If unset, Zulip does an anonymous bind.
AUTH_LDAP_BIND_DN = ""
# Passwords and secrets are not stored in this file. The password
# corresponding to AUTH_LDAP_BIND_DN goes in `/etc/zulip/zulip-secrets.conf`.
# In that file, set `auth_ldap_bind_password`. For example:
# auth_ldap_bind_password = abcd1234
# Mapping user info from LDAP to Zulip.
#
# For detailed instructions, see the Zulip documentation:
# https://zulip.readthedocs.io/en/latest/production/authentication-methods.html#ldap
# The LDAP search query to find a given user.
#
# The arguments to `LDAPSearch` are (base DN, scope, filter). In the
# filter, the string `%(user)s` is a Python placeholder. The Zulip
# server will replace this with the user's Zulip username, i.e. the
# name they type into the Zulip login form.
#
# For more details and alternatives, see the documentation linked above.
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
# Configuration to lookup a user's LDAP data given their email address
# (For Zulip reverse mapping). If users log in as e.g. "sam" when
# their email address is "sam@example.com", set LDAP_APPEND_DOMAIN to
# "example.com". Otherwise, leave LDAP_APPEND_DOMAIN=None and set
# AUTH_LDAP_REVERSE_EMAIL_SEARCH and AUTH_LDAP_USERNAME_ATTR below.
#LDAP_APPEND_DOMAIN = None
# LDAP attribute to find a user's email address.
#
# Leave as None if users log in with their email addresses,
# or if using LDAP_APPEND_DOMAIN.
#LDAP_EMAIL_ATTR = None
# AUTH_LDAP_REVERSE_EMAIL_SEARCH works like AUTH_LDAP_USER_SEARCH and
# should query an LDAP user given their email address. It and
# AUTH_LDAP_USERNAME_ATTR are required when LDAP_APPEND_DOMAIN is None.
#AUTH_LDAP_REVERSE_EMAIL_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
# ldap.SCOPE_SUBTREE, "(email=%(email)s)")
# AUTH_LDAP_USERNAME_ATTR should be the Zulip username attribute
# (defined in AUTH_LDAP_USER_SEARCH).
#AUTH_LDAP_USERNAME_ATTR = "uid"
# This map defines how to populate attributes of a Zulip user from LDAP.
#
# The format is `zulip_name: ldap_name`; each entry maps a Zulip
# concept (on the left) to the LDAP attribute name (on the right) your
# LDAP database uses for the same concept.
AUTH_LDAP_USER_ATTR_MAP = {
# full_name is required; common values include "cn" or "displayName".
# If names are encoded in your LDAP directory as first and last
# name, you can instead specify first_name and last_name, and
# Zulip will combine those to construct a full_name automatically.
"full_name": "cn",
# "first_name": "fn",
# "last_name": "ln",
# Profile pictures can be pulled from the LDAP "thumbnailPhoto"/"jpegPhoto" field.
# "avatar": "thumbnailPhoto",
# This line is for having Zulip to automatically deactivate users
# who are disabled in LDAP/Active Directory (and reactivate users who are not).
# See docs for usage details and precise semantics.
# "userAccountControl": "userAccountControl",
}
# Whether to automatically deactivate users not found in LDAP. If LDAP
# is the only authentication method, then this setting defaults to
# True. If other authentication methods are enabled, it defaults to
# False.
#LDAP_DEACTIVATE_NON_MATCHING_USERS = True
######## ########
# Google OAuth. # Google OAuth.
# #
@ -543,106 +639,6 @@ EMAIL_GATEWAY_IMAP_PORT = 993
EMAIL_GATEWAY_IMAP_FOLDER = "INBOX" EMAIL_GATEWAY_IMAP_FOLDER = "INBOX"
################
# LDAP integration.
#
# Zulip supports retrieving information about users via LDAP, and
# optionally using LDAP as an authentication mechanism.
import ldap
from django_auth_ldap.config import LDAPSearch
########
# LDAP integration, part 1: Connecting to the LDAP server.
#
# For detailed instructions, see the Zulip documentation:
# https://zulip.readthedocs.io/en/latest/production/authentication-methods.html#ldap
# The LDAP server to connect to. Setting this enables Zulip
# automatically fetching each new user's name from LDAP.
# Example: "ldaps://ldap.example.com"
AUTH_LDAP_SERVER_URI = ""
# The DN of the user to bind as (i.e., authenticate as) in order to
# query LDAP. If unset, Zulip does an anonymous bind.
AUTH_LDAP_BIND_DN = ""
# Passwords and secrets are not stored in this file. The password
# corresponding to AUTH_LDAP_BIND_DN goes in `/etc/zulip/zulip-secrets.conf`.
# In that file, set `auth_ldap_bind_password`. For example:
# auth_ldap_bind_password = abcd1234
########
# LDAP integration, part 2: Mapping user info from LDAP to Zulip.
#
# For detailed instructions, see the Zulip documentation:
# https://zulip.readthedocs.io/en/latest/production/authentication-methods.html#ldap
# The LDAP search query to find a given user.
#
# The arguments to `LDAPSearch` are (base DN, scope, filter). In the
# filter, the string `%(user)s` is a Python placeholder. The Zulip
# server will replace this with the user's Zulip username, i.e. the
# name they type into the Zulip login form.
#
# For more details and alternatives, see the documentation linked above.
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
# Configuration to lookup a user's LDAP data given their email address
# (For Zulip reverse mapping). If users log in as e.g. "sam" when
# their email address is "sam@example.com", set LDAP_APPEND_DOMAIN to
# "example.com". Otherwise, leave LDAP_APPEND_DOMAIN=None and set
# AUTH_LDAP_REVERSE_EMAIL_SEARCH and AUTH_LDAP_USERNAME_ATTR below.
#LDAP_APPEND_DOMAIN = None
# LDAP attribute to find a user's email address.
#
# Leave as None if users log in with their email addresses,
# or if using LDAP_APPEND_DOMAIN.
#LDAP_EMAIL_ATTR = None
# AUTH_LDAP_REVERSE_EMAIL_SEARCH works like AUTH_LDAP_USER_SEARCH and
# should query an LDAP user given their email address. It and
# AUTH_LDAP_USERNAME_ATTR are required when LDAP_APPEND_DOMAIN is None.
#AUTH_LDAP_REVERSE_EMAIL_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
# ldap.SCOPE_SUBTREE, "(email=%(email)s)")
# AUTH_LDAP_USERNAME_ATTR should be the Zulip username attribute
# (defined in AUTH_LDAP_USER_SEARCH).
#AUTH_LDAP_USERNAME_ATTR = "uid"
# This map defines how to populate attributes of a Zulip user from LDAP.
#
# The format is `zulip_name: ldap_name`; each entry maps a Zulip
# concept (on the left) to the LDAP attribute name (on the right) your
# LDAP database uses for the same concept.
AUTH_LDAP_USER_ATTR_MAP = {
# full_name is required; common values include "cn" or "displayName".
# If names are encoded in your LDAP directory as first and last
# name, you can instead specify first_name and last_name, and
# Zulip will combine those to construct a full_name automatically.
"full_name": "cn",
# "first_name": "fn",
# "last_name": "ln",
# Profile pictures can be pulled from the LDAP "thumbnailPhoto"/"jpegPhoto" field.
# "avatar": "thumbnailPhoto",
# This line is for having Zulip to automatically deactivate users
# who are disabled in LDAP/Active Directory (and reactivate users who are not).
# See docs for usage details and precise semantics.
# "userAccountControl": "userAccountControl",
}
# Whether to automatically deactivate users not found in LDAP. If LDAP
# is the only authentication method, then this setting defaults to
# True. If other authentication methods are enabled, it defaults to
# False.
#LDAP_DEACTIVATE_NON_MATCHING_USERS = True
################ ################
# Video call integrations. # Video call integrations.
# #