From cc9a6b05a80de3669fddfd1fb34f95a0ee14ffaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Mon, 14 Oct 2019 22:13:05 +0300 Subject: [PATCH 001/222] RFC: Mailu directory structure --- design/mailu-directory-structure.md | 199 ++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 design/mailu-directory-structure.md diff --git a/design/mailu-directory-structure.md b/design/mailu-directory-structure.md new file mode 100644 index 00000000..ad75eeaa --- /dev/null +++ b/design/mailu-directory-structure.md @@ -0,0 +1,199 @@ +# RFC: Mailu directory structure +The current layout of the Mailu directory structure can be improved to allow for easier replicated deployments, like Docker Swarm and Kubernetes. Please read https://usrpro.com/publications/mailu-persistent-storage/ for the background motivation of this RFC. + +## Scope +This document only describes the re-arrangement of the `$ROOT` Mailu filesystem as-is. This means moving around of current files and directories. The linked article also proposes more advanced improvements. Those are not included in this RFC and need to be evaluated and implemented independently. However, the changes proposed in this document should make such improvements easier. + +Currently some services are wrongfully sharing mountpoints or have unused volumes declared. Those are also taken care of in this RFC. + +## Compatibility +If these changes were to be accepted, it will break compatibility with previous Mailu version `<=1.7`. As such, we will propably increment to version `2.0`. + +## Root +The root of the Mailu filesystem is located at `/mailu` by default. For simplicity we will assume this location throughout the document. Within `/mailu` we will aim to define 3 main sub-directories: + +### Config + +- Path: `/mailu/config/` + +Small config bearing files, sometimes shared between multiple services. The performance and storage needs for this filesystem are low. Availability is important for correct functioning of the mail server. No file locking issues are expected from concurrent access. A basic (and redundant) filesystem should suffice. + +#### Dovecot + +- Old path: `/mailu/overrides` (shared with postfix, nginx and rspamd) +- New Path `/mailu/config/dovecot` + +Dovecot configuration overrides. + +#### Postfix + +- Old path: `/mailu/overrides` (shared with dovecot, nginx and rspamd) +- New Path `/mailu/config/posfix` + +Postfix configuration overrides. + +#### Rspamd + +- Old path: `/mailu/overrides/rspamd` +- New path: `/mailu/config/rspamd` + +RSpamD configuration overrides. + +#### Rainloop + +- Old path: `/mailu/webmail/_data_/_default_/storage` (part of `/mailu/webmail` mountpoint, shared with Roundcube) +- New path: `/mailu/config/rainloop` + +User specific configs. The remaining files under the old `/mailu/webmail` don't need to be persistent. Except for `AddressBook.sqlite`, see `/mailu/data`. + +#### Roundcube + +- Old path: `/mailu/webmail/gpg` (part of `/mailu/webmail` mountpoint, shared with Rainloop) +- New path: `/mailu/config/roundcube/gpg` + +User configured GPG keys. + +#### Redis + +- Old path: `/mailu/redis` +- New path: `/mailu/config/redis` + +Holds `dump.rdb` for data restoration. Although technically a database, Redis works from memory. The dump file is only written to every minute and read from during start. Hence it fits better in the replicated config directory filesystem. + +#### Share + +- Path: `/mailu/config/share/` + +Shared configuration between different services + +##### DKIM + +- Old path: `/mailu/dkim/` +- New path: `/mailu/config/share/dkim` + +DKIM private keys store. Read/write access by Admin. Read only access by rSpamD. + +##### Certs + +- Old path: `/mailu/certs` +- New path: `/mailu/config/share/certs` (Proposal in anticipation of the RFC outcome at: https://github.com/Mailu/Mailu/issues/1222.) + +TLS certificates. Write access from `nginx` in case `TLS_FLAVOR=letsencrypt`. Or write access from `treafik-certdumper` or any other tool obtaining certificates. + +`letsencrypt` setting is not compatible with replicated setups. Multiple instances would disrupt the ACME challenge verification and cause race conditions on requesting certificates. In such cases certificates will need to be provided by other tools. + +If RFC issue #1222 is accepted, Dovecot will need read-only access to the certificates. + +### Data + +- Path: `/mailu/data/` + +Database files, like SQLite or PostgreSQL files. Databases don't perform well on network filesystems as they depend heavily on file locking and full controll on the database files. Making it unfit for concurrent access from multiple hosts. This directory should always live on a local filesystem. This makes it only usable in `docker-compose` deployments. Usage of this directory should be avoided in Kubernetes and Docker Swarm deployments. Some services will need to be improved to allow for this. + +#### admin data + +- Old path: `/mailu/data/` +- New path: `/mailu/data/admin/` (mount point on `admin` directory) + +Holds `main.db` SQLite database file holding domains, users, aliases etcetera. Read/write access only by admin. Can be avoided by using a remote DB server like PostgreSQL, MySQL or MariaDB. + +Also holds `instance` for unique statistics transmission. Removing of this file is proposed in RFC [issue 129](https://github.com/Mailu/Mailu/issues/1219). + +This move is needed in order to be able to mount the directory without exposing data files from other services into admin. + +#### rspamd + +- Old path: `/mailu/filter` (shared with ClamAV) +- New path: `/mailu/data/rspamd` + +Storage of Bayes and Fuzzy learning SQLite databases and caches. As future optimization we should look into moving all this into Redis. + +#### Rainloop + +- Old path: `/mailu/webmail/_data_/_default_/AddressBook.sqlite` (part of `/mailu/webmail` mountpoint, shared with Roundcube) +- New path: `/mailu/data/rainloop/AddressBook.sqlite` (mount on `rainloop` directory) + +Addressbook SQLite file. For future replicated deployments this might better be configured to use an external DB. + +For this modification, the `AddressBook.sqlite` will need to be moved to a different directory inside the container. + +#### Roundcube + +- Old path: `/mailu/webmail/roundcube.db` (part of `/mailu/webmail` mountpoint, shared with Rainloop) +- New path: `/mailu/data/roundcube/roundcube.db` (mount on `roundcube` directory) + +User settings SQLite database file for roundcube. For future replicated deployments this might better be configured to use an external DB. + +For this modification, the `rouncube.db` file will need to be moved to a different directory inside the container. + +### Mail + +- Path: `/mailu/mail` (unmodified) + +User mail, managed my Dovecot IMAP server. In replicated deployments, this filesystem needs to be shared over all IMAP server instances. It should be high performant and capable of propgating file locks. Storage size is proportional to the users and their quotas. Old versions of NFS are known to be buggy with file locking. Also Samaba or CIFS should be avoided. + +In the old situation, Maildir indexes are stored on the same volume. However, they need not to be persistent and should be located on a voletile filesystem instead. This allows better performance on network filesystems. + +### Local + +- Path: `/mailu/local` (new) + +Persistent storage not suitable for replication. In `docker-compose` deployments it lives inside `/mailu` and in replicated deployments it should live somewhere on the local host machine. + +#### Mailqueue + +- Old path: `/mailu/mailqueue` +- New path: `/mailu/local/mailqueue` + +The SMTP mailqueue should be persistant, as per SMTP spec it is not allowed to loose mail. However, persistance should be local only for performance reasons and the possibility to replicate Postfix servers. In setups like Docker Swarm and Kubernetes, admins should take care that Postfix is always restarted on same hosts in order to empty any remaining queue after a crash. + +#### ClamAV + +- Old path: `/mailu/filters` (shared with rSpamD) +- New path: `/mailu/local/clamav` + +Virus definitions do not need to be replicated, as they can be easily pulled in when ClamAV instances migrate to other nodes. Persistance does allow for some bandwith and time saving if ClamAV would be restarted on a previously used node (in case of updates or similar cases). Local only storage also prevents `freshclam` race conditions. + +## Conclusion + +The final layout of the Mailu filesystem will look like: + +```` +/mailu +├── config +│   ├── dovecot +│   ├── postfix +│   ├── rainloop +│   ├── redis +│   ├── roundcube +│   │   └── gpg +│   ├── rspamd +│   └── share +│   ├── certs +│   └── dkim +├── data +│   ├── admin +│   ├── rainloop +│   ├── roundcube +│   └── rspamd +├── local +│   ├── clamav +│   └── mailqueue +└── mail +```` + +Where in replicated environments: + +- `/mailu/config/`: should be a small, low performant and shared filesystem. +- `/mailu/data`: should be avoided. More work will need to be done to configure external DB servers for relevant services. Ideally, this directory should only exist on docker-compose deployments. +- `/mailu/local/`: Should exist only on local file systems of worker nodes. +- `/mailu/mail`: A distributed filesystem with sufficient performance and storage requirements to hold and process all user mailboxes. Ideally only Maildir without indexes. + +### Implementing + +The works to implement this changes should happen outside the `master` branch. Inclusion into `master` can only be accepted if: + +1. `docker-compose.yml` from setup reflects this changes correctly. +2. Kubernetes documentation is updated. +3. Legacy `docker-compose.yml` is either updated or deleted. +4. A clear data migration guide is written. \ No newline at end of file From 4f96e991449b52400b5a1fe8d968f07ff9346842 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sun, 29 Aug 2021 17:40:37 +0200 Subject: [PATCH 002/222] MTA-STS (use rather than publish policies) --- core/postfix/Dockerfile | 5 +++++ core/postfix/conf/main.cf | 2 +- core/postfix/mta-sts-daemon.yml | 10 ++++++++++ core/postfix/start.py | 10 ++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 core/postfix/mta-sts-daemon.yml diff --git a/core/postfix/Dockerfile b/core/postfix/Dockerfile index 062155c1..8efe5da4 100644 --- a/core/postfix/Dockerfile +++ b/core/postfix/Dockerfile @@ -12,10 +12,15 @@ RUN pip3 install socrate==0.2.0 RUN pip3 install "podop>0.2.5" # Image specific layers under this line +RUN apk add --no-cache --virtual .build-deps gcc musl-dev python3-dev +RUN pip3 install --no-binary :all: postfix-mta-sts-resolver==1.0.1 +RUN apk del .build-deps gcc musl-dev python3-dev + RUN apk add --no-cache postfix postfix-pcre cyrus-sasl-login COPY conf /conf COPY start.py /start.py +COPY mta-sts-daemon.yml /etc/ EXPOSE 25/tcp 10025/tcp VOLUME ["/queue"] diff --git a/core/postfix/conf/main.cf b/core/postfix/conf/main.cf index 7f84ade7..0194324f 100644 --- a/core/postfix/conf/main.cf +++ b/core/postfix/conf/main.cf @@ -59,7 +59,7 @@ tls_ssl_options = NO_COMPRESSION, NO_TICKET smtp_tls_mandatory_protocols = !SSLv2, !SSLv3 smtp_tls_protocols =!SSLv2,!SSLv3 smtp_tls_security_level = {{ OUTBOUND_TLS_LEVEL|default('may') }} -smtp_tls_policy_maps=hash:/etc/postfix/tls_policy.map +smtp_tls_policy_maps=hash:/etc/postfix/tls_policy.map, socketmap:unix:/tmp/mta-sts.socket:postfix smtp_tls_CApath = /etc/ssl/certs smtp_tls_session_cache_database = lmdb:/dev/shm/postfix/smtp_scache smtpd_tls_session_cache_database = lmdb:/dev/shm/postfix/smtpd_scache diff --git a/core/postfix/mta-sts-daemon.yml b/core/postfix/mta-sts-daemon.yml new file mode 100644 index 00000000..39f60e48 --- /dev/null +++ b/core/postfix/mta-sts-daemon.yml @@ -0,0 +1,10 @@ +path: "/tmp/mta-sts.socket" +mode: 0600 +shutdown_timeout: 20 +cache: + type: internal + options: + cache_size: 10000 +default_zone: + strict_testing: false + timeout: 4 diff --git a/core/postfix/start.py b/core/postfix/start.py index 799d42f5..50565e3d 100755 --- a/core/postfix/start.py +++ b/core/postfix/start.py @@ -30,6 +30,12 @@ def start_podop(): ("senderrate", "url", url + "sender/rate/§") ]) +def start_mta_sts_daemon(): + os.chmod("/root/", 0o755) # read access to /root/.netrc required + os.setuid(getpwnam('postfix').pw_uid) + from postfix_mta_sts_resolver import daemon + daemon.main() + def is_valid_postconf_line(line): return not line.startswith("#") \ and not line == '' @@ -68,6 +74,9 @@ for map_file in glob.glob("/overrides/*.map"): os.system("postmap {}".format(destination)) os.remove(destination) +if os.path.exists("/overrides/mta-sts-daemon.yml"): + shutil.copyfile("/overrides/mta-sts-daemon.yml", "/etc/mta-sts-daemon.yml") + if not os.path.exists("/etc/postfix/tls_policy.map.db"): with open("/etc/postfix/tls_policy.map", "w") as f: for domain in ['gmail.com', 'yahoo.com', 'hotmail.com', 'aol.com', 'outlook.com', 'comcast.net', 'icloud.com', 'msn.com', 'hotmail.co.uk', 'live.com', 'yahoo.co.in', 'me.com', 'mail.ru', 'cox.net', 'yahoo.co.uk', 'verizon.net', 'ymail.com', 'hotmail.it', 'kw.com', 'yahoo.com.tw', 'mac.com', 'live.se', 'live.nl', 'yahoo.com.br', 'googlemail.com', 'libero.it', 'web.de', 'allstate.com', 'btinternet.com', 'online.no', 'yahoo.com.au', 'live.dk', 'earthlink.net', 'yahoo.fr', 'yahoo.it', 'gmx.de', 'hotmail.fr', 'shawinc.com', 'yahoo.de', 'moe.edu.sg', 'naver.com', 'bigpond.com', 'statefarm.com', 'remax.net', 'rocketmail.com', 'live.no', 'yahoo.ca', 'bigpond.net.au', 'hotmail.se', 'gmx.at', 'live.co.uk', 'mail.com', 'yahoo.in', 'yandex.ru', 'qq.com', 'charter.net', 'indeedemail.com', 'alice.it', 'hotmail.de', 'bluewin.ch', 'optonline.net', 'wp.pl', 'yahoo.es', 'hotmail.no', 'pindotmedia.com', 'orange.fr', 'live.it', 'yahoo.co.id', 'yahoo.no', 'hotmail.es', 'morganstanley.com', 'wellsfargo.com', 'wanadoo.fr', 'facebook.com', 'yahoo.se', 'fema.dhs.gov', 'rogers.com', 'yahoo.com.hk', 'live.com.au', 'nic.in', 'nab.com.au', 'ubs.com', 'shaw.ca', 'umich.edu', 'westpac.com.au', 'yahoo.com.mx', 'yahoo.com.sg', 'farmersagent.com', 'yahoo.dk', 'dhs.gov']: @@ -81,6 +90,7 @@ if "RELAYUSER" in os.environ: # Run Podop and Postfix multiprocessing.Process(target=start_podop).start() +multiprocessing.Process(target=start_mta_sts_daemon).start() os.system("/usr/libexec/postfix/post-install meta_directory=/etc/postfix create-missing") # Before starting postfix, we need to check permissions on /queue # in the event that postfix,postdrop id have changed From 52d3a338751c6f7dd14c63de6c2164d3a9d9f8da Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sun, 29 Aug 2021 17:41:55 +0200 Subject: [PATCH 003/222] Remove the domains that have a valid MTA-STS policy gmail.com comcast.net mail.ru googlemail.com wp.pl --- core/postfix/start.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/postfix/start.py b/core/postfix/start.py index 50565e3d..de559b27 100755 --- a/core/postfix/start.py +++ b/core/postfix/start.py @@ -79,7 +79,7 @@ if os.path.exists("/overrides/mta-sts-daemon.yml"): if not os.path.exists("/etc/postfix/tls_policy.map.db"): with open("/etc/postfix/tls_policy.map", "w") as f: - for domain in ['gmail.com', 'yahoo.com', 'hotmail.com', 'aol.com', 'outlook.com', 'comcast.net', 'icloud.com', 'msn.com', 'hotmail.co.uk', 'live.com', 'yahoo.co.in', 'me.com', 'mail.ru', 'cox.net', 'yahoo.co.uk', 'verizon.net', 'ymail.com', 'hotmail.it', 'kw.com', 'yahoo.com.tw', 'mac.com', 'live.se', 'live.nl', 'yahoo.com.br', 'googlemail.com', 'libero.it', 'web.de', 'allstate.com', 'btinternet.com', 'online.no', 'yahoo.com.au', 'live.dk', 'earthlink.net', 'yahoo.fr', 'yahoo.it', 'gmx.de', 'hotmail.fr', 'shawinc.com', 'yahoo.de', 'moe.edu.sg', 'naver.com', 'bigpond.com', 'statefarm.com', 'remax.net', 'rocketmail.com', 'live.no', 'yahoo.ca', 'bigpond.net.au', 'hotmail.se', 'gmx.at', 'live.co.uk', 'mail.com', 'yahoo.in', 'yandex.ru', 'qq.com', 'charter.net', 'indeedemail.com', 'alice.it', 'hotmail.de', 'bluewin.ch', 'optonline.net', 'wp.pl', 'yahoo.es', 'hotmail.no', 'pindotmedia.com', 'orange.fr', 'live.it', 'yahoo.co.id', 'yahoo.no', 'hotmail.es', 'morganstanley.com', 'wellsfargo.com', 'wanadoo.fr', 'facebook.com', 'yahoo.se', 'fema.dhs.gov', 'rogers.com', 'yahoo.com.hk', 'live.com.au', 'nic.in', 'nab.com.au', 'ubs.com', 'shaw.ca', 'umich.edu', 'westpac.com.au', 'yahoo.com.mx', 'yahoo.com.sg', 'farmersagent.com', 'yahoo.dk', 'dhs.gov']: + for domain in ['yahoo.com', 'hotmail.com', 'aol.com', 'outlook.com', 'icloud.com', 'msn.com', 'hotmail.co.uk', 'live.com', 'yahoo.co.in', 'me.com', 'cox.net', 'yahoo.co.uk', 'verizon.net', 'ymail.com', 'hotmail.it', 'kw.com', 'yahoo.com.tw', 'mac.com', 'live.se', 'live.nl', 'yahoo.com.br', 'libero.it', 'web.de', 'allstate.com', 'btinternet.com', 'online.no', 'yahoo.com.au', 'live.dk', 'earthlink.net', 'yahoo.fr', 'yahoo.it', 'gmx.de', 'hotmail.fr', 'shawinc.com', 'yahoo.de', 'moe.edu.sg', 'naver.com', 'bigpond.com', 'statefarm.com', 'remax.net', 'rocketmail.com', 'live.no', 'yahoo.ca', 'bigpond.net.au', 'hotmail.se', 'gmx.at', 'live.co.uk', 'mail.com', 'yahoo.in', 'yandex.ru', 'qq.com', 'charter.net', 'indeedemail.com', 'alice.it', 'hotmail.de', 'bluewin.ch', 'optonline.net', 'yahoo.es', 'hotmail.no', 'pindotmedia.com', 'orange.fr', 'live.it', 'yahoo.co.id', 'yahoo.no', 'hotmail.es', 'morganstanley.com', 'wellsfargo.com', 'wanadoo.fr', 'facebook.com', 'yahoo.se', 'fema.dhs.gov', 'rogers.com', 'yahoo.com.hk', 'live.com.au', 'nic.in', 'nab.com.au', 'ubs.com', 'shaw.ca', 'umich.edu', 'westpac.com.au', 'yahoo.com.mx', 'yahoo.com.sg', 'farmersagent.com', 'yahoo.dk', 'dhs.gov']: f.write(f'{domain}\tsecure\n') os.system("postmap /etc/postfix/tls_policy.map") From a01960787362ed3fc5e7774c3bc573b3c52a86d9 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sun, 29 Aug 2021 17:46:28 +0200 Subject: [PATCH 004/222] towncrier --- towncrier/newsfragments/1798.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 towncrier/newsfragments/1798.feature diff --git a/towncrier/newsfragments/1798.feature b/towncrier/newsfragments/1798.feature new file mode 100644 index 00000000..1b63a85c --- /dev/null +++ b/towncrier/newsfragments/1798.feature @@ -0,0 +1 @@ +Implement MTA-STS (use published policies) From 5634354911bf645ea6b8204af4964d51e06178db Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sun, 29 Aug 2021 18:28:56 +0200 Subject: [PATCH 005/222] document how to publish an MTA-STS policy --- docs/faq.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/faq.rst b/docs/faq.rst index a2c6bd33..98026ab1 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -369,6 +369,31 @@ How do I use webdav (radicale)? .. _`575`: https://github.com/Mailu/Mailu/issues/575 .. _`1591`: https://github.com/Mailu/Mailu/issues/1591 +How do I setup a MTA-STS policy? +```````````````````````````````` + +Mailu can serve an `MTA-STS policy`_; To configure it you will need to: + +1. setup the appropriate DNS/CNAME record (``mta-sts.example.com`` -> ``mailu.example.com``) and DNS/TXT record (``_mta-sts.example.com`` -> ``v=STSv1; id=1``) paying attention to the ``TTL`` as this is used by MTA-STS. + +2. configure an override with the policy itself; for example, your ``overrides/mta-sts.conf`` could read: + +.. code-block:: bash + + location ^~ /.well-known/mta-sts.txt { + return 200 "version: STSv1 + mode: enforce + max_age: 86401 + mx: mailu.example.com\r\n"; + } + +3. add ``mta-sts.example.com`` to the ``HOSTNAMES`` configuration variable (and ensure that a valid SSL certificate is available for it) + +*issue reference:* `1798`_. + +.. _`1798`: https://github.com/Mailu/Mailu/issues/1798 +.. _`MTA-STS policy`: https://datatracker.ietf.org/doc/html/rfc8461 + Technical issues ---------------- From 5efe35329be9862cf4dcb34292b7b5a9b1976b5f Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sun, 29 Aug 2021 18:29:44 +0200 Subject: [PATCH 006/222] doh --- docs/faq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.rst b/docs/faq.rst index 98026ab1..92b208de 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -376,7 +376,7 @@ Mailu can serve an `MTA-STS policy`_; To configure it you will need to: 1. setup the appropriate DNS/CNAME record (``mta-sts.example.com`` -> ``mailu.example.com``) and DNS/TXT record (``_mta-sts.example.com`` -> ``v=STSv1; id=1``) paying attention to the ``TTL`` as this is used by MTA-STS. -2. configure an override with the policy itself; for example, your ``overrides/mta-sts.conf`` could read: +2. configure an override with the policy itself; for example, your ``overrides/nginx/mta-sts.conf`` could read: .. code-block:: bash From 7c5dcfa025e8b7e1f7e5bb6b3ac56331bc928287 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sun, 29 Aug 2021 18:32:17 +0200 Subject: [PATCH 007/222] MTA-STS is a major feature --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c4354b28..8c7d1640 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Main features include: - **Web access**, multiple Webmails and administration interface - **User features**, aliases, auto-reply, auto-forward, fetched accounts - **Admin features**, global admins, announcements, per-domain delegation, quotas -- **Security**, enforced TLS, Letsencrypt!, outgoing DKIM, anti-virus scanner +- **Security**, enforced TLS, MTA-STS, Letsencrypt!, outgoing DKIM, anti-virus scanner - **Antispam**, auto-learn, greylisting, DMARC and SPF - **Freedom**, all FOSS components, no tracker included From a8142dabbe4df86a2aa87d3f323de20c045d7db3 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Mon, 30 Aug 2021 14:21:28 +0200 Subject: [PATCH 008/222] Introduce DEFER_ON_TLS_ERROR This will default to True and defer emails that fail even "loose" validation of DANE or MTA-STS It should work most of the time but if it doesn't and you would rather see your emails delivered, you can turn it off. --- core/postfix/conf/main.cf | 6 +++++- core/postfix/mta-sts-daemon.yml | 2 +- core/postfix/start.py | 1 + docs/configuration.rst | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/core/postfix/conf/main.cf b/core/postfix/conf/main.cf index 0194324f..78ffcee1 100644 --- a/core/postfix/conf/main.cf +++ b/core/postfix/conf/main.cf @@ -58,13 +58,17 @@ tls_ssl_options = NO_COMPRESSION, NO_TICKET # 2. not all will have and up-to-date TLS stack. smtp_tls_mandatory_protocols = !SSLv2, !SSLv3 smtp_tls_protocols =!SSLv2,!SSLv3 -smtp_tls_security_level = {{ OUTBOUND_TLS_LEVEL|default('may') }} +smtp_tls_security_level = {{ OUTBOUND_TLS_LEVEL|default('dane') }} +smtp_tls_dane_insecure_mx_policy = dane smtp_tls_policy_maps=hash:/etc/postfix/tls_policy.map, socketmap:unix:/tmp/mta-sts.socket:postfix smtp_tls_CApath = /etc/ssl/certs smtp_tls_session_cache_database = lmdb:/dev/shm/postfix/smtp_scache smtpd_tls_session_cache_database = lmdb:/dev/shm/postfix/smtpd_scache smtp_host_lookup = dns smtp_dns_support_level = dnssec +delay_warning_time = 5m +smtp_tls_loglevel = 1 +notify_classes = resource, software, delay ############### # Virtual diff --git a/core/postfix/mta-sts-daemon.yml b/core/postfix/mta-sts-daemon.yml index 39f60e48..361bcbf9 100644 --- a/core/postfix/mta-sts-daemon.yml +++ b/core/postfix/mta-sts-daemon.yml @@ -6,5 +6,5 @@ cache: options: cache_size: 10000 default_zone: - strict_testing: false + strict_testing: {{ DEFER_ON_TLS_ERROR |default('true') }} timeout: 4 diff --git a/core/postfix/start.py b/core/postfix/start.py index de559b27..5e439bdb 100755 --- a/core/postfix/start.py +++ b/core/postfix/start.py @@ -76,6 +76,7 @@ for map_file in glob.glob("/overrides/*.map"): if os.path.exists("/overrides/mta-sts-daemon.yml"): shutil.copyfile("/overrides/mta-sts-daemon.yml", "/etc/mta-sts-daemon.yml") +conf.jinja("/etc/mta-sts-daemon.yml", os.environ, "/etc/mta-sts-daemon.yml") if not os.path.exists("/etc/postfix/tls_policy.map.db"): with open("/etc/postfix/tls_policy.map", "w") as f: diff --git a/docs/configuration.rst b/docs/configuration.rst index 27f8db7d..4fd84c07 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -73,7 +73,7 @@ mail in following format: ``[HOST]:PORT``. By default postfix uses "opportunistic TLS" for outbound mail. This can be changed by setting ``OUTBOUND_TLS_LEVEL`` to ``encrypt`` or ``secure``. This setting is highly recommended -if you are using a relayhost that supports TLS. +if you are using a relayhost that supports TLS but discouraged otherwise. ``DEFER_ON_TLS_ERROR`` (default: True) controls whether incomplete policies (DANE without DNSSEC or "testing" MTA-STS policies) will be taken into account and whether emails will be defered if the additional checks enforced by those policies fail. Similarily by default nginx uses "opportunistic TLS" for inbound mail. This can be changed by setting ``INBOUND_TLS_ENFORCE`` to ``True``. Please note that this is forbidden for From 05b57c972ec3a6fbe6a8f9cc048246e24747390c Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Mon, 30 Aug 2021 14:44:13 +0200 Subject: [PATCH 009/222] remove the static policy as it will override MTA-STS and DANE --- core/postfix/start.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/postfix/start.py b/core/postfix/start.py index 5e439bdb..69855e29 100755 --- a/core/postfix/start.py +++ b/core/postfix/start.py @@ -80,7 +80,7 @@ conf.jinja("/etc/mta-sts-daemon.yml", os.environ, "/etc/mta-sts-daemon.yml") if not os.path.exists("/etc/postfix/tls_policy.map.db"): with open("/etc/postfix/tls_policy.map", "w") as f: - for domain in ['yahoo.com', 'hotmail.com', 'aol.com', 'outlook.com', 'icloud.com', 'msn.com', 'hotmail.co.uk', 'live.com', 'yahoo.co.in', 'me.com', 'cox.net', 'yahoo.co.uk', 'verizon.net', 'ymail.com', 'hotmail.it', 'kw.com', 'yahoo.com.tw', 'mac.com', 'live.se', 'live.nl', 'yahoo.com.br', 'libero.it', 'web.de', 'allstate.com', 'btinternet.com', 'online.no', 'yahoo.com.au', 'live.dk', 'earthlink.net', 'yahoo.fr', 'yahoo.it', 'gmx.de', 'hotmail.fr', 'shawinc.com', 'yahoo.de', 'moe.edu.sg', 'naver.com', 'bigpond.com', 'statefarm.com', 'remax.net', 'rocketmail.com', 'live.no', 'yahoo.ca', 'bigpond.net.au', 'hotmail.se', 'gmx.at', 'live.co.uk', 'mail.com', 'yahoo.in', 'yandex.ru', 'qq.com', 'charter.net', 'indeedemail.com', 'alice.it', 'hotmail.de', 'bluewin.ch', 'optonline.net', 'yahoo.es', 'hotmail.no', 'pindotmedia.com', 'orange.fr', 'live.it', 'yahoo.co.id', 'yahoo.no', 'hotmail.es', 'morganstanley.com', 'wellsfargo.com', 'wanadoo.fr', 'facebook.com', 'yahoo.se', 'fema.dhs.gov', 'rogers.com', 'yahoo.com.hk', 'live.com.au', 'nic.in', 'nab.com.au', 'ubs.com', 'shaw.ca', 'umich.edu', 'westpac.com.au', 'yahoo.com.mx', 'yahoo.com.sg', 'farmersagent.com', 'yahoo.dk', 'dhs.gov']: + for domain in ['example.com']: f.write(f'{domain}\tsecure\n') os.system("postmap /etc/postfix/tls_policy.map") From 67db72d7743a5f2850e6c6a7d19b2ff99e8ea6d5 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Mon, 30 Aug 2021 17:00:12 +0200 Subject: [PATCH 010/222] Behave like documented --- core/postfix/conf/main.cf | 2 +- docs/configuration.rst | 8 ++++++-- towncrier/newsfragments/1798.feature | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/core/postfix/conf/main.cf b/core/postfix/conf/main.cf index 78ffcee1..ae54326d 100644 --- a/core/postfix/conf/main.cf +++ b/core/postfix/conf/main.cf @@ -59,7 +59,7 @@ tls_ssl_options = NO_COMPRESSION, NO_TICKET smtp_tls_mandatory_protocols = !SSLv2, !SSLv3 smtp_tls_protocols =!SSLv2,!SSLv3 smtp_tls_security_level = {{ OUTBOUND_TLS_LEVEL|default('dane') }} -smtp_tls_dane_insecure_mx_policy = dane +smtp_tls_dane_insecure_mx_policy = {% if DEFER_ON_TLS_ERROR == 'false' %}may{% else %}dane{% endif %} smtp_tls_policy_maps=hash:/etc/postfix/tls_policy.map, socketmap:unix:/tmp/mta-sts.socket:postfix smtp_tls_CApath = /etc/ssl/certs smtp_tls_session_cache_database = lmdb:/dev/shm/postfix/smtp_scache diff --git a/docs/configuration.rst b/docs/configuration.rst index 4fd84c07..7cf3c926 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -72,8 +72,12 @@ mail in following format: ``[HOST]:PORT``. ``RELAYUSER`` and ``RELAYPASSWORD`` can be used when authentication is needed. By default postfix uses "opportunistic TLS" for outbound mail. This can be changed -by setting ``OUTBOUND_TLS_LEVEL`` to ``encrypt`` or ``secure``. This setting is highly recommended -if you are using a relayhost that supports TLS but discouraged otherwise. ``DEFER_ON_TLS_ERROR`` (default: True) controls whether incomplete policies (DANE without DNSSEC or "testing" MTA-STS policies) will be taken into account and whether emails will be defered if the additional checks enforced by those policies fail. +by setting ``OUTBOUND_TLS_LEVEL`` to ``encrypt`` or ``secure``. This setting is +highly recommended if you are using a relayhost that supports TLS but discouraged +otherwise. ``DEFER_ON_TLS_ERROR`` (default: True) controls whether incomplete +policies (DANE without DNSSEC or "testing" MTA-STS policies) will be taken into +account and whether emails will be defered if the additional checks enforced by +those policies fail. Similarily by default nginx uses "opportunistic TLS" for inbound mail. This can be changed by setting ``INBOUND_TLS_ENFORCE`` to ``True``. Please note that this is forbidden for diff --git a/towncrier/newsfragments/1798.feature b/towncrier/newsfragments/1798.feature index 1b63a85c..125b1767 100644 --- a/towncrier/newsfragments/1798.feature +++ b/towncrier/newsfragments/1798.feature @@ -1 +1 @@ -Implement MTA-STS (use published policies) +Implement MTA-STS and DANE validation. Introduce DEFER_ON_TLS_ERROR (default: True) to harden or loosen the policy enforcement. From fccb0cc57f82f88235d3d10d58be4aff7762a483 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Mon, 30 Aug 2021 17:16:41 +0200 Subject: [PATCH 011/222] Add a longer max_age (15days) --- docs/faq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.rst b/docs/faq.rst index 92b208de..b5627743 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -383,7 +383,7 @@ Mailu can serve an `MTA-STS policy`_; To configure it you will need to: location ^~ /.well-known/mta-sts.txt { return 200 "version: STSv1 mode: enforce - max_age: 86401 + max_age: 1296000 mx: mailu.example.com\r\n"; } From fb34f5349348ebc22af08737a6ef641bd6a156a7 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Mon, 30 Aug 2021 17:18:19 +0200 Subject: [PATCH 012/222] Do operations in the right (safe) order --- docs/faq.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index b5627743..fb6f66df 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -374,7 +374,7 @@ How do I setup a MTA-STS policy? Mailu can serve an `MTA-STS policy`_; To configure it you will need to: -1. setup the appropriate DNS/CNAME record (``mta-sts.example.com`` -> ``mailu.example.com``) and DNS/TXT record (``_mta-sts.example.com`` -> ``v=STSv1; id=1``) paying attention to the ``TTL`` as this is used by MTA-STS. +1. add ``mta-sts.example.com`` to the ``HOSTNAMES`` configuration variable (and ensure that a valid SSL certificate is available for it) 2. configure an override with the policy itself; for example, your ``overrides/nginx/mta-sts.conf`` could read: @@ -387,7 +387,7 @@ Mailu can serve an `MTA-STS policy`_; To configure it you will need to: mx: mailu.example.com\r\n"; } -3. add ``mta-sts.example.com`` to the ``HOSTNAMES`` configuration variable (and ensure that a valid SSL certificate is available for it) +3. setup the appropriate DNS/CNAME record (``mta-sts.example.com`` -> ``mailu.example.com``) and DNS/TXT record (``_mta-sts.example.com`` -> ``v=STSv1; id=1``) paying attention to the ``TTL`` as this is used by MTA-STS. *issue reference:* `1798`_. From d607ba0ef2b345a44f9f95cd227a73f0a63f9489 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Mon, 30 Aug 2021 17:52:31 +0200 Subject: [PATCH 013/222] Clarify that a restart may be required --- docs/faq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.rst b/docs/faq.rst index fb6f66df..43fb8606 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -374,7 +374,7 @@ How do I setup a MTA-STS policy? Mailu can serve an `MTA-STS policy`_; To configure it you will need to: -1. add ``mta-sts.example.com`` to the ``HOSTNAMES`` configuration variable (and ensure that a valid SSL certificate is available for it) +1. add ``mta-sts.example.com`` to the ``HOSTNAMES`` configuration variable (and ensure that a valid SSL certificate is available for it; this may mean restarting your smtp container) 2. configure an override with the policy itself; for example, your ``overrides/nginx/mta-sts.conf`` could read: From a1da4daa4c30668d98325cebd640ae3cf2f5fc97 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Tue, 31 Aug 2021 20:24:06 +0200 Subject: [PATCH 014/222] Implement the DANE-only lookup policyd https://github.com/Snawoot/postfix-mta-sts-resolver/issues/67 for context --- core/admin/mailu/internal/views/postfix.py | 3 +++ core/admin/mailu/utils.py | 24 +++++++++++++++++++++- core/postfix/conf/main.cf | 2 +- core/postfix/start.py | 1 + 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/core/admin/mailu/internal/views/postfix.py b/core/admin/mailu/internal/views/postfix.py index 2e7d0b9b..330fed5b 100644 --- a/core/admin/mailu/internal/views/postfix.py +++ b/core/admin/mailu/internal/views/postfix.py @@ -7,6 +7,9 @@ import idna import re import srslib +@internal.route("/postfix/dane/") +def postfix_dane_map(domain_name): + return flask.jsonify('dane-only') if utils.has_dane_record(domain_name) else flask.abort(404) @internal.route("/postfix/domain/") def postfix_mailbox_domain(domain_name): diff --git a/core/admin/mailu/utils.py b/core/admin/mailu/utils.py index 02150754..914638fa 100644 --- a/core/admin/mailu/utils.py +++ b/core/admin/mailu/utils.py @@ -6,6 +6,9 @@ try: except ImportError: import pickle +import dns +import dns.resolver + import hmac import secrets import time @@ -25,7 +28,6 @@ from itsdangerous.encoding import want_bytes from werkzeug.datastructures import CallbackDict from werkzeug.contrib import fixers - # Login configuration login = flask_login.LoginManager() login.login_view = "ui.login" @@ -37,6 +39,26 @@ def handle_needs_login(): flask.url_for('ui.login', next=flask.request.endpoint) ) +# DNS stub configured to do DNSSEC enabled queries +resolver = dns.resolver.Resolver() +resolver.use_edns(0, 0, 1500) +resolver.flags = dns.flags.AD | dns.flags.RD + +def has_dane_record(domain, timeout=5): + try: + result = resolver.query(f'_25._tcp.{domain}', dns.rdatatype.TLSA,dns.rdataclass.IN, lifetime=timeout) + if (result.response.flags & dns.flags.AD) == dns.flags.AD: + for record in result: + if isinstance(record, dns.rdtypes.ANY.TLSA.TLSA): + record.validate() + if record.usage in [2,3]: # postfix wants DANE-only + return True + except dns.resolver.NoNameservers: + # this could be an attack / a failed DNSSEC lookup + return True + except: + pass + # Rate limiter limiter = limiter.LimitWraperFactory() diff --git a/core/postfix/conf/main.cf b/core/postfix/conf/main.cf index ae54326d..16fdfa6e 100644 --- a/core/postfix/conf/main.cf +++ b/core/postfix/conf/main.cf @@ -60,7 +60,7 @@ smtp_tls_mandatory_protocols = !SSLv2, !SSLv3 smtp_tls_protocols =!SSLv2,!SSLv3 smtp_tls_security_level = {{ OUTBOUND_TLS_LEVEL|default('dane') }} smtp_tls_dane_insecure_mx_policy = {% if DEFER_ON_TLS_ERROR == 'false' %}may{% else %}dane{% endif %} -smtp_tls_policy_maps=hash:/etc/postfix/tls_policy.map, socketmap:unix:/tmp/mta-sts.socket:postfix +smtp_tls_policy_maps=hash:/etc/postfix/tls_policy.map, ${podop}dane, socketmap:unix:/tmp/mta-sts.socket:postfix smtp_tls_CApath = /etc/ssl/certs smtp_tls_session_cache_database = lmdb:/dev/shm/postfix/smtp_scache smtpd_tls_session_cache_database = lmdb:/dev/shm/postfix/smtpd_scache diff --git a/core/postfix/start.py b/core/postfix/start.py index 69855e29..03dca93c 100755 --- a/core/postfix/start.py +++ b/core/postfix/start.py @@ -21,6 +21,7 @@ def start_podop(): run_server(0, "postfix", "/tmp/podop.socket", [ ("transport", "url", url + "transport/§"), ("alias", "url", url + "alias/§"), + ("dane", "url", url + "dane/§"), ("domain", "url", url + "domain/§"), ("mailbox", "url", url + "mailbox/§"), ("recipientmap", "url", url + "recipient/map/§"), From 9f66e2672b3c646570fba1f644f1b35e44ebaeec Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Tue, 31 Aug 2021 20:44:57 +0200 Subject: [PATCH 015/222] Use DEFER_ON_TLS_ERROR here too We just don't know whether the lookup failed because we are under attack or whether it's a glitch; the safe behaviour is to defer --- core/admin/mailu/configuration.py | 1 + core/admin/mailu/utils.py | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/core/admin/mailu/configuration.py b/core/admin/mailu/configuration.py index 7cd3a56b..4c48fcc4 100644 --- a/core/admin/mailu/configuration.py +++ b/core/admin/mailu/configuration.py @@ -35,6 +35,7 @@ DEFAULT_CONFIG = { 'WILDCARD_SENDERS': '', 'TLS_FLAVOR': 'cert', 'INBOUND_TLS_ENFORCE': False, + 'DEFER_ON_TLS_ERROR': True, 'AUTH_RATELIMIT': '1000/minute;10000/hour', 'AUTH_RATELIMIT_SUBNET': False, 'DISABLE_STATISTICS': False, diff --git a/core/admin/mailu/utils.py b/core/admin/mailu/utils.py index 914638fa..2313a1e6 100644 --- a/core/admin/mailu/utils.py +++ b/core/admin/mailu/utils.py @@ -54,8 +54,10 @@ def has_dane_record(domain, timeout=5): if record.usage in [2,3]: # postfix wants DANE-only return True except dns.resolver.NoNameservers: - # this could be an attack / a failed DNSSEC lookup - return True + # If the DNSSEC data is invalid and the DNS resolver is DNSSEC enabled + # we will receive this non-specific exception. The safe behaviour is to + # accept to defer the email. + return app.config['DEFER_ON_TLS_ERROR'] except: pass From 489520f0673a25482294cb5b1b7d6bb28a3b8dd1 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Wed, 1 Sep 2021 08:41:39 +0200 Subject: [PATCH 016/222] forgot about alpine/lmdb --- core/postfix/conf/main.cf | 2 +- core/postfix/start.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/postfix/conf/main.cf b/core/postfix/conf/main.cf index 16fdfa6e..6152388c 100644 --- a/core/postfix/conf/main.cf +++ b/core/postfix/conf/main.cf @@ -60,7 +60,7 @@ smtp_tls_mandatory_protocols = !SSLv2, !SSLv3 smtp_tls_protocols =!SSLv2,!SSLv3 smtp_tls_security_level = {{ OUTBOUND_TLS_LEVEL|default('dane') }} smtp_tls_dane_insecure_mx_policy = {% if DEFER_ON_TLS_ERROR == 'false' %}may{% else %}dane{% endif %} -smtp_tls_policy_maps=hash:/etc/postfix/tls_policy.map, ${podop}dane, socketmap:unix:/tmp/mta-sts.socket:postfix +smtp_tls_policy_maps=lmdb:/etc/postfix/tls_policy.map, ${podop}dane, socketmap:unix:/tmp/mta-sts.socket:postfix smtp_tls_CApath = /etc/ssl/certs smtp_tls_session_cache_database = lmdb:/dev/shm/postfix/smtp_scache smtpd_tls_session_cache_database = lmdb:/dev/shm/postfix/smtpd_scache diff --git a/core/postfix/start.py b/core/postfix/start.py index 03dca93c..8aa279ef 100755 --- a/core/postfix/start.py +++ b/core/postfix/start.py @@ -20,7 +20,7 @@ def start_podop(): # TODO: Remove verbosity setting from Podop? run_server(0, "postfix", "/tmp/podop.socket", [ ("transport", "url", url + "transport/§"), - ("alias", "url", url + "alias/§"), + ("alias", "url", url + "alias/§"), ("dane", "url", url + "dane/§"), ("domain", "url", url + "domain/§"), ("mailbox", "url", url + "mailbox/§"), @@ -79,7 +79,7 @@ if os.path.exists("/overrides/mta-sts-daemon.yml"): shutil.copyfile("/overrides/mta-sts-daemon.yml", "/etc/mta-sts-daemon.yml") conf.jinja("/etc/mta-sts-daemon.yml", os.environ, "/etc/mta-sts-daemon.yml") -if not os.path.exists("/etc/postfix/tls_policy.map.db"): +if not os.path.exists("/etc/postfix/tls_policy.map.lmdb"): with open("/etc/postfix/tls_policy.map", "w") as f: for domain in ['example.com']: f.write(f'{domain}\tsecure\n') From 92cc664e82f3f40f9a247093d440c3b75ff768d8 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Wed, 1 Sep 2021 08:41:59 +0200 Subject: [PATCH 017/222] Cosmetic change --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8c7d1640..4c19ad78 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Main features include: - **Web access**, multiple Webmails and administration interface - **User features**, aliases, auto-reply, auto-forward, fetched accounts - **Admin features**, global admins, announcements, per-domain delegation, quotas -- **Security**, enforced TLS, MTA-STS, Letsencrypt!, outgoing DKIM, anti-virus scanner +- **Security**, enforced TLS, DANE, MTA-STS, Letsencrypt!, outgoing DKIM, anti-virus scanner - **Antispam**, auto-learn, greylisting, DMARC and SPF - **Freedom**, all FOSS components, no tracker included From c1d94bb72563430d151916de0e3d9e31708348fe Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Wed, 1 Sep 2021 09:01:04 +0200 Subject: [PATCH 018/222] Ensure that postfix will be able to use the TLSA records see https://www.huque.com/dane/testsite/ for the testcases --- core/admin/mailu/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/admin/mailu/utils.py b/core/admin/mailu/utils.py index 2313a1e6..66cf0476 100644 --- a/core/admin/mailu/utils.py +++ b/core/admin/mailu/utils.py @@ -44,14 +44,14 @@ resolver = dns.resolver.Resolver() resolver.use_edns(0, 0, 1500) resolver.flags = dns.flags.AD | dns.flags.RD -def has_dane_record(domain, timeout=5): +def has_dane_record(domain, timeout=10): try: result = resolver.query(f'_25._tcp.{domain}', dns.rdatatype.TLSA,dns.rdataclass.IN, lifetime=timeout) if (result.response.flags & dns.flags.AD) == dns.flags.AD: for record in result: if isinstance(record, dns.rdtypes.ANY.TLSA.TLSA): record.validate() - if record.usage in [2,3]: # postfix wants DANE-only + if record.usage in [2,3] and record.selector in [0,1] and record.mtype in [0,1,2]: return True except dns.resolver.NoNameservers: # If the DNSSEC data is invalid and the DNS resolver is DNSSEC enabled From 4abf49edf429e2ebb594a002c9f5e922a6e824e7 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Wed, 1 Sep 2021 09:15:13 +0200 Subject: [PATCH 019/222] indent --- core/postfix/start.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/postfix/start.py b/core/postfix/start.py index 8aa279ef..19c23c19 100755 --- a/core/postfix/start.py +++ b/core/postfix/start.py @@ -19,10 +19,10 @@ def start_podop(): url = "http://" + os.environ["ADMIN_ADDRESS"] + "/internal/postfix/" # TODO: Remove verbosity setting from Podop? run_server(0, "postfix", "/tmp/podop.socket", [ - ("transport", "url", url + "transport/§"), - ("alias", "url", url + "alias/§"), - ("dane", "url", url + "dane/§"), - ("domain", "url", url + "domain/§"), + ("transport", "url", url + "transport/§"), + ("alias", "url", url + "alias/§"), + ("dane", "url", url + "dane/§"), + ("domain", "url", url + "domain/§"), ("mailbox", "url", url + "mailbox/§"), ("recipientmap", "url", url + "recipient/map/§"), ("sendermap", "url", url + "sender/map/§"), From fe186afb6f319fb6cc9dc5f903db7fa4a7414532 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Wed, 1 Sep 2021 18:52:35 +0200 Subject: [PATCH 020/222] Revert "Switch to openssl to workaround alpine #12763" This reverts commit f8362d04e4d46c44ab07beffb77cdd041af193c0. --- core/admin/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/admin/Dockerfile b/core/admin/Dockerfile index 97cf1736..f10d3251 100644 --- a/core/admin/Dockerfile +++ b/core/admin/Dockerfile @@ -24,9 +24,9 @@ RUN mkdir -p /app WORKDIR /app COPY requirements-prod.txt requirements.txt -RUN apk add --no-cache openssl curl postgresql-libs mariadb-connector-c \ +RUN apk add --no-cache libressl curl postgresql-libs mariadb-connector-c \ && apk add --no-cache --virtual build-dep \ - openssl-dev libffi-dev python3-dev build-base postgresql-dev mariadb-connector-c-dev cargo \ + libressl-dev libffi-dev python3-dev build-base postgresql-dev mariadb-connector-c-dev cargo \ && pip3 install -r requirements.txt \ && apk del --no-cache build-dep From 0c4455ccf5e453f5dc2e1810601696d7b4707f01 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Wed, 1 Sep 2021 18:53:20 +0200 Subject: [PATCH 021/222] Revert "Rollback to alpine 1.12" This reverts commit e1ddbb6eec85cde7a6efed13f66e887c56334a80. --- optional/unbound/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optional/unbound/Dockerfile b/optional/unbound/Dockerfile index abb45420..2b472d44 100644 --- a/optional/unbound/Dockerfile +++ b/optional/unbound/Dockerfile @@ -1,4 +1,4 @@ -ARG DISTRO=alpine:3.12 +ARG DISTRO=alpine:3.14 FROM $DISTRO # python3 shared with most images RUN apk add --no-cache \ From d7c2b510c7321cfa79989f6817f16e63e88946de Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Wed, 1 Sep 2021 18:56:44 +0200 Subject: [PATCH 022/222] Give alpine 3.14.2 a shot --- core/admin/Dockerfile | 2 +- core/dovecot/Dockerfile | 2 +- core/nginx/Dockerfile | 2 +- core/none/Dockerfile | 2 +- core/postfix/Dockerfile | 2 +- core/rspamd/Dockerfile | 2 +- optional/clamav/Dockerfile | 2 +- optional/fetchmail/Dockerfile | 2 +- optional/postgresql/Dockerfile | 2 +- optional/radicale/Dockerfile | 2 +- optional/unbound/Dockerfile | 2 +- setup/Dockerfile | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/admin/Dockerfile b/core/admin/Dockerfile index f10d3251..4b991269 100644 --- a/core/admin/Dockerfile +++ b/core/admin/Dockerfile @@ -1,5 +1,5 @@ # First stage to build assets -ARG DISTRO=alpine:3.14 +ARG DISTRO=alpine:3.14.2 ARG ARCH="" FROM ${ARCH}node:8 as assets COPY --from=balenalib/rpi-alpine:3.14 /usr/bin/qemu-arm-static /usr/bin/qemu-arm-static diff --git a/core/dovecot/Dockerfile b/core/dovecot/Dockerfile index 22145bde..49fcb866 100644 --- a/core/dovecot/Dockerfile +++ b/core/dovecot/Dockerfile @@ -1,4 +1,4 @@ -ARG DISTRO=alpine:3.14 +ARG DISTRO=alpine:3.14.2 FROM $DISTRO as builder WORKDIR /tmp RUN apk add git build-base automake autoconf libtool dovecot-dev xapian-core-dev icu-dev diff --git a/core/nginx/Dockerfile b/core/nginx/Dockerfile index 1906ed31..3837e64b 100644 --- a/core/nginx/Dockerfile +++ b/core/nginx/Dockerfile @@ -1,4 +1,4 @@ -ARG DISTRO=alpine:3.14 +ARG DISTRO=alpine:3.14.2 FROM $DISTRO # python3 shared with most images RUN apk add --no-cache \ diff --git a/core/none/Dockerfile b/core/none/Dockerfile index 51b8d1c5..bae5e8a3 100644 --- a/core/none/Dockerfile +++ b/core/none/Dockerfile @@ -1,6 +1,6 @@ # This is an idle image to dynamically replace any component if disabled. -ARG DISTRO=alpine:3.14 +ARG DISTRO=alpine:3.14.2 FROM $DISTRO CMD sleep 1000000d diff --git a/core/postfix/Dockerfile b/core/postfix/Dockerfile index 062155c1..652404e8 100644 --- a/core/postfix/Dockerfile +++ b/core/postfix/Dockerfile @@ -1,4 +1,4 @@ -ARG DISTRO=alpine:3.14 +ARG DISTRO=alpine:3.14.2 FROM $DISTRO # python3 shared with most images RUN apk add --no-cache \ diff --git a/core/rspamd/Dockerfile b/core/rspamd/Dockerfile index 6706ef14..0b3b94f7 100644 --- a/core/rspamd/Dockerfile +++ b/core/rspamd/Dockerfile @@ -1,4 +1,4 @@ -ARG DISTRO=alpine:3.14 +ARG DISTRO=alpine:3.14.2 FROM $DISTRO # python3 shared with most images RUN apk add --no-cache \ diff --git a/optional/clamav/Dockerfile b/optional/clamav/Dockerfile index 20cebcdc..efad01ad 100644 --- a/optional/clamav/Dockerfile +++ b/optional/clamav/Dockerfile @@ -1,4 +1,4 @@ -ARG DISTRO=alpine:3.14 +ARG DISTRO=alpine:3.14.2 FROM $DISTRO # python3 shared with most images RUN apk add --no-cache \ diff --git a/optional/fetchmail/Dockerfile b/optional/fetchmail/Dockerfile index 506e409a..d3397a22 100644 --- a/optional/fetchmail/Dockerfile +++ b/optional/fetchmail/Dockerfile @@ -1,4 +1,4 @@ -ARG DISTRO=alpine:3.14 +ARG DISTRO=alpine:3.14.2 FROM $DISTRO # python3 shared with most images diff --git a/optional/postgresql/Dockerfile b/optional/postgresql/Dockerfile index 0f5034da..ab197b62 100644 --- a/optional/postgresql/Dockerfile +++ b/optional/postgresql/Dockerfile @@ -1,4 +1,4 @@ -ARG DISTRO=alpine:3.14 +ARG DISTRO=alpine:3.14.2 FROM $DISTRO # python3 shared with most images RUN apk add --no-cache \ diff --git a/optional/radicale/Dockerfile b/optional/radicale/Dockerfile index 13761164..21c1d437 100644 --- a/optional/radicale/Dockerfile +++ b/optional/radicale/Dockerfile @@ -1,4 +1,4 @@ -ARG DISTRO=alpine:3.14 +ARG DISTRO=alpine:3.14.2 FROM $DISTRO # python3 shared with most images diff --git a/optional/unbound/Dockerfile b/optional/unbound/Dockerfile index 2b472d44..da979496 100644 --- a/optional/unbound/Dockerfile +++ b/optional/unbound/Dockerfile @@ -1,4 +1,4 @@ -ARG DISTRO=alpine:3.14 +ARG DISTRO=alpine:3.14.2 FROM $DISTRO # python3 shared with most images RUN apk add --no-cache \ diff --git a/setup/Dockerfile b/setup/Dockerfile index 5775ab6b..e0f685ee 100644 --- a/setup/Dockerfile +++ b/setup/Dockerfile @@ -1,4 +1,4 @@ -ARG DISTRO=alpine:3.14 +ARG DISTRO=alpine:3.14.2 FROM $DISTRO RUN mkdir -p /app From 9fac3d7ad3895c1e03016dd3eadbff186c875711 Mon Sep 17 00:00:00 2001 From: Diman0 Date: Thu, 2 Sep 2021 13:36:42 +0200 Subject: [PATCH 023/222] Initial implementation for standalone sso page --- core/admin/Dockerfile | 1 + core/admin/mailu/__init__.py | 4 +- core/admin/mailu/sso/__init__.py | 6 +++ core/admin/mailu/sso/forms.py | 14 ++++++ core/admin/mailu/sso/templates/base_sso.html | 51 ++++++++++++++++++++ core/admin/mailu/sso/templates/form_sso.html | 7 +++ core/admin/mailu/sso/templates/login.html | 9 ++++ core/admin/mailu/sso/views/__init__.py | 3 ++ core/admin/mailu/sso/views/base.py | 30 ++++++++++++ core/admin/mailu/sso/views/hello.py | 6 +++ core/admin/mailu/ui/forms.py | 4 +- core/admin/mailu/ui/templates/login.html | 9 ---- core/admin/mailu/ui/views/base.py | 3 +- core/admin/mailu/utils.py | 4 +- 14 files changed, 135 insertions(+), 16 deletions(-) create mode 100644 core/admin/mailu/sso/__init__.py create mode 100644 core/admin/mailu/sso/forms.py create mode 100644 core/admin/mailu/sso/templates/base_sso.html create mode 100644 core/admin/mailu/sso/templates/form_sso.html create mode 100644 core/admin/mailu/sso/templates/login.html create mode 100644 core/admin/mailu/sso/views/__init__.py create mode 100644 core/admin/mailu/sso/views/base.py create mode 100644 core/admin/mailu/sso/views/hello.py delete mode 100644 core/admin/mailu/ui/templates/login.html diff --git a/core/admin/Dockerfile b/core/admin/Dockerfile index 97cf1736..2406bb5c 100644 --- a/core/admin/Dockerfile +++ b/core/admin/Dockerfile @@ -31,6 +31,7 @@ RUN apk add --no-cache openssl curl postgresql-libs mariadb-connector-c \ && apk del --no-cache build-dep COPY --from=assets static ./mailu/ui/static +COPY --from=assets static ./mailu/sso/static COPY mailu ./mailu COPY migrations ./migrations COPY start.py /start.py diff --git a/core/admin/mailu/__init__.py b/core/admin/mailu/__init__.py index 8ab8ed0e..f80533fe 100644 --- a/core/admin/mailu/__init__.py +++ b/core/admin/mailu/__init__.py @@ -48,10 +48,10 @@ def create_app_from_config(config): ) # Import views - from mailu import ui, internal + from mailu import ui, internal, sso app.register_blueprint(ui.ui, url_prefix='/ui') app.register_blueprint(internal.internal, url_prefix='/internal') - + app.register_blueprint(sso.sso, url_prefix='/sso') return app diff --git a/core/admin/mailu/sso/__init__.py b/core/admin/mailu/sso/__init__.py new file mode 100644 index 00000000..2d3c6d84 --- /dev/null +++ b/core/admin/mailu/sso/__init__.py @@ -0,0 +1,6 @@ +from flask import Blueprint + + +sso = Blueprint('sso', __name__, static_folder='static', template_folder='templates') + +from mailu.sso.views import * diff --git a/core/admin/mailu/sso/forms.py b/core/admin/mailu/sso/forms.py new file mode 100644 index 00000000..a81667a2 --- /dev/null +++ b/core/admin/mailu/sso/forms.py @@ -0,0 +1,14 @@ +from wtforms import validators, fields, widgets +from wtforms_components import fields as fields_ +from flask_babel import lazy_gettext as _ + +import flask_login +import flask_wtf +import re + +class LoginForm(flask_wtf.FlaskForm): + class Meta: + csrf = False + email = fields.StringField(_('E-mail'), [validators.Email()]) + pw = fields.PasswordField(_('Password'), [validators.DataRequired()]) + submit = fields.SubmitField(_('Sign in')) diff --git a/core/admin/mailu/sso/templates/base_sso.html b/core/admin/mailu/sso/templates/base_sso.html new file mode 100644 index 00000000..a95cb23b --- /dev/null +++ b/core/admin/mailu/sso/templates/base_sso.html @@ -0,0 +1,51 @@ +{% import "macros.html" as macros %} +{% import "bootstrap/utils.html" as utils %} + + + + + + + Mailu-login - {{ config["SITENAME"] }} + + +
+
+ +
+ +
+
+
+ {% block main_action %} + {% endblock %} +
+

