Compare commits
11 Commits
Author | SHA1 | Date |
---|---|---|
|
fe6566c427 | |
|
313d954381 | |
|
08a3e3ec9b | |
|
0fb026a934 | |
|
bd88becc7c | |
|
4579fd70ad | |
|
70b6ab3a9f | |
|
e0fc560dd3 | |
|
36cd823be8 | |
|
6e32101634 | |
|
b9c2529745 |
29
CHANGELOG.md
29
CHANGELOG.md
|
@ -1,3 +1,32 @@
|
|||
# 1.3.1
|
||||
|
||||
- [docker](Dockerfile) Update Z-Push package URL.
|
||||
- [kopano](src/kopano) Now use ubuntu:20.04 in `kopano-webaddr.sh`.
|
||||
- [docker](ROADMAP.md) Update roadmap.
|
||||
|
||||
# 1.3.0
|
||||
|
||||
- [docker](Dockerfile) Now use ubuntu:20.04 as base image.
|
||||
- [docker](Makefile) Don't set BLD_ARG in Makefile.
|
||||
- [kopano](src/kopano/plugin/movetopublicldap.py) Need explicitly to set `allow_no_value=True` due to update in `configparser.3.8.py`.
|
||||
- [demo](demo) Update to `mta-apk_list` target.
|
||||
|
||||
# 1.2.9
|
||||
|
||||
- [kopano](src/kopano) Bug in kopano-dagent out-of-office now fixed.
|
||||
- [demo](demo) Make demo less dependent on that curl and firefox beeing installed on the host.
|
||||
- [demo](demo) Don't expose any ports on the host, avoiding potential conflicts.
|
||||
|
||||
# 1.2.8
|
||||
|
||||
- [test](test) Update to use `mlan/openldap:2`.
|
||||
- [demo](demo) Update to use `mlan/openldap:2`.
|
||||
|
||||
# 1.2.7
|
||||
|
||||
- [kopano](src/kopano/plugin/movetopublicldap.py) Add LDAP support to the [Move to public](https://documentation.kopano.io/kopanocore_administrator_manual/special_kc_configurations.html#move-to-public ) kopano-dagent python plugin.
|
||||
- [kopano](src/kopano/plugin/movetopublicldap.py) Add sample config file for the kopano-dagent python plugin movetopublicldap.
|
||||
|
||||
# 1.2.6
|
||||
|
||||
- [docker](src/docker/bin/docker-config.sh) Allow the crontab support to work also when the file `/etc/kopano/docker-crontab` is missing.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# build arguments, amd64 is the default
|
||||
#
|
||||
ARG DIST=ubuntu
|
||||
ARG REL=18.04
|
||||
ARG REL=20.04
|
||||
ARG ARCH
|
||||
|
||||
FROM ${ARCH:+$ARCH/}$DIST:$REL AS base
|
||||
|
@ -17,6 +17,7 @@ ENV DEBIAN_FRONTEND=noninteractive \
|
|||
DOCKER_CRONTAB_DIR=/etc/cron.d \
|
||||
DOCKER_CONF_DIR1=/etc/kopano \
|
||||
DOCKER_SMPL_DIR1=/usr/share/doc/kopano/example-config \
|
||||
DOCKER_PLUG_DIR=/usr/share/kopano-dagent/python/plugins \
|
||||
DOCKER_CONF_DIR2=/usr/share/z-push \
|
||||
DOCKER_APPL_LIB=/var/lib/kopano \
|
||||
DOCKER_APPL_SSL_DIR=/etc/kopano/ssl \
|
||||
|
@ -35,6 +36,7 @@ COPY src/*/bin $DOCKER_BIN_DIR/
|
|||
COPY src/*/entry.d $DOCKER_ENTRY_DIR/
|
||||
COPY src/*/exit.d $DOCKER_EXIT_DIR/
|
||||
COPY src/*/config $DOCKER_CONF_DIR1/
|
||||
COPY src/*/plugin $DOCKER_PLUG_DIR/
|
||||
|
||||
#
|
||||
# Install helpers. Set bash as default shell. Setup syslogs service.
|
||||
|
@ -115,6 +117,7 @@ RUN mkdir -p $DOCKER_BUILD_DEB_DIR \
|
|||
&& for i in $(seq ${DOCKER_BUILD_PASSES}); do echo "\033[1;36mKOPANO CORE INSTALL PASS: $i\033[0m" \
|
||||
&& dpkg --install --force-depends --skip-same-version --recursive $DOCKER_BUILD_DEB_DIR \
|
||||
&& apt-get install --yes --no-install-recommends --fix-broken; done \
|
||||
&& apt-get install --yes --no-install-recommends python3-ldap \
|
||||
&& mkdir -p /var/lib/kopano/attachments && chown $DOCKER_APPL_RUNAS: /var/lib/kopano/attachments \
|
||||
&& mkdir -p $DOCKER_APPL_SSL_DIR \
|
||||
&& mkdir -p $DOCKER_ACME_SSL_DIR \
|
||||
|
@ -233,7 +236,7 @@ ENV DEBIAN_FRONTEND=noninteractive \
|
|||
#
|
||||
# Add Z-Push repository and install Z-Push configured to be used with Kopano and Apache
|
||||
#
|
||||
RUN debaddr="$(kopano-webaddr.sh --deb final http://repo.z-hub.io/z-push: ${DIST} ${REL})" \
|
||||
RUN debaddr="$(kopano-webaddr.sh --deb final https://download.kopano.io/zhub/z-push: ${DIST} ${REL})" \
|
||||
&& echo "deb $debaddr/ /" > /etc/apt/sources.list.d/z-push.list \
|
||||
&& wget -qO - $debaddr/Release.key | apt-key add - \
|
||||
&& mkdir -p /var/lib/z-push && chown www-data: /var/lib/z-push \
|
||||
|
|
17
Makefile
17
Makefile
|
@ -1,17 +1,26 @@
|
|||
# Makefile
|
||||
#
|
||||
# build
|
||||
#
|
||||
|
||||
-include *.mk
|
||||
|
||||
#BLD_ARG ?= --build-arg DIST=ubuntu --build-arg REL=18.04 --build-arg ARCH=i386
|
||||
BLD_ARG ?=
|
||||
#BLD_ARG ?= --build-arg DIST=ubuntu --build-arg REL=20.04
|
||||
BLD_REPO ?= mlan/kopano
|
||||
BLD_VER ?= latest
|
||||
BLD_TGT ?= full
|
||||
|
||||
SRC_CMD ?= src/kopano/bin/kopano-webaddr.sh -VV
|
||||
SRC_VER ?= $(shell $(SRC_CMD))
|
||||
IMG_REPO ?= $(BLD_REPO)
|
||||
IMG_VER ?= $(BLD_VER)
|
||||
|
||||
TST_REPO ?= $(BLD_REPO)
|
||||
TST_VER ?= $(BLD_VER)
|
||||
TST_ENV ?= -C test
|
||||
TST_TGTE ?= $(addprefix test-,all diff down env htop imap lmtp logs mail pop3 pull sh sv up)
|
||||
TST_TGTI ?= test_% test-up_%
|
||||
|
||||
export TST_REPO TST_VER
|
||||
|
||||
_version = $(if $(findstring $(BLD_TGT),$(1)),\
|
||||
$(if $(findstring latest,$(2)),latest $(1) $(SRC_VER) $(1)-$(SRC_VER),$(2) $(1)-$(2)),\
|
||||
$(if $(findstring latest,$(2)),$(1) $(1)-$(SRC_VER),$(1)-$(2)))
|
||||
|
|
104
README.md
104
README.md
|
@ -3,8 +3,9 @@
|
|||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
This (non official) repository provides dockerized web mail service as well as Exchange ActiveSync (EAS), IMAP, POP3 and ICAL service (and their secure variants IMAPS, POP3S and ICALS). It is based on [Kopano](https://kopano.com) core components, as well as the Kopano WebApp and [Z-Push](http://z-push.org/). The image uses [nightly built packages](https://download.kopano.io/community/) which are provided by the Kopano community.
|
||||
|
||||
|
@ -21,6 +22,7 @@ Hopefully this repository can be retired once the Kopano community make official
|
|||
- Configuration using environment variables
|
||||
- Log directed to docker daemon with configurable level
|
||||
- Built in utility script [run](src/docker/bin/run) helping configuring Kopano components, WebApp and Z-Push
|
||||
- [Move to public with LDAP lookup](#move-to-public-with-ldap-lookup)
|
||||
- [Crontab](https://en.wikipedia.org/wiki/Cron) support.
|
||||
- Health check
|
||||
- Hook for theming
|
||||
|
@ -65,14 +67,14 @@ services:
|
|||
image: mlan/kopano
|
||||
networks:
|
||||
- backend
|
||||
ports:
|
||||
- "127.0.0.1:8008:80" # WebApp & EAS (alt. HTTP)
|
||||
- "127.0.0.1:143:143" # IMAP (not needed if all devices can use EAS)
|
||||
- "127.0.0.1:110:110" # POP3 (not needed if all devices can use EAS)
|
||||
- "127.0.0.1:8080:8080" # ICAL (not needed if all devices can use EAS)
|
||||
- "127.0.0.1:993:993" # IMAPS (not needed if all devices can use EAS)
|
||||
- "127.0.0.1:995:995" # POP3S (not needed if all devices can use EAS)
|
||||
- "127.0.0.1:8443:8443" # ICALS (not needed if all devices can use EAS)
|
||||
ports: # Expose ports to host interfaces
|
||||
- "80:80" # WebApp & EAS (alt. HTTP)
|
||||
- "143:143" # IMAP (not needed if all devices can use EAS)
|
||||
- "110:110" # POP3 (not needed if all devices can use EAS)
|
||||
- "8080:8080" # ICAL (not needed if all devices can use EAS)
|
||||
- "993:993" # IMAPS (not needed if all devices can use EAS)
|
||||
- "995:995" # POP3S (not needed if all devices can use EAS)
|
||||
- "8443:8443" # ICALS (not needed if all devices can use EAS)
|
||||
depends_on:
|
||||
- auth
|
||||
- db
|
||||
|
@ -82,10 +84,12 @@ services:
|
|||
- LDAP_URI=ldap://auth:389/
|
||||
- MYSQL_HOST=db
|
||||
- SMTP_SERVER=mta
|
||||
- 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_SEARCH_BASE=${AD_BASE-dc=example,dc=com}
|
||||
- LDAP_USER_TYPE_ATTRIBUTE_VALUE=${AD_USR_OB-kopano-user}
|
||||
- LDAP_GROUP_TYPE_ATTRIBUTE_VALUE=${AD_GRP_OB-kopano-group}
|
||||
- LDAP_GROUPMEMBERS_ATTRIBUTE_TYPE=dn
|
||||
- LDAP_PROPMAP=
|
||||
- DAGENT_PLUGINS=movetopublicldap
|
||||
- MYSQL_DATABASE=${MYSQL_DATABASE-kopano}
|
||||
- MYSQL_USER=${MYSQL_USER-kopano}
|
||||
- MYSQL_PASSWORD=${MYSQL_PASSWORD-secret}
|
||||
|
@ -95,8 +99,9 @@ services:
|
|||
- IMAPS_LISTEN=*:993 # enable TLS
|
||||
- POP3S_LISTEN=*:995 # enable TLS
|
||||
- ICALS_LISTEN=*:8443 # enable TLS
|
||||
- DISABLED_FEATURES=${DISABLED_FEATURES-} # also enable IMAP and POP3
|
||||
- PLUGIN_SMIME_USER_DEFAULT_ENABLE_SMIME=true
|
||||
- SYSLOG_LEVEL=${SYSLOG_LEVEL-3}
|
||||
- LOG_LEVEL=${LOG_LEVEL-3}
|
||||
volumes:
|
||||
- app-conf:/etc/kopano
|
||||
- app-atch:/var/lib/kopano/attachments
|
||||
|
@ -111,16 +116,16 @@ services:
|
|||
hostname: ${MAIL_SRV-mx}.${MAIL_DOMAIN-example.com}
|
||||
networks:
|
||||
- backend
|
||||
ports:
|
||||
- "127.0.0.1:25:25" # SMTP
|
||||
- "127.0.0.1:465:465" # SMTPS authentication required
|
||||
ports: # Expose ports to host interfaces
|
||||
- "25:25" # SMTP
|
||||
- "465:465" # SMTPS authentication required
|
||||
depends_on:
|
||||
- auth
|
||||
environment: # Virgin config, ignored on restarts unless FORCE_CONFIG given.
|
||||
- LDAP_HOST=auth
|
||||
- VIRTUAL_TRANSPORT=lmtp:app:2003
|
||||
- LDAP_USER_BASE=ou=${LDAP_USEROU-users},${LDAP_BASE-dc=example,dc=com}
|
||||
- LDAP_QUERY_FILTER_USER=(&(objectclass=${LDAP_USEROBJ-posixAccount})(mail=%s))
|
||||
- LDAP_USER_BASE=ou=${AD_USR_OU-users},${AD_BASE-dc=example,dc=com}
|
||||
- LDAP_QUERY_FILTER_USER=(&(objectclass=${AD_USR_OB-kopano-user})(mail=%s))
|
||||
volumes:
|
||||
- mta:/srv
|
||||
- app-spam:/var/lib/kopano/spamd # kopano-spamd integration
|
||||
|
@ -147,8 +152,10 @@ services:
|
|||
image: mlan/openldap
|
||||
networks:
|
||||
- backend
|
||||
command: --root-cn ${AD_ROOT_CN-admin} --root-pw ${AD_ROOT_PW-secret}
|
||||
environment:
|
||||
- LDAP_LOGLEVEL=parse
|
||||
- LDAPBASE=${AD_BASE-dc=example,dc=com}
|
||||
- LDAPDEBUG=${AD_DEBUG-parse}
|
||||
volumes:
|
||||
- auth:/srv
|
||||
- /etc/localtime:/etc/localtime:ro # Use host timezone
|
||||
|
@ -168,7 +175,7 @@ volumes:
|
|||
|
||||
## Demo
|
||||
|
||||
This repository contains a [demo](demo) directory which hold the [docker-compose.yml](demo/docker-compose.yml) file as well as a [Makefile](demo/Makefile) which might come handy. Start with cloning the [github](https://github.com/mlan/docker-kopano) repository.
|
||||
This repository contains a [demo](demo) directory which hold the [docker-compose.yml](demo/docker-compose.yml) file as well as a [Makefile](demo/Makefile) which might come handy. To run the demo you need [docker-compose](https://docs.docker.com/compose/install/) installed. By default `curl` and `firefox` is expected to be installed, but if not, run `make utils-container` within the [demo](demo) directory once the repository has been cloned. The `make` utility works nicely with [bash-completion](https://github.com/scop/bash-completion) so it can be worth considering having it installed too. Once the dependencies are met, start with cloning the [github](https://github.com/mlan/docker-kopano) repository.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/mlan/docker-kopano.git
|
||||
|
@ -180,7 +187,7 @@ From within the [demo](demo) directory you can start the containers by typing:
|
|||
make init
|
||||
```
|
||||
|
||||
Then you can assess WebApp on the URL [`http://localhost:8008`](http://localhost:8008) and log in with the user name `demo` and password `demo` . You can send yourself a test email by typing:
|
||||
Now you can assess WebApp on the custom docker network at URL `http://app` and log in with the user name `demo` and password `demo`.
|
||||
|
||||
```bash
|
||||
make web
|
||||
|
@ -192,7 +199,7 @@ You can send yourself a test email by typing:
|
|||
make test
|
||||
```
|
||||
|
||||
When you are done testing you can destroy the test containers by typing
|
||||
When you are done testing you can destroy the test containers and their volumes by typing:
|
||||
|
||||
```bash
|
||||
make destroy
|
||||
|
@ -438,13 +445,64 @@ make app-create_smime
|
|||
|
||||
The [Mobile Device Management](https://documentation.kopano.io/webapp_mdm_manual/) WebApp plugin comes pre-installed. With it you can resync, remove, refresh and even wipe your devices, connected via [Exchange ActiveSync (EAS)](https://en.wikipedia.org/wiki/Exchange_ActiveSync).
|
||||
|
||||
## Public folders
|
||||
|
||||
There are two type of stores (folders containing communication elements); private and public stores. There can only be one public store. It is the Kopano dagent that places incoming messages into mail boxes, that is the private and public stores.
|
||||
|
||||
Public folders are configured by the system admin. Users have them mapped automatically. The shared and public folders can be synced with mobile devices via [Exchange ActiveSync (EAS)](https://wiki.z-hub.io/display/ZP/Sharing+folders+and+Read-only). Each user can manage which shared or public folders to sync with her mobile devices by using the [Mobile Device Management](#mobile-device-management) WebApp plugin.
|
||||
|
||||
With the current Kopano implementation, delivering to the public store is configured separately from normal user management. There is the [move to public](https://documentation.kopano.io/kopanocore_administrator_manual/special_kc_configurations.html#move-to-public) plugin which moves incoming messages to a folder in the public store. It has a static configuration and does not support LDAP lookup.
|
||||
|
||||
### Move to public with LDAP lookup
|
||||
|
||||
The `mlan/kopano` image include a extended version of the move to public plugin which use LDAP lookup, instead of a static file based lookup. When the plugin [move to public with LDAP](src/kopano/plugin/movetopublicldap.py) is enabled, `DAGENT_PLUGINS=movetopublicldap`, the kopano dagent will do two LDAP queries. The first is to search for an entry/user with matching email address. The second, introduced with this plugin, is to get the public folder from this entry. If found the message will be delivered to the public folder otherwise it will be delivered to the mailbox of the user.
|
||||
|
||||
Lets demonstrate how delivery to a public folder is configured. With this LDAP entry:
|
||||
|
||||
```yaml
|
||||
dn: uid=public,ou=users,dc=example,dc=com
|
||||
cn: public
|
||||
objectClass: top
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: kopano-user
|
||||
sn: public
|
||||
uid: public
|
||||
mail: public@example.com
|
||||
kopanoAccount: 1
|
||||
kopanoHidden: 1
|
||||
kopanoSharedStoreOnly: 1
|
||||
kopanoResourceType: publicFolder:Public Stores/public
|
||||
```
|
||||
|
||||
messages to `public@example.com` will be delivered to the public store in `Public Stores/public`.
|
||||
The central [attribute](https://documentation.kopano.io/kopanocore_administrator_manual/appendix_b.html#appendix-b-ldap-attribute-description) is `kopanoResourceType: publicFolder:Public Stores/public`. It contains a token and a folder name. The token match is case sensitive and there must be a colon `:` separating
|
||||
the token and the public folder name. The folder name can contain space and
|
||||
sub folders, which are distinguished using a forward slash `/`.
|
||||
|
||||
The parameters in `/etc/kopano/ldap.cfg` will be used to arrange the LDAP queries.
|
||||
The LDAP attribute holding the token and the token itself have the following
|
||||
default values, which can be modified in `/etc/kopano/movetopublicldap.cfg` if desired.
|
||||
|
||||
```yaml
|
||||
ldap_public_folder_attribute = kopanoResourceType
|
||||
ldap_public_folder_attribute_token = publicFolder
|
||||
```
|
||||
|
||||
As with other parameters, environment variables can be used to define them: `LDAP_PUBLIC_FOLDER_ATTRIBUTE=kopanoResourceType` and `LDAP_PUBLIC_FOLDER_ATTRIBUTE_TOKEN=publicFolder`.
|
||||
|
||||
## Shared folders
|
||||
|
||||
Users can share folders when sufficient permission have been granted. When logged into WebApp with an administrative account (`kopanoAdmin: 1`) you can modify the permissions on users shares and folders. Users can then, when logged into WebApp, open the inbox of other users by selecting `Open Shared Mails`.
|
||||
|
||||
The [impersonation](https://wiki.z-hub.io/display/ZP/Impersonation) mechanism allow such shared folders to be synced over [Exchange ActiveSync (EAS)](https://wiki.z-hub.io/display/ZP/Sharing+folders+and+Read-only) too. Each user can manage which shared or public folders to sync with her mobile devices by using the [Mobile Device Management](#mobile-device-management) WebApp plugin.
|
||||
|
||||
## Crontab
|
||||
|
||||
The `mlan/kopano` has a [cron](https://en.wikipedia.org/wiki/Cron) service activated. You can use environment variables to set up [crontab](https://man7.org/linux/man-pages/man5/crontab.5.html) entries. Any environment variable name staring with `CRONTAB_ENTRY` will be use to add entries to cron.
|
||||
|
||||
One trivial example is `CRONTAB_ENTRY_TEST=* * * * * root logger -t cron -p user.notice "SHELL=$$SHELL, PATH=$$PATH"`.
|
||||
|
||||
During the initial configuration procedure any `CRONTAB_ENTRY` will add crontab entries to the file `/etc/kopano/docker-crontab`, all the while previously present entries are deleted. This file defines the `PATH` variable so that you don't need to give full path names to commands in the crontab entry. This is, you need to provide the full path names to commands if this `PATH` definition is missing in the `/etc/kopano/docker-crontab` file.
|
||||
During the initial configuration procedure any `CRONTAB_ENTRY` will add crontab entries to the file `/etc/kopano/docker-crontab`, all the while previously present entries are deleted. This file defines the `PATH` variable so that you don't need to give full path names to commands in the crontab entry. This is, you need to provide the full path names to commands if this `PATH` definition is missing in the `/etc/kopano/docker-crontab` file.
|
||||
|
||||
## Mail transfer agent interaction
|
||||
|
||||
|
|
23
ROADMAP.md
23
ROADMAP.md
|
@ -1,18 +1,33 @@
|
|||
# Road map
|
||||
|
||||
## kDAV
|
||||
|
||||
Consider integrating support for kDAV which provides CalDAV and CardDAV.
|
||||
|
||||
## Revisit Persistent Data
|
||||
|
||||
Consider consolidating directories which are candidates for persistence under `/srv`.
|
||||
|
||||
- /etc/kopano
|
||||
- /var/lib/kopano
|
||||
- /var/lib/z-push
|
||||
|
||||
### Kopano Search
|
||||
|
||||
The kopano-search module keeps its database here, /var/lib/kopano/search.
|
||||
Consider to also consolidating it under /srv to simplify making it persistent?
|
||||
|
||||
## kDAV
|
||||
|
||||
Consider integrating support for kDAV which provides CalDAV and CardDAV.
|
||||
|
||||
## webapp-passwd
|
||||
|
||||
Integrate [webapp-passwd](https://github.com/silentsakky/zarafa-webapp-passwd)?
|
||||
|
||||
## kopano-spamd and kopano-search logs
|
||||
|
||||
In [KC-1858](https://github.com/Kopano-dev/kopano-core/commit/4a7f833e170167ebfa4f4c55835f8760ce7617f3) we find:
|
||||
|
||||
> The syslog log method does not work correctly and thus this change
|
||||
> disables it. Until it is fixed, Python services do not support
|
||||
> the syslog log_method. Additionally an environment variable is
|
||||
> added, which allow to lift this restriction for testing when it
|
||||
> it set.
|
||||
|
||||
|
|
45
demo/.env
45
demo/.env
|
@ -1,23 +1,34 @@
|
|||
COMPOSE_PROJECT_NAME=demo
|
||||
SYSLOG_LEVEL=6
|
||||
SYSLOG_LEVEL=7
|
||||
LOG_LEVEL=6
|
||||
AD_DEBUG=stats
|
||||
AD_ADM_CN=admin
|
||||
AD_ADM_PW=admin
|
||||
AD_ADM_TEL=555-540-9637
|
||||
AD_ADM_TIT=System Admin
|
||||
AD_BASE=dc=example,dc=com
|
||||
AD_GRP_CN=team
|
||||
AD_GRP_OB=kopano-group
|
||||
AD_GRP_OU=groups
|
||||
AD_PUB_CN=public
|
||||
AD_ROOT_CN=admin
|
||||
AD_ROOT_PW=secret
|
||||
AD_SHR_CN=shared
|
||||
AD_USR_AS=trial
|
||||
AD_USR_CN=demo
|
||||
AD_USR_OB=kopano-user
|
||||
AD_USR_OU=users
|
||||
AD_USR_PW=demo
|
||||
AD_USR_TEL=555-439-2736
|
||||
AD_USR_TIT=First User
|
||||
DKIM_SELECTOR=default
|
||||
MAIL_DOMAIN=example.com
|
||||
MAIL_SRV=mx
|
||||
REGEX_ALIAS='/([^+]+)[+-].*@(.+)/ $1@$2'
|
||||
DKIM_SELECTOR=default
|
||||
SA_TAG_LEVEL_DEFLT=-999
|
||||
SA_DEBUG=0
|
||||
RAZOR_REGISTRATION=
|
||||
LDAP_BASE=dc=example,dc=com
|
||||
LDAP_USEROU=users
|
||||
LDAP_USEROBJ=kopano-user
|
||||
LDAP_USERFLT=
|
||||
LDAP_GROUPOU=groups
|
||||
LDAP_GROUPOBJ=kopano-group
|
||||
LDAP_TEST_USER=demo
|
||||
LDAP_TEST_PASSWD=demo
|
||||
LDAP_TEST_GROUP=team
|
||||
MYSQL_ROOT_PASSWORD=secret
|
||||
MYSQL_DATABASE=kopano
|
||||
MYSQL_USER=kopano
|
||||
MYSQL_PASSWORD=secret
|
||||
MYSQL_ROOT_PASSWORD=secret
|
||||
MYSQL_USER=kopano
|
||||
RAZOR_REGISTRATION=
|
||||
REGEX_ALIAS='/([^+]+)[+-].*@(.+)/ $1@$2'
|
||||
SA_DEBUG=0
|
||||
SA_TAG_LEVEL_DEFLT=-999
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
ssl
|
||||
utils-container.mk
|
||||
|
|
398
demo/Makefile
398
demo/Makefile
|
@ -1,19 +1,46 @@
|
|||
# Makefile
|
||||
#
|
||||
# demo
|
||||
#
|
||||
|
||||
-include *.mk .env .init.env
|
||||
|
||||
srv_list ?= auth app db mta
|
||||
curl_dbg ?= -v -s
|
||||
_ip = $(shell docker inspect -f \
|
||||
'{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' \
|
||||
$(1) | head -n1)
|
||||
SRV_LIST ?= auth app db mta
|
||||
|
||||
.PHONY:
|
||||
AD_DOM ?= $(call ad_sub_dot, $(AD_BASE))
|
||||
AD_DC ?= $(call ad_cut_dot, 1, 1, $(AD_DOM))
|
||||
|
||||
SSL_O = $(MAIL_DOMAIN)
|
||||
SSL_MAIL = auto
|
||||
SSL_PASS = $(AD_USR_PW)
|
||||
#SSL_TRST = $(SSL_SMIME)
|
||||
|
||||
NET_NAME ?= $(COMPOSE_PROJECT_NAME)_backend
|
||||
CURL_OPT ?= -s -v
|
||||
TSSL_CMD ?= docker run -i --rm --network $(NET_NAME) drwetter/testssl.sh
|
||||
CURL_CMD ?= curl
|
||||
webb_cmd ?= firefox $(1) &
|
||||
APP_NAME = app
|
||||
AUT_NAME = auth
|
||||
AUW_NAME = auth-web
|
||||
DB_NAME = db
|
||||
DBW_NAME = db-web
|
||||
MTA_NAME = mta
|
||||
APP_FQDN ?= $(call dkr_srv_ip,$(APP_NAME))
|
||||
AUT_FQDN ?= $(call dkr_srv_ip,$(AUT_NAME))
|
||||
AUW_FQDN ?= $(call dkr_cnt_ip,$(AUW_NAME))
|
||||
DB_FQDN ?= $(call dkr_srv_ip,$(DB_NAME))
|
||||
DBW_FQDN ?= $(call dkr_cnt_ip,$(DBW_NAME))
|
||||
MTA_FQDN ?= $(call dkr_srv_ip,$(MTA_NAME))
|
||||
|
||||
MAIL_FROM ?= test@my-domain.biz
|
||||
|
||||
variables:
|
||||
make -pn | grep -A1 "^# makefile"| grep -v "^#\|^--" | sort | uniq
|
||||
|
||||
test: all-test_quiet mta-test_smtp
|
||||
|
||||
init: up auth-init db-init mta-init app-init
|
||||
init: up auth-init db-init app-restart mta-init app-init
|
||||
|
||||
ps:
|
||||
docker-compose ps
|
||||
|
@ -24,7 +51,7 @@ up:
|
|||
down:
|
||||
docker-compose down
|
||||
|
||||
destroy: auth-gui-down all-destroy_smime
|
||||
destroy: auth-web-down db-web-down all-destroy_smime
|
||||
docker-compose down -v
|
||||
|
||||
config:
|
||||
|
@ -36,49 +63,48 @@ logs:
|
|||
images:
|
||||
docker-compose images
|
||||
|
||||
$(addsuffix -up,$(srv_list)):
|
||||
$(addsuffix -up,$(SRV_LIST)):
|
||||
docker-compose up -d $(patsubst %-up,%,$@)
|
||||
|
||||
$(addsuffix -down,$(srv_list)):
|
||||
docker-compose rm -s $(patsubst %-down,%,$@)
|
||||
$(addsuffix -down,$(SRV_LIST)):
|
||||
docker-compose rm -sf $(patsubst %-down,%,$@)
|
||||
|
||||
$(addsuffix -restart,$(srv_list)):
|
||||
$(addsuffix -restart,$(SRV_LIST)):
|
||||
docker-compose restart $(patsubst %-restart,%,$@)
|
||||
|
||||
$(addsuffix -renew,$(srv_list)):
|
||||
$(addsuffix -renew,$(SRV_LIST)):
|
||||
docker-compose rm -s $(patsubst %-renew,%,$@)
|
||||
docker-compose up -d $(patsubst %-renew,%,$@)
|
||||
|
||||
$(addsuffix -top,$(srv_list)):
|
||||
$(addsuffix -top,$(SRV_LIST)):
|
||||
docker-compose top $(patsubst %-top,%,$@)
|
||||
|
||||
$(addsuffix -logs,$(srv_list)):
|
||||
$(addsuffix -logs,$(SRV_LIST)):
|
||||
docker-compose logs $(patsubst %-logs,%,$@)
|
||||
|
||||
$(addsuffix -pull,$(srv_list)):
|
||||
$(addsuffix -pull,$(SRV_LIST)):
|
||||
docker-compose pull $(patsubst %-pull,%,$@)
|
||||
|
||||
$(addsuffix -sh,$(srv_list)):
|
||||
$(addsuffix -sh,$(SRV_LIST)):
|
||||
docker-compose exec $(patsubst %-sh,%,$@) sh -c 'exec $$(getent passwd root | sed "s/.*://g")'
|
||||
|
||||
$(addsuffix -env,$(srv_list)):
|
||||
$(addsuffix -env,$(SRV_LIST)):
|
||||
docker-compose exec $(patsubst %-env,%,$@) env
|
||||
|
||||
$(addsuffix -sv,$(srv_list)):
|
||||
$(addsuffix -sv,$(SRV_LIST)):
|
||||
docker-compose exec $(patsubst %-sv,%,$@) sh -c 'sv status $$SVDIR/*'
|
||||
|
||||
$(addsuffix -diff,$(srv_list)):
|
||||
$(addsuffix -diff,$(SRV_LIST)):
|
||||
docker container diff $(COMPOSE_PROJECT_NAME)_$(patsubst %-diff,%,$@)_1
|
||||
|
||||
wait_%:
|
||||
sleep $*
|
||||
|
||||
web:
|
||||
firefox localhost:8008 &
|
||||
web: app-web
|
||||
|
||||
auth-init: wait_11 auth-mod_conf auth-add_schema auth-add_data
|
||||
auth-init: wait_3 auth-mod_conf auth-add_schema auth-add_data
|
||||
|
||||
define LDIF_MOD_CONF
|
||||
export define LDIF_MOD_CONF
|
||||
dn: olcDatabase={-1}frontend,cn=config
|
||||
changetype: modify
|
||||
add: olcPasswordHash
|
||||
|
@ -94,66 +120,135 @@ changetype: modify
|
|||
add: olcDbIndex
|
||||
olcDbIndex: cn,ou,uid,mail eq
|
||||
endef
|
||||
export LDIF_MOD_CONF
|
||||
|
||||
define LDIF_ADD_DATA
|
||||
dn: ou=$(LDAP_USEROU),$(LDAP_BASE)
|
||||
changetype: add
|
||||
ou: $(LDAP_USEROU)
|
||||
objectClass: top
|
||||
export define LDIF_ADD_DATA
|
||||
dn: $(AD_BASE)
|
||||
objectClass: organization
|
||||
objectClass: dcObject
|
||||
dc: $(AD_DC)
|
||||
o: $(AD_DOM)
|
||||
|
||||
dn: ou=$(AD_USR_OU),$(AD_BASE)
|
||||
ou: $(AD_USR_OU)
|
||||
objectClass: organizationalUnit
|
||||
|
||||
dn: ou=$(LDAP_GROUPOU),$(LDAP_BASE)
|
||||
changetype: add
|
||||
ou: $(LDAP_GROUPOU)
|
||||
objectClass: top
|
||||
dn: ou=$(AD_GRP_OU),$(AD_BASE)
|
||||
ou: $(AD_GRP_OU)
|
||||
objectClass: organizationalUnit
|
||||
|
||||
dn: cn=$(LDAP_TEST_GROUP),ou=$(LDAP_GROUPOU),$(LDAP_BASE)
|
||||
changetype: add
|
||||
cn: $(LDAP_TEST_GROUP)
|
||||
objectClass: top
|
||||
dn: cn=$(AD_GRP_CN),ou=$(AD_GRP_OU),$(AD_BASE)
|
||||
cn: $(AD_GRP_CN)
|
||||
objectClass: groupOfNames
|
||||
objectClass: kopano-group
|
||||
member: uid=$(LDAP_TEST_USER),ou=$(LDAP_USEROU),$(LDAP_BASE)
|
||||
mail: $(LDAP_TEST_GROUP)@$(MAIL_DOMAIN)
|
||||
member: uid=$(AD_ADM_CN),ou=$(AD_USR_OU),$(AD_BASE)
|
||||
member: uid=$(AD_USR_CN),ou=$(AD_USR_OU),$(AD_BASE)
|
||||
mail: $(AD_GRP_CN)@$(MAIL_DOMAIN)
|
||||
|
||||
dn: uid=$(LDAP_TEST_USER),ou=$(LDAP_USEROU),$(LDAP_BASE)
|
||||
dn: uid=$(AD_ADM_CN),ou=$(AD_USR_OU),$(AD_BASE)
|
||||
changetype: add
|
||||
cn: $(LDAP_TEST_USER)
|
||||
objectClass: top
|
||||
cn: $(AD_ADM_CN)
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: kopano-user
|
||||
sn: $(LDAP_TEST_USER)
|
||||
uid: $(LDAP_TEST_USER)
|
||||
mail: $(LDAP_TEST_USER)@$(MAIL_DOMAIN)
|
||||
userPassword: $(LDAP_TEST_PASSWD)
|
||||
telephoneNumber: 0123 123456789
|
||||
title: MCP
|
||||
sn: $(AD_ADM_CN)
|
||||
uid: $(AD_ADM_CN)
|
||||
mail: $(AD_ADM_CN)@$(MAIL_DOMAIN)
|
||||
userPassword: $(AD_ADM_PW)
|
||||
telephoneNumber: $(AD_ADM_TEL)
|
||||
title: $(AD_ADM_TIT)
|
||||
kopanoAccount: 1
|
||||
kopanoAdmin: 1
|
||||
kopanoEnabledFeatures: imap
|
||||
kopanoEnabledFeatures: pop3
|
||||
|
||||
dn: uid=$(AD_USR_CN),ou=$(AD_USR_OU),$(AD_BASE)
|
||||
changetype: add
|
||||
cn: $(AD_USR_CN)
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: kopano-user
|
||||
sn: $(AD_USR_CN)
|
||||
uid: $(AD_USR_CN)
|
||||
mail: $(AD_USR_CN)@$(MAIL_DOMAIN)
|
||||
userPassword: $(AD_USR_PW)
|
||||
telephoneNumber: $(AD_USR_TEL)
|
||||
title: $(AD_USR_TIT)
|
||||
kopanoAccount: 1
|
||||
kopanoAliases: $(AD_USR_AS)@$(MAIL_DOMAIN)
|
||||
kopanoEnabledFeatures: imap
|
||||
kopanoEnabledFeatures: pop3
|
||||
|
||||
dn: uid=$(AD_SHR_CN),ou=$(AD_USR_OU),$(AD_BASE)
|
||||
cn: $(AD_SHR_CN)
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: kopano-user
|
||||
sn: $(AD_SHR_CN)
|
||||
uid: $(AD_SHR_CN)
|
||||
mail: $(AD_SHR_CN)@$(MAIL_DOMAIN)
|
||||
kopanoAccount: 1
|
||||
kopanoSharedStoreOnly: 1
|
||||
|
||||
dn: uid=$(AD_PUB_CN),ou=$(AD_USR_OU),$(AD_BASE)
|
||||
cn: $(AD_PUB_CN)
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: kopano-user
|
||||
sn: $(AD_PUB_CN)
|
||||
uid: $(AD_PUB_CN)
|
||||
mail: $(AD_PUB_CN)@$(MAIL_DOMAIN)
|
||||
kopanoAccount: 1
|
||||
kopanoHidden: 1
|
||||
kopanoSharedStoreOnly: 1
|
||||
kopanoResourceType: publicFolder:Public Stores/public
|
||||
endef
|
||||
export LDIF_ADD_DATA
|
||||
|
||||
define smtp_mail
|
||||
@printf "From: <$(2)>\nTo: <$(3)>\nDate: $$(date -R)\nSubject: $(4)\
|
||||
\n\nGreat news! You can receive email.\n" | tee /dev/tty \
|
||||
| $(CURL_CMD) $(1) -T - --mail-from $(2) --mail-rcpt $(3) $(CURL_OPT)
|
||||
endef
|
||||
|
||||
define lmtp_mail
|
||||
printf "LHLO mx\nMAIL FROM: <$(2)>\nRCPT TO: <$(3)>\nDATA\
|
||||
\nFrom: <$(2)>\nTo: <$(3)>\nDate: $$(date -R)\nSubject: $(4)\
|
||||
\n\nGreat news! You can receive email.\n.\nQUIT\n" | tee /dev/tty \
|
||||
| $(CURL_CMD) $(1) -T - $(CURL_OPT)
|
||||
endef
|
||||
|
||||
export define MAKE_UTILS_CONTAINER
|
||||
CURL_CMD ?= docker run -i --rm --network $(NET_NAME) curlimages/curl
|
||||
webb_cmd ?= docker run -d --rm --network $(NET_NAME) \
|
||||
-e DISPLAY=$$$$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix \
|
||||
-v /etc/localtime:/etc/localtime:ro -v $$$$(pwd)/ssl:/ssl \
|
||||
kennethkl/firefox $$(1)
|
||||
APP_FQDN ?= $(APP_NAME)
|
||||
AUT_FQDN ?= $(AUT_NAME)
|
||||
AUW_FQDN ?= $(AUW_NAME)
|
||||
DB_FQDN ?= $(DB_NAME)
|
||||
DBW_FQDN ?= $(DBW_NAME)
|
||||
MTA_FQDN ?= $(MTA_NAME)
|
||||
endef
|
||||
|
||||
utils-container:
|
||||
echo "$$MAKE_UTILS_CONTAINER" > utils-container.mk
|
||||
|
||||
utils-default:
|
||||
rm -f utils-container.mk
|
||||
|
||||
auth-mod_conf:
|
||||
echo "$$LDIF_MOD_CONF" | docker-compose exec -T auth ldap modify
|
||||
echo "$$LDIF_MOD_CONF" | docker-compose exec -T auth ldapmodify -Q
|
||||
|
||||
auth-add_data:
|
||||
echo "$$LDIF_ADD_DATA" | docker-compose exec -T auth ldap modify
|
||||
echo "$$LDIF_ADD_DATA" | docker-compose exec -T auth ldapadd -Q
|
||||
|
||||
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
|
||||
| docker-compose exec -T auth ldapadd -Q
|
||||
|
||||
auth-show_conf:
|
||||
docker-compose exec auth ldap search -b cn=config "(cn=config)"
|
||||
docker-compose exec auth ldap search -b cn=config olcDatabase={-1}frontend
|
||||
docker-compose exec auth ldap search -b cn=config olcDatabase={1}mdb
|
||||
docker-compose exec auth ldapsearch -QLLLb cn=config "(cn=config)"
|
||||
docker-compose exec auth ldapsearch -QLLLb cn=config olcDatabase={-1}frontend
|
||||
docker-compose exec auth ldapsearch -QLLLb cn=config olcDatabase={1}mdb
|
||||
|
||||
auth-show_data:
|
||||
docker-compose exec auth ldap search -b "$(LDAP_BASE)"
|
||||
docker-compose exec auth ldapsearch -QLLL
|
||||
|
||||
auth-show_cat0:
|
||||
docker-compose exec auth slapcat -n0
|
||||
|
@ -161,15 +256,17 @@ auth-show_cat0:
|
|||
auth-show_cat1:
|
||||
docker-compose exec auth slapcat -n1
|
||||
|
||||
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
|
||||
auth-web: auth-web-up
|
||||
sleep 2
|
||||
firefox localhost:8001 &
|
||||
$(call webb_cmd,http://$(AUW_FQDN))
|
||||
|
||||
auth-gui-down:
|
||||
docker stop auth-gui || true
|
||||
auth-web-up:
|
||||
docker run -d --name $(AUW_NAME) --network $(NET_NAME) \
|
||||
-e PHPLDAPADMIN_LDAP_HOSTS=auth -e PHPLDAPADMIN_HTTPS=false \
|
||||
osixia/phpldapadmin || true
|
||||
|
||||
auth-web-down:
|
||||
docker rm -f $(AUW_NAME) || true
|
||||
|
||||
mta-init:
|
||||
|
||||
|
@ -177,26 +274,26 @@ 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'
|
||||
|
||||
mta-test_smtp:
|
||||
printf "From: A tester <test@example.biz>\nTo: <$(LDAP_TEST_USER)@$(MAIL_DOMAIN)>\nDate: $$(date)\nSubject: A SMTP test message\n\nGreat news! You can receive email.\n" \
|
||||
| curl smtp://localhost -T - --mail-from test@example.biz \
|
||||
--mail-rcpt $(LDAP_TEST_USER)@$(MAIL_DOMAIN) $(curl_dbg)
|
||||
$(call smtp_mail,smtp://$(MTA_FQDN),$(MAIL_FROM),$(AD_USR_CN)@$(MAIL_DOMAIN),A SMTP test message.)
|
||||
|
||||
mta-test_regexp:
|
||||
$(call smtp_mail,smtp://$(MTA_FQDN),$(MAIL_FROM),$(AD_USR_CN)+info@$(MAIL_DOMAIN),A regexp SMTP test message.)
|
||||
|
||||
mta-test_smtps:
|
||||
printf "From: A tester <test@example.biz>\nTo: <$(LDAP_TEST_USER)@$(MAIL_DOMAIN)>\nDate: $$(date)\nSubject: A SMTPS test message\n\nGreat news! You can receive secure email.\n" \
|
||||
| curl smtps://localhost -T - --mail-from test@example.biz -k \
|
||||
-u $(LDAP_TEST_USER):$(LDAP_TEST_PASSWD) \
|
||||
--mail-rcpt $(LDAP_TEST_USER)@$(MAIL_DOMAIN) $(curl_dbg)
|
||||
$(call smtp_mail,smtps://$(MTA_FQDN),$(MAIL_FROM),$(AD_USR_CN)@$(MAIL_DOMAIN),A secure SMTPS test message.) \
|
||||
-k -u $(AD_USR_CN):$(AD_USR_PW)
|
||||
|
||||
mta-test_smtp2:
|
||||
printf "From: A info tester <test-info@example.biz>\nTo: <$(LDAP_TEST_USER)-info@$(MAIL_DOMAIN)>\nDate: $$(date)\nSubject: A SMTP test message \n\nGreat news! $(LDAP_TEST_USER)-info@$(MAIL_DOMAIN) can also receive email.\n" \
|
||||
| curl smtp://localhost -T - --mail-from test@example.biz \
|
||||
--mail-rcpt $(LDAP_TEST_USER)@$(MAIL_DOMAIN) $(curl_dbg)
|
||||
mta-test_shared: all-test_quiet
|
||||
$(call smtp_mail,smtp://$(MTA_FQDN),$(MAIL_FROM),$(AD_SHR_CN)@$(MAIL_DOMAIN),A shared SMTP test message.)
|
||||
|
||||
mta-test_public: all-test_quiet
|
||||
$(call smtp_mail,smtp://$(MTA_FQDN),$(MAIL_FROM),$(AD_PUB_CN)@$(MAIL_DOMAIN),A public SMTP test message.)
|
||||
|
||||
mta-razor:
|
||||
docker-compose exec mta run amavis_register_razor
|
||||
|
||||
mta-apk_list:
|
||||
docker-compose exec mta /bin/sh -c 'for pkg in $$(apk info 2>/dev/null); do printf "%9s %s\n" $$(apk info -s $$pkg 2>/dev/null | sed -n "2{p;q}") $$pkg; done | sort'
|
||||
docker-compose exec mta /bin/sh -c 'for pkg in $$(apk info 2>/dev/null); do printf "%9s %3s %s\n" $$(apk info -s $$pkg 2>/dev/null | sed -n "2{p;q}") $$pkg; done | sort | sort -k 2,2'
|
||||
|
||||
mta-quarantine_list:
|
||||
docker-compose exec mta amavis-ls
|
||||
|
@ -207,11 +304,11 @@ mta-freshclam_nodns:
|
|||
mta-clamdtop:
|
||||
docker-compose exec mta clamdtop
|
||||
|
||||
mta-debugtools:
|
||||
mta-tools:
|
||||
docker-compose exec mta apk --no-cache --update add \
|
||||
nano less lsof htop openldap-clients bind-tools iputils strace
|
||||
nano less lsof htop openldap-clients bind-tools iputils strace iproute2
|
||||
|
||||
mta-htop: mta-debugtools
|
||||
mta-htop: mta-tools
|
||||
docker-compose exec mta htop
|
||||
|
||||
mta-encrypt:
|
||||
|
@ -230,131 +327,112 @@ mta-show_mailq:
|
|||
mta-flush_mailq:
|
||||
docker-compose exec mta postqueue -f
|
||||
|
||||
mta-hostaddr:
|
||||
$(eval myhost := $(call _ip,$(COMPOSE_PROJECT_NAME)_mta_1))
|
||||
|
||||
mta-test_auth:
|
||||
docker-compose exec mta doveadm auth test $(LDAP_TEST_USER) $(LDAP_TEST_PASSWD)
|
||||
docker-compose exec mta doveadm auth test $(AD_USR_CN) $(AD_USR_PW)
|
||||
|
||||
mta-test_imap: mta-hostaddr
|
||||
curl imap://$(myhost) -X CAPABILITY
|
||||
curl imap://$(myhost) -u $(LDAP_TEST_USER):$(LDAP_TEST_PASSWD)
|
||||
mta-test_imaps:
|
||||
$(CURL_CMD) imaps://$(MTA_FQDN)//inbox -X "fetch 1 all" \
|
||||
--ssl --anyauth -k -u $(AD_USR_CN):$(AD_USR_PW) $(CURL_OPT)
|
||||
|
||||
mta-test_rimap:
|
||||
docker-compose exec mta curl imap://app -X CAPABILITY
|
||||
docker-compose exec mta curl imap://app -u $(LDAP_TEST_USER):$(LDAP_TEST_PASSWD)
|
||||
|
||||
mta-test_ldap: mta-debugtools
|
||||
docker-compose exec mta ldapsearch -H ldap://auth:389 -xLLL -s base namingContexts
|
||||
mta-man:
|
||||
docker-compose exec mta apk --no-cache --update add man-db man-pages \
|
||||
postfix-doc cyrus-sasl-doc dovecot-doc spamassassin-doc clamav-doc razor-doc
|
||||
|
||||
db-init:
|
||||
|
||||
db-test:
|
||||
docker-compose exec db mysqlshow -u $(MYSQL_USER) $(MYSQL_DATABASE) -p$(MYSQL_PASSWORD)
|
||||
|
||||
app-init: #wait_21 app-public_store
|
||||
db-web: db-web-up
|
||||
sleep 2
|
||||
$(call webb_cmd,http://$(DBW_FQDN))
|
||||
|
||||
app-debugtools:
|
||||
db-web-up:
|
||||
docker run -d --name $(DBW_NAME) --network $(NET_NAME) \
|
||||
-e PMA_HOST=db phpmyadmin/phpmyadmin || true
|
||||
|
||||
db-web-down:
|
||||
docker rm -f $(DBW_NAME) || true
|
||||
|
||||
app-init: app-public_store app-create_smime
|
||||
|
||||
app-tools:
|
||||
docker-compose exec app apt-get update
|
||||
docker-compose exec app apt-get install --yes \
|
||||
less nano ldap-utils htop net-tools lsof iputils-ping dnsutils strace
|
||||
|
||||
app-htop: app-debugtools
|
||||
app-htop: app-tools
|
||||
docker-compose exec app htop
|
||||
|
||||
app-man_server:
|
||||
docker-compose exec app man kopano-server.cfg
|
||||
|
||||
app-man_ldap:
|
||||
docker-compose exec app man kopano-ldap.cfg
|
||||
|
||||
app-hostaddr:
|
||||
$(eval myhost := $(call _ip,$(COMPOSE_PROJECT_NAME)_app_1))
|
||||
|
||||
app-test_smtp: mta-test_smtp
|
||||
|
||||
app-test_lmtp: app-hostaddr
|
||||
printf "LHLO mx\nMAIL FROM: <test@example.biz>\nRCPT TO: <$(LDAP_TEST_USER)@$(MAIL_DOMAIN)>\nDATA\nFrom: A tester <test@example.biz>\nTo: <$(LDAP_TEST_USER)@$(MAIL_DOMAIN)>\nDate: $$(date)\nSubject: A LMTP test message from me to you\n\nDelete me, please \n.\nQUIT\n" | nc -C $(myhost) 2003
|
||||
app-test_lmtp:
|
||||
$(call lmtp_mail,telnet://$(APP_FQDN):2003,$(MAIL_FROM),$(AD_USR_CN)@$(MAIL_DOMAIN),A LMTP test message.)
|
||||
|
||||
app-test_all: all-test_muted app-test_imap app-test_pop3 app-test_ical app-test_imaps app-test_pop3s app-test_icals
|
||||
app-test_all: all-test_muted $(addprefix app-test_,imap pop3 ical imaps pop3s icals)
|
||||
|
||||
app-test_imap: app-hostaddr
|
||||
curl imap://$(myhost) -u $(LDAP_TEST_USER):$(LDAP_TEST_PASSWD) $(curl_dbg)
|
||||
app-test_imap:
|
||||
$(CURL_CMD) imap://$(APP_FQDN) -u $(AD_USR_CN):$(AD_USR_PW) $(CURL_OPT)
|
||||
|
||||
app-test_imaps: app-hostaddr
|
||||
curl imaps://$(myhost) -k -u $(LDAP_TEST_USER):$(LDAP_TEST_PASSWD) $(curl_dbg)
|
||||
app-test_imaps:
|
||||
$(CURL_CMD) imaps://$(APP_FQDN) -k -u $(AD_USR_CN):$(AD_USR_PW) $(CURL_OPT)
|
||||
|
||||
app-test_pop3: app-hostaddr
|
||||
curl pop3://$(myhost) -u $(LDAP_TEST_USER):$(LDAP_TEST_PASSWD) $(curl_dbg)
|
||||
app-test_pop3:
|
||||
$(CURL_CMD) pop3://$(APP_FQDN) -u $(AD_USR_CN):$(AD_USR_PW) $(CURL_OPT)
|
||||
|
||||
app-test_pop3s: app-hostaddr
|
||||
curl pop3s://$(myhost) -k -u $(LDAP_TEST_USER):$(LDAP_TEST_PASSWD) $(curl_dbg)
|
||||
app-test_pop3s:
|
||||
$(CURL_CMD) pop3s://$(APP_FQDN) -k -u $(AD_USR_CN):$(AD_USR_PW) $(CURL_OPT)
|
||||
|
||||
app-test_ical: app-hostaddr
|
||||
curl http://$(myhost):8080 -u $(LDAP_TEST_USER):$(LDAP_TEST_PASSWD) $(curl_dbg)
|
||||
app-test_ical:
|
||||
$(CURL_CMD) http://$(APP_FQDN):8080 -u $(AD_USR_CN):$(AD_USR_PW) $(CURL_OPT)
|
||||
|
||||
app-test_icals: app-hostaddr
|
||||
curl https://$(myhost):8443 -k -u $(LDAP_TEST_USER):$(LDAP_TEST_PASSWD) $(curl_dbg)
|
||||
app-test_icals:
|
||||
$(CURL_CMD) https://$(APP_FQDN):8443 -k -u $(AD_USR_CN):$(AD_USR_PW) $(CURL_OPT)
|
||||
|
||||
app-test_tls: app-hostaddr
|
||||
docker run --rm -it --network demo_backend drwetter/testssl.sh app:993 || true
|
||||
app-test_tls:
|
||||
$(TSSL_CMD) $(APP_FQDN):993 || true
|
||||
|
||||
app-web:
|
||||
$(call webb_cmd,http://$(APP_FQDN))
|
||||
|
||||
app-test_oof1:
|
||||
docker-compose exec app kopano-oof -u $(AD_USR_CN) -m 1 -t "Dunno when I return"
|
||||
|
||||
app-test_oof0:
|
||||
docker-compose exec app kopano-oof -u $(AD_USR_CN) -m 0
|
||||
|
||||
app-show_user1:
|
||||
docker-compose exec app kopano-admin --details $(LDAP_TEST_USER)
|
||||
docker-compose exec app kopano-admin --details $(AD_USR_CN)
|
||||
|
||||
app-show_user2: app-debugtools
|
||||
docker-compose exec app ldapsearch -H ldap://auth:389 -xLLL -b $(LDAP_BASE) '*'
|
||||
app-show_user2: app-tools
|
||||
docker-compose exec app ldapsearch -H ldap://auth:389 -xLLL -b $(AD_BASE) '*'
|
||||
|
||||
app-show_sync:
|
||||
docker-compose exec app z-push-top
|
||||
|
||||
app-create_store:
|
||||
docker-compose exec app kopano-admin --create-store $(LDAP_TEST_USER)
|
||||
docker-compose exec app kopano-admin --create-store $(AD_USR_CN)
|
||||
|
||||
app-public_store:
|
||||
docker-compose exec app kopano-storeadm -h default: -P
|
||||
docker-compose exec app kopano-storeadm -P
|
||||
|
||||
#app-add_user:
|
||||
# docker-compose exec app kopano-admin -c $(AD_USR_CN) -p $(AD_USR_PW) \
|
||||
# -e $(AD_USR_CN)@$(MAIL_DOMAIN) -f $(AD_USR_CN) -a 1
|
||||
|
||||
$(addprefix app-parms_,archiver dagent gateway ical ldap search server spamd spooler):
|
||||
docker-compose exec app run list_parms $(patsubst app-parms_%,%,$@)
|
||||
|
||||
app-create_smime: all-create_smime ssl/ca.crt
|
||||
docker cp ssl/ca.crt $$(docker-compose ps -q app):/usr/local/share/ca-certificates/$(MAIL_DOMAIN)_CA.crt
|
||||
app-create_smime: all-create_smime
|
||||
docker cp ssl/ca.crt $(call dkr_srv_cnt,app):/usr/local/share/ca-certificates/$(MAIL_DOMAIN)_CA.crt
|
||||
docker-compose exec app update-ca-certificates
|
||||
|
||||
all-test_quiet:
|
||||
$(eval curl_dbg := -s -S )
|
||||
$(eval CURL_OPT := -s -S )
|
||||
|
||||
all-test_muted:
|
||||
$(eval curl_dbg := -s -S >/dev/null || true)
|
||||
$(eval CURL_OPT := -s -S >/dev/null || true)
|
||||
|
||||
all-create_smime: ssl/$(LDAP_TEST_USER).smime.p12
|
||||
all-create_smime: ssl/$(AD_USR_CN).p12
|
||||
|
||||
all-destroy_smime:
|
||||
rm -rf ssl
|
||||
|
||||
#.PRECIOUS: %.key %.crt %.smime.crt
|
||||
|
||||
%.p12: %.crt
|
||||
openssl pkcs12 -export -in $< -inkey $*.key -out $@ \
|
||||
-passout pass:$(LDAP_TEST_PASSWD)
|
||||
|
||||
%.csr: %.key
|
||||
openssl req -new -key $< -out $@ \
|
||||
-subj "/O=$(MAIL_DOMAIN)/CN=$(LDAP_TEST_USER)/emailAddress=$(LDAP_TEST_USER)@$(MAIL_DOMAIN)"
|
||||
|
||||
%.smime.crt: %.smime.csr ssl/ca.crt
|
||||
openssl x509 -req -in $< -CA $(@D)/ca.crt -CAkey $(@D)/ca.key -out $@ \
|
||||
-setalias "Self Signed SMIME" -addtrust emailProtection \
|
||||
-addreject clientAuth -addreject serverAuth -trustout \
|
||||
-CAcreateserial
|
||||
|
||||
%.crt: %.key
|
||||
openssl req -x509 -batch -key $< -out $@ \
|
||||
-subj "/O=$(MAIL_DOMAIN)"
|
||||
|
||||
%.key: ssl
|
||||
openssl genrsa -out $@
|
||||
chmod a+r $@
|
||||
|
||||
ssl:
|
||||
mkdir -p $@
|
||||
all-destroy_smime: ssl-destroy
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../test/ad.mk
|
|
@ -0,0 +1 @@
|
|||
../test/dkr.mk
|
|
@ -5,28 +5,29 @@ services:
|
|||
image: mlan/kopano
|
||||
networks:
|
||||
- backend
|
||||
ports:
|
||||
- "127.0.0.1:8008:80" # WebApp & EAS (alt. HTTP)
|
||||
- "127.0.0.1:143:143" # IMAP (not needed if all devices can use EAS)
|
||||
- "127.0.0.1:110:110" # POP3 (not needed if all devices can use EAS)
|
||||
- "127.0.0.1:8080:8080" # ICAL (not needed if all devices can use EAS)
|
||||
- "127.0.0.1:993:993" # IMAPS (not needed if all devices can use EAS)
|
||||
- "127.0.0.1:995:995" # POP3S (not needed if all devices can use EAS)
|
||||
- "127.0.0.1:8443:8443" # ICALS (not needed if all devices can use EAS)
|
||||
# ports: # Uncomment to expose ports to host interfaces
|
||||
# - "80:80" # WebApp & EAS (alt. HTTP)
|
||||
# - "143:143" # IMAP (not needed if all devices can use EAS)
|
||||
# - "110:110" # POP3 (not needed if all devices can use EAS)
|
||||
# - "8080:8080" # ICAL (not needed if all devices can use EAS)
|
||||
# - "993:993" # IMAPS (not needed if all devices can use EAS)
|
||||
# - "995:995" # POP3S (not needed if all devices can use EAS)
|
||||
# - "8443:8443" # ICALS (not needed if all devices can use EAS)
|
||||
depends_on:
|
||||
- auth
|
||||
- db
|
||||
- mta
|
||||
environment: # Virgin config, ignored on restarts unless FORCE_CONFIG given.
|
||||
- USER_PLUGIN=ldap
|
||||
- LDAP_URI=ldap://auth:389/
|
||||
- LDAP_URI=ldap://auth/
|
||||
- MYSQL_HOST=db
|
||||
- SMTP_SERVER=mta
|
||||
- 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_SEARCH_BASE=${AD_BASE-dc=example,dc=com}
|
||||
- LDAP_USER_TYPE_ATTRIBUTE_VALUE=${AD_USR_OB-kopano-user}
|
||||
- LDAP_GROUP_TYPE_ATTRIBUTE_VALUE=${AD_GRP_OB-kopano-group}
|
||||
- LDAP_GROUPMEMBERS_ATTRIBUTE_TYPE=dn
|
||||
- LDAP_PROPMAP=
|
||||
- DAGENT_PLUGINS=movetopublicldap
|
||||
- MYSQL_DATABASE=${MYSQL_DATABASE-kopano}
|
||||
- MYSQL_USER=${MYSQL_USER-kopano}
|
||||
- MYSQL_PASSWORD=${MYSQL_PASSWORD-secret}
|
||||
|
@ -53,9 +54,9 @@ services:
|
|||
hostname: ${MAIL_SRV-mx}.${MAIL_DOMAIN-example.com}
|
||||
networks:
|
||||
- backend
|
||||
ports:
|
||||
- "127.0.0.1:25:25" # SMTP
|
||||
- "127.0.0.1:465:465" # SMTPS authentication required
|
||||
# ports: # # Uncomment to expose ports to host interfaces
|
||||
# - "25:25" # SMTP
|
||||
# - "465:465" # SMTPS authentication required
|
||||
depends_on:
|
||||
- auth
|
||||
environment: # Virgin config, ignored on restarts unless FORCE_CONFIG given.
|
||||
|
@ -66,8 +67,8 @@ services:
|
|||
- SMTP_TLS_SECURITY_LEVEL=${SMTP_TLS_SECURITY_LEVEL-}
|
||||
- SMTP_TLS_WRAPPERMODE=${SMTP_TLS_WRAPPERMODE-no}
|
||||
- SMTPD_USE_TLS=yes
|
||||
- LDAP_USER_BASE=ou=${LDAP_USEROU-users},${LDAP_BASE-dc=example,dc=com}
|
||||
- LDAP_QUERY_FILTER_USER=(&(objectclass=${LDAP_USEROBJ-posixAccount})(mail=%s))
|
||||
- LDAP_USER_BASE=ou=${AD_USR_OU-users},${AD_BASE-dc=example,dc=com}
|
||||
- LDAP_QUERY_FILTER_USER=(&(objectclass=${AD_USR_OB-kopano-user})(mail=%s))
|
||||
- LDAP_QUERY_ATTRS_PASS=uid=user
|
||||
- REGEX_ALIAS=${REGEX_ALIAS-}
|
||||
- DKIM_SELECTOR=${DKIM_SELECTOR-default}
|
||||
|
@ -102,8 +103,10 @@ services:
|
|||
image: mlan/openldap
|
||||
networks:
|
||||
- backend
|
||||
command: --root-cn ${AD_ROOT_CN-admin} --root-pw ${AD_ROOT_PW-secret}
|
||||
environment:
|
||||
- LDAP_LOGLEVEL=parse
|
||||
- LDAPBASE=${AD_BASE-dc=example,dc=com}
|
||||
- LDAPDEBUG=${AD_DEBUG-parse}
|
||||
volumes:
|
||||
- auth:/srv
|
||||
- /etc/localtime:/etc/localtime:ro # Use host timezone
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../test/ssl.mk
|
|
@ -7,7 +7,7 @@
|
|||
#
|
||||
|
||||
#
|
||||
# Setup ACME monitor and arrange certificate sym-links
|
||||
# Setup ACME monitor and arrange certificate symbolic links
|
||||
#
|
||||
acme_monitor_tls_cert
|
||||
acme_symlink_tls_cert
|
||||
|
|
|
@ -8,8 +8,8 @@ _webroot="https://download.kopano.io/community"
|
|||
_debroot="http://repo.z-hub.io/z-push:"
|
||||
_component="core"
|
||||
_stage="final"
|
||||
_dist="debian"
|
||||
_rel="9"
|
||||
_dist="ubuntu"
|
||||
_rel="20.04"
|
||||
_arch="amd64"
|
||||
|
||||
#
|
||||
|
|
|
@ -30,12 +30,13 @@ webapp_smime_cfg_file=$DOCKER_CONF_DIR1/webapp/config-smime.php
|
|||
# CLI commands
|
||||
#
|
||||
list_parms() {
|
||||
local services="$DOCKER_LDAP_SERVICES"
|
||||
local services="$DOCKER_LDAP_SERVICES $DAGENT_PLUGINS"
|
||||
[ $# -ge 0 ] && services="$@"
|
||||
for service in $services; do
|
||||
[ $# -ne 1 ] && echo "# $service"
|
||||
local man_file=$(kopano_gen_filename_man $service)
|
||||
kopano_get_envvars_man $man_file
|
||||
kopano_get_envvars_ext $service
|
||||
[ $# -ne 1 ] && echo
|
||||
done
|
||||
}
|
||||
|
@ -48,7 +49,8 @@ list_parms() {
|
|||
# With kopano-webapp and z-push use installed config file to find valid keys.
|
||||
#
|
||||
kopano_apply_envvars_core() {
|
||||
for service in $DOCKER_LDAP_SERVICES; do
|
||||
kopano_enable_envvars_plugin
|
||||
for service in $DOCKER_LDAP_SERVICES $DAGENT_PLUGINS; do
|
||||
kopano_apply_envvars_cfg $service
|
||||
done
|
||||
}
|
||||
|
@ -71,17 +73,18 @@ kopano_apply_envvars_cfg() {
|
|||
local service=$1
|
||||
local cfg_file=$(kopano_gen_filename_cfg $service)
|
||||
local man_file=$(kopano_gen_filename_man $service)
|
||||
local env_vars="$(kopano_get_envvars_ext $service)"
|
||||
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.bak
|
||||
fi
|
||||
for env_var in $env_vars; do
|
||||
kopano_set_envvars_cfg $service $env_var
|
||||
done
|
||||
env_vars="$(kopano_get_envvars_man $man_file) $env_vars"
|
||||
else
|
||||
dc_log 4 "Could not find $man_file"
|
||||
dc_log 6 "Could not find $man_file"
|
||||
fi
|
||||
if [ -e $cfg_file ]; then
|
||||
mv -f $cfg_file $cfg_file.bak
|
||||
fi
|
||||
for env_var in $env_vars; do
|
||||
kopano_set_envvars_cfg $service $env_var
|
||||
done
|
||||
}
|
||||
|
||||
kopano_apply_envvars_php() {
|
||||
|
@ -118,6 +121,42 @@ kopano_set_envvars_cfg() {
|
|||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# External parameters
|
||||
# services without a man file or additional parameters are listed here.
|
||||
#
|
||||
kopano_get_envvars_ext() {
|
||||
local service=$1
|
||||
local vars
|
||||
case $service in
|
||||
movetopublicldap)
|
||||
vars="ldap_public_folder_attribute ldap_public_folder_attribute_token"
|
||||
;;
|
||||
esac
|
||||
echo $vars | tr "[:lower:]" "[:upper:]" | tr " " "\n" | sort -u
|
||||
}
|
||||
|
||||
#
|
||||
# Python plugins
|
||||
#
|
||||
kopano_enable_envvars_plugin() {
|
||||
for service in dagent spooler; do
|
||||
local envvar=${service^^}_PLUGINS
|
||||
for plugin in ${!envvar}; do
|
||||
local pyfile=$(find /usr/share/kopano* -name ${plugin}.py)
|
||||
if [ -z "$pyfile" ]; then
|
||||
dc_log 5 "Unable to locate ${service} python plugin ${plugin}"
|
||||
else
|
||||
dc_log 5 "Enabling ${service} python plugin ${plugin}"
|
||||
mkdir -p $DOCKER_CONF_DIR1/${service}
|
||||
ln -sf ${pyfile} $DOCKER_CONF_DIR1/${service}
|
||||
export ${service^^}_PLUGIN_PATH=$DOCKER_CONF_DIR1/${service}
|
||||
export ${service^^}_PLUGIN_ENABLED=yes
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
#
|
||||
# Helpers
|
||||
#
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
# movetopublicldap.cfg
|
||||
#
|
||||
# Move mail to public ldap configuration
|
||||
#
|
||||
# This configuration file is used by the dagent plugin 'movetopublicldap.py'
|
||||
# The default location is '/etc/kopano/movetopublicldap.cfg'
|
||||
#
|
||||
|
||||
#
|
||||
# The plugin reads two coniguration files.
|
||||
# First it looks for parameters in '/etc/kopano/ldap.cfg' allowing
|
||||
# common conifuration parameters to be kept in on place.
|
||||
# Second it reads '/etc/kopano/movetopublicldap.cfg'
|
||||
#
|
||||
|
||||
#
|
||||
# Parameters that can be set in both files:
|
||||
#
|
||||
# ldap_uri =
|
||||
# ldap_starttls = no
|
||||
# ldap_search_base =
|
||||
# ldap_bind_user =
|
||||
# ldap_bind_passwd =
|
||||
# ldap_user_unique_attribute = uid
|
||||
#
|
||||
|
||||
#
|
||||
# Parameters that can only be set in ovetopublicldap.cfg:
|
||||
#
|
||||
# ldap_public_folder_attribute = kopanoResourceType
|
||||
# ldap_public_folder_attribute_token = publicFolder
|
||||
#
|
|
@ -0,0 +1,229 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
""" (c) 2019 Kopano
|
||||
|
||||
movetopublicldap.py
|
||||
|
||||
This is an LDAP lookup extension to the move to public plugin movetopublic.py.
|
||||
The changes to the original work are highlihgted below.
|
||||
|
||||
The move to public plugin moves incoming messages to a folder in the public
|
||||
store. If folders are missing they will be created.
|
||||
|
||||
A LDAP entry including:
|
||||
|
||||
kopanoAccount: 1
|
||||
kopanoResourceType: publicFolder:<public folder>
|
||||
|
||||
will have its email delivered to the public store in <public folder>.
|
||||
The token match is case sensitive and there must be a colon ':' separating
|
||||
the token and the public folder name. The folder name can contain space and
|
||||
sub folders, which are distinguished using forward slash '/'.
|
||||
So if we have 'kopanoResourceType: publicFolder:Public Stores/public' emails will
|
||||
be delivered to 'Public Folders/Public Stores/public'.
|
||||
|
||||
The parameters in /etc/kopano/ldap.cfg will be used for the LDAP query.
|
||||
The LDAP attribute holding the token and the token itself have the following
|
||||
default values, which can be modified in /etc/kopano/movetopublicldap.cfg
|
||||
if desired.
|
||||
|
||||
ldap_public_folder_attribute = kopanoResourceType
|
||||
ldap_public_folder_attribute_token = publicFolder
|
||||
|
||||
"""
|
||||
from sys import hexversion
|
||||
from MAPI.Util import GetPublicStore
|
||||
from MAPI.Struct import NEWMAIL_NOTIFICATION
|
||||
from MAPI import MAPI_UNICODE, MAPI_MODIFY, OPEN_IF_EXISTS, MDB_WRITE
|
||||
from MAPI.Tags import (PR_RECEIVED_BY_EMAIL_ADDRESS_W, PR_EC_COMPANY_NAME_W,
|
||||
PR_IPM_PUBLIC_FOLDERS_ENTRYID, PR_ENTRYID, PR_MAILBOX_OWNER_ENTRYID,
|
||||
IID_IMessage, IID_IExchangeManageStore)
|
||||
from plugintemplates import IMapiDAgentPlugin, MP_CONTINUE, MP_STOP_SUCCESS
|
||||
from zconfig import ZConfigParser
|
||||
import configparser
|
||||
import ldap
|
||||
import os.path
|
||||
|
||||
class KConfigParser(ZConfigParser):
|
||||
""" Extends zconfig.ZConfigParser to also allow !directive in cfg files.
|
||||
Change type addition. """
|
||||
def __init__(self, configfile, defaultconfig={}):
|
||||
self.config = configparser.ConfigParser(defaults=defaultconfig,
|
||||
delimiters=('='), comment_prefixes=('#', '!'), allow_no_value=True)
|
||||
self.readZConfig(configfile)
|
||||
|
||||
class MoveToPublic(IMapiDAgentPlugin):
|
||||
|
||||
prioPreDelivery = 50
|
||||
|
||||
config = {}
|
||||
|
||||
CONFIGFILES = ['/etc/kopano/ldap.cfg', '/etc/kopano/movetopublicldap.cfg']
|
||||
|
||||
DEFAULTCONFIG = {
|
||||
'ldap_uri': None,
|
||||
'ldap_starttls': "no",
|
||||
'ldap_search_base': None,
|
||||
'ldap_bind_user': None,
|
||||
'ldap_bind_passwd': None,
|
||||
'ldap_user_unique_attribute': "uid",
|
||||
'ldap_public_folder_attribute': "kopanoResourceType",
|
||||
'ldap_public_folder_attribute_token': "publicFolder"
|
||||
}
|
||||
|
||||
def __init__(self, logger):
|
||||
IMapiDAgentPlugin.__init__(self, logger)
|
||||
self.readconfig(self.CONFIGFILES, self.DEFAULTCONFIG)
|
||||
|
||||
def readconfig(self, configfiles=CONFIGFILES, defaultconfig=DEFAULTCONFIG):
|
||||
""" Reads ldap.cfg and movetopublicldap.cfg into self.config.
|
||||
Change type addition. """
|
||||
options = [opt.split('_', 1)[1] for opt in defaultconfig.keys()]
|
||||
config = None
|
||||
for configfile in configfiles:
|
||||
if os.path.isfile(configfile):
|
||||
self.logger.logDebug("*--- Reading file {}".format(configfile))
|
||||
if not config:
|
||||
config = KConfigParser(configfile, defaultconfig)
|
||||
else:
|
||||
config = KConfigParser(configfile, config.options())
|
||||
else:
|
||||
self.logger.logDebug("*--- Can't find file {}".format(configfile))
|
||||
self.config = config.getdict('ldap',options)
|
||||
self.logger.logDebug("*--- Config list {}".format(self.config))
|
||||
return self.config
|
||||
|
||||
def searchfilter(self, recipient):
|
||||
""" (&(uid=recipient)(kopanoResourceType=publicFolder:*)).
|
||||
Change type addition. """
|
||||
return ("(&({}={})({}={}:*))"
|
||||
.format(self.config['user_unique_attribute'],
|
||||
recipient,
|
||||
self.config['public_folder_attribute'],
|
||||
self.config['public_folder_attribute_token']))
|
||||
|
||||
def searchquery(self, recipient):
|
||||
""" Query a LDAP/AD driectory server to lookup recipient using
|
||||
search_base and return public_folder_attribute.
|
||||
Change type addition. """
|
||||
if not self.config or not self.config['uri']:
|
||||
self.logger.logError(("!--- ldap_uri is not defined."
|
||||
" Please check {}" .format(self.CONFIGFILES)))
|
||||
return None
|
||||
else:
|
||||
l = ldap.initialize(self.config['uri'])
|
||||
try:
|
||||
l.protocol_version = ldap.VERSION3
|
||||
if self.config['starttls'] == 'yes':
|
||||
l.start_tls_s()
|
||||
l.simple_bind_s(self.config['bind_user'] or u'', \
|
||||
self.config['bind_passwd'] or u'')
|
||||
except ldap.SERVER_DOWN as e:
|
||||
self.logger.logError(("!--- LDAP server is not reachable {}"
|
||||
.format(e)))
|
||||
return None
|
||||
except ldap.INVALID_CREDENTIALS as e:
|
||||
self.logger.logError(("!--- Invalid LDAP credentials {}"
|
||||
" Please check {}" .format(e, self.CONFIGFILES)))
|
||||
l.unbind_s()
|
||||
return None
|
||||
except ldap.LDAPError as e:
|
||||
self.logger.logError("!--- LDAPError {}".format(e))
|
||||
l.unbind_s()
|
||||
return None
|
||||
try:
|
||||
result = l.search_s(self.config['search_base'], \
|
||||
ldap.SCOPE_SUBTREE, self.searchfilter(recipient), \
|
||||
[self.config['public_folder_attribute']])
|
||||
except ldap.LDAPError as e:
|
||||
self.logger.logError("!--- LDAPError {}".format(e))
|
||||
l.unbind_s()
|
||||
return result
|
||||
|
||||
def publicfolder(self, recipient):
|
||||
""" Check for ldap_public_folder_attribute_token and return folder.
|
||||
Change type addition. """
|
||||
destination_folder = []
|
||||
result = self.searchquery(recipient)
|
||||
if result:
|
||||
tokenandfolder = (result[0][1]
|
||||
.get(self.config['public_folder_attribute'])[0].decode('utf-8'))
|
||||
if tokenandfolder:
|
||||
destination_folder = tokenandfolder.split(':')[1]
|
||||
if destination_folder:
|
||||
self.logger.logDebug(("*--- Found public folder {}"
|
||||
"for recipient {}".format(
|
||||
destination_folder.encode('utf-8'),
|
||||
recipient.encode('utf-8'))))
|
||||
return destination_folder
|
||||
|
||||
def PreDelivery(self, session, addrbook, store, folder, message):
|
||||
""" Original code from movetopublic.py with call to self.publicfolder().
|
||||
Change type modification. """
|
||||
|
||||
props = message.GetProps([PR_RECEIVED_BY_EMAIL_ADDRESS_W], 0)
|
||||
if props[0].ulPropTag != PR_RECEIVED_BY_EMAIL_ADDRESS_W:
|
||||
self.logger.logError("!--- Not received by emailaddress")
|
||||
return MP_CONTINUE,
|
||||
|
||||
recipient = props[0].Value.lower()
|
||||
if not recipient:
|
||||
self.logger.logError("!--- No recipient in props {}".format(props))
|
||||
return MP_CONTINUE,
|
||||
|
||||
recipfolder = self.publicfolder(recipient)
|
||||
if not recipfolder:
|
||||
self.logger.logDebug(("*--- No public folder for recipient {}"
|
||||
.format(recipient.encode('utf-8'))))
|
||||
return MP_CONTINUE,
|
||||
|
||||
publicstore = GetPublicStore(session)
|
||||
if not publicstore:
|
||||
storeprops = store.GetProps([PR_MAILBOX_OWNER_ENTRYID], 0)
|
||||
if storeprops[0].ulPropTag == PR_MAILBOX_OWNER_ENTRYID:
|
||||
user = addrbook.OpenEntry(storeprops[0].Value, None, 0)
|
||||
userprops = user.GetProps([PR_EC_COMPANY_NAME_W], 0)
|
||||
if userprops[0].ulPropTag == PR_EC_COMPANY_NAME_W:
|
||||
companyname = userprops[0].Value
|
||||
else:
|
||||
companyname = None
|
||||
|
||||
if not companyname:
|
||||
self.logger.logError(("!--- Cannot open a public store."
|
||||
' Use "kopano-storeadm -P"'
|
||||
" to create one if it is missing."))
|
||||
return MP_CONTINUE,
|
||||
|
||||
ema = store.QueryInterface(IID_IExchangeManageStore)
|
||||
publicstoreid = ema.CreateStoreEntryID(None, companyname, MAPI_UNICODE)
|
||||
publicstore = session.OpenMsgStore(0, publicstoreid, None, MDB_WRITE)
|
||||
|
||||
publicfolders = publicstore.OpenEntry(
|
||||
publicstore.GetProps([PR_IPM_PUBLIC_FOLDERS_ENTRYID], 0)[0].Value,
|
||||
None, MAPI_MODIFY)
|
||||
|
||||
folderlist = recipfolder.split('/')
|
||||
folder = publicfolders
|
||||
for foldername in folderlist:
|
||||
if len(foldername) > 0:
|
||||
if hexversion >= 0x03000000:
|
||||
folder = folder.CreateFolder(0, foldername,
|
||||
"Create by Move to Public plugin", None,
|
||||
OPEN_IF_EXISTS | MAPI_UNICODE)
|
||||
else:
|
||||
folder = folder.CreateFolder(0, foldername,
|
||||
"Create by Move to Public plugin", None, OPEN_IF_EXISTS)
|
||||
|
||||
msgnew = folder.CreateMessage(None, 0)
|
||||
tags = message.GetPropList(MAPI_UNICODE)
|
||||
message.CopyProps(tags, 0, None, IID_IMessage, msgnew, 0)
|
||||
|
||||
msgnew.SaveChanges(0)
|
||||
folderid = folder.GetProps([PR_ENTRYID], 0)[0].Value
|
||||
msgid = msgnew.GetProps([PR_ENTRYID], 0)[0].Value
|
||||
|
||||
publicstore.NotifyNewMail(NEWMAIL_NOTIFICATION(msgid, folderid, 0, None, 0))
|
||||
|
||||
self.logger.logInfo(("*--- Message moved to public folder {}"
|
||||
.format(recipfolder)))
|
||||
|
||||
return MP_STOP_SUCCESS,
|
|
@ -0,0 +1,97 @@
|
|||
"""
|
||||
This code will query a LDAP/AD driectory server and updated the cgf file
|
||||
/etc/kopano/movetopublic.cfg
|
||||
/usr/share/kopano-dagent/python/plugins/movetopublic.cfg
|
||||
|
||||
export PYTHONPATH=/usr/share/kopano-dagent/python
|
||||
|
||||
"""
|
||||
from zconfig import ZConfigParser
|
||||
import configparser
|
||||
import ldap
|
||||
|
||||
class KConfigParser(ZConfigParser):
|
||||
""" allow !directive in cfg files """
|
||||
def __init__(self, configfile, defaultconfig={}):
|
||||
self.config = configparser.ConfigParser(defaults=defaultconfig, \
|
||||
delimiters=('='), comment_prefixes=('#', '!'))
|
||||
self.readZConfig(configfile)
|
||||
|
||||
class ldapstores():
|
||||
defaultconfig = {
|
||||
'ldap_uri': None,
|
||||
'ldap_search_base': None,
|
||||
'ldap_bind_user': None,
|
||||
'ldap_bind_passwd': None,
|
||||
'ldap_user_unique_attribute': "uid",
|
||||
'ldap_user_search_filter': "(kopanoAccount=1)",
|
||||
# 'ldap_user_search_filter': "(&(kopanoAccount=1)(kopanoResourceType=publicFolder:*))",
|
||||
'ldap_public_folder_attribute': "kopanoResourceType",
|
||||
'ldap_public_folder_attribute_token': "publicFolder"
|
||||
}
|
||||
|
||||
def __init__(self, configfile = '/etc/kopano/ldap.cfg'):
|
||||
self.readconfig(configfile)
|
||||
|
||||
def readconfig(self, configfile):
|
||||
config = KConfigParser(configfile, self.defaultconfig)
|
||||
options = [opt.split('_', 1)[1] for opt in self.defaultconfig.keys()]
|
||||
self.config = config.getdict('ldap',options)
|
||||
return self.config
|
||||
|
||||
def searchquery(self):
|
||||
if (self.config['uri'] is None):
|
||||
print ("ldap_uri is None")
|
||||
sys.exit(0)
|
||||
else:
|
||||
l = ldap.initialize(self.config['uri'])
|
||||
try:
|
||||
l.protocol_version = ldap.VERSION3
|
||||
l.simple_bind_s(self.config['bind_user'] or u'', \
|
||||
self.config['bind_passwd'] or u'')
|
||||
except ldap.INVALID_CREDENTIALS:
|
||||
sys.exit(0)
|
||||
except ldap.LDAPError as e:
|
||||
print (e)
|
||||
sys.exit(0)
|
||||
try:
|
||||
ldap_result_id = l.search(self.config['search_base'], \
|
||||
ldap.SCOPE_SUBTREE, self.config['user_search_filter'], \
|
||||
[self.config['user_unique_attribute'], \
|
||||
self.config['public_folder_attribute']])
|
||||
results = []
|
||||
while 1:
|
||||
result_type, result_data = l.result(ldap_result_id, 0)
|
||||
if (result_data == []):
|
||||
break
|
||||
else:
|
||||
if result_type == ldap.RES_SEARCH_ENTRY:
|
||||
results.append(result_data[0])
|
||||
except ldap.LDAPError as e:
|
||||
print (e)
|
||||
l.unbind_s()
|
||||
return results
|
||||
|
||||
def findpublic(self):
|
||||
stores = self.searchquery()
|
||||
public = []
|
||||
for store in stores:
|
||||
recipient = store[1].get(self.config['user_unique_attribute'])
|
||||
tokenandfolder = store[1].get(self.config['ldap_public_folder_attribute'])
|
||||
if tokenandfolder:
|
||||
token = tokenandfolder.split(':')[0]
|
||||
destination_folder = tokenandfolder.split(':')[1]
|
||||
if (token == self.config['ldap_public_folder_attribute_token']);
|
||||
public[recipient] = destination_folder
|
||||
return public
|
||||
|
||||
def printpublic(self, outputfile = '/etc/kopano/movetopublic.cfg'):
|
||||
public = self.findpublic()
|
||||
i = 1
|
||||
for recipient in public.keys():
|
||||
print ("rule%d_recipient = %s", i, recipient)
|
||||
print ("rule%d_destination_folder = %s", i, public[recipient])
|
||||
i += 1
|
||||
|
||||
s = ldapstores()
|
||||
s.printpublic()
|
|
@ -1,3 +0,0 @@
|
|||
local.*
|
||||
ssl
|
||||
acme
|
176
test/Makefile
176
test/Makefile
|
@ -1,37 +1,26 @@
|
|||
# Makefile
|
||||
#
|
||||
# test
|
||||
#
|
||||
|
||||
-include *.mk
|
||||
|
||||
IMG_REPO ?= mlan/kopano
|
||||
IMG_VER ?= latest
|
||||
_ver = $(if $(findstring latest,$(1)),$(2),$(1)-$(2))
|
||||
_ip = $(shell docker inspect -f \
|
||||
'{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' \
|
||||
$(1) | head -n1)
|
||||
|
||||
TST_NAME ?= test
|
||||
SSL_KEYF ?= priv_key.pem
|
||||
SSL_CRTF ?= cert.pem
|
||||
SSL_CRTD ?= 30
|
||||
SSL_ACMF ?= acme.json
|
||||
TST_SSLD ?= ssl
|
||||
TST_ACMD ?= acme
|
||||
TST_KEY ?= $(TST_SSLD)/$(SSL_KEYF)
|
||||
TST_CERT ?= $(TST_SSLD)/$(SSL_CRTF)
|
||||
TST_ACME ?= $(TST_ACMD)/$(SSL_ACMF)
|
||||
TST_REPO ?= mlan/kopano
|
||||
TST_VER ?= latest
|
||||
_ver = $(if $(findstring latest,$(1)),$(2),$(2)-$(1))
|
||||
|
||||
NET_NAME ?= test-net
|
||||
NET_ENV ?= --network $(NET_NAME)
|
||||
|
||||
LDAP_BAS ?= dc=example,dc=com
|
||||
LDAP_UOU ?= users
|
||||
LDAP_UOB ?= posixAccount
|
||||
LDAP_GOU ?= groups
|
||||
LDAP_FOU ?= "(&(objectclass=$(LDAP_UOB))(mail=%s))"
|
||||
LDAP_FPW ?= "(&(objectclass=$(LDAP_UOB))(uid=%u))"
|
||||
LDAP_APW ?= uid=user
|
||||
AD_BASE ?= dc=example,dc=com
|
||||
AD_DOM ?= $(call ad_sub_dot, $(AD_BASE))
|
||||
AD_DC ?= $(call ad_cut_dot, 1, 1, $(AD_DOM))
|
||||
AD_GRP_OU ?= groups
|
||||
AD_USR_OB ?= kopano-user
|
||||
AD_USR_OU ?= users
|
||||
AD_USR_CN ?= hero
|
||||
AD_USR_PW ?= enigma
|
||||
|
||||
MAIL_DOM ?= example.com
|
||||
MAIL_US1 ?= hero
|
||||
MAIL_PW1 ?= enigma
|
||||
MAIL_SUB ?= ~~~test~message~~~
|
||||
MAIL_MSG ?= Enjoy!
|
||||
|
||||
|
@ -46,10 +35,10 @@ SQL_ENV ?= \
|
|||
-e MYSQL_PASSWORD=$(SQL_PASS) \
|
||||
|
||||
APP_NAME ?= app
|
||||
APP_IMG ?=
|
||||
APP_FQDN ?= $(APP_NAME).$(MAIL_DOM)
|
||||
APP_FQDN ?= $(APP_NAME).$(AD_DOM)
|
||||
APP_CERT ?= ssl/$(APP_FQDN).crt
|
||||
APP_KEY ?= ssl/$(APP_FQDN).key
|
||||
APP_VOL ?=
|
||||
APP_SSLD ?= /etc/kopano/ssl
|
||||
APP_SLOG ?= 7
|
||||
APP_ALOG ?= 6
|
||||
APP_ENV ?= $(NET_ENV) $(SQL_ENV) \
|
||||
|
@ -58,9 +47,9 @@ APP_ENV ?= $(NET_ENV) $(SQL_ENV) \
|
|||
-e MYSQL_HOST=$(DB_NAME) \
|
||||
-e USER_PLUGIN=ldap \
|
||||
-e LDAP_URI=ldap://$(AUT_NAME):389/ \
|
||||
-e LDAP_SEARCH_BASE=$(LDAP_BAS) \
|
||||
-e LDAP_USER_TYPE_ATTRIBUTE_VALUE=$(LDAP_UOB) \
|
||||
-e LDAP_GROUP_TYPE_ATTRIBUTE_VALUE=$(LDAP_GOU) \
|
||||
-e LDAP_SEARCH_BASE=$(AD_BASE) \
|
||||
-e LDAP_USER_TYPE_ATTRIBUTE_VALUE=$(AD_USR_OB) \
|
||||
-e LDAP_GROUP_TYPE_ATTRIBUTE_VALUE=$(AD_GRP_OU) \
|
||||
-e LDAP_PROPMAP= \
|
||||
-e IMAP_LISTEN=*:143 \
|
||||
-e POP3_LISTEN=*:110 \
|
||||
|
@ -73,21 +62,22 @@ APPS_ENV ?= $(APP_ENV) \
|
|||
-e IMAPS_LISTEN=*:993 \
|
||||
-e POP3S_LISTEN=*:995 \
|
||||
-e ICALS_LISTEN=*:8443
|
||||
APPF_ENV ?= $(APPS_ENV) \
|
||||
-v $(shell pwd)/$(TST_SSLD):/$(APP_SSLD)
|
||||
APPA_ENV ?= $(APPS_ENV) \
|
||||
-v $(shell pwd)/$(TST_ACMD):/$(TST_ACMD)
|
||||
|
||||
#-p "127.0.0.1:2003:2003"
|
||||
-v $(shell pwd)/acme:/acme
|
||||
|
||||
AUT_NAME ?= auth
|
||||
AUT_IMG ?= mlan/openldap
|
||||
AUT_FQDN ?= $(AUT_NAME).$(MAIL_DOM)
|
||||
AUT_FQDN ?= $(AUT_NAME).$(AD_DOM)
|
||||
AUT_VOL ?=
|
||||
AUT_ENV ?= $(NET_ENV) \
|
||||
--name $(AUT_NAME) \
|
||||
--hostname $(AUT_FQDN)
|
||||
|
||||
TAW_NAME ?= throwaway
|
||||
TAW_ENV ?= --entrypoint /bin/bash \
|
||||
--name $(TAW_NAME) \
|
||||
--rm
|
||||
|
||||
CURL_OPT ?= -s -v
|
||||
CURL_IMG ?= curlimages/curl
|
||||
CURL_ENV ?= $(NET_ENV) \
|
||||
|
@ -95,7 +85,7 @@ CURL_ENV ?= $(NET_ENV) \
|
|||
|
||||
DB_NAME ?= db
|
||||
DB_IMG ?= mariadb
|
||||
DB_FQDN ?= $(DB_NAME).$(MAIL_DOM)
|
||||
DB_FQDN ?= $(DB_NAME).$(AD_DOM)
|
||||
DB_VOL ?=
|
||||
DB_CMD ?= --log_warnings=1
|
||||
DB_ENV ?= $(NET_ENV) $(SQL_ENV) \
|
||||
|
@ -110,6 +100,33 @@ TST_W8S2 ?= 20
|
|||
TST_W8L1 ?= 20
|
||||
TST_W8L2 ?= 120
|
||||
|
||||
export define LDIF_ADD_DATA
|
||||
dn: $(AD_BASE)
|
||||
objectClass: organization
|
||||
objectClass: dcObject
|
||||
dc: $(AD_DC)
|
||||
o: $(AD_DOM)
|
||||
|
||||
dn: ou=$(AD_USR_OU),$(AD_BASE)
|
||||
objectClass: organizationalUnit
|
||||
ou: $(AD_USR_OU)
|
||||
|
||||
dn: ou=$(AD_GRP_OU),$(AD_BASE)
|
||||
objectClass: organizationalUnit
|
||||
ou: $(AD_GRP_OU)
|
||||
|
||||
dn: uid=$(AD_USR_CN),ou=$(AD_USR_OU),$(AD_BASE)
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: $(AD_USR_OB)
|
||||
cn: $(AD_USR_CN)
|
||||
sn: $(AD_USR_CN)
|
||||
uid: $(AD_USR_CN)
|
||||
mail: $(AD_USR_CN)@$(AD_DOM)
|
||||
kopanoAccount: 1
|
||||
userPassword: $(AD_USR_PW)
|
||||
endef
|
||||
|
||||
|
||||
variables:
|
||||
make -pn | grep -A1 "^# makefile"| grep -v "^#\|^--" | sort | uniq
|
||||
|
||||
|
@ -121,6 +138,7 @@ test-all: test-up_0 test_1 test_2 test_3
|
|||
|
||||
test_%: test-up_% test-waitl_% test-logs_% test-service_% test-down_%
|
||||
|
||||
|
||||
test-pull:
|
||||
$(addprefix docker pull ,$(CURL_IMG); $(AUT_IMG); $(DB_IMG))
|
||||
|
||||
|
@ -133,24 +151,24 @@ test-up_0: test-up-net
|
|||
# run containers see if there are logs and stop.
|
||||
#
|
||||
#
|
||||
docker run -d --name $(APP_NAME) $(IMG_REPO):$(call _ver,$(IMG_VER),core)
|
||||
docker run -d --name $(APP_NAME) $(TST_REPO):$(call _ver,$(TST_VER),core)
|
||||
sleep $(TST_W8L1)
|
||||
docker container logs $(APP_NAME) | grep 'docker-entrypoint.sh'
|
||||
docker rm -fv $(APP_NAME)
|
||||
sleep $(TST_W8S1)
|
||||
docker run -d --name $(APP_NAME) $(IMG_REPO):$(call _ver,$(IMG_VER),full)
|
||||
docker run -d --name $(APP_NAME) $(TST_REPO):$(call _ver,$(TST_VER),full)
|
||||
sleep $(TST_W8L1)
|
||||
docker container logs $(APP_NAME) | grep 'docker-entrypoint.sh'
|
||||
docker rm -fv $(APP_NAME)
|
||||
sleep $(TST_W8S1)
|
||||
#
|
||||
#
|
||||
# test (0) successful
|
||||
# test (0) success ☺
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
test-up_1: test-up-net test-up-auth_1
|
||||
test-up_1: test-up-net test-up-deps_1 test-up-auth_1
|
||||
#
|
||||
#
|
||||
#
|
||||
|
@ -161,11 +179,9 @@ test-up_1: test-up-net test-up-auth_1
|
|||
# recv: curl imap://app
|
||||
#
|
||||
#
|
||||
docker run -d $(DB_ENV) $(DB_VOL) $(DB_IMG) $(DB_CMD)
|
||||
sleep $(TST_W8L1)
|
||||
docker run -d $(APP_ENV) $(APP_VOL) $(IMG_REPO):$(call _ver,$(IMG_VER),core)
|
||||
docker run -d $(APP_ENV) $(APP_VOL) $(TST_REPO):$(call _ver,$(TST_VER),core)
|
||||
|
||||
test-up_2: test-up-net test-up-auth_2 $(TST_CERT)
|
||||
test-up_2: test-up-net test-up-deps_2 test-up-auth_2
|
||||
#
|
||||
#
|
||||
#
|
||||
|
@ -176,11 +192,9 @@ test-up_2: test-up-net test-up-auth_2 $(TST_CERT)
|
|||
# recv: curl imaps://app
|
||||
#
|
||||
#
|
||||
docker run -d $(DB_ENV) $(DB_VOL) $(DB_IMG) $(DB_CMD)
|
||||
sleep $(TST_W8L1)
|
||||
docker run -d $(APPF_ENV) $(APP_VOL) $(IMG_REPO):$(call _ver,$(IMG_VER),core)
|
||||
docker run -d $(APPS_ENV) $(APP_VOL) $(TST_REPO):$(call _ver,$(TST_VER),core)
|
||||
|
||||
test-up_3: test-up-net test-up-auth_3 $(TST_ACME)
|
||||
test-up_3: test-up-net test-up-deps_3 test-up-auth_3 acme/acme.json
|
||||
#
|
||||
#
|
||||
#
|
||||
|
@ -192,9 +206,7 @@ test-up_3: test-up-net test-up-auth_3 $(TST_ACME)
|
|||
# web: curl http://app
|
||||
#
|
||||
#
|
||||
docker run -d $(DB_ENV) $(DB_VOL) $(DB_IMG) $(DB_CMD)
|
||||
sleep $(TST_W8L1)
|
||||
docker run -d $(APPA_ENV) $(APP_VOL) $(IMG_REPO):$(call _ver,$(IMG_VER),full)
|
||||
docker run -d $(APPA_ENV) $(APP_VOL) $(TST_REPO):$(call _ver,$(TST_VER),full)
|
||||
|
||||
test-up: test-up_1
|
||||
|
||||
|
@ -203,7 +215,7 @@ test-service: test-service_0
|
|||
test-service_%: test-pop3_% test-imap_% test-http_%
|
||||
#
|
||||
#
|
||||
# test ($*) successful
|
||||
# test ($*) success ☺
|
||||
#
|
||||
#
|
||||
#
|
||||
|
@ -224,26 +236,30 @@ test-up-net:
|
|||
docker network create $(NET_NAME) 2>/dev/null || true
|
||||
|
||||
test-down-net:
|
||||
docker network rm $(NET_NAME) || true
|
||||
|
||||
test-down: test-down_0
|
||||
docker network rm $(NET_NAME) 2>/dev/null || true
|
||||
|
||||
test-down: test-down_0 test-down-net acme-destroy
|
||||
|
||||
|
||||
test-down_%:
|
||||
docker rm -fv $(APP_NAME) $(DB_NAME) $(AUT_NAME) 2>/dev/null || true
|
||||
if [ $* -ge 0 ]; then sleep $(TST_W8S1); fi
|
||||
@docker rm -fv $(APP_NAME) $(DB_NAME) $(AUT_NAME) $(TAW_NAME) 2>/dev/null || true
|
||||
@if [ $* -ge 0 ]; then sleep $(TST_W8S1); fi
|
||||
|
||||
test-up-deps_%:
|
||||
docker run -d $(DB_ENV) $(DB_VOL) $(DB_IMG) $(DB_CMD)
|
||||
docker run -d $(AUT_ENV) $(AUT_VOL) $(AUT_IMG)
|
||||
|
||||
test-up-auth_%:
|
||||
docker run -d $(AUT_ENV) $(AUT_VOL) $(AUT_IMG)
|
||||
sleep $(TST_W8L1)
|
||||
printf "dn: ou=$(LDAP_UOU),$(LDAP_BAS)\nchangetype: add\nobjectClass: organizationalUnit\nobjectClass: top\nou: $(LDAP_UOU)\n\ndn: ou=$(LDAP_GOU),$(LDAP_BAS)\nchangetype: add\nobjectClass: organizationalUnit\nobjectClass: top\nou: $(LDAP_GOU)\n\ndn: uid=$(MAIL_US1),ou=$(LDAP_UOU),$(LDAP_BAS)\nchangetype: add\nobjectClass: top\nobjectClass: inetOrgPerson\nobjectClass: $(LDAP_UOB)\ncn: $(MAIL_US1)\nsn: $(MAIL_US1)\nuid: $(MAIL_US1)\nmail: $(MAIL_US1)@$(MAIL_DOM)\nuidNumber: 1234\ngidNumber: 1234\nhomeDirectory: /home/$(MAIL_US1)\nuserPassword: $(MAIL_PW1)\n" \
|
||||
| docker exec -i $(AUT_NAME) ldap modify
|
||||
docker run $(TAW_ENV) $(TST_REPO):$(call _ver,$(TST_VER),core) \
|
||||
zcat /usr/share/doc/kopano/kopano.ldif.gz \
|
||||
| docker exec -i $(AUT_NAME) ldapadd -Q
|
||||
echo "$$LDIF_ADD_DATA" | docker exec -i $(AUT_NAME) ldapadd -Q
|
||||
|
||||
test-lmtp: test-lmtp_0
|
||||
test-lmtp_%:
|
||||
printf "LHLO mx\nMAIL FROM: <test@example.biz>\nRCPT TO: <$(MAIL_US1)@$(MAIL_DOM)>\nDATA\nFrom: A tester <test@example.biz>\nTo: <$(MAIL_US1)@$(MAIL_DOM)>\nDate: $$(date)\nSubject: $(MAIL_SUB)$*\n$(MAIL_MSG)$*\n.\nQUIT\n"\
|
||||
| nc -C $(call _ip,$(APP_NAME)) 2003 # > /dev/null
|
||||
# | nc -C localhost 2003 # > /dev/null
|
||||
printf "LHLO mx\nMAIL FROM: <test@example.biz>\nRCPT TO: <$(AD_USR_CN)@$(AD_DOM)>\nDATA\nFrom: A tester <test@example.biz>\nTo: <$(AD_USR_CN)@$(AD_DOM)>\nDate: $$(date)\nSubject: $(MAIL_SUB)$*\n$(MAIL_MSG)$*\n.\nQUIT\n"\
|
||||
| nc -C $(call dkr_cnt_ip,$(APP_NAME)) 2003 # > /dev/null
|
||||
case $* in [1-3]) sleep $(TST_W8S1);; [4-9]) sleep $(TST_W8S2);; esac
|
||||
|
||||
test-cfg_%:
|
||||
|
@ -255,13 +271,13 @@ test-imap: test-imap_0
|
|||
test-imaps: test-imap_9
|
||||
test-imap_%: test-cfg_% test-lmtp_%
|
||||
docker run $(CURL_ENV) $(CURL_IMG) $(CURL_OPT) imap$(cfg_s)://$(APP_NAME)/inbox \
|
||||
--ssl --anyauth -k -X "fetch 1 all" -u $(MAIL_US1):$(MAIL_PW1) | grep $(GREP_ENV) $(MAIL_SUB)$*
|
||||
--ssl --anyauth -k -X "fetch 1 all" -u $(AD_USR_CN):$(AD_USR_PW) | grep $(GREP_ENV) $(MAIL_SUB)$*
|
||||
|
||||
test-pop3: test-pop3_0
|
||||
test-pop3s: test-pop3_9
|
||||
test-pop3_%: test-cfg_% test-lmtp_%
|
||||
docker run $(CURL_ENV) $(CURL_IMG) $(CURL_OPT) pop3$(cfg_s)://$(APP_NAME)/1 \
|
||||
--ssl --anyauth -k -u $(MAIL_US1):$(MAIL_PW1) | grep $(GREP_ENV) $(MAIL_SUB)$*
|
||||
--ssl --anyauth -k -u $(AD_USR_CN):$(AD_USR_PW) | grep $(GREP_ENV) $(MAIL_SUB)$*
|
||||
|
||||
test-http: test-http_9
|
||||
test-http_%: test-cfg_%
|
||||
|
@ -295,22 +311,8 @@ test-htop: test-debugtools
|
|||
test-tls: #--starttls imap
|
||||
docker run --rm -it $(NET_ENV) drwetter/testssl.sh $(APP_NAME):993 || true
|
||||
|
||||
test-ssl-gen: $(TST_ACME)
|
||||
acme-destroy: ssl-destroy
|
||||
rm -f acme/*
|
||||
|
||||
test-ssl-rm:
|
||||
rm -rf $(TST_SSLD) $(TST_ACMD)
|
||||
# rm $(TST_KEY) $(TST_CRT) $(TST_ACME)
|
||||
|
||||
$(TST_ACME): $(TST_ACMD) $(TST_CERT)
|
||||
bin/gen-acme-json.sh $(MAIL_US1)@$(MAIL_DOM) $(APP_FQDN) $(TST_KEY) $(TST_CERT) > $(TST_ACME)
|
||||
|
||||
$(TST_CERT): $(TST_KEY)
|
||||
openssl req -x509 -utf8 -new -batch -days $(SSL_CRTD) \
|
||||
-subj "/CN=$(APP_FQDN)" -key $(TST_KEY) -out $@
|
||||
|
||||
$(TST_KEY): $(TST_SSLD)
|
||||
openssl genrsa -out $@
|
||||
chmod a+r $@
|
||||
|
||||
$(TST_SSLD) $(TST_ACMD):
|
||||
mkdir -p $@
|
||||
acme/acme.json: $(APP_CERT)
|
||||
bin/gen-acme-json.sh $(AD_USR_CN)@$(AD_DOM) $(APP_FQDN) $(APP_KEY) $(APP_CERT) > $@
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
|
@ -0,0 +1,38 @@
|
|||
# ad.mk
|
||||
#
|
||||
# AD and LDAP make-functions
|
||||
#
|
||||
|
||||
#
|
||||
# chars
|
||||
#
|
||||
char_null :=
|
||||
char_space := $(char_null) #
|
||||
char_comma := ,
|
||||
char_dot := .
|
||||
char_colon := :
|
||||
|
||||
#
|
||||
# $(call ad_sub_dc,example.com) -> dc=example,dc=com
|
||||
#
|
||||
ad_sub_dc = $(subst $(char_space),$(char_comma),$(addprefix dc=, $(subst ., ,$(1))))
|
||||
#
|
||||
# $(call ad_sub_dot,dc=example,dc=com) -> example.com
|
||||
#
|
||||
ad_sub_dot = $(subst $(char_comma)dc=,$(char_dot),$(patsubst dc=%,%,$(1)))
|
||||
#
|
||||
# $(call ad_cat_dn,admin,dc=example,dc=com) -> cn=admin,dc=example,dc=com
|
||||
#
|
||||
ad_cat_dn = cn=$(1),$(2)
|
||||
#
|
||||
# $(call ad_cut_dot,1,1,example.com) -> example
|
||||
#
|
||||
ad_cut_dot = $(subst $(char_space),$(char_dot),$(wordlist $(1), $(2), $(subst $(char_dot),$(char_space),$(3))))
|
||||
#
|
||||
# $(call ad_rootdc,2,9,adm.dom.org:secret) -> dom.org
|
||||
#
|
||||
ad_rootdc = $(subst $(char_space),$(char_dot),$(wordlist $(1), $(2), $(subst $(char_dot),$(char_space),$(firstword $(subst $(char_colon),$(char_space),$(3))))))
|
||||
#
|
||||
# $(call ad_rootpw,adm.dom.org:secret) -> secret
|
||||
#
|
||||
ad_rootpw = $(lastword $(subst $(char_colon),$(char_space),$(1)))
|
|
@ -5,6 +5,13 @@ host=$2
|
|||
keyfile=$3
|
||||
certfile=$4
|
||||
|
||||
#
|
||||
# The "PrivateKey": attribute needs a PKCS#1 key without tags and line breaks
|
||||
# "openssl req -newkey rsa" generates a key stored in PKCS#8 so needs conversion
|
||||
#
|
||||
#acme_strip_tag() { openssl rsa -in $1 | sed '/^-----/d' | sed ':a;N;$!ba;s/\n//g' ;}
|
||||
acme_strip_tag() { sed '/^-----/d' $1 | sed ':a;N;$!ba;s/\n//g' ;}
|
||||
|
||||
cat <<-!cat
|
||||
{
|
||||
"Account": {
|
||||
|
@ -18,7 +25,7 @@ cat <<-!cat
|
|||
},
|
||||
"uri": "https://acme-v02.api.letsencrypt.org/acme/acct/$RANDOM"
|
||||
},
|
||||
"PrivateKey": "$(sed '/^-----/d' $keyfile | sed ':a;N;$!ba;s/\n//g')",
|
||||
"PrivateKey": "$(acme_strip_tag $keyfile)",
|
||||
"KeyType": "2048"
|
||||
},
|
||||
"Certificates": [
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# dkr.mk
|
||||
#
|
||||
# Container make-functions
|
||||
#
|
||||
|
||||
#
|
||||
# $(call dkr_srv_cnt,app) -> d03dda046e0b90c...
|
||||
#
|
||||
dkr_srv_cnt = $(shell docker-compose ps -q $(1) | head -n1)
|
||||
#
|
||||
# $(call dkr_cnt_ip,demo_app_1) -> 172.28.0.3
|
||||
#
|
||||
dkr_cnt_ip = $(shell docker inspect -f \
|
||||
'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \
|
||||
$(1) | head -n1)
|
||||
#
|
||||
# $(call dkr_srv_ip,app) -> 172.28.0.3
|
||||
#
|
||||
dkr_srv_ip = $(shell docker inspect -f \
|
||||
'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \
|
||||
$$(docker-compose ps -q $(1)) | head -n1)
|
||||
#
|
||||
#cnt_ip_old = $(shell docker inspect -f \
|
||||
# '{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' \
|
||||
# $(1) | head -n1)
|
||||
|
||||
#
|
||||
# List IPs of containers
|
||||
#
|
||||
ip-list:
|
||||
@for srv in $$(docker ps --format "{{.Names}}"); do \
|
||||
echo $$srv $$(docker inspect -f \
|
||||
'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $$srv); \
|
||||
done | column -t
|
|
@ -0,0 +1,125 @@
|
|||
# ssl.mk
|
||||
#
|
||||
# SSL and TLS make-functions
|
||||
#
|
||||
|
||||
SSL_O ?= example.com
|
||||
SSL_KEY ?= rsa:2048 # rsa:2048 rsa:4096
|
||||
SSL_MAIL ?=
|
||||
SSL_PASS ?= secret
|
||||
SSL_SAN ?=
|
||||
SSL_TRST ?=
|
||||
|
||||
#
|
||||
# Usage: OpenLDAP
|
||||
#
|
||||
#SSL_O = $(AD_DOM)
|
||||
#target: ssl/auth.crt ssl/demo.crt
|
||||
|
||||
#
|
||||
# Usage: SMIME
|
||||
#
|
||||
#SSL_O = $(MAIL_DOMAIN)
|
||||
#SSL_MAIL = auto
|
||||
#SSL_PASS = $(AD_USR_PW)
|
||||
##SSL_TRST = $(SSL_SMIME)
|
||||
#target: ssl/$(AD_USR_CN)@$(MAIL_DOMAIN).p12
|
||||
SSL_SMIME = -setalias "Self Signed SMIME" -addtrust emailProtection \
|
||||
-addreject clientAuth -addreject serverAuth
|
||||
|
||||
#
|
||||
# Usage: SUbject Alternate Name SAN
|
||||
#
|
||||
#SSL_O = example.com
|
||||
#SSL_SAN = "subjectAltName=DNS:auth,DNS:*.docker"
|
||||
#target: ssl/auth.crt
|
||||
|
||||
|
||||
#
|
||||
# $(call ssl_subj,root,example.com,) -> -subj "/CN=root/O=example.com"
|
||||
# $(call ssl_subj,root,example.com,auto) -> -subj "/CN=root/O=example.com/emailAddress=root@example.com"
|
||||
# $(call ssl_subj,root,example.com,admin@my.org) -> -subj "/CN=root/O=example.com/emailAddress=admin@my.org"
|
||||
#
|
||||
ssl_subj = -subj "/CN=$(1)/O=$(2)$(if $(3),/emailAddress=$(if $(findstring @,$(3)),$(3),$(1)@$(2)),)"
|
||||
|
||||
#
|
||||
# $(call ssl_extfile,"subjectAltName=DNS:auth") -> -extfile <(printf "subjectAltName=DNS:auth")
|
||||
#
|
||||
ssl_extfile = $(if $(1),-extfile <(printf $(1)),)
|
||||
|
||||
|
||||
.PRECIOUS: %.crt %.csr %.key
|
||||
SHELL = /bin/bash
|
||||
|
||||
#
|
||||
# Personal information exchange file PKCS#12
|
||||
#
|
||||
%.p12: %.crt
|
||||
openssl pkcs12 -export -in $< -inkey $*.key -out $@ \
|
||||
-passout pass:$(SSL_PASS)
|
||||
|
||||
#
|
||||
# Certificate PEM
|
||||
#
|
||||
%.crt: %.csr ssl/ca.crt
|
||||
openssl x509 -req -in $< -CA $(@D)/ca.crt -CAkey $(@D)/ca.key -out $@ \
|
||||
$(call ssl_extfile,$(SSL_SAN)) $(SSL_TRST) -CAcreateserial
|
||||
|
||||
#
|
||||
# Certificate signing request PEM
|
||||
#
|
||||
%.csr: ssl
|
||||
openssl req -new -newkey $(SSL_KEY) -nodes -keyout $*.key -out $@ \
|
||||
$(call ssl_subj,$(*F),$(SSL_O),$(SSL_MAIL))
|
||||
|
||||
#
|
||||
# Certificate authority certificate PEM
|
||||
#
|
||||
ssl/ca.crt: ssl
|
||||
openssl req -x509 -new -newkey $(SSL_KEY) -nodes -keyout ssl/ca.key -out $@ \
|
||||
$(call ssl_subj,root,$(SSL_O),$(SSL_MAIL))
|
||||
|
||||
#
|
||||
# SSL directory
|
||||
#
|
||||
ssl:
|
||||
mkdir -p $@
|
||||
|
||||
#
|
||||
# Remove all files in SSL directory
|
||||
#
|
||||
ssl-destroy:
|
||||
rm -f ssl/*
|
||||
|
||||
#
|
||||
# Inspect all files in SSL directory
|
||||
#
|
||||
ssl-list:
|
||||
@for file in $$(ls ssl/*); do \
|
||||
case $$file in \
|
||||
*.crt) \
|
||||
printf "\e[33;1m%s\e[0m\n" $$file; \
|
||||
openssl x509 -noout -issuer -subject -ext basicConstraints,keyUsage,extendedKeyUsage,subjectAltName -in $$file;; \
|
||||
*.csr) \
|
||||
printf "\e[33;1m%s\e[0m\n" $$file; \
|
||||
openssl req -noout -subject -in $$file;; \
|
||||
*.key) \
|
||||
printf "\e[33;1m%s\e[0m\n" $$file; \
|
||||
openssl rsa -text -noout -in $$file | head -n 1;; \
|
||||
esac \
|
||||
done
|
||||
|
||||
ssl-inspect:
|
||||
@for file in $$(ls ssl/*); do \
|
||||
case $$file in \
|
||||
*.crt) \
|
||||
printf "\e[33;1m%s\e[0m " $$file; \
|
||||
openssl x509 -text -noout -certopt no_sigdump,no_pubkey -in $$file;; \
|
||||
*.csr) \
|
||||
printf "\e[33;1m%s\e[0m " $$file; \
|
||||
openssl req -text -noout -reqopt no_sigdump,no_pubkey,ext_default -in $$file;; \
|
||||
*.key) \
|
||||
printf "\e[33;1m%s\e[0m " $$file; \
|
||||
openssl rsa -text -noout -in $$file | head -n 1;; \
|
||||
esac \
|
||||
done
|
|
@ -0,0 +1,4 @@
|
|||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
Loading…
Reference in New Issue