From 611d4ae6949d3e07cb1bdf7e6bcb37d4f976d7d3 Mon Sep 17 00:00:00 2001 From: mlan Date: Thu, 19 Nov 2020 20:02:46 +0100 Subject: [PATCH] - [kopano](src/kopano) Load Kopano LDAP attributes using `LDAP_PROPMAP=`. --- CHANGELOG.md | 1 + README.md | 68 ++++++++++++++++++++++++----- demo/Makefile | 30 +++++++++---- demo/docker-compose.yml | 1 + src/kopano/entry.d/10-kopano-common | 46 ++++++++++++++++--- src/kopano/entry.d/50-kopano-config | 1 + test/Makefile | 1 + 7 files changed, 122 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb5451f..1d73331 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # 1.2.4 - [kopano](src/kopano) Now, use man pages and sample config files to find valid keys to match envvar. +- [kopano](src/kopano) Load Kopano LDAP attributes using `LDAP_PROPMAP=`. # 1.2.3 diff --git a/README.md b/README.md index be4174e..9d1f44b 100644 --- a/README.md +++ b/README.md @@ -260,27 +260,25 @@ When the `ATTACHMENT_STORAGE` option is `ATTACHMENT_STORAGE=files`, this option When creating the SQL container you can use environment variables to initiate it. For example, `MYSQL_ROOT_PASSWORD=topsecret`, `MYSQL_DATABASE=kopano`, `MYSQL_USER=kopano` and `MYSQL_PASSWORD=verysecret`. -## User authentication `USER_PLUGIN` +## User management `USER_PLUGIN` -Kopano supports three different ways to manage user authentication. Use the `USER_PLUGIN` environment variable to select the source of the user base. Possible values are: `db` (default), `ldap` and `unix`. +Kopano supports three different plugins for user management. Use the `USER_PLUGIN` environment variable to select the source of the user base. Possible values are: `db` (default), `ldap` and `unix`. - `db`: Retrieve the users from the Kopano database. Use the kopano-admin tool to create users and groups. There are no additional settings for this plug-in. +`db`: Retrieve the users from the Kopano database. Use the `kopano-admin` tool to create users and groups. There are no additional settings for this plug-in. -`ldap`: Retrieve the users and groups information from an LDAP server. All additional LDAP settings are needed see below +`ldap`: Retrieve the users and groups information from an LDAP directory server. Additional LDAP settings are needed, see below. `unix`: Retrieve the users and groups information from the Linux password files. This option is probably not interesting here. -### LDAP authentication +### Accessing an LDAP directory server -An LDAP server with user accounts configured to be used with Kopano is needed, but how to set one up is out of our scope here, instead see: [Kopano Knowledge Base/Install and optimize OpenLDAP for use with Kopano Groupware Core](https://kb.kopano.io/display/WIKI/Install+and+optimize+OpenLDAP+for+use+with+Kopano+Groupware+Core). +The `USER_PLUGIN=ldap` retrieves user information from an LDAP directory server. A brief description of how that is achieved is described in [Setup an LDAP directory server](#setup-an-ldap-directory-server). Once the LDAP directory server is up and running, the `mlan/kopano` container can be configured to use it using environment variables. -Once the LDAP server is up and running, the `mlan/kopano` container can be configured to use it using environment variables. In addition to the variables discussed below also set `USER_PLUGIN=ldap`. - -#### `LDAP_URI` +#### Host address `LDAP_URI` Specifies the URI of one or more LDAP server(s) to use, without any DN portion, such as `ldap://server:389/`, `ldaps://server:636/` or `ldapi:///`. Defaults: `LDAP_URI=ldap://localhost:389/`. -The historic directives `LDAP_HOST`, `LDAP_PORT`, `LDAP_PROTOCOL` are no longer supported (8.7.85). +Note that. the historic directives `LDAP_HOST`, `LDAP_PORT`, `LDAP_PROTOCOL` are no longer supported (8.7.85). #### `LDAP_SEARCH_BASE` @@ -298,7 +296,11 @@ This variable determines what defines a valid Kopano group. Default: `LDAP_GROUP Adds an extra filter to the user search. Default `LDAP_USER_SEARCH_FILTER=` -Hint: Use the `kopanoAccount` attribute in the filter to differentiate between non-kopano and kopano users. +Hint: Use the `kopanoAccount` attribute in the filter to differentiate between non-Kopano and Kopano users. + +### Kopano LDAP attributes `LDAP_PROPMAP` + +The Kopano services needs to know which of the users LDAP attributes, like addresses, phone numbers and company information, to use. This information is defined in the `propmap` file, which is included in the Kopano installation files here `/usr/share/kopano/ldap.propmap.cfg`. When using `USER_PLUGIN=ldap` this LDAP `propmap` file is used by the Kopano services by setting `LDAP_PROPMAP=` to an empty string. Optionally you can use another file, for example`LDAP_PROPMAP=/etc/kopano/ldap.propmap.cfg`. If no file can be found there the installed one will be copied there. ## Enabling IMAP, POP3 and ICAL @@ -445,6 +447,50 @@ Prior to Kopano WebApp version 5.0.0 the parameter was `define("INSECURE_COOKIES Here some topics relevant for arranging a mail server are presented. +## Setup an LDAP directory server + +A [Directory Server Agent (DSA)](https://en.wikipedia.org/wiki/Directory_System_Agent) is used to store, organize and present data in a key-value type format. Typically, directories are optimized for lookups, searches, and read operations over write operations, so they function extremely well for data that is referenced often but changes infrequently. + +The [Lightweight Directory Access Protocol (LDAP)](https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol), is an open protocol used to store and retrieve data from a hierarchical directory structure. Commonly used to store information about an organization and its assets and users. + +[OpenLDAP](https://www.openldap.org/) is a cross platform LDAP based directory service. [Active Directory (AD)](https://en.wikipedia.org/wiki/Active_Directory) is a [directory service](https://en.wikipedia.org/wiki/Directory_service) developed by [Microsoft](https://en.wikipedia.org/wiki/Microsoft) for [Windows domain](https://en.wikipedia.org/wiki/Windows_domain) networks which also uses LDAP. + +There are many dockerized OpenLDAP server to choose from. One such example is [mlan/openldap](https://github.com/mlan/docker-openldap). + +### LDAP user entry + +The data itself in an LDAP system is mainly stored in elements called attributes. Attributes are basically key-value pairs. Unlike in some other systems, the keys have predefined names which are dictated by the objectClasses selected for an entry. An entry is basically a collection of attributes under a name used to describe something; a user for example. + +```yaml +dn: uid=demo,ou=users,dc=example,dc=com +objectclass: inetOrgPerson +uid: demo +``` + +### Kopano LDAP schema + +The Kopano LDAP schema defines additional objectClasses and attributes. These allow the Kopano services, access for example, to controlled on per-user basis. More information on available attributes can be find here, [Fine-tuning user configuration](https://documentation.kopano.io/kopanocore_administrator_manual/configure_kc_components.html?highlight=propmap#fine-tuning-user-configuration). + +```shell +dn: uid=demo,ou=users,dc=example,dc=com +objectClass: top +objectClass: inetOrgPerson +objectClass: kopano-user +objectClass: posixAccount +cn: demo +sn: demo +uid: demo +mail: demo@example.com +uidNumber: 1234 +gidNumber: 1234 +homeDirectory: /home/demo +telephoneNumber: 0123 123456789 +title: MCP +kopanoEnabledFeatures: imap pop3 +``` + +The schema needs to be added to the directory server. The Kopano installation files include the LDAP schema and can be found here `/usr/share/doc/kopano/kopano.ldif.gz`. For more details, see: [Kopano Knowledge Base/Install and optimize OpenLDAP for use with Kopano Groupware Core](https://kb.kopano.io/display/WIKI/Install+and+optimize+OpenLDAP+for+use+with+Kopano+Groupware+Core). + ## Kopano WebApp HTTP access The distribution installation of `kopano-webapp` only allow HTTPS access. The `mlan/kopano` image updates the configuration to [`define("SECURE_COOKIES", false);`](https://documentation.kopano.io/webapp_admin_manual/config.html#secure-cookies) in `/etc/kopano/webapp/config.php` also allowing HTTP access. This can be useful when arranging the `mlan/kopano` container behind a reverse proxy, like [Traefik](https://doc.traefik.io/traefik/), which then does the enforcement of HTTPS. Also see [`MIGRATE_CONFIG=1` Rejected insecure request as configuration for SECURE_COOKIES is true](#migrate_config1-rejected-insecure-request-as-configuration-for-secure_cookies-is-true). diff --git a/demo/Makefile b/demo/Makefile index ce5a4bb..640c18a 100644 --- a/demo/Makefile +++ b/demo/Makefile @@ -13,7 +13,7 @@ variables: test: all-test_quiet mta-test_smtp -init: auth-init db-init mta-init app-init +init: up auth-init db-init mta-init app-init ps: docker-compose ps @@ -24,7 +24,7 @@ up: down: docker-compose down -destroy: +destroy: auth-gui-down docker-compose down -v config: @@ -73,7 +73,7 @@ wait_%: web: firefox localhost:8008 & -auth-init: auth-up wait_11 auth-mod_hash auth-mod_index auth-add_user +auth-init: wait_11 auth-mod_hash auth-mod_index auth-add_schema auth-add_user auth-show_conf: docker-compose exec auth ldap search -b cn=config "(cn=config)" @@ -90,7 +90,7 @@ auth-show_cat1: docker-compose exec auth slapcat -n1 auth-add_user: - printf "dn: ou=$(LDAP_USEROU),$(LDAP_BASE)\nchangetype: add\nobjectClass: organizationalUnit\nobjectClass: top\nou: $(LDAP_USEROU)\n\ndn: ou=$(LDAP_GROUPOU),$(LDAP_BASE)\nchangetype: add\nobjectClass: organizationalUnit\nobjectClass: top\nou: $(LDAP_GROUPOU)\n\ndn: uid=$(LDAP_TEST_USER),ou=$(LDAP_USEROU),$(LDAP_BASE)\nchangetype: add\nobjectClass: top\nobjectClass: inetOrgPerson\nobjectClass: $(LDAP_USEROBJ)\ncn: $(LDAP_TEST_USER)\nsn: $(LDAP_TEST_USER)\nuid: $(LDAP_TEST_USER)\nmail: $(LDAP_TEST_USER)@$(MAIL_DOMAIN)\nuidNumber: 1234\ngidNumber: 1234\nhomeDirectory: /home/$(LDAP_TEST_USER)\nuserPassword: $(LDAP_TEST_PASSWD)\n" \ + printf "dn: ou=$(LDAP_USEROU),$(LDAP_BASE)\nchangetype: add\nobjectClass: organizationalUnit\nobjectClass: top\nou: $(LDAP_USEROU)\n\ndn: ou=$(LDAP_GROUPOU),$(LDAP_BASE)\nchangetype: add\nobjectClass: organizationalUnit\nobjectClass: top\nou: $(LDAP_GROUPOU)\n\ndn: uid=$(LDAP_TEST_USER),ou=$(LDAP_USEROU),$(LDAP_BASE)\nchangetype: add\nobjectClass: top\nobjectClass: inetOrgPerson\nobjectClass: kopano-user\nobjectClass: $(LDAP_USEROBJ)\ncn: $(LDAP_TEST_USER)\nsn: $(LDAP_TEST_USER)\nuid: $(LDAP_TEST_USER)\nmail: $(LDAP_TEST_USER)@$(MAIL_DOMAIN)\nuidNumber: 1234\ngidNumber: 1234\nhomeDirectory: /home/$(LDAP_TEST_USER)\nuserPassword: $(LDAP_TEST_PASSWD)\ntelephoneNumber: 0123 123456789\ntitle: MCP\nkopanoEnabledFeatures: imap pop3\n" \ | docker-compose exec -T auth ldap modify auth-mod_index: @@ -101,7 +101,21 @@ auth-mod_hash: printf "dn: olcDatabase={-1}frontend,cn=config\nchangetype: modify\nadd: olcPasswordHash\nolcPasswordHash: {CRYPT}\n\ndn: cn=config\nchangetype: modify\nadd: olcPasswordCryptSaltFormat\nolcPasswordCryptSaltFormat: \$$6\$$%%.16s\n" \ | docker-compose exec -T auth ldap modify -mta-init: mta-up +auth-add_schema: + docker-compose exec app zcat /usr/share/doc/kopano/kopano.ldif.gz \ + | docker-compose exec -T auth ldapadd -H ldapi://%2Fvar%2Frun%2Fopenldap%2Fldapi/ -Y EXTERNAL + +auth-gui-up: + docker run -d --rm --name auth-gui --network demo_backend \ + -p 127.0.0.1:8001:80 -e PHPLDAPADMIN_LDAP_HOSTS=auth \ + -e PHPLDAPADMIN_HTTPS=false osixia/phpldapadmin || true + sleep 2 + firefox localhost:8001 & + +auth-gui-down: + docker stop auth-gui || true + +mta-init: mta-bayes: docker-compose exec mta sh -c 'rm -f bayesian.database.gz && wget http://artinvoice.hu/spams/bayesian.database.gz && gunzip bayesian.database.gz && sa-learn --restore bayesian.database && chown -R amavis: /var/amavis/.spamassassin && rm -rf bayesian.database' @@ -177,12 +191,12 @@ mta-test_rimap: mta-test_ldap: mta-debugtools docker-compose exec mta ldapsearch -H ldap://auth:389 -xLLL -s base namingContexts -db-init: db-up +db-init: db-test: docker-compose exec db mysqlshow -u $(MYSQL_USER) $(MYSQL_DATABASE) -p$(MYSQL_PASSWORD) -app-init: app-up wait_12 app-public_store +app-init: wait_12 app-public_store app-debugtools: docker-compose exec app apt-get update @@ -230,7 +244,7 @@ app-test_tls: app-hostaddr docker run --rm -it --network demo_backend drwetter/testssl.sh app:993 || true app-show_user1: - docker-compose exec app kopano-admin -l + docker-compose exec app kopano-admin --details $(LDAP_TEST_USER) app-show_user2: app-debugtools docker-compose exec app ldapsearch -H ldap://auth:389 -xLLL -b $(LDAP_BASE) '*' diff --git a/demo/docker-compose.yml b/demo/docker-compose.yml index 0e7d2cd..41ede43 100644 --- a/demo/docker-compose.yml +++ b/demo/docker-compose.yml @@ -25,6 +25,7 @@ services: - LDAP_SEARCH_BASE=${LDAP_BASE-dc=example,dc=com} - LDAP_USER_TYPE_ATTRIBUTE_VALUE=${LDAP_USEROBJ-posixAccount} - LDAP_GROUP_TYPE_ATTRIBUTE_VALUE=${LDAP_GROUPOBJ-posixGroup} + - LDAP_PROPMAP= - MYSQL_DATABASE=${MYSQL_DATABASE-kopano} - MYSQL_USER=${MYSQL_USER-kopano} - MYSQL_PASSWORD=${MYSQL_PASSWORD-secret} diff --git a/src/kopano/entry.d/10-kopano-common b/src/kopano/entry.d/10-kopano-common index 9288c3b..33f4867 100755 --- a/src/kopano/entry.d/10-kopano-common +++ b/src/kopano/entry.d/10-kopano-common @@ -17,7 +17,7 @@ DOCKER_CONF_DIR2=${DOCKER_CONF_DIR2-/usr/share/z-push} DOCKER_MAN5_DIR=${DOCKER_MAN5_DIR-/usr/share/man/man5/} DOCKER_APPL_SSL_CERT=${DOCKER_APPL_SSL_CERT-$DOCKER_APPL_SSL_DIR/cert.pem} DOCKER_APPL_SSL_KEY=${DOCKER_APPL_SSL_KEY-$DOCKER_APPL_SSL_DIR/priv_key.pem} - +DOCKER_LDAP_PMAP_FILE=${DOCKER_LDAP_PMAP_FILE-/usr/share/kopano/ldap.propmap.cfg} sqlstate_cfg_file=$DOCKER_CONF_DIR2/backend/sqlstatemachine/config.php zpush_cfg_file=$DOCKER_CONF_DIR2/config.php webapp_cfg_file=$DOCKER_CONF_DIR1/webapp/config.php @@ -45,18 +45,15 @@ kopano_apply_envvars_zpush() { } kopano_apply_envvars_cfg() { - local cfg_file=$DOCKER_CONF_DIR1/$1.cfg - local man_file=$DOCKER_MAN5_DIR/kopano-$1.cfg.5.gz + local cfg_file=$(kopano_gen_filename_cfg $1) + local man_file=$(kopano_gen_filename_man $1) if [ -f $man_file ]; then local env_vars="$(kopano_get_envvars_man $man_file)" if [ -e $cfg_file ]; then mv -f $cfg_file $cfg_file.orig fi for env_var in $env_vars; do - if [ -n "${!env_var+x}" ]; then - dc_log 5 "Setting ${env_var,,} = ${!env_var} in $cfg_file" - echo ${env_var,,} = ${!env_var} >> $cfg_file - fi + kopano_set_envvars_cfg $cfg_file $env_var done else dc_log 4 "Could not find $man_file" @@ -77,6 +74,24 @@ kopano_apply_envvars_php() { fi } +# +# kopano_set_envvars_cfg [explicit parameter name] +# +kopano_set_envvars_cfg() { + local cfg_file=$1 + local env_var=$2 + local cfg_par="${3-$env_var =}" + if [ -n "${!env_var+x}" ]; then + dc_log 5 "Setting ${cfg_par,,} ${!env_var} in $cfg_file" + echo ${cfg_par,,} ${!env_var} >> $cfg_file + fi +} + +# +# Helpers +# +kopano_gen_filename_man() { echo $DOCKER_MAN5_DIR/kopano-$1.cfg.5.gz ;} +kopano_gen_filename_cfg() { echo $DOCKER_CONF_DIR1/$1.cfg ;} kopano_get_envvars_man() { zcat $1 | sed -r "/^\.SS/!d;{s/^\.SS (.*)/\U\1/g;s/,//g}" | sort -u ;} kopano_get_envvars_php() { sed -nr "/define\(/s/.*define\(['\"](.*)['\"], .*/\1/p" $1 | sort -u ;} @@ -110,3 +125,20 @@ kopano_generate_tls_cert() { dc_tls_setup_selfsigned_cert $DOCKER_APPL_SSL_CERT $DOCKER_APPL_SSL_KEY fi } + +# +# LDAP directives +# +kopano_apply_envvar_propmap() { + local cfg_file=$(kopano_gen_filename_cfg ldap) + local env_var=LDAP_PROPMAP + if [ -n "${!env_var+x}" ]; then + if [ -z "${!env_var}" ]; then + typeset "$env_var=$DOCKER_LDAP_PMAP_FILE" + elif [ ! -e "${!env_var}" ]; then + dc_log 5 "No ${!env_var} so copying $DOCKER_LDAP_PMAP_FILE there." + cp -f $DOCKER_LDAP_PMAP_FILE ${!env_var} + fi + kopano_set_envvars_cfg $cfg_file $env_var '!propmap' + fi +} diff --git a/src/kopano/entry.d/50-kopano-config b/src/kopano/entry.d/50-kopano-config index 9c69b7c..1919ba0 100755 --- a/src/kopano/entry.d/50-kopano-config +++ b/src/kopano/entry.d/50-kopano-config @@ -17,4 +17,5 @@ if dc_is_unlocked; then kopano_apply_envvars_core kopano_apply_envvars_webapp kopano_apply_envvars_zpush + kopano_apply_envvar_propmap fi diff --git a/test/Makefile b/test/Makefile index 904f7fb..2740616 100644 --- a/test/Makefile +++ b/test/Makefile @@ -61,6 +61,7 @@ APP_ENV ?= $(NET_ENV) $(SQL_ENV) \ -e LDAP_SEARCH_BASE=$(LDAP_BAS) \ -e LDAP_USER_TYPE_ATTRIBUTE_VALUE=$(LDAP_UOB) \ -e LDAP_GROUP_TYPE_ATTRIBUTE_VALUE=$(LDAP_GOU) \ +-e LDAP_PROPMAP= \ -e IMAP_LISTEN=*:143 \ -e POP3_LISTEN=*:110 \ -e ICAL_LISTEN=*:8080 \