+ {% block title %}{% endblock %} + {% block subtitle %}{% endblock %} +

+
+ +
+ {{ utils.flashed_messages(container=False) }} + {% block content %}{% endblock %} +
+
+ +
+ + + + diff --git a/core/admin/mailu/sso/templates/form_sso.html b/core/admin/mailu/sso/templates/form_sso.html new file mode 100644 index 00000000..fcabad41 --- /dev/null +++ b/core/admin/mailu/sso/templates/form_sso.html @@ -0,0 +1,7 @@ +{% extends "base_sso.html" %} + +{% block content %} +{% call macros.box() %} +{{ macros.form(form) }} +{% endcall %} +{% endblock %} diff --git a/core/admin/mailu/sso/templates/login.html b/core/admin/mailu/sso/templates/login.html new file mode 100644 index 00000000..851e6643 --- /dev/null +++ b/core/admin/mailu/sso/templates/login.html @@ -0,0 +1,9 @@ +{% extends "form_sso.html" %} + +{% block title %} +{% trans %}Sign in{% endtrans %} +{% endblock %} + +{% block subtitle %} +{% trans %}to access IF statement for switch text for loggin in what the administration tools{% endtrans %} +{% endblock %} diff --git a/core/admin/mailu/sso/views/__init__.py b/core/admin/mailu/sso/views/__init__.py new file mode 100644 index 00000000..38efde4c --- /dev/null +++ b/core/admin/mailu/sso/views/__init__.py @@ -0,0 +1,3 @@ +__all__ = [ + 'base', 'hello' +] diff --git a/core/admin/mailu/sso/views/base.py b/core/admin/mailu/sso/views/base.py new file mode 100644 index 00000000..dd6f60c9 --- /dev/null +++ b/core/admin/mailu/sso/views/base.py @@ -0,0 +1,30 @@ +from mailu import models +from mailu.sso import sso, forms + +from flask import current_app as app +import flask +import flask_login + +@sso.route('/login', methods=['GET', 'POST']) +def login(): + form = forms.LoginForm() + if form.validate_on_submit(): + user = models.User.login(form.email.data, form.pw.data) + if user: + flask.session.regenerate() + flask_login.login_user(user) + endpoint = flask.request.args.get('next', 'ui.index') + return flask.redirect(flask.url_for(endpoint) + or flask.url_for('ui.index')) + else: + flask.flash('Wrong e-mail or password', 'error') + return flask.render_template('login.html', form=form) + +""" +@ui.route('/logout', methods=['GET']) +@access.authenticated +def logout(): + flask_login.logout_user() + flask.session.destroy() + return flask.redirect(flask.url_for('.index')) +""" \ No newline at end of file diff --git a/core/admin/mailu/sso/views/hello.py b/core/admin/mailu/sso/views/hello.py new file mode 100644 index 00000000..2e9b5e35 --- /dev/null +++ b/core/admin/mailu/sso/views/hello.py @@ -0,0 +1,6 @@ +from mailu.sso import sso +from flask import current_app as app + +@sso.route("/") +def hello_world(): + return "

Hello, World!

" diff --git a/core/admin/mailu/ui/forms.py b/core/admin/mailu/ui/forms.py index 32bb31ab..dff7008e 100644 --- a/core/admin/mailu/ui/forms.py +++ b/core/admin/mailu/ui/forms.py @@ -44,14 +44,14 @@ class MultipleEmailAddressesVerify(object): class ConfirmationForm(flask_wtf.FlaskForm): submit = fields.SubmitField(_('Confirm')) - +""" class LoginForm(flask_wtf.FlaskForm): class Meta: csrf = False email = fields.StringField(_('E-mail'), [validators.Email()]) pw = fields.PasswordField(_('Password'), [validators.DataRequired()]) submit = fields.SubmitField(_('Sign in')) - +""" class DomainForm(flask_wtf.FlaskForm): name = fields.StringField(_('Domain name'), [validators.DataRequired()]) diff --git a/core/admin/mailu/ui/templates/login.html b/core/admin/mailu/ui/templates/login.html deleted file mode 100644 index 26c47c08..00000000 --- a/core/admin/mailu/ui/templates/login.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "form.html" %} - -{% block title %} -{% trans %}Sign in{% endtrans %} -{% endblock %} - -{% block subtitle %} -{% trans %}to access the administration tools{% endtrans %} -{% endblock %} diff --git a/core/admin/mailu/ui/views/base.py b/core/admin/mailu/ui/views/base.py index eb5490bc..fc9daba6 100644 --- a/core/admin/mailu/ui/views/base.py +++ b/core/admin/mailu/ui/views/base.py @@ -12,6 +12,7 @@ def index(): return flask.redirect(flask.url_for('.user_settings')) +""" @ui.route('/login', methods=['GET', 'POST']) def login(): form = forms.LoginForm() @@ -26,7 +27,7 @@ def login(): else: flask.flash('Wrong e-mail or password', 'error') return flask.render_template('login.html', form=form) - +""" @ui.route('/logout', methods=['GET']) @access.authenticated diff --git a/core/admin/mailu/utils.py b/core/admin/mailu/utils.py index 02150754..c30f259e 100644 --- a/core/admin/mailu/utils.py +++ b/core/admin/mailu/utils.py @@ -28,13 +28,13 @@ from werkzeug.contrib import fixers # Login configuration login = flask_login.LoginManager() -login.login_view = "ui.login" +login.login_view = "sso.login" @login.unauthorized_handler def handle_needs_login(): """ redirect unauthorized requests to login page """ return flask.redirect( - flask.url_for('ui.login', next=flask.request.endpoint) + flask.url_for('sso.login', next=flask.request.endpoint) ) # Rate limiter From 8868aec0dcd02101ea09396c5d064b046b75ea44 Mon Sep 17 00:00:00 2001 From: Diman0 Date: Thu, 2 Sep 2021 17:08:50 +0200 Subject: [PATCH 024/222] Merge master. Make sso login working for admin. --- core/admin/mailu/configuration.py | 1 + core/admin/mailu/sso/forms.py | 2 + core/admin/mailu/sso/templates/base_sso.html | 61 +++++++++++-------- core/admin/mailu/sso/templates/form_sso.html | 2 +- core/admin/mailu/sso/templates/login.html | 6 +- .../mailu/sso/templates/sidebar_sso.html | 60 ++++++++++++++++++ core/admin/mailu/sso/views/base.py | 15 ++--- core/admin/mailu/ui/templates/sidebar.html | 2 +- core/nginx/conf/nginx.conf | 8 ++- 9 files changed, 115 insertions(+), 42 deletions(-) create mode 100644 core/admin/mailu/sso/templates/sidebar_sso.html diff --git a/core/admin/mailu/configuration.py b/core/admin/mailu/configuration.py index 7cd3a56b..025a173c 100644 --- a/core/admin/mailu/configuration.py +++ b/core/admin/mailu/configuration.py @@ -51,6 +51,7 @@ DEFAULT_CONFIG = { # Web settings 'SITENAME': 'Mailu', 'WEBSITE': 'https://mailu.io', + 'ADMIN' : 'none', 'WEB_ADMIN': '/admin', 'WEB_WEBMAIL': '/webmail', 'WEBMAIL': 'none', diff --git a/core/admin/mailu/sso/forms.py b/core/admin/mailu/sso/forms.py index a81667a2..bc2b5363 100644 --- a/core/admin/mailu/sso/forms.py +++ b/core/admin/mailu/sso/forms.py @@ -6,6 +6,8 @@ import flask_login import flask_wtf import re +LOCALPART_REGEX = "^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*$" + class LoginForm(flask_wtf.FlaskForm): class Meta: csrf = False diff --git a/core/admin/mailu/sso/templates/base_sso.html b/core/admin/mailu/sso/templates/base_sso.html index a95cb23b..0edd692c 100644 --- a/core/admin/mailu/sso/templates/base_sso.html +++ b/core/admin/mailu/sso/templates/base_sso.html @@ -6,46 +6,53 @@ - Mailu-login - {{ config["SITENAME"] }} + Mailu - {{ config["SITENAME"] }} - +
-
- -
- + +
-
- {% block main_action %} - {% endblock %} +
+
+
+

{% block title %}{% endblock %}

+ {% block subtitle %}{% endblock %} +
+
+ {% block main_action %} + {% endblock %} +
+
-

- {% block title %}{% endblock %} - {% block subtitle %}{% endblock %} -

-
+
{{ utils.flashed_messages(container=False) }} {% block content %}{% endblock %} -
+
- - + + diff --git a/core/admin/mailu/sso/templates/form_sso.html b/core/admin/mailu/sso/templates/form_sso.html index fcabad41..d28b82bf 100644 --- a/core/admin/mailu/sso/templates/form_sso.html +++ b/core/admin/mailu/sso/templates/form_sso.html @@ -1,7 +1,7 @@ {% extends "base_sso.html" %} {% block content %} -{% call macros.box() %} +{% call macros.card() %} {{ macros.form(form) }} {% endcall %} {% endblock %} diff --git a/core/admin/mailu/sso/templates/login.html b/core/admin/mailu/sso/templates/login.html index 851e6643..81d83a46 100644 --- a/core/admin/mailu/sso/templates/login.html +++ b/core/admin/mailu/sso/templates/login.html @@ -5,5 +5,9 @@ {% endblock %} {% block subtitle %} -{% trans %}to access IF statement for switch text for loggin in what the administration tools{% endtrans %} +{% if endpoint == 'ui.index' %} +{% trans %}to access the configuration page{% endtrans %} +{% elif endpoint == 'ui.webmail' %} +{% trans %}to access the webmail page{% endtrans %} +{% endif %} {% endblock %} diff --git a/core/admin/mailu/sso/templates/sidebar_sso.html b/core/admin/mailu/sso/templates/sidebar_sso.html new file mode 100644 index 00000000..6e919731 --- /dev/null +++ b/core/admin/mailu/sso/templates/sidebar_sso.html @@ -0,0 +1,60 @@ + diff --git a/core/admin/mailu/sso/views/base.py b/core/admin/mailu/sso/views/base.py index dd6f60c9..702b1582 100644 --- a/core/admin/mailu/sso/views/base.py +++ b/core/admin/mailu/sso/views/base.py @@ -1,5 +1,6 @@ from mailu import models from mailu.sso import sso, forms +from mailu.ui import access from flask import current_app as app import flask @@ -8,23 +9,15 @@ import flask_login @sso.route('/login', methods=['GET', 'POST']) def login(): form = forms.LoginForm() + endpoint = flask.request.args.get('next', 'ui.index') if form.validate_on_submit(): user = models.User.login(form.email.data, form.pw.data) if user: flask.session.regenerate() flask_login.login_user(user) - endpoint = flask.request.args.get('next', 'ui.index') return flask.redirect(flask.url_for(endpoint) or flask.url_for('ui.index')) else: flask.flash('Wrong e-mail or password', 'error') - return flask.render_template('login.html', form=form) - -""" -@ui.route('/logout', methods=['GET']) -@access.authenticated -def logout(): - flask_login.logout_user() - flask.session.destroy() - return flask.redirect(flask.url_for('.index')) -""" \ No newline at end of file + return flask.render_template('login.html', form=form, endpoint=endpoint) + \ No newline at end of file diff --git a/core/admin/mailu/ui/templates/sidebar.html b/core/admin/mailu/ui/templates/sidebar.html index 0fdae9db..0938f8ac 100644 --- a/core/admin/mailu/ui/templates/sidebar.html +++ b/core/admin/mailu/ui/templates/sidebar.html @@ -125,7 +125,7 @@ {% else %}