Compare commits

...

475 Commits

Author SHA1 Message Date
FreddleSpl0it
527f27d249
Merge pull request #6632 from mailcow/staging
Update 2025-07
2025-07-15 07:48:37 +02:00
FreddleSpl0it
02557b2098
Merge pull request #6631 from mailcow/fix/jinja2-rendering
[Dovecot] Use Jinja2 sandbox for rendering quota and quarantine notif…
2025-07-15 07:38:20 +02:00
milkmaker
4c7a9ed195
Translations update from Weblate (#6629)
* [Web] Updated lang.fr-fr.json

Co-authored-by: Tagada <githubtagada@cant.at>

* [Web] Updated lang.pl-pl.json

Co-authored-by: robsonek <admin@licytant.pl>

---------

Co-authored-by: Tagada <githubtagada@cant.at>
Co-authored-by: robsonek <admin@licytant.pl>
2025-07-13 16:20:40 +02:00
milkmaker
d5b30a7a08
[Web] Updated lang.pt-pt.json (#6622)
Co-authored-by: luiscanato <luiscanato@gmail.com>
2025-07-06 16:42:58 +02:00
FreddleSpl0it
b7acef4d9d
Merge pull request #6617 from mailcow/fix/acme-contact
[ACME] Remove deprecated ACME_CONTACT variable
2025-07-03 12:49:17 +02:00
FreddleSpl0it
fc43c26c48
Remove obsolete ACME_CONTACT option and related comments from mailcow.conf 2025-07-03 12:38:28 +02:00
milkmaker
b12ce1eacd
update postscreen_access.cidr (#6611) 2025-07-02 17:12:06 +02:00
FreddleSpl0it
ec6dbb099a
[ACME] Remove deprecated ACME_CONTACT variable 2025-07-02 10:37:23 +02:00
FreddleSpl0it
2fbbbbe9a9
[Dovecot] Use Jinja2 sandbox for rendering quota and quarantine notifications 2025-07-02 08:59:29 +02:00
milkmaker
1e4f3c55d8
[Web] Updated lang.pt-pt.json (#6614)
Co-authored-by: luiscanato <luiscanato@gmail.com>
2025-07-01 17:14:24 +02:00
milkmaker
a0f5454c2a
[Web] Updated lang.si-si.json (#6609)
Co-authored-by: Matjaž Tekavec <matjaz@moj-svet.si>
2025-06-29 18:12:59 +02:00
milkmaker
4e7adacda9
[Web] Updated lang.si-si.json (#6601)
Co-authored-by: Matjaž Tekavec <matjaz@moj-svet.si>
2025-06-24 20:06:38 +02:00
milkmaker
4c64cf18a6
[Web] Updated lang.si-si.json (#6600)
Co-authored-by: Matjaž Tekavec <matjaz@moj-svet.si>
2025-06-23 21:58:50 +02:00
DerLinkman
8a89f5c685
updated Readme (sponsors) 2025-06-23 10:36:49 +02:00
milkmaker
cc0e4fee9d
[Web] Updated lang.si-si.json (#6599)
Co-authored-by: Matjaž Tekavec <matjaz@moj-svet.si>
2025-06-20 18:14:22 +02:00
milkmaker
5861c9af29
[Web] Updated lang.cs-cz.json (#6589) 2025-06-16 19:45:13 +02:00
milkmaker
dd475c0ab3
update postscreen_access.cidr (#6573) 2025-06-12 13:14:20 +02:00
milkmaker
407e9d3584
Translations update from Weblate (#6582)
* [Web] Updated lang.en-gb.json

[Web] Updated lang.en-gb.json

Co-authored-by: Filip Hajny <filip@hajny.net>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Updated lang.cs-cz.json

Co-authored-by: Filip Hajny <filip@hajny.net>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Updated lang.si-si.json

[Web] Updated lang.si-si.json

Co-authored-by: Matjaž Tekavec <matjaz@moj-svet.si>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Updated lang.it-it.json

Co-authored-by: Stefano <stefano.vassena@gmail.com>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

---------

Co-authored-by: Filip Hajny <filip@hajny.net>
Co-authored-by: Matjaž Tekavec <matjaz@moj-svet.si>
Co-authored-by: Stefano <stefano.vassena@gmail.com>
2025-06-12 13:13:57 +02:00
DerLinkman
d4f899b091
compose: add selinux label to mysql-socket-vol to prevent "access denied" (#6560) 2025-05-26 11:35:34 +02:00
milkmaker
372923ae2f
[Web] Updated lang.zh-cn.json (#6552)
Co-authored-by: Easton Man <me@eastonman.com>
2025-05-19 13:36:15 +02:00
milkmaker
3bd01190bf
Translations update from Weblate (#6548)
* [Web] Updated lang.fr-fr.json

[Web] Updated lang.fr-fr.json

Co-authored-by: Samuel F. <20537389+samuelfranzini@users.noreply.github.com>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Updated lang.ru-ru.json

[Web] Updated lang.ru-ru.json

[Web] Updated lang.ru-ru.json

Co-authored-by: Habetdin <15926758+Habetdin@users.noreply.github.com>
Co-authored-by: Peter <magic@kthx.at>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Updated lang.zh-cn.json

Co-authored-by: Easton Man <me@eastonman.com>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Updated lang.en-gb.json

Co-authored-by: Habetdin <15926758+Habetdin@users.noreply.github.com>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

---------

Co-authored-by: Samuel F. <20537389+samuelfranzini@users.noreply.github.com>
Co-authored-by: Habetdin <15926758+Habetdin@users.noreply.github.com>
Co-authored-by: Peter <magic@kthx.at>
Co-authored-by: Easton Man <me@eastonman.com>
2025-05-16 23:07:25 +02:00
FreddleSpl0it
1994b9895b
Merge pull request #6537 from mailcow/staging
Update 2025-05_2
2025-05-13 10:16:30 +02:00
FreddleSpl0it
03d979c089
[Web] Fix get custom_login 2025-05-13 10:14:58 +02:00
FreddleSpl0it
798e6a4c00
Merge pull request #6535 from mailcow/staging
Update 2025-05
2025-05-13 09:58:32 +02:00
FreddleSpl0it
ffa2933873
increase Olefy, Rspamd and Watchdog docker images 2025-05-13 09:49:53 +02:00
FreddleSpl0it
7f47a3f00e
Merge pull request #6530 from mailcow/feat/auto-create-user-option
[Web] Add identity_provider option to disable auto-creation of users …
2025-05-12 13:24:34 +02:00
FreddleSpl0it
1bcab9a9a5
Merge pull request #6518 from seclution/patch-2
fix: typo in default_template
2025-05-12 13:08:07 +02:00
FreddleSpl0it
1b2f424edc
[Web] Add identity_provider option to disable auto-creation of users on login 2025-05-12 12:20:23 +02:00
krzsztf1
486b297409
Fix typo in rspamd rule in composites.conf (#6515)
Co-authored-by: Krzysztof Nowak <k.nowak@intalio.pl>
2025-05-09 18:33:19 +02:00
FreddleSpl0it
75d7f06b25
Merge pull request #6521 from mailcow/feat/login-quicklinks
[Web] Add quick links to other login pages and mailcow login toggle
2025-05-09 15:24:36 +02:00
FreddleSpl0it
ea0944d743
[Web] Add quick links to other login pages and option to disable mailcow login form 2025-05-09 15:13:44 +02:00
Kai Biebel
cb6ffe65c8
fix: typo in default_template 2025-05-09 11:24:49 +02:00
FreddleSpl0it
580dabd276
Merge pull request #6509 from mailcow/update/postscreen_access.cidr
[Postfix] update postscreen_access.cidr
2025-05-09 10:02:50 +02:00
FreddleSpl0it
846862aa80
Merge pull request #6506 from mrclschstr/staging
[Fix] Moving mails by functions.quarantine.inc.php to inbox failed
2025-05-09 10:00:56 +02:00
FreddleSpl0it
e7a1f24c78
Merge pull request #6483 from PseudoResonance/oauth2-redirect-extra-domain
Allow additional domains in OAuth2 redirect URLs
2025-05-09 09:48:08 +02:00
FreddleSpl0it
8ff0e029f0
Merge pull request #6398 from marvinruder/feat/skip-olefy
feat(olefy): Allow disabling Olefy
2025-05-08 15:34:51 +02:00
FreddleSpl0it
0680b21938
Merge pull request #6342 from mailcow/fix/6339
rspamd: remove .info from fishy tlds (default)
2025-05-08 14:36:39 +02:00
FreddleSpl0it
0c8e7bfeca
Merge pull request #6376 from NickBouwhuis/staging
feat/replace bgp.he.net with bgp.tools
2025-05-08 14:31:57 +02:00
FreddleSpl0it
badcd27b93
Merge pull request #6485 from Habetdin/fix/update-legacy-links
Update legacy admin dashboard links
2025-05-08 14:16:44 +02:00
FreddleSpl0it
7d3ef3d67f
Merge pull request #6487 from mailcow/fix/6469
[Web] Fix force password update at next login
2025-05-08 14:04:56 +02:00
FreddleSpl0it
5b89e253a6
Merge remote-tracking branch 'origin/staging' into fix/6469 2025-05-08 13:50:50 +02:00
FreddleSpl0it
a90f4c2a2e
Merge pull request #6495 from Kuehn-Andreas/fix/redirection-after-login-with-skip-SOGo
Check if skip_sogo is not set before redirecting to SOGo
2025-05-08 11:53:47 +02:00
FreddleSpl0it
db7b917944
Merge pull request #6488 from mailcow/fix/6470
[Dovecot] Fix EAS login issue with app passwords and improve auth cache handling in Dovecot
2025-05-08 11:49:55 +02:00
FreddleSpl0it
401b744808
[Dovecot] return PASSDB_RESULT_PASSWORD_MISMATCH instead of PASSDB_RESULT_INTERNAL_FAILURE 2025-05-08 11:38:29 +02:00
milkmaker
0c83255573 update postscreen_access.cidr 2025-05-01 00:21:11 +00:00
Marcel Schuster
d55f0fc366
Update functions.quarantine.inc.php
Fix regex for quarantine release functions
2025-04-29 22:14:43 +02:00
milkmaker
06b3ba91a0
[Web] Updated lang.zh-cn.json (#6502)
Co-authored-by: Easton Man <me@eastonman.com>
2025-04-27 18:47:08 +02:00
DerLinkman
aa4125fe62
sogo: enabled SOGoEnableMailCleaning per default 2025-04-23 15:13:52 +02:00
Andreas Kühn
d8c6ed9191 Check if skip_sogo is not set before redirecting to SOGo 2025-04-22 14:23:33 +02:00
FreddleSpl0it
cb47fa406f
[Web] Fix force password update at next login 2025-04-15 13:48:13 +02:00
FreddleSpl0it
c4d0f35008
[Dovecot] Fix EAS login and improve logging 2025-04-15 10:49:56 +02:00
Habetdin
0d3e8dd738 Update legacy admin dashboard links 2025-04-13 16:09:47 +03:00
PseudoResonance
692355a08a
Allow additional domains in OAuth2 redirect URLs 2025-04-12 06:24:37 -07:00
renovate[bot]
a370499aaa
Update dependency Imagick/imagick to v3.8.0 (#6480) 2025-04-11 08:30:54 +02:00
milkmaker
84f67d6608
Translations update from Weblate (#6478)
* [Web] Updated lang.de-de.json

Co-authored-by: Jonas Leiner <jonas.leiner.123@gmail.com>

* [Web] Updated lang.fr-fr.json

Co-authored-by: Neuronnexion <support@nnx.com>

---------

Co-authored-by: Jonas Leiner <jonas.leiner.123@gmail.com>
Co-authored-by: Neuronnexion <support@nnx.com>
2025-04-10 17:16:44 +02:00
milkmaker
4ac839cf49
Translations update from Weblate (#6463)
* [Web] Updated lang.hu-hu.json

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Updated lang.lv-lv.json

[Web] Updated lang.lv-lv.json

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Edgars Andersons <Edgars+Mailcow+Weblate@gaitenis.id.lv>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Added lang.bg-bg.json

Co-authored-by: Peter <magic@kthx.at>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Updated lang.tr-tr.json

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Updated lang.uk-ua.json

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Updated lang.zh-cn.json

Co-authored-by: Easton Man <me@eastonman.com>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

---------

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Edgars Andersons <Edgars+Mailcow+Weblate@gaitenis.id.lv>
Co-authored-by: Peter <magic@kthx.at>
Co-authored-by: Easton Man <me@eastonman.com>
2025-04-10 17:11:30 +02:00
FreddleSpl0it
b96a5b1efd
[Web] Fix AJAX urls to absolute path 2025-04-09 08:07:46 +02:00
FreddleSpl0it
766c5e8580
[Dovecot] Ignore app passwords protocol access on SOGo request 2025-04-09 08:02:30 +02:00
FreddleSpl0it
3f493e043d
Merge pull request #6468 from mailcow/staging
Update 2025-03b
2025-04-07 09:09:39 +02:00
FreddleSpl0it
3ddad9dee8
Merge pull request #6460 from mailcow/ui/improve-ldap-ssl-labels
[Web] Improve clarity of LDAP SSL/TLS settings
2025-04-07 08:58:19 +02:00
FreddleSpl0it
2c10c39bc4
[Web] Update 2FA Info tooltip 2025-04-07 08:06:43 +02:00
FreddleSpl0it
0eb8f38792
[Web] Update LDAP SSL/TLS tooltips 2025-04-07 07:59:43 +02:00
FreddleSpl0it
402bf53a5c
[Web] Improve clarity of LDAP SSL/TLS settings 2025-04-04 13:18:42 +02:00
FreddleSpl0it
428a59dd3f
Merge branch 'fix/dovecot-lua-timeout' into staging 2025-04-03 14:18:33 +02:00
FreddleSpl0it
153890b283
Merge pull request #6439 from mailcow/fix/6430
[SOGo] Use JS for mailcow logout
2025-04-03 12:57:24 +02:00
FreddleSpl0it
a741c2ba4a
Merge pull request #6426 from sardaukar/fix/typo-on-backup-and-restore-script
Fix tiny typo
2025-04-03 12:41:46 +02:00
FreddleSpl0it
741e5c719f
Merge pull request #6438 from mailcow/fix/6405
[Netfilter] Downgrade to 1.61
2025-04-03 12:39:48 +02:00
FreddleSpl0it
34e4f93db9
Merge pull request #6451 from mailcow/fix/6437
[Web] Fix transport routing test
2025-04-03 12:38:43 +02:00
FreddleSpl0it
3758135dc3
Merge pull request #6450 from mailcow/fix/sasl_logs
Fix sasl_logs
2025-04-03 12:38:13 +02:00
FreddleSpl0it
6794e6ff43
[Dovecot] Add service for authentication cache_key 2025-04-03 12:31:43 +02:00
FreddleSpl0it
62f816e64a
[Web] Check app password before user password on web login 2025-04-03 12:19:04 +02:00
FreddleSpl0it
e65478076b
[Web] Prevent user sync for mismatched authsource 2025-04-03 11:58:35 +02:00
FreddleSpl0it
ceeabded73
[Web] Fix transport routing test 2025-04-03 10:29:47 +02:00
FreddleSpl0it
805634f9a9
Fix sasl_logs 2025-04-03 10:19:30 +02:00
DerLinkman
a92832d115
update README.md to include first 50 and 100$ monthly sponsors 2025-04-02 14:39:24 +02:00
milkmaker
4c5f485587
update postscreen_access.cidr (#6443) 2025-04-01 22:00:11 +02:00
FreddleSpl0it
db3a577ae3
[Web] Fix password reset 2025-04-01 16:39:15 +02:00
FreddleSpl0it
e452917de9
[SOGo] Show mailcow Settings Button to SOGoSuperUsers 2025-03-31 12:14:43 +02:00
FreddleSpl0it
f37961b7d0
[SOGo] Use JS for mailcow logout 2025-03-31 11:32:01 +02:00
FreddleSpl0it
0157cbddaf
[Netfilter] Downgrade to 1.61 2025-03-31 10:36:20 +02:00
Bruno Antunes
65d872cc14
Fix tiny typo 2025-03-27 20:21:25 +00:00
FreddleSpl0it
4ad2422810
[Dovecot] Increase Timeout for HTTP Login Request 2025-03-27 16:52:15 +01:00
FreddleSpl0it
2c47145dee
Merge pull request #6419 from mailcow/staging
Update 2025-03a
2025-03-27 09:19:29 +01:00
FreddleSpl0it
9b41b24522
Merge pull request #6402 from marvinruder/fix/long-dropdown-label
fix(ui): Swap translations for oversized dropdown
2025-03-27 08:07:51 +01:00
FreddleSpl0it
1c9d80f554
Merge pull request #6406 from mailcow/fix/6392
[Web] Fix SOGo access after Passwordless auth
2025-03-27 07:42:07 +01:00
FreddleSpl0it
7172cad257
Merge pull request #6407 from mailcow/fix/6396
[Web] Fix oauth2 redirect after user login
2025-03-27 07:41:08 +01:00
FreddleSpl0it
b550c6f88e
Merge pull request #6408 from mailcow/fix/6373
[Swagger] Fix type property for /api/v1/add/bcc endpoint
2025-03-27 07:40:19 +01:00
FreddleSpl0it
5baf9eb375
Merge pull request #6409 from mailcow/fix/6372
[Web] Check if mailbox is active before renaming
2025-03-27 07:40:03 +01:00
FreddleSpl0it
4eb89f67ed
Merge pull request #6410 from mailcow/fix/6395
[Web] Use absolute paths for flag SVGs
2025-03-27 07:39:34 +01:00
FreddleSpl0it
efdc798238
Merge pull request #6411 from mailcow/fix/6340
[Nginx] Move conf.d include before SNI vhosts
2025-03-27 07:39:06 +01:00
Marvin A. Ruder
8408b82e9c
Add new option with description to existing configuration files during next update
* Remove Olefy settings file from rspamd configuration
* Have rspamd container generate Olefy settings file at startup if not disabled

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>
2025-03-26 17:17:13 +01:00
FreddleSpl0it
65fb4c2aa8
[Nginx] Move conf.d include before SNI vhosts 2025-03-26 13:04:43 +01:00
FreddleSpl0it
a5ca3353da
[Web] Use absolute paths for flag SVGs 2025-03-26 10:59:56 +01:00
FreddleSpl0it
95aa35e133
[Web] Check if mailbox is active before renaming 2025-03-26 10:10:22 +01:00
FreddleSpl0it
21b11ed999
[Swagger] Fix type property for /api/v1/add/bcc endpoint 2025-03-26 09:24:03 +01:00
FreddleSpl0it
348107dae8
[Web] Fix oauth2 redirect after user login 2025-03-26 09:13:05 +01:00
FreddleSpl0it
fcb1b29c89
[Web] Fix SOGo access after Passwordless auth 2025-03-26 08:32:34 +01:00
Marvin A. Ruder
05fc4f7aba
fix(ui): Swap translations for oversized dropdown
* Fix other typos
* Fixes #6400

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>
2025-03-25 21:24:22 +01:00
Marvin A. Ruder
cd3b1ab828
Allow disabling Olefy
* Fixes #6389

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>
2025-03-25 20:24:33 +01:00
FreddleSpl0it
c3c68360dc
Merge pull request #6391 from mailcow/staging
Update 2025-03
2025-03-25 08:10:50 +01:00
FreddleSpl0it
d584dd387e
Merge pull request #6390 from mailcow/nightly
Nightly 2025-03 to staging
2025-03-25 07:36:20 +01:00
FreddleSpl0it
986b0afbfa
ldap-sync: Fix template selection 2025-03-24 15:33:42 +01:00
FreddleSpl0it
59d139bc63
Merge branch 'nightly' into staging 2025-03-24 13:39:43 +01:00
FreddleSpl0it
ad5f07f077
update.sh: add 2025-03 as major version 2025-03-24 11:47:27 +01:00
FreddleSpl0it
cf2d3c1b4e
Merge branch 'staging' into nightly 2025-03-24 11:38:59 +01:00
FreddleSpl0it
91c82e8a67
Merge pull request #6384 from mailcow/feat/update-components-alp-3.21
os: updated alpine containers to 3.21
2025-03-24 11:30:58 +01:00
FreddleSpl0it
ba7437a8f3
Merge pull request #6380 from mailcow/feat/legacy-switch
Add Legacy Updates
2025-03-20 14:25:13 +01:00
FreddleSpl0it
684256b66e
update.sh: Fix legacy typo 2025-03-20 14:23:28 +01:00
FreddleSpl0it
70ba361583
update.sh: Fix text in legacy update prompt 2025-03-20 14:15:15 +01:00
FreddleSpl0it
94d4817ecb
[Web] Add default_template parameter to edit/identity-provider documentation 2025-03-20 13:38:27 +01:00
FreddleSpl0it
72ced70e33
[Web] Fix mailbox authsource selection 2025-03-20 13:08:42 +01:00
FreddleSpl0it
887b7114a8
Add default template for IdP attribute mapping 2025-03-19 14:35:32 +01:00
Nick Bouwhuis
ceebc56e62 feat/replace bgp.he.net with bgp.tools 2025-03-18 10:08:08 +00:00
FreddleSpl0it
8910135f02
[Web] Add edit/identity-provider Api Documentation 2025-03-17 13:21:28 +01:00
DerLinkman
463e3ab78c
rspamd: update rspamd to 3.11.1 (#6374) 2025-03-14 12:18:59 +01:00
FreddleSpl0it
2a15914324
Fix major update prompt 2025-03-14 11:22:57 +01:00
FreddleSpl0it
e21696ff27
Add error message when mailbox creation fails 2025-03-14 09:36:40 +01:00
FreddleSpl0it
5a7275843a
Add error message when mailbox creation fails 2025-03-14 09:30:33 +01:00
FreddleSpl0it
c93106f9d6
[Web] Fix redirect after renaming mailbox 2025-03-14 09:29:02 +01:00
FreddleSpl0it
43c1597051
[Web] Check if authsource is configured before adding or updating a mailbox 2025-03-14 09:19:39 +01:00
FreddleSpl0it
c3aa4f7418
[Web] Add authsource property to mailbox API Documentation 2025-03-14 09:17:51 +01:00
FreddleSpl0it
cb08132a74
[Web] Fix authentication when mailbox or domain is deactivated 2025-03-13 14:39:03 +01:00
FreddleSpl0it
2596b9d386
[Web] Improve auth logging and language strings 2025-03-12 11:42:14 +01:00
Marvin A. Ruder
062539b7d7
dkim: Add support for 3072 and 4096 bit RSA keys (#6365)
* dkim: Add support for 3072 and 4096 bit RSA keys

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>

* php: added missing ; in dkim function

* php: make 4096 DKIM default

* db: update schema to set dkim 4096 as default

* Revert "db: update schema to set dkim 4096 as default"

This reverts commit 790b40a69563722513cda540ba34e3ae30874e05.

* Revert "php: make 4096 DKIM default"

This reverts commit 7e643376c7e11d23b0dae95ae59a2a5cc195e057.

---------

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>
Co-authored-by: DerLinkman <niklas.meyer@servercow.de>
2025-03-11 15:30:46 +01:00
DerLinkman
18acbc7a4c
cold-standby: changed texts + removed --no-parallel for pull 2025-03-11 12:35:13 +01:00
DerLinkman
2f93f1d0c5
os: fixes for newer mariadb-client versions (especially on alpine 3.21) 2025-03-10 16:45:57 +01:00
DerLinkman
0860a7503e
os: updated alpine containers to 3.21 2025-03-10 11:56:12 +01:00
renovate[bot]
86df78255d
chore(deps): update dependency composer/composer to v2.8.6 (#5719)
Signed-off-by: milkmaker <milkmaker@mailcow.de>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 11:39:19 +01:00
FreddleSpl0it
aac0a900ce
[Web] Fix JSON parsing issue for api requests 2025-03-10 10:49:27 +01:00
milkmaker
03565df48d
[Web] Updated lang.ko-kr.json (#6356)
Co-authored-by: dongsu8142 <dongsu8142@naver.com>
2025-03-07 21:37:31 +01:00
FreddleSpl0it
5f15475b55
[Rspamd] Remove redis.conf from tracking 2025-03-07 15:18:42 +01:00
FreddleSpl0it
25d34b5acf
[Web] Remove default ui help text 2025-03-07 14:52:08 +01:00
FreddleSpl0it
6b165887d8
Merge branch 'staging' into nightly 2025-03-07 13:21:57 +01:00
FreddleSpl0it
82eb3c64cd
[Web] Use SQL password only when authsource is mailcow 2025-03-07 13:15:27 +01:00
FreddleSpl0it
bc21e7fe50
[Web] Separate FIDO2 logins 2025-03-07 13:12:48 +01:00
FreddleSpl0it
6f9c8deab7
[Web] Support old style app links 2025-03-07 09:56:20 +01:00
FreddleSpl0it
8761d8fc47
[Web] Fix app layout issue 2025-03-07 09:54:35 +01:00
milkmaker
0435766c17
[Web] Updated lang.ko-kr.json (#6353)
Co-authored-by: dongsu8142 <dongsu8142@naver.com>
2025-03-05 17:43:37 +01:00
renovate[bot]
79f4cf4021
chore(deps): update docker/build-push-action action to v6 (#6334) 2025-03-05 16:35:46 +01:00
milkmaker
81803836f0
[Web] Updated lang.ko-kr.json (#6350)
Co-authored-by: dongsu8142 <dongsu8142@naver.com>
2025-03-03 22:49:23 +01:00
milkmaker
4bd267515a
update postscreen_access.cidr (#6345) 2025-03-01 13:32:21 +01:00
DerLinkman
70190e5230
rspamd: remove .info from fishy tlds (default) 2025-02-28 15:38:05 +01:00
FreddleSpl0it
a632980871
Merge pull request #6336 from mailcow/staging
Update 2025-02
2025-02-27 11:48:57 +01:00
DerLinkman
5296085189
update.sh: corrected typos inside update.sh 2025-02-27 11:47:08 +01:00
DerLinkman
a4c2cf4c67
scripts: adapted new docker image names to docker_garbage function + removed dup 2025-02-27 11:44:52 +01:00
FreddleSpl0it
2d1ef41d32
Merge pull request #6335 from mailcow/staging
Update 2025-02
2025-02-27 11:05:55 +01:00
Peter
3c9d0c9d57
use ghcr.io for backupimage (#6333)
* use ghcr.io for backup image

* backup script: use renamed script + improved build of image

---------

Co-authored-by: DerLinkman <niklas.meyer@servercow.de>
2025-02-27 10:58:23 +01:00
FreddleSpl0it
35a6f81d0d
[Redis] use 7.4.2-alpine image 2025-02-27 09:28:52 +01:00
FreddleSpl0it
4b31c04e3e
Merge pull request #6330 from mailcow/feat/major-update-prompt
Prompt user before applying major updates
2025-02-27 08:15:21 +01:00
FreddleSpl0it
3d9cc2f6dd
add 2025-02 to major versions 2025-02-27 08:14:34 +01:00
DerLinkman
704dd50262
compose: use ghcr.io for new/current mailcow docker images instead of docker hub (#6332) 2025-02-26 15:20:57 +01:00
FreddleSpl0it
8d0c03b2fc
small adjustment for legacy version 2025-02-26 10:39:41 +01:00
FreddleSpl0it
c4a0e370b7
Merge pull request #6155 from PseudoResonance/fix2752
Fix #2752 - Allow domain recipients for address rewrite
2025-02-26 10:01:03 +01:00
FreddleSpl0it
b77ff2f51c
Add switch to legacy version 2025-02-26 09:47:59 +01:00
FreddleSpl0it
787fa49d0c
prompt user before applying major updates 2025-02-25 12:08:21 +01:00
DerLinkman
a6c38590ca
rspamd: upgraded rspamd to 3.11.0-2 (incl. NIXSPAM Removal) (#6328) 2025-02-25 09:23:10 +01:00
PseudoResonance
e52323bf1d
Fix @ prefixing domain rewrite and update localization 2025-02-24 22:36:17 -08:00
PseudoResonance
f15ee39b63
Fix #2752: Domain recipient for address rewrite
(cherry picked from commit 40f6d691d8774d6f813153974f8fe462a8db9ab3)
2025-02-24 22:07:23 -08:00
FreddleSpl0it
fcebe98557
Merge branch 'staging' into nightly 2025-02-24 15:09:36 +01:00
FreddleSpl0it
6ec5e88793
Merge pull request #6309 from mailcow/fix/6308
[Dovecot][Netfilter] Fix dovecot failed login regex
2025-02-24 11:26:06 +01:00
FreddleSpl0it
7d35646342
[Netfilter] adjust dovecot failed login regex 2025-02-24 09:20:41 +01:00
FreddleSpl0it
321965adee
[Netfilter] Fix dovecot password mismatch regex 2025-02-18 15:05:59 +01:00
Peter
7bce5d836b
Move sed cmd to remove discontinued DNSBLs (#6315)
* Move sed cmd to remove discontinued DNSBLs

* compose: bump postfix version

---------

Co-authored-by: DerLinkman <niklas.meyer@servercow.de>
2025-02-18 11:20:03 +01:00
FreddleSpl0it
351f4ce787
[Redis] Add support for masterauth via env var 2025-02-18 11:16:06 +01:00
FreddleSpl0it
a567d5dc31
[Nginx] Add support for trusted proxies via env var 2025-02-18 11:03:34 +01:00
DerLinkman
4ac541f671
[Mariadb] Update to 10.11 (LTS) (#5152)
* [Mariadb] Update to 10.11 (LTS)

* mysql: set default collation to general_ci
2025-02-17 15:48:25 +01:00
Dmitriy Alekseev
f6dc0b463f
Update Rspamd to 3.11.0 and enable SMTPUTF8 for outgoing mail (#6216)
* Update Rspamd to 3.11

* Enable SMTPUTF8 and hide it from SMTPD greeting

* Update options.inc

* compose: increased rspamd tag
2025-02-17 14:41:39 +01:00
DerLinkman
16e22e23dc
sogo: switched apt source to sogo again (supports aarch64 now) 2025-02-17 14:31:50 +01:00
FreddleSpl0it
d8afa6f393
[Dovecot][Netfilter] Fix dovecot failed login regex 2025-02-14 13:12:12 +01:00
milkmaker
836e3f15b7
[Web] Updated lang.es-es.json (#6307)
Co-authored-by: Julie GINESTIERE <julien.ginestiere+git@gmail.com>
2025-02-13 19:32:39 +01:00
FreddleSpl0it
aaa7e4a184
[Web] Fix incorrect session lifetime in sogo-auth.php 2025-02-13 11:54:55 +01:00
FreddleSpl0it
3912341b32
[SOGo] rename custom logo 2025-02-12 11:31:14 +01:00
FreddleSpl0it
735d5f0e56
Merge pull request #6220 from Babybatrick/staging
Adding lines to docker-compose.yml to allow for simpler SOGo web client UI customisation
2025-02-12 10:54:16 +01:00
FreddleSpl0it
f375794fb7
Merge pull request #6223 from mailcow/ffdhe2048
Ffdhe2048
2025-02-12 10:48:22 +01:00
renovate[bot]
4ed3017a02
chore(deps): update devops-infra/action-pull-request action to v0.6.0 (#6302) 2025-02-12 06:56:10 +01:00
FreddleSpl0it
ef2f5f7be0
[Dovecot] Use Redis ACL user quota_notify with restricted access 2025-02-11 16:59:18 +01:00
FreddleSpl0it
54728bf780
[Dovecot] Fix create sogo-sso.conf 2025-02-11 14:40:38 +01:00
Henry Williams
743e88fd67
Update generate_config.sh version checking for wider compatibility (#6270)
* Update generate_config.sh version checking for wider compatibility 

fix: replace `grep -oP` with `grep -oE` for broader compatibility

The `-P` option (Perl-compatible regex) is not supported in all versions of `grep`, particularly the default BSD `grep` on macOS. This change replaces `-P` with `-E` (extended regex), which is more widely available and ensures compatibility across different environments.

Tested on macOS and Linux.

* Update generate_config.sh to remove use of platform dependent grep

Replaced version checking using free-form text. Instead, uses Docker’s built-in templating instead of parsing free-form text. This gives cross-platform consistency without dependency on particular versions of grep.
2025-02-11 13:55:03 +01:00
DerLinkman
ac2f0c7db1
Merge pull request #6286 from mailcow/fix-workflow-staging
Fix check_prs_if_on_staging workflow
2025-02-11 13:52:44 +01:00
FreddleSpl0it
f64c6aa1d4
Merge pull request #6269 from mailcow/staging
Automatic PR to nightly from 2025-01-27T10:00:26Z
2025-02-07 15:10:10 +01:00
FreddleSpl0it
e2cf22ff9e
Merge pull request #6268 from mailcow/feat/nightly-separated-login
[Web] Separate Login pages
2025-02-07 15:09:39 +01:00
FreddleSpl0it
55dcae4a01
[Web] Fix Generic-OIDC connection test 2025-02-07 15:05:43 +01:00
FreddleSpl0it
f0016eeecd
[Web] Add german translation for idp settings 2025-02-07 14:19:20 +01:00
FreddleSpl0it
120366fec7
Merge pull request #6291 from mailcow/staging
Update 2025-01a
2025-02-04 13:55:30 +01:00
FreddleSpl0it
3544a2246e
[Nginx] fix ADDITIONAL_SERVER_NAMES array 2025-02-04 13:30:00 +01:00
FreddleSpl0it
97890b71f1
[Nginx] Invert SKIP container condition 2025-02-03 12:22:13 +01:00
FreddleSpl0it
e645f931dc
[Nginx] Add env var for HTTP to HTTPS redirection 2025-02-03 12:05:08 +01:00
FreddleSpl0it
bbdec0960a
Merge pull request #6290 from mailcow/fix/nginx-vhosts
[Nginx] Use vhosts for additional server names
2025-02-03 11:35:09 +01:00
milkmaker
41ba7d97fa
update postscreen_access.cidr (#6287) 2025-02-01 17:06:07 +01:00
Peter
83fc2c6387
It's github-token now 2025-01-31 17:20:28 +01:00
DerLinkman
aac4c6b5f4
postfix: added master.pid removal and startsecs to supervisord (#6284) 2025-01-31 12:49:39 +01:00
FreddleSpl0it
3c0f775e2f
Merge pull request #6281 from mailcow/fix/6275
[Nginx] Fix
2025-01-31 10:49:21 +01:00
FreddleSpl0it
3a81b84cf7
[Nginx] Fix #6275 2025-01-30 14:49:18 +01:00
FreddleSpl0it
a2e87e0880
[Web] Add validation for server_name against allow list 2025-01-30 11:47:55 +01:00
DerLinkman
2407aa7895
Merge branch 'feat/clamd-rebuild' into staging 2025-01-29 14:01:39 +01:00
DerLinkman
244d4b8c4c
compose: rollback clamd version until next major... accidentally pushed 2025-01-29 13:46:53 +01:00
FreddleSpl0it
0ad327bbe5
[Nginx] Use separate vhosts for additional server names 2025-01-29 09:51:45 +01:00
DerLinkman
f92ddd86c5
clamd: update to 1.4.2 + build from source instead using alpine packages (#6273)
* clamd: update to 1.4.2 + build from source instead using alpine packages

* clamd: remove exposed ports from buildfile

* clamd: cleanup dockerfile
2025-01-29 09:49:04 +01:00
DerLinkman
1a087bb2c8
clamd: cleanup dockerfile 2025-01-28 14:49:11 +01:00
DerLinkman
65bc581fab
clamd: remove exposed ports from buildfile 2025-01-28 14:36:43 +01:00
DerLinkman
60a2270d1e
clamd: update to 1.4.2 + build from source instead using alpine packages 2025-01-28 14:25:56 +01:00
FreddleSpl0it
cb5cae3e44
Merge branch 'nightly' into feat/nightly-separated-login 2025-01-27 16:37:09 +01:00
FreddleSpl0it
8ed51e500f
Merge pull request #6260 from mailcow/manitu
Remove discontinued Nixspam DNSBL
2025-01-27 16:21:29 +01:00
FreddleSpl0it
aca01c8aa2
[Web] Separate Login pages 2025-01-27 15:59:50 +01:00
FreddleSpl0it
45d14254f2
[Postfix] Remove discontinued Nixspam DNSBL from existing dns_blocklists.cf 2025-01-24 10:06:50 +01:00
FreddleSpl0it
de6bd222fc
[Web] increase db_version 2025-01-24 09:25:19 +01:00
Michael Kuron
04116982a5
Remove discontinued Nixspam DNSBL 2025-01-23 22:16:54 +01:00
FreddleSpl0it
36d4fcbf39
Merge pull request #6255 from mailcow/staging
Automatic PR to nightly from 2025-01-23T11:01:42Z
2025-01-23 15:21:39 +01:00
FreddleSpl0it
ba0349a911
Merge pull request #6256 from mailcow/staging
[Nginx] move conf.d include to end of nginx.conf
2025-01-23 14:55:38 +01:00
FreddleSpl0it
04058ab06e
[Nginx] move conf.d include to end of nginx.conf 2025-01-23 14:54:28 +01:00
FreddleSpl0it
9d791d0c4f
Merge branch 'staging' into nightly 2025-01-23 12:06:47 +01:00
FreddleSpl0it
8caf09cd80
Merge pull request #6253 from mailcow/staging
2025-01
2025-01-23 12:01:38 +01:00
FreddleSpl0it
da02e26172
[Web] Delete old session_id after regenerate 2025-01-23 11:59:01 +01:00
DerLinkman
43f945fe01
dovecot: fix index timeout seconds 2025-01-23 11:51:41 +01:00
DerLinkman
e76c0ba9a6
Merge branch 'staging' 2025-01-23 11:31:01 +01:00
DerLinkman
d83111568e
update.sh: remove accidentally added exit at end of solr volume removal 2025-01-23 11:30:05 +01:00
FreddleSpl0it
1b578caabb
Merge pull request #6251 from mailcow/staging
2025-01
2025-01-23 11:16:38 +01:00
FreddleSpl0it
1e77f8d8a1
Merge pull request #6250 from mailcow/staging
Automatic PR to nightly from 2025-01-22T19:10:32Z
2025-01-23 11:01:55 +01:00
FreddleSpl0it
5f45f8ae34
[Web] Fix mailbox datatable search 2025-01-23 09:18:45 +01:00
DerLinkman
1dac8f1f66
scripts: changed SKIP_FTS text to warn on lower threaded systems 2025-01-23 08:42:22 +01:00
DerLinkman
5a04942d89
update.sh: changed SKIP_FTS default to y instead n for updates 2025-01-23 08:38:14 +01:00
DerLinkman
a30f6696a3
update.sh: fixed --force for solr-removal + code optimization 2025-01-23 08:30:48 +01:00
FreddleSpl0it
d430b595c1
Merge branch 'staging' into nightly 2025-01-23 08:11:45 +01:00
FreddleSpl0it
1fca328266
[Nginx] Disable IPv6 listener for Rspamd dynmaps when DISABLE_IPv6=y 2025-01-22 15:11:46 +01:00
FreddleSpl0it
7bcd61ecb5
[Nginx] Generate includes for custom configs 2025-01-22 14:30:47 +01:00
renovate[bot]
ee7a8624fc
chore(deps): update actions/stale action to v9.1.0 (#6247)
Signed-off-by: milkmaker <milkmaker@mailcow.de>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 06:38:13 +01:00
DerLinkman
4708b1398b
update.sh: fix mailcow fts update versioning 2025-01-20 15:41:48 +01:00
DerLinkman
746915cbdd
fts: change autoindex to occur on mailboxes of receiving 20 or more mails daily 2025-01-20 14:21:15 +01:00
Alyx
36db68677c
Reduce sa rules download retry limit to 5 (#6225)
Reduces the retry limit for the sa rules download to a more reasonable 5 retries to prevent running in a timeout condition.
2025-01-20 14:10:29 +01:00
gwelch-contegix
08599c1960
Fix community support url (#6245) 2025-01-20 14:09:31 +01:00
DerLinkman
31e001ebee
flatcurve: change default amount of processes to 1 2025-01-16 11:37:15 +01:00
FreddleSpl0it
1e70a20188
[SOGo] Add mailcow Buttons to SOGo navbar 2025-01-15 16:15:25 +01:00
FreddleSpl0it
8048e0a53c
[Web] Fix permission exception in IdP actions 2025-01-15 12:48:10 +01:00
FreddleSpl0it
8fea9fc21f
Merge pull request #6211 from jan-oratowski/patch-1
Fix missing property in Create Sync Job request
2025-01-14 12:18:29 +01:00
FreddleSpl0it
2f1884e94b
Merge pull request #6205 from PhoenixPeca/master
Improve the existing validation flow for sieve filter
2025-01-14 12:08:56 +01:00
FreddleSpl0it
24b3d8f850
Merge pull request #6001 from marekfilip/feat/temp-email-aliases
add temporary email description
2025-01-14 11:52:44 +01:00
FreddleSpl0it
d280025b51
[Web] Regenerate session_id on successful login 2025-01-14 11:30:41 +01:00
FreddleSpl0it
abd789f629
[Web] Escape mailbox name before querying aliases 2025-01-14 11:18:20 +01:00
milkmaker
69f6a82905
[Web] Updated lang.fr-fr.json (#6238)
Co-authored-by: Neuronnexion <support@nnx.com>
2025-01-09 06:51:42 +01:00
milkmaker
10328981b6
Translations update from Weblate (#6235)
* [Web] Updated lang.fr-fr.json

Co-authored-by: Neuronnexion <support@nnx.com>

* [Web] Updated lang.zh-cn.json

Co-authored-by: Easton Man <me@eastonman.com>

---------

Co-authored-by: Neuronnexion <support@nnx.com>
Co-authored-by: Easton Man <me@eastonman.com>
2025-01-05 15:25:45 +01:00
Filip Marek
150b2bbd9d
Merge branch 'mailcow:master' into feat/temp-email-aliases 2025-01-03 11:40:01 +01:00
milkmaker
40a8bc808a
update postscreen_access.cidr (#6232) 2025-01-01 03:26:18 +01:00
Dmitriy Alekseev
d92aa4b15d
Update dhparams.pem
Use https://ssl-config.mozilla.org/ffdhe2048.txt due to better security of the key
2024-12-20 15:39:41 +01:00
milkmaker
2d2dacb70e
[Web] Updated lang.fr-fr.json (#6221)
[Web] Updated lang.fr-fr.json

Co-authored-by: Neuronnexion <support@nnx.com>
Co-authored-by: Peter <magic@kthx.at>
2024-12-19 17:10:43 +01:00
Amin
ade20d79d4
Uploading of the necessary files, after new volumes were added to docker-compose.yml (sogo-mailcow container)
After new volumes were added to docker-compose.yml in the sogo-mailcow container, it is necessary to include the specified files in the path, in order for docker to correctly start after running `docker compose up` command, otherwise error will appear, as necessary files would be missing.
The files uploaded are original SOGo UI elements, obtained from the sogo-mailcow container. Whenever users will need to change the UI elements, they would just need to change these files. Hence simplifying the process.
2024-12-19 22:13:27 +08:00
Amin
65bc8f0972
Update docker-compose.yml (sogo-mailcow)
This commit includes the addition of 3 lines, in the volumes part of the sogo-mailcow container, to allow for better customisation of the user interface on the web client page.
2024-12-19 21:59:05 +08:00
Jan Oratowski
c6f6eda0bf
Fix missing property in Create Sync Job request
In example there was property called "user1", but it was missing from request definition.

This resulted in nswagger generating incorrect C# API code.
2024-12-14 15:27:37 +01:00
milkmaker
357a4d7fb3
[Web] Updated lang.fr-fr.json (#6209)
Co-authored-by: Neuronnexion <support@nnx.com>
2024-12-13 12:21:12 +01:00
DerLinkman
1c6684a539
compose: fix dovecot tagging 2024-12-12 17:02:21 +01:00
DerLinkman
de80c120c9
update.sh: added silent fix for removing old fts.conf in order to update properly 2024-12-12 16:57:32 +01:00
Niklas Meyer
3e8bb06a37
dovecot: replace solr fts with flatcurve (xapian) (#5680)
* fts-flatcurve: inital implementation

* fts: removed solr from compose.yml

* flatcurve: added heap and proc logic to dovecot

* added logic for update.sh & generate for Flatcurve

* delete old iteration of fts-flatcurve.conf

* updated default fts.conf

* updated .gitignore to exclude fts.conf for further git updates

* Remove autogeneration of fts.conf (disable override)

* cleanup all left solr stuff

* renamed SKIP_FLATCURVE to SKIP_FTS

* cleanup leftovers solr in lang files

* moved lazy_expunge plugin only to mail_plugins

* added fts timeout value

* compose: remove dev image of dovecot

* updated japanese translation
2024-12-12 16:44:42 +01:00
milkmaker
b087ac9e27
Translations update from Weblate (#6206)
* [Web] Updated lang.fr-fr.json

Co-authored-by: Neuronnexion <support@nnx.com>

* [Web] Updated lang.si-si.json

Co-authored-by: Matjaž Tekavec <matjaz@moj-svet.si>

---------

Co-authored-by: Neuronnexion <support@nnx.com>
Co-authored-by: Matjaž Tekavec <matjaz@moj-svet.si>
2024-12-11 18:10:51 +01:00
Phoenix Eve Aspacio
d09e4ff020
Convert AJAX to POST request
This AJAX request sends form data in $_GET request query. This is problematic and unreliable when validating superrrr loooooong conditions, especially in environments that use reverse-proxy.

Been having this problem and this PR solves it. :)
2024-12-11 10:06:10 +08:00
Phoenix Eve Aspacio
f065842402
Updated to $_REQUEST.
tested from my end.
2024-12-11 10:03:47 +08:00
Niklas Meyer
3875e8377a
sogo: added SOGoDisableOrganizerEventCheck value to sogo.conf (#6204) 2024-12-10 15:59:02 +01:00
Christian 🦄
7c8e5c10ca
Add create command to prevent external: true warnings (#6203)
This is related to https://github.com/mailcow/mailcow-dockerized/issues/5970 and https://community.mailcow.email/d/2126-backup-restore/2

It adds `docker compose create` to the script which gets executed directly after the sync of the mailcow-dockerized directory. This way the Docker daemon on the remote side creates everything and we get rid of the warning "volume "XYZ" already exists but was not created by Docker Compose. Use `external: true` to use an existing volume"

This is helpful if you use the create-cold-standby.sh script to migrate your mailcow installation to another server and don't want to get those warnings after migration.

Co-authored-by: Niklas Meyer <niklas.meyer@servercow.de>
2024-12-10 09:25:29 +01:00
Filip Marek
1a8e1a2677 add escape html for description 2024-12-09 23:07:43 +01:00
Filip Marek
0d635e2658 increase migrations verion 2024-12-09 23:07:43 +01:00
Filip Marek
60ca25026d add temporary email description 2024-12-09 23:07:02 +01:00
FreddleSpl0it
69b03791a2
Add missing Redis authentication 2024-12-09 13:54:44 +01:00
Peter
ed2837edd8
Remove legacy Nextcloud settings (#6050) 2024-12-09 13:49:24 +01:00
FreddleSpl0it
fa3b789fbb
[Web] fix issue #6185 2024-12-09 13:07:00 +01:00
FreddleSpl0it
49e05f5120
[Web] fix oauth2 redirect after login 2024-12-09 11:36:05 +01:00
FreddleSpl0it
24453993f3
Merge pull request #6186 from h3ssan/feat/search-mailbox-by-full-name
Implement search mailboxes by fullname
2024-12-09 10:21:39 +01:00
FreddleSpl0it
8853e2c44a
[Nginx] Use SOGo IPv4 for upstream 2024-12-09 09:50:16 +01:00
FreddleSpl0it
c9dd102741
[Dovecot] use auth_cache 2024-12-06 12:55:44 +01:00
Tatsuya Yokota
d1af52b4e7
Add initial Japanese language files (#6198)
* Add initial Japanese language files

* Reordered language list: moved Japanese (日本語) below Italian (Italiano)

---------

Co-authored-by: Tatsuya Yokota <git@acoustype.com>
2024-12-06 09:44:16 +01:00
FreddleSpl0it
bbddfc3eab
[Web] rearrange login buttons 2024-12-05 15:21:07 +01:00
FreddleSpl0it
a41bb55c83
Merge remote-tracking branch 'origin/staging' into nightly 2024-12-05 14:33:41 +01:00
FreddleSpl0it
b6174fae23
Merge pull request #6194 from mailcow/feat/nightly-enhancements
[Nightly] Enhancements
2024-12-05 13:18:39 +01:00
FreddleSpl0it
1d6513ffba
[Web] fix idp login alerts and updates 2024-12-04 14:49:31 +01:00
i-curve
6e8e13cebc
fix: check docker version fail in generate_config.sh #6187 (#6188)
close #6187

Signed-off-by: i-curve <i-curve@qq.com>
Co-authored-by: Niklas Meyer <niklas.meyer@servercow.de>
2024-12-04 12:28:14 +01:00
FreddleSpl0it
896a9638d6
Fix mailcowauth 2024-12-02 14:16:43 +01:00
FreddleSpl0it
83e53eb524
[Web] fix incomplete session on broken logins 2024-12-02 11:55:17 +01:00
FreddleSpl0it
f36184df64
[Web] update mailbox on idp login 2024-12-02 10:35:45 +01:00
FreddleSpl0it
6fa1c9f63d
[Web] protect /get/identity-provider 2024-12-02 10:24:15 +01:00
milkmaker
f3060b37a6
update postscreen_access.cidr (#6189) 2024-12-01 17:49:28 +01:00
milkmaker
59c68f2603
Translations update from Weblate (#6190) 2024-12-01 17:49:10 +01:00
FreddleSpl0it
ccc8595665
[SOGo] redirect to /user if unauthenticated 2024-12-01 16:51:56 +01:00
FreddleSpl0it
45c13c687b
[Web] update user based on template after login 2024-12-01 16:36:16 +01:00
FreddleSpl0it
d61a08c2a9
[Web] hide auth heading for external managed users 2024-11-30 14:39:05 +01:00
FreddleSpl0it
c8c4cfd939
[Web] add ignore ssl option for keycloak and generic-oidc provider 2024-11-30 14:37:07 +01:00
FreddleSpl0it
ec4b9b088c
[Web] support multiple ldap hosts separated by comma 2024-11-29 18:59:07 +01:00
FreddleSpl0it
b2db8e6b31
[Dovecot] init identity provider before user login 2024-11-29 16:52:34 +01:00
FreddleSpl0it
05e4bd7602
[Web] use global vars for iam_provider and iam_settings 2024-11-29 15:50:35 +01:00
Hassan A Hashim
31185e3de1
Implement search mailboxes by fullname 2024-11-27 14:47:57 +03:00
Habetdin
4dbfd3abad
Update lang.ru-ru.json (#6184) 2024-11-25 16:01:17 +01:00
FreddleSpl0it
b4e6002bcf
Merge pull request #6076 from Habetdin/staging
Only show active protocols on "last login" in mailbox overview
2024-11-21 10:24:41 +01:00
FreddleSpl0it
6af907cff0
Merge pull request #6182 from mailcow/fix/4518
[Web] allow dots in dkim selectors
2024-11-20 13:11:34 +01:00
FreddleSpl0it
ba282233ea
[Web] allow dots in dkim selectors 2024-11-20 13:05:02 +01:00
FreddleSpl0it
6f4c2b3361
Merge pull request #6181 from mailcow/fix/5703
[Web] Add additional columns to _sogo_static_view
2024-11-20 11:15:35 +01:00
FreddleSpl0it
d08b9aec32
[Web] Add additional columns to _sogo_static_view 2024-11-20 11:09:49 +01:00
FreddleSpl0it
bb310600b2
Merge pull request #6180 from mailcow/fix/6046
[Web] add missing translation for ratelimit in templates overview
2024-11-20 10:02:34 +01:00
FreddleSpl0it
fe7211f27f
[Web] add missing translation for ratelimit in templates overview 2024-11-20 09:57:14 +01:00
FreddleSpl0it
8e9a9364a8
Merge pull request #6146 from mailcow/feat/redis-pw
Enable password protection for Redis
2024-11-19 15:32:36 +01:00
FreddleSpl0it
6831f94fdb
[Redis] redis-cli suppress auth warning 2024-11-19 15:10:52 +01:00
FreddleSpl0it
b0de756a7c
[Redis] Rename docker-entrypoint.sh to redis-conf.sh 2024-11-19 14:54:36 +01:00
FreddleSpl0it
922f8777b0
Merge pull request #6168 from mailcow/fix/f2b-banlist
[Web] remove f2b banlist from json_api.php
2024-11-19 14:32:31 +01:00
FreddleSpl0it
c1903f121d
[Redis] set password via docker-entrypoint.sh 2024-11-19 14:25:31 +01:00
FreddleSpl0it
89fb1322c6
Enable password protection for Redis 2024-11-19 14:25:31 +01:00
FreddleSpl0it
852d944cfb
[Web] remove f2b banlist from json_api.php 2024-11-19 14:13:37 +01:00
Niklas Meyer
bca4e1a03d
update.sh: precaution ask for deletion of dns_blocklists.cf if old format (#6154) 2024-11-19 14:13:37 +01:00
FreddleSpl0it
326a446f8b
Merge pull request #6177 from mailcow/feat/jinja2-nginx
[Nginx] Use jinja2 for templating nginx configuration
2024-11-19 14:08:37 +01:00
FreddleSpl0it
70ca5fde95
[Nginx] Use jinja2 for templating nginx configuration 2024-11-19 08:39:52 +01:00
DerLinkman
5ad4ab5b60
update.sh: fixed typos 2024-11-15 16:39:06 +01:00
Niklas Meyer
bd9f4ba0a5
Merge pull request #6173 from mailcow/staging
2024-11b
2024-11-15 16:21:17 +01:00
DerLinkman
d10d64dd92
mysql: increased thread_stack to 192k since 10.5.27 2024-11-15 16:18:22 +01:00
FreddleSpl0it
6d1f7482ed
[Web] broadcast maildir move to dovecot containers on mailbox_rename 2024-11-15 16:18:21 +01:00
FreddleSpl0it
b9f52df3f1
[Web] update _sogo_static_view on password reset 2024-11-15 16:18:21 +01:00
FreddleSpl0it
dc379267a9
Merge remote-tracking branch 'origin/staging' into nightly 2024-11-12 16:07:55 +01:00
Niklas Meyer
b90375b6e5
php: use correct php image + workaround of #6149 (#6159)
* compose: bump php-fpm container to correctly use patched c-ares

* [Web] check $containers_info contains required fields

---------

Co-authored-by: FreddleSpl0it <patschul@posteo.de>
2024-11-12 15:56:23 +01:00
FreddleSpl0it
9542698e95
Merge remote-tracking branch 'origin/staging' into nightly 2024-11-12 15:10:03 +01:00
Niklas Meyer
afe0ba74d2
compose: bump sogo version to include 5.11.2 (#6156) 2024-11-12 11:11:34 +01:00
milkmaker
dc5a28111d
[Web] Updated lang.zh-cn.json (#6151)
[Web] Updated lang.zh-cn.json

Co-authored-by: Easton Man <me@eastonman.com>
2024-11-11 21:39:15 +01:00
Niklas Meyer
52f3f93aee
update.sh: precaution ask for deletion of dns_blocklists.cf if old format (#6154) 2024-11-11 16:50:14 +01:00
Habetdin
6550f0a3e8 Only show active protocols on "last login" in mailbox overview 2024-11-11 12:44:05 +03:00
FreddleSpl0it
f9304dcd9b
[Web] check if $iam_provider is null on ldap_mbox_login 2024-10-09 12:34:39 +02:00
FreddleSpl0it
0b9b8c9060
[Web] Ensure correct SOGo SSO password is used after Dovecot restart 2024-09-06 10:05:00 +02:00
FreddleSpl0it
0d2046baeb
Merge branch 'staging' into nightly 2024-09-05 14:53:37 +02:00
FreddleSpl0it
82fcddb177
[Web] Fix catch block in LDAP connection test 2024-09-02 10:12:51 +02:00
FreddleSpl0it
320bd31d37
[Web] fix LDAP "ignore ssl errors" option 2024-09-02 10:02:10 +02:00
FreddleSpl0it
b307e0a0d5
[PHP-FPM] Add missing space in log message 2024-09-02 09:57:33 +02:00
FreddleSpl0it
ef238e5332
[LDAP] skip sync user if username_field in LDAP is empty 2024-08-28 11:28:37 +02:00
FreddleSpl0it
dbf87e99fc
[Web] Convert LDAP username_field and attribute_field to lowercase 2024-08-21 10:48:04 +02:00
milkmaker
aeeac63e1f
Fix: Escape a ' character in update.sh (#6034) (#6035)
Co-authored-by: Hassan A Hashim <h3ssan@protonmail.com>
2024-08-20 14:22:51 +02:00
Niklas Meyer
ffcd242048
Merge pull request #6027 from mailcow/staging
Automatic PR to nightly from 2024-08-19T12:28:50Z
2024-08-20 13:41:54 +02:00
DerLinkman
e21157c10d
Merge branch 'staging' into nightly 2024-08-19 11:42:12 +02:00
FreddleSpl0it
fa3c453d6e
Use DN instead of DistinguishedName for LDAP login 2024-08-15 12:49:57 +02:00
FreddleSpl0it
962ac39e4a
Merge remote-tracking branch 'origin/staging' into nightly 2024-08-15 12:45:52 +02:00
Niklas Meyer
ebc8e6b838
Merge pull request #6008 from mailcow/staging
Automatic PR to nightly from 2024-08-15T07:42:17Z
2024-08-15 09:53:17 +02:00
DerLinkman
1fc964d72e
compose: bump dovecot image to newest nightly (20240814) 2024-08-14 10:12:10 +02:00
DerLinkman
5571d80ae6
Merge branch 'staging' into nightly 2024-08-14 10:10:34 +02:00
DerLinkman
3396e1b427
Merge branch 'staging' into nightly 2024-08-13 16:03:30 +02:00
FreddleSpl0it
58a5a4578c
[Web] use cn as fallback ldap login 2024-08-13 12:14:05 +02:00
FreddleSpl0it
519d95cb8b
[Web] extend ldap auth logging 2024-08-13 09:30:54 +02:00
FreddleSpl0it
092d3cd80b
[Web] extend ldap auth logging 2024-08-12 10:14:53 +02:00
FreddleSpl0it
c034f4bd27
[Web] fix wrong log type on PDOException 2024-08-12 10:10:33 +02:00
FreddleSpl0it
73d60eb085
Merge pull request #5997 from mailcow/fix/nightly-tfa
[Web] fix incorrect user role assignment after TFA verification
2024-08-08 17:11:57 +02:00
FreddleSpl0it
b39b7c24a5
[Web] fix incorrect user role assignment after TFA verification 2024-08-08 17:04:56 +02:00
DerLinkman
d0ecb72e08
Merge branch 'staging' into nightly 2024-08-08 08:44:12 +02:00
DerLinkman
772d5c51fd
Merge branch 'staging' into nightly 2024-08-07 14:21:23 +02:00
FreddleSpl0it
9b86ff764e
Merge pull request #5975 from mailcow/staging
Automatic PR to nightly from 2024-08-01T03:13:55Z
2024-08-01 11:07:55 +02:00
FreddleSpl0it
57bc03b878
Merge remote-tracking branch 'origin/staging' into nightly 2024-07-31 10:35:44 +02:00
FreddleSpl0it
f7ae2a6162
[Nightly][SOGo] Update 5.10.0 2024-06-06 12:12:30 +02:00
FreddleSpl0it
3080a70287
[Nightly][SOGo] Update to 5.10.0 2024-06-03 09:20:54 +02:00
Patrick Schult
caee770e36
Merge pull request #5850 from mailcow/fix/nightly-autodiscover
[Web] Fix autodiscover fails with external IdP
2024-04-19 20:38:56 +02:00
FreddleSpl0it
95d6eeb37a
[Web] revert include prerequisites in autodiscover - include autoload 2024-04-19 20:32:44 +02:00
FreddleSpl0it
eadf70d809
[Web] include prerequisites in autodiscover 2024-04-18 14:23:35 +02:00
Patrick Schult
6e9c3e2687
Merge pull request #5821 from mailcow/fix/web-nightly
Fix/web nightly
2024-04-04 09:33:22 +02:00
FreddleSpl0it
cf2fda66e2
[Web] escape html of alert messages 2024-04-04 09:31:20 +02:00
FreddleSpl0it
cd24057f1a
[Web] use SEC_FETCH_DEST header to block api requests 2024-04-04 09:31:03 +02:00
FreddleSpl0it
c68a436a22
[Web] fix invalid rspamd map check 2024-04-04 09:30:30 +02:00
FreddleSpl0it
0807c122f6
[Web] set default LDAP options on get 2024-03-08 15:11:49 +01:00
FreddleSpl0it
e0bda6ca6a
[Web] prevent multiple dual-logins 2024-03-08 14:05:37 +01:00
FreddleSpl0it
2ba64e93f9
[Web] allow SSL / TLS connections for LDAP 2024-03-08 13:50:20 +01:00
FreddleSpl0it
e1c3ad9fe8
[Web] return idp instance after init 2024-03-08 13:15:35 +01:00
FreddleSpl0it
ffbf1758e0
[Web] fix identity_provider ArgumentCountError 2024-02-26 13:40:34 +01:00
Patrick Schult
a3af2d8392
Merge pull request #5764 from mailcow/fix/nightly-issues
Fix nightly issues with new ldap provider
2024-02-26 13:33:44 +01:00
FreddleSpl0it
39a4b115ed
[SOGo] fix plist_ldap.sh example 2024-02-26 13:14:08 +01:00
FreddleSpl0it
881c2d6e02
[SOGo] remove custom logout from toolbar 2024-02-26 13:13:50 +01:00
FreddleSpl0it
d237157c0b
init identity_provider only after all conditions are met 2024-02-26 13:12:44 +01:00
FreddleSpl0it
6928eb632e
[Dovecot] move sogo sso to mailcowauth.php 2024-02-26 13:10:08 +01:00
FreddleSpl0it
010d898786
[Web] apply LDAP filter 2024-02-23 10:01:56 +01:00
FreddleSpl0it
766c270b1f
[SOGo] fix custom html elements and wrong redirection 2024-02-23 09:12:17 +01:00
FreddleSpl0it
916d0fd46a
[Web] catch all exceptions on ldap connect 2024-02-23 08:12:06 +01:00
Patrick Schult
9561526f33
Merge pull request #5754 from mailcow/feat/idp-ldap2
[Nightly] add LDAP as direct IdP
2024-02-20 15:33:58 +01:00
FreddleSpl0it
45811bc2dc
[Web] fix mailbox datatable search 2024-02-20 15:00:06 +01:00
FreddleSpl0it
132e37bfec
[SOGo] use bash script for ldap plist template 2024-02-20 12:42:37 +01:00
FreddleSpl0it
b3e26e14ef
[Ofelia] add ldap sync cronjob 2024-02-20 12:01:08 +01:00
FreddleSpl0it
a3bb889def
[Web] fix set_tfa for ldap users 2024-02-20 11:41:02 +01:00
FreddleSpl0it
3a1dcb3aaf
[Web] fix set_tfa for ldap users 2024-02-20 11:34:01 +01:00
FreddleSpl0it
d22cafacc8
[Web] fix ldap filter if empty 2024-02-20 11:21:25 +01:00
FreddleSpl0it
78e7266368
[Web] add LDAP query filter 2024-02-20 10:46:23 +01:00
FreddleSpl0it
a06c78362a
[Web] add ldap idp 2024-02-20 10:31:14 +01:00
FreddleSpl0it
d479d18507
[Web] update directorytree/ldaprecord 2024-02-20 10:30:11 +01:00
DerLinkman
40146839ef
docker-compose: bumped versions 2024-02-08 12:42:36 +01:00
DerLinkman
448f85abe8
Remove unused files 2024-02-08 12:42:36 +01:00
FreddleSpl0it
9a4b79a629
[Web] fix idp mailbox login 2024-02-08 12:42:35 +01:00
DerLinkman
058b79ed5c
dovecot: corrected dockerfile inside nightly 2024-02-08 12:42:35 +01:00
FreddleSpl0it
216398355b
fix functions.inc.php, update sogo and dovecot nightly image 2024-02-08 12:42:35 +01:00
Geert Hauwaerts
1cda16523d
Fixed SQL query for retrieving SSO users. 2024-02-08 12:42:34 +01:00
FreddleSpl0it
2f1e1438e9
[Web] add log messages to verify-sso function 2024-02-08 12:42:34 +01:00
FreddleSpl0it
9039ab4e12
[Web] add missing file to autodiscover.php 2024-02-08 12:42:34 +01:00
DerLinkman
db47696ba7
Updated Dovecot Image to use OpenSSL 3.0 fix 2024-02-08 12:42:33 +01:00
FreddleSpl0it
eb9e3b8391
[Web] add configurable client scopes for generic-oidc 2024-02-08 12:42:33 +01:00
FreddleSpl0it
ba32f1131e
[Web] dont rtrim generic-oidc urls 2024-02-08 12:42:32 +01:00
DerLinkman
27ef04baa0
Update Dovecot to reuse lz4 compression 2024-02-08 12:42:32 +01:00
DerLinkman
b3a94e79e3
Use dedicated nightly images on nightly branch + updates of images itself 2024-02-08 12:42:32 +01:00
FreddleSpl0it
3a4c0c84a3
fix keycloak mailpassword flow 2024-02-08 12:42:31 +01:00
Mirko Ceroni
73a044ec14
Update sogo-auth.php 2024-02-08 12:42:31 +01:00
Mirko Ceroni
389eb99c10
Update sogo-auth.php
initialize db before validating credentials
2024-02-08 12:42:31 +01:00
FreddleSpl0it
597d98e1d7
Fixes #5408 2024-02-08 12:42:30 +01:00
FreddleSpl0it
981307a1c6
[Web] add missing break 2024-02-08 12:42:30 +01:00
FreddleSpl0it
2d51881ae3
[Web] fix user protocol badges 2024-02-08 12:42:30 +01:00
FreddleSpl0it
788f03e993
[Dovecot] remove passwd-verify.lua generation 2024-02-08 12:42:29 +01:00
DerLinkman
81024b8c12
Clamd using Alpine Packages instead self compile 2024-02-08 12:42:29 +01:00
DerLinkman
89c5064213
Rebased Dovecot on Alpine + fixed logging 2024-02-08 12:42:29 +01:00
DerLinkman
4b18a99e55
Small fixes for CLAMD Health Check 2024-02-08 12:42:28 +01:00
DerLinkman
92d2cca7c3
Added missing Labels to Dockerfiles 2024-02-08 12:42:28 +01:00
DerLinkman
466e36ecbb
Optimized Build Process for Dovecot 2024-02-08 12:42:28 +01:00
DerLinkman
7ec7bd21cb
Changed Dovecot Base to Bullseye again (Self compile) 2024-02-08 12:42:27 +01:00
DerLinkman
38db7226a8
Optimized CLAMAV Builds to match exact version instead of Repo 2024-02-08 12:42:27 +01:00
DerLinkman
60f9412bb8
Switched to Alpine Edge (for IMAPSYNC Deps) 2024-02-08 12:42:26 +01:00
DerLinkman
737c0502ac
Rebased Dovecot on Alpine 3.17 instead Bullseye (ARM64 Support) 2024-02-08 12:42:26 +01:00
DerLinkman
6da41b1027
Removed Test self compiled SOGo Dockerfile 2024-02-08 12:42:26 +01:00
DerLinkman
2bd46ae0fd
Changed Maintainer to tinc within Dockerfiles 2024-02-08 12:42:25 +01:00
DerLinkman
c15ab10b1b
Updated Clamd Building to be x86 and ARM Compatible 2024-02-08 12:42:25 +01:00
DerLinkman
ddaeebc822
[Rspamd] Update to 3.6 (Ratelimit fix) 2024-02-08 12:42:25 +01:00
FreddleSpl0it
e64293c82f
[Web] minor fixes 2024-02-08 12:42:24 +01:00
FreddleSpl0it
ccc17e4a20
[SOGo] deny direct login on external users 2024-02-08 12:42:24 +01:00
FreddleSpl0it
a53ef2ed7a
[SOGo] remove sogo_view and triggers 2024-02-08 12:42:24 +01:00
FreddleSpl0it
185c36cdfe
[Web] catch update_sogo exceptions 2024-02-08 12:42:23 +01:00
FreddleSpl0it
9beb47c067
[Web] fix malformed_username check 2024-02-08 12:42:23 +01:00
FreddleSpl0it
3d486678ae
[Web] remove keycloak sync disabled warning 2024-02-08 12:42:23 +01:00
FreddleSpl0it
04e2494af8
deny changes on identity provider if it's in use 2024-02-08 12:42:22 +01:00
FreddleSpl0it
7b47159478
rework auth - move dovecot sasl log to php 2024-02-08 12:42:22 +01:00
FreddleSpl0it
17b6ac3313
[Web] allow mailbox authsource to be switchable 2024-02-08 12:42:22 +01:00
FreddleSpl0it
43600cd127
[Web] fix identity-provider settings layout 2024-02-08 12:42:21 +01:00
FreddleSpl0it
6d3a32c1d9
[Web] trim CRON_LOG 2024-02-08 12:42:21 +01:00
FreddleSpl0it
21fa3c8458
[Web] remove unnecessary if block 2024-02-08 12:42:21 +01:00
FreddleSpl0it
6df663825a
[Web] add curl timeouts to oidc requests 2024-02-08 12:42:20 +01:00
FreddleSpl0it
8ce4600562
[Web] update lang files 2024-02-08 12:42:20 +01:00
FreddleSpl0it
3179c0e712
[Dovecot] mailcowauth minor fixes 2024-02-08 12:42:19 +01:00
FreddleSpl0it
37254738e2
[Web] improve identity-provider template 2024-02-08 12:42:19 +01:00
FreddleSpl0it
a4cce147aa
[Web] improve attribute sync performance & make authsource editable 2024-02-08 12:42:19 +01:00
FreddleSpl0it
b176585a9c
[Web] add crontasks logs 2024-02-08 12:42:18 +01:00
FreddleSpl0it
f8647bb15e
[Web] add keycloak sync crontask 2024-02-08 12:42:18 +01:00
FreddleSpl0it
85368971fd
[Web] handle fatal errors on getAccessToken 2024-02-08 12:42:18 +01:00
FreddleSpl0it
e4284b8e19
[Web] fix attribute mapping list 2024-02-08 12:42:17 +01:00
FreddleSpl0it
5545d8a56c
[Web] hide auth settings for external users 2024-02-08 12:42:17 +01:00
FreddleSpl0it
4dc3222f03
[Web] fix bug on mailbox login 2024-02-08 12:42:17 +01:00
FreddleSpl0it
7cf6a9d808
[Web] update lang.en-gb.json 2024-02-08 12:42:16 +01:00
FreddleSpl0it
95a15d18a7
[Web] update guzzlehttp/psr7 2024-02-08 12:42:16 +01:00
FreddleSpl0it
cee771a3fb
[Web] update stevenmaguire/oauth2-keycloak and firebase/php-jwt 2024-02-08 12:42:16 +01:00
FreddleSpl0it
a805d3b2e3
[Web] add league/oauth2-client 2024-02-08 12:42:15 +01:00
FreddleSpl0it
b251c58b23
update gitignore 2024-02-08 12:42:15 +01:00
FreddleSpl0it
cc7516685f
[Web] functions.auth.inc.php corrections 2024-02-08 12:42:15 +01:00
FreddleSpl0it
ad19ff5429
[Web] remove ropc flow 2024-02-08 12:42:14 +01:00
FreddleSpl0it
e784c98a5a
[Web] add "add mailbox_from_template" function 2024-02-08 12:42:14 +01:00
FreddleSpl0it
28679eb916
[Web] add generic-oidc provider 2024-02-08 12:42:13 +01:00
FreddleSpl0it
c8fec24da3
[Web] add "edit mailbox_from_template" function 2024-02-08 12:42:13 +01:00
FreddleSpl0it
0c1e2ed6f2
[Web] revert configurable authsource 2024-02-08 12:42:13 +01:00
FreddleSpl0it
90476ae057
[Web] rename var for tab-config-identity-provider.twig 2024-02-08 12:42:12 +01:00
FreddleSpl0it
3b6a1d50bd
[Web] add generic-oidc provider 2024-02-08 12:42:12 +01:00
FreddleSpl0it
1ab1505c88
[Web] remove sso login alertbox 2024-02-08 12:42:12 +01:00
FreddleSpl0it
593e581cf3
[Web] move iam sso functions 2024-02-08 12:42:11 +01:00
FreddleSpl0it
e202d00beb
[Dovecot] group auth files 2024-02-08 12:42:11 +01:00
FreddleSpl0it
dca5f1baab
[Web] move /process/login to internal endpoint 2024-02-08 12:42:11 +01:00
FreddleSpl0it
f0689e08d9
[Web] iam - add switch for direct login flow 2024-02-08 12:42:10 +01:00
FreddleSpl0it
5bbb12b53e
[Dovecot] fix wrong lua syntax 2024-02-08 12:42:10 +01:00
FreddleSpl0it
c6a56e0748
[Web] add IAM delete button & fix add mbox modal 2024-02-08 12:42:10 +01:00
FreddleSpl0it
3c62a7fd9f
[Web] IAM - add delete option & fix test connection 2024-02-08 12:42:09 +01:00
FreddleSpl0it
61ab17d8a1
[Web] fix iam attribute mapping ui 2024-02-08 12:42:09 +01:00
FreddleSpl0it
d4ae616460
replace ropc flow with keycloak rest api flow 2024-02-08 12:42:09 +01:00
FreddleSpl0it
b7a18255fe
[Web] rename role mapping to attribute mapping 2024-02-08 12:42:08 +01:00
FreddleSpl0it
1c73a16ca0
new dovecout lua auth - use https 2024-02-08 12:42:08 +01:00
FreddleSpl0it
1aeb36d40e
[Web] create ratelimit acl on iam mbox creation 2 2024-02-08 12:42:07 +01:00
FreddleSpl0it
f251c9826e
[Web] create ratelimit acl on iam mbox creation 2024-02-08 12:42:07 +01:00
FreddleSpl0it
204063819c
[Web] fix broken sogo-sso 2024-02-08 12:42:07 +01:00
FreddleSpl0it
13f8882616
[Web] fix app_pass ignore_access 2024-02-08 12:42:06 +01:00
FreddleSpl0it
eba1d469c8
[Web] keycloak auth functions 2024-02-08 12:42:06 +01:00
FreddleSpl0it
6e9980bf0f
[Web] add manage identity provider 2024-02-08 12:42:06 +01:00
FreddleSpl0it
67c9c5b8ed
[Web] remove u2f lib from prerequisites 2024-02-08 12:42:05 +01:00
FreddleSpl0it
cd3660a96d
[Web] add oauth2-keycloak lib 2024-02-08 12:42:05 +01:00
FreddleSpl0it
9d8c1a01ac
[Web] remove u2f lib 2024-02-08 12:42:05 +01:00
FreddleSpl0it
0a77cad2dd
[Web] limit identity_provider function better 2024-02-08 12:42:04 +01:00
FreddleSpl0it
f6869da3a0
[Web] manage keycloak identity provider 2024-02-08 12:42:04 +01:00
FreddleSpl0it
6adad79e5c
[Web] organize auth functions+api auth w/ dovecot 2024-02-08 12:42:04 +01:00
FreddleSpl0it
50d4d59626
[Web] update de-de + en-gb lang 2024-02-08 12:42:03 +01:00
FreddleSpl0it
56a9f1a411
[Web] organize user landing 2024-02-08 12:42:03 +01:00
FreddleSpl0it
84ff6ff2c5
[Web] fix user login history 2024-02-08 12:42:03 +01:00
FreddleSpl0it
6e35574c72
[Web] add app hide option 2024-02-08 12:42:02 +01:00
FreddleSpl0it
415c1d0574
[Web] add seperate link for logged in users 2024-02-08 12:42:02 +01:00
FreddleSpl0it
cfce7086a5
[Web] few style changes 2024-02-08 12:42:01 +01:00
FreddleSpl0it
c90d637a48
[Web] redirect to sogo after failed sogo-auth 2024-02-08 12:42:01 +01:00
890 changed files with 54709 additions and 12081 deletions

View File

@ -1,7 +1,7 @@
blank_issues_enabled: false
contact_links:
- name: ❓ Community-driven support (Free)
url: https://docs.mailcow.email/#get-support
url: https://docs.mailcow.email/#community-support-and-chat
about: Please use the community forum for questions or assistance
- name: 🔥 Premium Support (Paid)
url: https://www.servercow.de/mailcow?lang=en#support

View File

@ -15,12 +15,6 @@
"data\/web\/inc\/lib\/vendor\/**"
],
"regexManagers": [
{
"fileMatch": ["^helper-scripts\/nextcloud.sh$"],
"matchStrings": [
"#\\srenovate:\\sdatasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?( extractVersion=(?<extractVersion>.*?))?\\s.*?_VERSION=(?<currentValue>.*)"
]
},
{
"fileMatch": ["(^|/)Dockerfile[^/]*$"],
"matchStrings": [

View File

@ -12,7 +12,7 @@ jobs:
- name: Send message
uses: thollander/actions-comment-pull-request@v3.0.1
with:
GITHUB_TOKEN: ${{ secrets.CHECKIFPRISSTAGING_ACTION_PAT }}
github-token: ${{ secrets.CHECKIFPRISSTAGING_ACTION_PAT }}
message: |
Thanks for contributing!

View File

@ -14,7 +14,7 @@ jobs:
pull-requests: write
steps:
- name: Mark/Close Stale Issues and Pull Requests 🗑️
uses: actions/stale@v9.0.0
uses: actions/stale@v9.1.0
with:
repo-token: ${{ secrets.STALE_ACTION_PAT }}
days-before-stale: 60

View File

@ -23,7 +23,6 @@ jobs:
- "postfix-mailcow"
- "rspamd-mailcow"
- "sogo-mailcow"
- "solr-mailcow"
- "unbound-mailcow"
- "watchdog-mailcow"
runs-on: ubuntu-latest

View File

@ -12,7 +12,7 @@ jobs:
with:
fetch-depth: 0
- name: Run the Action
uses: devops-infra/action-pull-request@v0.5.5
uses: devops-infra/action-pull-request@v0.6.0
with:
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}

View File

@ -9,6 +9,8 @@ on:
jobs:
docker_image_build:
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
@ -19,11 +21,13 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
- name: Login to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_USERNAME }}
password: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_TOKEN }}
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
@ -32,4 +36,4 @@ jobs:
platforms: linux/amd64,linux/arm64
file: data/Dockerfiles/backup/Dockerfile
push: true
tags: mailcow/backup:latest
tags: ghcr.io/mailcow/backup:latest

7
.gitignore vendored
View File

@ -23,6 +23,7 @@ data/conf/dovecot/sni.conf
data/conf/dovecot/sogo-sso.conf
data/conf/dovecot/sogo_trusted_ip.conf
data/conf/dovecot/sql
data/conf/dovecot/conf.d/fts.conf
data/conf/nextcloud-*.bak
data/conf/nginx/*.active
data/conf/nginx/*.bak
@ -44,9 +45,12 @@ data/conf/rspamd/local.d/*
data/conf/rspamd/override.d/*
data/conf/sogo/custom-theme.js
data/conf/sogo/plist_ldap
data/conf/sogo/plist_ldap.sh
data/conf/sogo/sieve.creds
data/conf/sogo/cron.creds
data/conf/sogo/sogo-full.svg
data/conf/sogo/custom-fulllogo.svg
data/conf/sogo/custom-shortlogo.svg
data/conf/sogo/custom-fulllogo.png
data/gitea/
data/gogs/
data/hooks/dovecot/*
@ -70,3 +74,4 @@ rebuild-images.sh
refresh_images.sh
update_diffs/
create_cold_standby.sh
!data/conf/nginx/mailcow_auth.conf

View File

@ -13,6 +13,25 @@ You can also [get a SAL](https://www.servercow.de/mailcow?lang=en#sal) which is
Or just spread the word: moo.
## Many thanks to our GitHub Sponsors ❤️
A big thank you to everyone supporting us on GitHub Sponsors—your contributions mean the world to us! Special thanks to the following amazing supporters:
### 100$/Month Sponsors
<a href="https://www.colba.net/" target=_blank><img
src="https://avatars.githubusercontent.com/u/204464723" height="58"
/></a>
<a href="https://www.maehdros.com/" target=_blank><img
src="https://avatars.githubusercontent.com/u/173894712" height="58"
/></a>
<a href="https://macarne.com/" target=_blank><img
src="https://avatars.githubusercontent.com/u/149550368?s=200&v=4" height="58"
/></a>
### 50$/Month Sponsors
<a href="https://github.com/vnukhr" target=_blank><img
src="https://avatars.githubusercontent.com/u/7805987?s=52&v=4" height="58"
/></a>
## Info, documentation and support
Please see [the official documentation](https://docs.mailcow.email/) for installation and support instructions. 🐄

View File

@ -1,8 +1,7 @@
FROM alpine:3.20
FROM alpine:3.21
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
RUN apk upgrade --no-cache \
&& apk add --update --no-cache \
bash \
@ -15,7 +14,7 @@ RUN apk upgrade --no-cache \
tini \
tzdata \
python3 \
acme-tiny --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community/
acme-tiny
COPY acme.sh /srv/acme.sh
COPY functions.sh /srv/functions.sh

View File

@ -4,9 +4,9 @@ exec 5>&1
# Do not attempt to write to slave
if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then
export REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT}"
export REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} --no-auth-warning"
else
export REDIS_CMDLINE="redis-cli -h redis -p 6379"
export REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS} --no-auth-warning"
fi
until [[ $(${REDIS_CMDLINE} PING) == "PONG" ]]; do
@ -138,7 +138,7 @@ log_f "Resolver OK"
log_f "Waiting for domain table..."
while [[ -z ${DOMAIN_TABLE} ]]; do
curl --silent http://nginx.${COMPOSE_PROJECT_NAME}_mailcow-network/ >/dev/null 2>&1
DOMAIN_TABLE=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SHOW TABLES LIKE 'domain'" -Bs)
DOMAIN_TABLE=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SHOW TABLES LIKE 'domain'" -Bs)
[[ -z ${DOMAIN_TABLE} ]] && sleep 10
done
log_f "OK" no_date
@ -159,18 +159,6 @@ while true; do
fi
if [[ ! -f ${ACME_BASE}/acme/account.pem ]]; then
log_f "Generating missing Lets Encrypt account key..."
if [[ ! -z ${ACME_CONTACT} ]]; then
if ! verify_email "${ACME_CONTACT}"; then
log_f "Invalid email address, will not start registration!"
sleep 365d
exec $(readlink -f "$0")
else
ACME_CONTACT_PARAMETER="--contact mailto:${ACME_CONTACT}"
log_f "Valid email address, using ${ACME_CONTACT} for registration"
fi
else
ACME_CONTACT_PARAMETER=""
fi
openssl genrsa 4096 > ${ACME_BASE}/acme/account.pem
else
log_f "Using existing Lets Encrypt account key ${ACME_BASE}/acme/account.pem"
@ -231,7 +219,7 @@ while true; do
#########################################
# IP and webroot challenge verification #
SQL_DOMAINS=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain FROM domain WHERE backupmx=0 and active=1" -Bs)
SQL_DOMAINS=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain FROM domain WHERE backupmx=0 and active=1" -Bs)
if [[ ! $? -eq 0 ]]; then
log_f "Failed to read SQL domains, retrying in 1 minute..."
sleep 1m
@ -299,7 +287,7 @@ while true; do
VALIDATED_CERTIFICATES+=("${CERT_NAME}")
# obtain server certificate if required
ACME_CONTACT_PARAMETER=${ACME_CONTACT_PARAMETER} DOMAINS=${SERVER_SAN_VALIDATED[@]} /srv/obtain-certificate.sh rsa
DOMAINS=${SERVER_SAN_VALIDATED[@]} /srv/obtain-certificate.sh rsa
RETURN="$?"
if [[ "$RETURN" == "0" ]]; then # 0 = cert created successfully
CERT_AMOUNT_CHANGED=1

View File

@ -93,8 +93,8 @@ until dig letsencrypt.org +time=3 +tries=1 @unbound > /dev/null; do
sleep 2
done
log_f "Resolver OK"
log_f "Using command acme-tiny ${DIRECTORY_URL} ${ACME_CONTACT_PARAMETER} --account-key ${ACME_BASE}/acme/account.pem --disable-check --csr ${CSR} --acme-dir /var/www/acme/"
ACME_RESPONSE=$(acme-tiny ${DIRECTORY_URL} ${ACME_CONTACT_PARAMETER} \
log_f "Using command acme-tiny ${DIRECTORY_URL} --account-key ${ACME_BASE}/acme/account.pem --disable-check --csr ${CSR} --acme-dir /var/www/acme/"
ACME_RESPONSE=$(acme-tiny ${DIRECTORY_URL} \
--account-key ${ACME_BASE}/acme/account.pem \
--disable-check \
--csr ${CSR} \
@ -124,7 +124,7 @@ case "$SUCCESS" in
;;
*) # non-zero is non-fun
log_f "Failed to obtain certificate ${CERT} for domains '${CERT_DOMAINS[*]}'"
redis-cli -h redis SET ACME_FAIL_TIME "$(date +%s)"
redis-cli -h redis -a ${REDISPASS} --no-auth-warning SET ACME_FAIL_TIME "$(date +%s)"
exit 100${SUCCESS}
;;
esac

View File

@ -1,3 +1,3 @@
FROM debian:bookworm-slim
RUN apt update && apt install pigz
RUN apt update && apt install pigz -y --no-install-recommends

View File

@ -1,14 +1,99 @@
FROM alpine:3.20
FROM alpine:3.21 AS builder
WORKDIR /src
ENV CLAMD_VERSION=1.4.2
RUN apk upgrade --no-cache \
&& apk add --update --no-cache \
g++ \
gcc \
gdb \
make \
cmake \
py3-pytest \
python3 \
valgrind \
bzip2-dev \
check-dev \
curl-dev \
json-c-dev \
libmilter-dev \
libxml2-dev \
linux-headers \
ncurses-dev \
openssl-dev \
pcre2-dev \
zlib-dev \
cargo \
rust
RUN wget -P /src https://www.clamav.net/downloads/production/clamav-${CLAMD_VERSION}.tar.gz \
&& tar xzfv /src/clamav-${CLAMD_VERSION}.tar.gz \
&& cd /src/clamav-${CLAMD_VERSION} \
&& cmake . \
-D CMAKE_BUILD_TYPE="Release" \
-D CMAKE_INSTALL_PREFIX="/usr" \
-D CMAKE_INSTALL_LIBDIR="/usr/lib" \
-D APP_CONFIG_DIRECTORY="/etc/clamav" \
-D DATABASE_DIRECTORY="/var/lib/clamav" \
-D ENABLE_CLAMONACC=OFF \
-D ENABLE_EXAMPLES=OFF \
-D ENABLE_MILTER=ON \
-D ENABLE_MAN_PAGES=OFF \
-D ENABLE_STATIC_LIB=OFF \
-D ENABLE_JSON_SHARED=ON \
&& cmake --build . \
&& make DESTDIR="/clamav" -j$(($(nproc) - 1)) install \
&& rm -r "/clamav/usr/lib/pkgconfig/" \
&& sed -e "s|^\(Example\)|\# \1|" \
-e "s|.*\(LocalSocket\) .*|\1 /tmp/clamd.sock|" \
-e "s|.*\(TCPSocket\) .*|\1 3310|" \
-e "s|.*\(TCPAddr\) .*|#\1 0.0.0.0|" \
-e "s|.*\(User\) .*|\1 clamav|" \
-e "s|^\#\(LogFile\) .*|\1 /var/log/clamav/clamd.log|" \
-e "s|^\#\(LogTime\).*|\1 yes|" \
"/clamav/etc/clamav/clamd.conf.sample" > "/clamav/etc/clamav/clamd.conf" \
&& sed -e "s|^\(Example\)|\# \1|" \
-e "s|.*\(DatabaseOwner\) .*|\1 clamav|" \
-e "s|^\#\(UpdateLogFile\) .*|\1 /var/log/clamav/freshclam.log|" \
-e "s|^\#\(NotifyClamd\).*|\1 /etc/clamav/clamd.conf|" \
-e "s|^\#\(ScriptedUpdates\).*|\1 yes|" \
"/clamav/etc/clamav/freshclam.conf.sample" > "/clamav/etc/clamav/freshclam.conf" \
&& sed -e "s|^\(Example\)|\# \1|" \
-e "s|.*\(MilterSocket\) .*|\1 inet:7357|" \
-e "s|.*\(User\) .*|\1 clamav|" \
-e "s|^\#\(LogFile\) .*|\1 /var/log/clamav/milter.log|" \
-e "s|^\#\(LogTime\).*|\1 yes|" \
-e "s|.*\(\ClamdSocket\) .*|\1 unix:/tmp/clamd.sock|" \
"/clamav/etc/clamav/clamav-milter.conf.sample" > "/clamav/etc/clamav/clamav-milter.conf" || exit 1
FROM alpine:3.21
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
RUN apk upgrade --no-cache \
&& apk add --update --no-cache \
rsync \
clamav \
bind-tools \
bash \
tini
tzdata \
rsync \
bind-tools \
bash \
tini \
json-c \
libbz2 \
libcurl \
libmilter \
libxml2 \
ncurses-libs \
pcre2 \
zlib \
libgcc \
&& addgroup -S "clamav" && \
adduser -D -G "clamav" -h "/var/lib/clamav" -s "/bin/false" -S "clamav" && \
install -d -m 755 -g "clamav" -o "clamav" "/var/log/clamav" && \
chown -R clamav:clamav /var/lib/clamav
COPY --from=builder "/clamav" "/"
# init
COPY clamd.sh /clamd.sh

View File

@ -91,6 +91,7 @@ done
) &
BACKGROUND_TASKS+=($!)
echo "$(clamd -V) is starting... please wait a moment."
nice -n10 clamd &
BACKGROUND_TASKS+=($!)

View File

@ -11,4 +11,4 @@ if [ "${CLAMAV_NO_CLAMD:-}" != "false" ]; then
echo "Clamd is up"
fi
exit 0
exit 0

View File

@ -1,4 +1,4 @@
FROM alpine:3.20
FROM alpine:3.21
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"

View File

@ -34,9 +34,9 @@ async def lifespan(app: FastAPI):
# Init redis client
if os.environ['REDIS_SLAVEOF_IP'] != "":
redis_client = redis = await aioredis.from_url(f"redis://{os.environ['REDIS_SLAVEOF_IP']}:{os.environ['REDIS_SLAVEOF_PORT']}/0")
redis_client = redis = await aioredis.from_url(f"redis://{os.environ['REDIS_SLAVEOF_IP']}:{os.environ['REDIS_SLAVEOF_PORT']}/0", password=os.environ['REDISPASS'])
else:
redis_client = redis = await aioredis.from_url("redis://redis-mailcow:6379/0")
redis_client = redis = await aioredis.from_url("redis://redis-mailcow:6379/0", password=os.environ['REDISPASS'])
# Init docker clients
sync_docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock', version='auto')
@ -241,9 +241,9 @@ async def handle_pubsub_messages(channel: aioredis.client.PubSub):
else:
dockerapi.logger.error("api call: missing container_name, post_action or request")
else:
dockerapi.logger.error("Unknwon PubSub recieved - %s" % json.dumps(data_json))
dockerapi.logger.error("Unknown PubSub received - %s" % json.dumps(data_json))
else:
dockerapi.logger.error("Unknwon PubSub recieved - %s" % json.dumps(data_json))
dockerapi.logger.error("Unknown PubSub received - %s" % json.dumps(data_json))
await asyncio.sleep(0.0)
except asyncio.TimeoutError:

View File

@ -1,4 +1,4 @@
FROM alpine:3.20
FROM alpine:3.21
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
@ -34,9 +34,13 @@ RUN addgroup -g 5000 vmail \
lua5.3-sql-mysql \
icu-data-full \
mariadb-connector-c \
lua-sec \
mariadb-dev \
glib-dev \
gcompat \
mariadb-client \
perl \
perl-dev \
perl-ntlm \
perl-cgi \
perl-crypt-openssl-rsa \
@ -65,7 +69,7 @@ RUN addgroup -g 5000 vmail \
perl-par-packer \
perl-parse-recdescent \
perl-lockfile-simple \
libproc \
libproc2 \
perl-readonly \
perl-regexp-common \
perl-sys-meminfo \
@ -105,7 +109,6 @@ RUN addgroup -g 5000 vmail \
dovecot-submissiond \
dovecot-pigeonhole-plugin \
dovecot-pop3d \
dovecot-fts-solr \
dovecot-fts-flatcurve \
&& arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) \
&& wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$arch" \

View File

@ -2,7 +2,7 @@
source /source_env.sh
MAX_AGE=$(redis-cli --raw -h redis-mailcow GET Q_MAX_AGE)
MAX_AGE=$(redis-cli --raw -h redis-mailcow -a ${REDISPASS} --no-auth-warning GET Q_MAX_AGE)
if [[ -z ${MAX_AGE} ]]; then
echo "Max age for quarantine items not defined"
@ -15,6 +15,6 @@ if ! [[ ${MAX_AGE} =~ ${NUM_REGEXP} ]] ; then
exit 1
fi
TO_DELETE=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT COUNT(id) FROM quarantine WHERE created < NOW() - INTERVAL ${MAX_AGE//[!0-9]/} DAY" -BN)
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM quarantine WHERE created < NOW() - INTERVAL ${MAX_AGE//[!0-9]/} DAY"
TO_DELETE=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT COUNT(id) FROM quarantine WHERE created < NOW() - INTERVAL ${MAX_AGE//[!0-9]/} DAY" -BN)
mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM quarantine WHERE created < NOW() - INTERVAL ${MAX_AGE//[!0-9]/} DAY"
echo "Deleted ${TO_DELETE} items from quarantine table (max age is ${MAX_AGE//[!0-9]/} days)"

View File

@ -14,9 +14,9 @@ done
# Do not attempt to write to slave
if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then
REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT}"
REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} --no-auth-warning"
else
REDIS_CMDLINE="redis-cli -h redis -p 6379"
REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS} --no-auth-warning"
fi
until [[ $(${REDIS_CMDLINE} PING) == "PONG" ]]; do
@ -28,7 +28,7 @@ ${REDIS_CMDLINE} SET DOVECOT_REPL_HEALTH 1 > /dev/null
# Create missing directories
[[ ! -d /etc/dovecot/sql/ ]] && mkdir -p /etc/dovecot/sql/
[[ ! -d /etc/dovecot/lua/ ]] && mkdir -p /etc/dovecot/lua/
[[ ! -d /etc/dovecot/auth/ ]] && mkdir -p /etc/dovecot/auth/
[[ ! -d /etc/dovecot/conf.d/ ]] && mkdir -p /etc/dovecot/conf.d/
[[ ! -d /var/vmail/_garbage ]] && mkdir -p /var/vmail/_garbage
[[ ! -d /var/vmail/sieve ]] && mkdir -p /var/vmail/sieve
@ -110,21 +110,16 @@ EOF
echo -n ${ACL_ANYONE} > /etc/dovecot/acl_anyone
if [[ "${FLATCURVE_EXPERIMENTAL}" =~ ^([yY][eE][sS]|[yY]) ]]; then
echo -e "\e[33mActivating Flatcurve as FTS Backend...\e[0m"
echo -e "\e[33mDepending on your previous setup a full reindex might be needed... \e[0m"
echo -e "\e[34mVisit https://docs.mailcow.email/manual-guides/Dovecot/u_e-dovecot-fts/#fts-related-dovecot-commands to learn how to reindex\e[0m"
echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify fts fts_flatcurve listescape replication lazy_expunge' > /etc/dovecot/mail_plugins
echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify mail_log fts fts_flatcurve listescape replication' > /etc/dovecot/mail_plugins_imap
echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl fts fts_flatcurve notify listescape replication' > /etc/dovecot/mail_plugins_lmtp
elif [[ "${SKIP_SOLR}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
if [[ "${SKIP_FTS}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo -e "\e[33mDetecting SKIP_FTS=y... not enabling Flatcurve (FTS) then...\e[0m"
echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify listescape replication lazy_expunge' > /etc/dovecot/mail_plugins
echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify listescape replication mail_log' > /etc/dovecot/mail_plugins_imap
echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl notify listescape replication' > /etc/dovecot/mail_plugins_lmtp
else
echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify fts fts_solr listescape replication lazy_expunge' > /etc/dovecot/mail_plugins
echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify mail_log fts fts_solr listescape replication' > /etc/dovecot/mail_plugins_imap
echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl fts fts_solr notify listescape replication' > /etc/dovecot/mail_plugins_lmtp
echo -e "\e[32mDetecting SKIP_FTS=n... enabling Flatcurve (FTS)\e[0m"
echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify fts fts_flatcurve listescape replication lazy_expunge' > /etc/dovecot/mail_plugins
echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify mail_log fts fts_flatcurve listescape replication' > /etc/dovecot/mail_plugins_imap
echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl fts fts_flatcurve notify listescape replication' > /etc/dovecot/mail_plugins_lmtp
fi
chmod 644 /etc/dovecot/mail_plugins /etc/dovecot/mail_plugins_imap /etc/dovecot/mail_plugins_lmtp /templates/quarantine.tpl
@ -136,168 +131,6 @@ user_query = SELECT CONCAT(JSON_UNQUOTE(JSON_VALUE(attributes, '$.mailbox_format
iterate_query = SELECT username FROM mailbox WHERE active = '1' OR active = '2';
EOF
cat <<EOF > /etc/dovecot/lua/passwd-verify.lua
function auth_password_verify(req, pass)
if req.domain == nil then
return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, "No such user"
end
if cur == nil then
script_init()
end
if req.user == nil then
req.user = ''
end
respbody = {}
-- check against mailbox passwds
local cur,errorString = con:execute(string.format([[SELECT password FROM mailbox
WHERE username = '%s'
AND active = '1'
AND domain IN (SELECT domain FROM domain WHERE domain='%s' AND active='1')
AND IFNULL(JSON_UNQUOTE(JSON_VALUE(mailbox.attributes, '$.force_pw_update')), 0) != '1'
AND IFNULL(JSON_UNQUOTE(JSON_VALUE(attributes, '$.%s_access')), 1) = '1']], con:escape(req.user), con:escape(req.domain), con:escape(req.service)))
local row = cur:fetch ({}, "a")
while row do
if req.password_verify(req, row.password, pass) == 1 then
con:execute(string.format([[REPLACE INTO sasl_log (service, app_password, username, real_rip)
VALUES ("%s", 0, "%s", "%s")]], con:escape(req.service), con:escape(req.user), con:escape(req.real_rip)))
cur:close()
con:close()
return dovecot.auth.PASSDB_RESULT_OK, ""
end
row = cur:fetch (row, "a")
end
-- check against app passwds for imap and smtp
-- app passwords are only available for imap, smtp, sieve and pop3 when using sasl
if req.service == "smtp" or req.service == "imap" or req.service == "sieve" or req.service == "pop3" then
local cur,errorString = con:execute(string.format([[SELECT app_passwd.id, %s_access AS has_prot_access, app_passwd.password FROM app_passwd
INNER JOIN mailbox ON mailbox.username = app_passwd.mailbox
WHERE mailbox = '%s'
AND app_passwd.active = '1'
AND mailbox.active = '1'
AND app_passwd.domain IN (SELECT domain FROM domain WHERE domain='%s' AND active='1')]], con:escape(req.service), con:escape(req.user), con:escape(req.domain)))
local row = cur:fetch ({}, "a")
while row do
if req.password_verify(req, row.password, pass) == 1 then
-- if password is valid and protocol access is 1 OR real_rip matches SOGo, proceed
if tostring(req.real_rip) == "__IPV4_SOGO__" then
cur:close()
con:close()
return dovecot.auth.PASSDB_RESULT_OK, ""
elseif row.has_prot_access == "1" then
con:execute(string.format([[REPLACE INTO sasl_log (service, app_password, username, real_rip)
VALUES ("%s", %d, "%s", "%s")]], con:escape(req.service), row.id, con:escape(req.user), con:escape(req.real_rip)))
cur:close()
con:close()
return dovecot.auth.PASSDB_RESULT_OK, ""
end
end
row = cur:fetch (row, "a")
end
end
cur:close()
con:close()
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Failed to authenticate"
-- PoC
-- local reqbody = string.format([[{
-- "success":0,
-- "service":"%s",
-- "app_password":false,
-- "username":"%s",
-- "real_rip":"%s"
-- }]], con:escape(req.service), con:escape(req.user), con:escape(req.real_rip))
-- http.request {
-- method = "POST",
-- url = "http://nginx:8081/sasl_log.php",
-- source = ltn12.source.string(reqbody),
-- headers = {
-- ["content-type"] = "application/json",
-- ["content-length"] = tostring(#reqbody)
-- },
-- sink = ltn12.sink.table(respbody)
-- }
end
function auth_passdb_lookup(req)
return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, ""
end
function script_init()
mysql = require "luasql.mysql"
http = require "socket.http"
http.TIMEOUT = 5
ltn12 = require "ltn12"
env = mysql.mysql()
con = env:connect("__DBNAME__","__DBUSER__","__DBPASS__","localhost")
return 0
end
function script_deinit()
con:close()
env:close()
end
EOF
# Temporarily set FTS depending on user choice inside mailcow.conf. Will be removed as soon as Solr is dropped
if [[ "${FLATCURVE_EXPERIMENTAL}" =~ ^([yY][eE][sS]|[yY])$ ]]; then
cat <<EOF > /etc/dovecot/conf.d/fts.conf
# Autogenerated by mailcow
plugin {
fts_autoindex = yes
fts_autoindex_exclude = \Junk
fts_autoindex_exclude2 = \Trash
fts = flatcurve
# Maximum term length can be set via the 'maxlen' argument (maxlen is
# specified in bytes, not number of UTF-8 characters)
fts_tokenizer_email_address = maxlen=100
fts_tokenizer_generic = algorithm=simple maxlen=30
# These are not flatcurve settings, but required for Dovecot FTS. See
# Dovecot FTS Configuration link above for further information.
fts_languages = en es de
fts_tokenizers = generic email-address
# OPTIONAL: Recommended default FTS core configuration
fts_filters = normalizer-icu snowball stopwords
fts_filters_en = lowercase snowball english-possessive stopwords
}
EOF
elif [[ ! "${SKIP_SOLR}" =~ ^([yY][eE][sS]|[yY])$ ]]; then
cat <<EOF > /etc/dovecot/conf.d/fts.conf
# Autogenerated by mailcow
plugin {
fts = solr
fts_autoindex = yes
fts_autoindex_exclude = \Junk
fts_autoindex_exclude2 = \Trash
fts_solr = url=http://solr:8983/solr/dovecot-fts/
fts_tokenizers = generic email-address
fts_tokenizer_generic = algorithm=simple
fts_filters = normalizer-icu snowball stopwords
fts_filters_en = lowercase snowball english-possessive stopwords
}
EOF
fi
# Replace patterns in app-passdb.lua
sed -i "s/__DBUSER__/${DBUSER}/g" /etc/dovecot/lua/passwd-verify.lua
sed -i "s/__DBPASS__/${DBPASS}/g" /etc/dovecot/lua/passwd-verify.lua
sed -i "s/__DBNAME__/${DBNAME}/g" /etc/dovecot/lua/passwd-verify.lua
sed -i "s/__IPV4_SOGO__/${IPV4_NETWORK}.248/g" /etc/dovecot/lua/passwd-verify.lua
# Migrate old sieve_after file
[[ -f /etc/dovecot/sieve_after ]] && mv /etc/dovecot/sieve_after /etc/dovecot/global_sieve_after
@ -398,6 +231,15 @@ mail_replica = tcp:${MAILCOW_REPLICA_IP}:${DOVEADM_REPLICA_PORT}
EOF
fi
# Setting variables for indexer-worker inside fts.conf automatically according to mailcow.conf settings
if [[ "${SKIP_FTS}" =~ ^([nN][oO]|[nN])+$ ]]; then
echo -e "\e[94mConfiguring FTS Settings...\e[0m"
echo -e "\e[94mSetting FTS Memory Limit (per process) to ${FTS_HEAP} MB\e[0m"
sed -i "s/vsz_limit\s*=\s*[0-9]*\s*MB*/vsz_limit=${FTS_HEAP} MB/" /etc/dovecot/conf.d/fts.conf
echo -e "\e[94mSetting FTS Process Limit to ${FTS_PROCS}\e[0m"
sed -i "s/process_limit\s*=\s*[0-9]*/process_limit=${FTS_PROCS}/" /etc/dovecot/conf.d/fts.conf
fi
# 401 is user dovecot
if [[ ! -s /mail_crypt/ecprivkey.pem || ! -s /mail_crypt/ecpubkey.pem ]]; then
openssl ecparam -name prime256v1 -genkey | openssl pkey -out /mail_crypt/ecprivkey.pem
@ -426,8 +268,8 @@ sievec /usr/lib/dovecot/sieve/report-ham.sieve
# Fix permissions
chown root:root /etc/dovecot/sql/*.conf
chown root:dovecot /etc/dovecot/sql/dovecot-dict-sql-sieve* /etc/dovecot/sql/dovecot-dict-sql-quota* /etc/dovecot/lua/passwd-verify.lua
chmod 640 /etc/dovecot/sql/*.conf /etc/dovecot/lua/passwd-verify.lua
chown root:dovecot /etc/dovecot/sql/dovecot-dict-sql-sieve* /etc/dovecot/sql/dovecot-dict-sql-quota* /etc/dovecot/auth/passwd-verify.lua
chmod 640 /etc/dovecot/sql/*.conf /etc/dovecot/auth/passwd-verify.lua
chown -R vmail:vmail /var/vmail/sieve
chown -R vmail:vmail /var/volatile
chown -R vmail:vmail /var/vmail_index
@ -455,15 +297,15 @@ printenv | sed 's/^\(.*\)$/export \1/g' > /source_env.sh
# Clean stopped imapsync jobs
rm -f /tmp/imapsync_busy.lock
IMAPSYNC_TABLE=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SHOW TABLES LIKE 'imapsync'" -Bs)
[[ ! -z ${IMAPSYNC_TABLE} ]] && mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "UPDATE imapsync SET is_running='0'"
IMAPSYNC_TABLE=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SHOW TABLES LIKE 'imapsync'" -Bs)
[[ ! -z ${IMAPSYNC_TABLE} ]] && mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "UPDATE imapsync SET is_running='0'"
# Envsubst maildir_gc
echo "$(envsubst < /usr/local/bin/maildir_gc.sh)" > /usr/local/bin/maildir_gc.sh
# GUID generation
while [[ ${VERSIONS_OK} != 'OK' ]]; do
if [[ ! -z $(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = \"${DBNAME}\" AND TABLE_NAME = 'versions'") ]]; then
if [[ ! -z $(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = \"${DBNAME}\" AND TABLE_NAME = 'versions'") ]]; then
VERSIONS_OK=OK
else
echo "Waiting for versions table to be created..."
@ -474,11 +316,11 @@ PUBKEY_MCRYPT=$(doveconf -P 2> /dev/null | grep -i mail_crypt_global_public_key
if [ -f ${PUBKEY_MCRYPT} ]; then
GUID=$(cat <(echo ${MAILCOW_HOSTNAME}) /mail_crypt/ecpubkey.pem | sha256sum | cut -d ' ' -f1 | tr -cd "[a-fA-F0-9.:/] ")
if [ ${#GUID} -eq 64 ]; then
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
REPLACE INTO versions (application, version) VALUES ("GUID", "${GUID}");
EOF
else
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
REPLACE INTO versions (application, version) VALUES ("GUID", "INVALID");
EOF
fi
@ -497,7 +339,7 @@ done
# For some strange, unknown and stupid reason, Dovecot may run into a race condition, when this file is not touched before it is read by dovecot/auth
# May be related to something inside Docker, I seriously don't know
touch /etc/dovecot/lua/passwd-verify.lua
touch /etc/dovecot/auth/passwd-verify.lua
if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then
cp /etc/syslog-ng/syslog-ng-redis_slave.conf /etc/syslog-ng/syslog-ng.conf

View File

@ -1,7 +1,7 @@
#!/bin/bash
if [[ "${SKIP_SOLR}" =~ ^([yY][eE][sS]|[yY])+$ && ! "${FLATCURVE_EXPERIMENTAL}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
if [[ "${SKIP_FTS}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
exit 0
else
doveadm fts optimize -A
fi
fi

View File

@ -8,7 +8,8 @@ from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate
import jinja2
from jinja2 import Template
from jinja2 import TemplateError
from jinja2.sandbox import SandboxedEnvironment
import json
import redis
import time
@ -31,7 +32,7 @@ try:
while True:
try:
r = redis.StrictRedis(host='redis', decode_responses=True, port=6379, db=0)
r = redis.StrictRedis(host='redis', decode_responses=True, port=6379, db=0, password=os.environ['REDISPASS'])
r.ping()
except Exception as ex:
print('%s - trying again...' % (ex))
@ -80,17 +81,22 @@ try:
if len(meta_query) == 0:
return
msg_count = len(meta_query)
env = SandboxedEnvironment()
if r.get('Q_HTML'):
try:
template = Template(r.get('Q_HTML'))
except:
print("Error: Cannot parse quarantine template, falling back to default template.")
with open('/templates/quarantine.tpl') as file_:
template = Template(file_.read())
try:
template = env.from_string(r.get('Q_HTML'))
except Exception:
print("Error: Cannot parse quarantine template, falling back to default template.")
with open('/templates/quarantine.tpl') as file_:
template = env.from_string(file_.read())
else:
with open('/templates/quarantine.tpl') as file_:
template = Template(file_.read())
html = template.render(meta=meta_query, username=rcpt, counter=msg_count, hostname=mailcow_hostname, quarantine_acl=quarantine_acl)
with open('/templates/quarantine.tpl') as file_:
template = env.from_string(file_.read())
try:
html = template.render(meta=meta_query, username=rcpt, counter=msg_count, hostname=mailcow_hostname, quarantine_acl=quarantine_acl)
except (jinja2.exceptions.SecurityError, TemplateError) as ex:
print(f"SecurityError or TemplateError in template rendering: {ex}")
return
text = html2text.html2text(html)
count = 0
while count < 15:

View File

@ -6,7 +6,7 @@ from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate
import jinja2
from jinja2 import Template
from jinja2.sandbox import SandboxedEnvironment
import redis
import time
import json
@ -23,7 +23,7 @@ else:
while True:
try:
r = redis.StrictRedis(host='redis', decode_responses=True, port=6379, db=0)
r = redis.StrictRedis(host='redis', decode_responses=True, port=6379, db=0, username='quota_notify', password='')
r.ping()
except Exception as ex:
print('%s - trying again...' % (ex))
@ -33,16 +33,24 @@ while True:
if r.get('QW_HTML'):
try:
template = Template(r.get('QW_HTML'))
except:
print("Error: Cannot parse quarantine template, falling back to default template.")
env = SandboxedEnvironment()
template = env.from_string(r.get('QW_HTML'))
except Exception:
print("Error: Cannot parse quota template, falling back to default template.")
with open('/templates/quota.tpl') as file_:
template = Template(file_.read())
env = SandboxedEnvironment()
template = env.from_string(file_.read())
else:
with open('/templates/quota.tpl') as file_:
template = Template(file_.read())
env = SandboxedEnvironment()
template = env.from_string(file_.read())
try:
html = template.render(username=username, percent=percent)
except (jinja2.exceptions.SecurityError, jinja2.TemplateError) as ex:
print(f"SecurityError or TemplateError in template rendering: {ex}")
sys.exit(1)
html = template.render(username=username, percent=percent)
text = html2text.html2text(html)
try:

View File

@ -4,9 +4,9 @@ source /source_env.sh
# Do not attempt to write to slave
if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then
REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT}"
REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} --no-auth-warning"
else
REDIS_CMDLINE="redis-cli -h redis -p 6379"
REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS} --no-auth-warning"
fi
# Is replication active?

View File

@ -11,7 +11,7 @@ else
fi
# Deploy
if curl --connect-timeout 15 --retry 10 --max-time 30 https://www.spamassassin.heinlein-support.de/$(dig txt 1.4.3.spamassassin.heinlein-support.de +short | tr -d '"' | tr -dc '0-9').tar.gz --output /tmp/sa-rules-heinlein.tar.gz; then
if curl --connect-timeout 15 --retry 5 --max-time 30 https://www.spamassassin.heinlein-support.de/$(dig txt 1.4.3.spamassassin.heinlein-support.de +short | tr -d '"' | tr -dc '0-9').tar.gz --output /tmp/sa-rules-heinlein.tar.gz; then
if gzip -t /tmp/sa-rules-heinlein.tar.gz; then
tar xfvz /tmp/sa-rules-heinlein.tar.gz -C /tmp/sa-rules-heinlein
cat /tmp/sa-rules-heinlein/*cf > /etc/rspamd/custom/sa-rules

View File

@ -20,6 +20,7 @@ destination d_redis_ui_log {
host("`REDIS_SLAVEOF_IP`")
persist-name("redis1")
port(`REDIS_SLAVEOF_PORT`)
auth("`REDISPASS`")
command("LPUSH" "DOVECOT_MAILLOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n")
);
};
@ -28,6 +29,7 @@ destination d_redis_f2b_channel {
host("`REDIS_SLAVEOF_IP`")
persist-name("redis2")
port(`REDIS_SLAVEOF_PORT`)
auth("`REDISPASS`")
command("PUBLISH" "F2B_CHANNEL" "$(sanitize $MESSAGE)")
);
};
@ -36,8 +38,13 @@ filter f_replica {
not match("User has no mail_replica in userdb" value("MESSAGE"));
not match("Error: sync: Unknown user in remote" value("MESSAGE"));
};
filter f_dovecot_auth_try {
not match("- trying the next passdb" value("MESSAGE")) and
not match("- trying the next userdb" value("MESSAGE"));
};
log {
source(s_dgram);
filter(f_dovecot_auth_try);
filter(f_replica);
destination(d_stdout);
filter(f_mail);

View File

@ -20,6 +20,7 @@ destination d_redis_ui_log {
host("redis-mailcow")
persist-name("redis1")
port(6379)
auth("`REDISPASS`")
command("LPUSH" "DOVECOT_MAILLOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n")
);
};
@ -28,6 +29,7 @@ destination d_redis_f2b_channel {
host("redis-mailcow")
persist-name("redis2")
port(6379)
auth("`REDISPASS`")
command("PUBLISH" "F2B_CHANNEL" "$(sanitize $MESSAGE)")
);
};
@ -36,8 +38,13 @@ filter f_replica {
not match("User has no mail_replica in userdb" value("MESSAGE"));
not match("Error: sync: Unknown user in remote" value("MESSAGE"));
};
filter f_dovecot_auth_try {
not match("- trying the next passdb" value("MESSAGE")) and
not match("- trying the next userdb" value("MESSAGE"));
};
log {
source(s_dgram);
filter(f_dovecot_auth_try);
filter(f_replica);
destination(d_stdout);
filter(f_mail);

View File

@ -10,9 +10,9 @@ catch_non_zero() {
source /source_env.sh
# Do not attempt to write to slave
if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then
REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT}"
REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} --no-auth-warning"
else
REDIS_CMDLINE="redis-cli -h redis -p 6379"
REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS} --no-auth-warning"
fi
catch_non_zero "${REDIS_CMDLINE} LTRIM ACME_LOG 0 ${LOG_LINES}"
catch_non_zero "${REDIS_CMDLINE} LTRIM POSTFIX_MAILLOG 0 ${LOG_LINES}"
@ -23,3 +23,4 @@ catch_non_zero "${REDIS_CMDLINE} LTRIM AUTODISCOVER_LOG 0 ${LOG_LINES}"
catch_non_zero "${REDIS_CMDLINE} LTRIM API_LOG 0 ${LOG_LINES}"
catch_non_zero "${REDIS_CMDLINE} LTRIM RL_LOG 0 ${LOG_LINES}"
catch_non_zero "${REDIS_CMDLINE} LTRIM WATCHDOG_LOG 0 ${LOG_LINES}"
catch_non_zero "${REDIS_CMDLINE} LTRIM CRON_LOG 0 ${LOG_LINES}"

View File

@ -1,4 +1,4 @@
FROM alpine:3.20
FROM alpine:3.21
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"

View File

@ -85,11 +85,10 @@ def refreshF2bregex():
f2bregex[3] = r'warning: .*\[([0-9a-f\.:]+)\]: SASL .+ authentication failed: (?!.*Connection lost to authentication server).+'
f2bregex[4] = r'warning: non-SMTP command from .*\[([0-9a-f\.:]+)]:.+'
f2bregex[5] = r'NOQUEUE: reject: RCPT from \[([0-9a-f\.:]+)].+Protocol error.+'
f2bregex[6] = r'-login: Disconnected.+ \(auth failed, .+\): user=.*, method=.+, rip=([0-9a-f\.:]+),'
f2bregex[7] = r'-login: Aborted login.+ \(auth failed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
f2bregex[8] = r'-login: Aborted login.+ \(tried to use disallowed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
f2bregex[9] = r'SOGo.+ Login from \'([0-9a-f\.:]+)\' for user .+ might not have worked'
f2bregex[10] = r'([0-9a-f\.:]+) \"GET \/SOGo\/.* HTTP.+\" 403 .+'
f2bregex[6] = r'\w+\([^,]+,([0-9a-f\.:]+),<[^>]+>\): Password mismatch \(SHA1 of given password: [a-f0-9]+\)'
f2bregex[7] = r'\w+\([^,]+,([0-9a-f\.:]+),<[^>]+>\): unknown user \(SHA1 of given password: [a-f0-9]+\)'
f2bregex[8] = r'SOGo.+ Login from \'([0-9a-f\.:]+)\' for user .+ might not have worked'
f2bregex[9] = r'([0-9a-f\.:]+) \"GET \/SOGo\/.* HTTP.+\" 403 .+'
r.set('F2B_REGEX', json.dumps(f2bregex, ensure_ascii=False))
else:
try:
@ -106,7 +105,7 @@ def get_ip(address):
ip = ip.ipv4_mapped
if ip.is_private or ip.is_loopback:
return False
return ip
def ban(address):
@ -434,9 +433,9 @@ if __name__ == '__main__':
redis_slaveof_ip = os.getenv('REDIS_SLAVEOF_IP', '')
redis_slaveof_port = os.getenv('REDIS_SLAVEOF_PORT', '')
if "".__eq__(redis_slaveof_ip):
r = redis.StrictRedis(host=os.getenv('IPV4_NETWORK', '172.22.1') + '.249', decode_responses=True, port=6379, db=0)
r = redis.StrictRedis(host=os.getenv('IPV4_NETWORK', '172.22.1') + '.249', decode_responses=True, port=6379, db=0, password=os.environ['REDISPASS'])
else:
r = redis.StrictRedis(host=redis_slaveof_ip, decode_responses=True, port=redis_slaveof_port, db=0)
r = redis.StrictRedis(host=redis_slaveof_ip, decode_responses=True, port=redis_slaveof_port, db=0, password=os.environ['REDISPASS'])
r.ping()
pubsub = r.pubsub()
except Exception as ex:
@ -452,7 +451,7 @@ if __name__ == '__main__':
# clear bans in redis
r.delete('F2B_ACTIVE_BANS')
r.delete('F2B_PERM_BANS')
refreshF2boptions()
watch_thread = Thread(target=watch)

View File

@ -0,0 +1,18 @@
FROM nginx:alpine
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
ENV PIP_BREAK_SYSTEM_PACKAGES=1
RUN apk add --no-cache nginx \
python3 \
py3-pip && \
pip install --upgrade pip && \
pip install Jinja2
RUN mkdir -p /etc/nginx/includes
COPY ./bootstrap.py /
COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

View File

@ -0,0 +1,100 @@
import os
import subprocess
from jinja2 import Environment, FileSystemLoader
def includes_conf(env, template_vars):
server_name = "server_name.active"
listen_plain = "listen_plain.active"
listen_ssl = "listen_ssl.active"
server_name_config = f"server_name {template_vars['MAILCOW_HOSTNAME']} autodiscover.* autoconfig.* {' '.join(template_vars['ADDITIONAL_SERVER_NAMES'])};"
listen_plain_config = f"listen {template_vars['HTTP_PORT']};"
listen_ssl_config = f"listen {template_vars['HTTPS_PORT']};"
if not template_vars['DISABLE_IPv6']:
listen_plain_config += f"\nlisten [::]:{template_vars['HTTP_PORT']};"
listen_ssl_config += f"\nlisten [::]:{template_vars['HTTPS_PORT']} ssl;"
listen_ssl_config += "\nhttp2 on;"
with open(f"/etc/nginx/conf.d/{server_name}", "w") as f:
f.write(server_name_config)
with open(f"/etc/nginx/conf.d/{listen_plain}", "w") as f:
f.write(listen_plain_config)
with open(f"/etc/nginx/conf.d/{listen_ssl}", "w") as f:
f.write(listen_ssl_config)
def sites_default_conf(env, template_vars):
config_name = "sites-default.conf"
template = env.get_template(f"{config_name}.j2")
config = template.render(template_vars)
with open(f"/etc/nginx/includes/{config_name}", "w") as f:
f.write(config)
def nginx_conf(env, template_vars):
config_name = "nginx.conf"
template = env.get_template(f"{config_name}.j2")
config = template.render(template_vars)
with open(f"/etc/nginx/{config_name}", "w") as f:
f.write(config)
def prepare_template_vars():
ipv4_network = os.getenv("IPV4_NETWORK", "172.22.1")
additional_server_names = os.getenv("ADDITIONAL_SERVER_NAMES", "")
trusted_proxies = os.getenv("TRUSTED_PROXIES", "")
template_vars = {
'IPV4_NETWORK': ipv4_network,
'TRUSTED_PROXIES': [item.strip() for item in trusted_proxies.split(",") if item.strip()],
'SKIP_RSPAMD': os.getenv("SKIP_RSPAMD", "n").lower() in ("y", "yes"),
'SKIP_SOGO': os.getenv("SKIP_SOGO", "n").lower() in ("y", "yes"),
'NGINX_USE_PROXY_PROTOCOL': os.getenv("NGINX_USE_PROXY_PROTOCOL", "n").lower() in ("y", "yes"),
'MAILCOW_HOSTNAME': os.getenv("MAILCOW_HOSTNAME", ""),
'ADDITIONAL_SERVER_NAMES': [item.strip() for item in additional_server_names.split(",") if item.strip()],
'HTTP_PORT': os.getenv("HTTP_PORT", "80"),
'HTTPS_PORT': os.getenv("HTTPS_PORT", "443"),
'SOGOHOST': os.getenv("SOGOHOST", ipv4_network + ".248"),
'RSPAMDHOST': os.getenv("RSPAMDHOST", "rspamd-mailcow"),
'PHPFPMHOST': os.getenv("PHPFPMHOST", "php-fpm-mailcow"),
'DISABLE_IPv6': os.getenv("DISABLE_IPv6", "n").lower() in ("y", "yes"),
'HTTP_REDIRECT': os.getenv("HTTP_REDIRECT", "n").lower() in ("y", "yes"),
}
ssl_dir = '/etc/ssl/mail/'
template_vars['valid_cert_dirs'] = []
for d in os.listdir(ssl_dir):
full_path = os.path.join(ssl_dir, d)
if not os.path.isdir(full_path):
continue
cert_path = os.path.join(full_path, 'cert.pem')
key_path = os.path.join(full_path, 'key.pem')
domains_path = os.path.join(full_path, 'domains')
if os.path.isfile(cert_path) and os.path.isfile(key_path) and os.path.isfile(domains_path):
with open(domains_path, 'r') as file:
domains = file.read().strip()
domains_list = domains.split()
if domains_list and template_vars["MAILCOW_HOSTNAME"] not in domains_list:
template_vars['valid_cert_dirs'].append({
'cert_path': full_path + '/',
'domains': domains
})
return template_vars
def main():
env = Environment(loader=FileSystemLoader('./etc/nginx/conf.d/templates'))
# Render config
print("Render config")
template_vars = prepare_template_vars()
sites_default_conf(env, template_vars)
nginx_conf(env, template_vars)
includes_conf(env, template_vars)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,26 @@
#!/bin/sh
PHPFPMHOST=${PHPFPMHOST:-"php-fpm-mailcow"}
SOGOHOST=${SOGOHOST:-"$IPV4_NETWORK.248"}
RSPAMDHOST=${RSPAMDHOST:-"rspamd-mailcow"}
until ping ${PHPFPMHOST} -c1 > /dev/null; do
echo "Waiting for PHP..."
sleep 1
done
if ! printf "%s\n" "${SKIP_SOGO}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then
until ping ${SOGOHOST} -c1 > /dev/null; do
echo "Waiting for SOGo..."
sleep 1
done
fi
if ! printf "%s\n" "${SKIP_RSPAMD}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then
until ping ${RSPAMDHOST} -c1 > /dev/null; do
echo "Waiting for Rspamd..."
sleep 1
done
fi
python3 /bootstrap.py
exec "$@"

View File

@ -1,4 +1,4 @@
FROM alpine:3.20
FROM alpine:3.21
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"

View File

@ -32,6 +32,13 @@ import time
import magic
import re
skip_olefy = os.getenv('SKIP_OLEFY', '')
if skip_olefy.lower() in ['yes', 'y']:
print("SKIP_OLEFY=y, skipping Olefy...")
time.sleep(365 * 24 * 60 * 60)
sys.exit(0)
# merge variables from /etc/olefy.conf and the defaults
olefy_listen_addr_string = os.getenv('OLEFY_BINDADDRESS', '127.0.0.1,::1')
olefy_listen_port = int(os.getenv('OLEFY_BINDPORT', '10050'))
@ -113,7 +120,7 @@ def oletools( stream, tmp_file_name, lid ):
out = bytes(out.decode('utf-8', 'ignore').replace(' ', ' ').replace('\t', '').replace('\n', '').replace('XLMMacroDeobfuscator: pywin32 is not installed (only is required if you want to use MS Excel)', ''), encoding="utf-8")
failed = False
if out.__len__() < 30:
logger.error('{} olevba returned <30 chars - rc: {!r}, response: {!r}, error: {!r}'.format(lid,cmd_tmp.returncode,
logger.error('{} olevba returned <30 chars - rc: {!r}, response: {!r}, error: {!r}'.format(lid,cmd_tmp.returncode,
out.decode('utf-8', 'ignore'), err.decode('utf-8', 'ignore')))
out = b'[ { "error": "Unhandled error - too short olevba response" } ]'
failed = True

View File

@ -1,11 +1,11 @@
FROM php:8.2-fpm-alpine3.20
FROM php:8.2-fpm-alpine3.21
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced extractVersion=^v(?<version>.*)$
ARG APCU_PECL_VERSION=5.1.24
# renovate: datasource=github-tags depName=Imagick/imagick versioning=semver-coerced extractVersion=(?<version>.*)$
ARG IMAGICK_PECL_VERSION=3.7.0
ARG IMAGICK_PECL_VERSION=3.8.0
# renovate: datasource=github-tags depName=php/pecl-mail-mailparse versioning=semver-coerced extractVersion=^v(?<version>.*)$
ARG MAILPARSE_PECL_VERSION=3.1.8
# renovate: datasource=github-tags depName=php-memcached-dev/php-memcached versioning=semver-coerced extractVersion=^v(?<version>.*)$
@ -13,7 +13,7 @@ ARG MEMCACHED_PECL_VERSION=3.2.0
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced extractVersion=(?<version>.*)$
ARG REDIS_PECL_VERSION=6.1.0
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=(?<version>.*)$
ARG COMPOSER_VERSION=2.6.6
ARG COMPOSER_VERSION=2.8.6
RUN apk add -U --no-cache autoconf \
aspell-dev \
@ -77,7 +77,7 @@ RUN apk add -U --no-cache autoconf \
--with-webp \
--with-xpm \
--with-avif \
&& docker-php-ext-install -j 4 exif gd gettext intl ldap opcache pcntl pdo pdo_mysql pspell soap sockets sysvsem zip bcmath gmp \
&& docker-php-ext-install -j 4 exif gd gettext intl ldap opcache pcntl pdo pdo_mysql pspell soap sockets zip bcmath gmp \
&& docker-php-ext-configure imap --with-imap --with-imap-ssl \
&& docker-php-ext-install -j 4 imap \
&& curl --silent --show-error https://getcomposer.org/installer | php -- --version=${COMPOSER_VERSION} \

View File

@ -16,7 +16,7 @@ else
REDIS_HOST="redis"
REDIS_PORT="6379"
fi
REDIS_CMDLINE="redis-cli -h ${REDIS_HOST} -p ${REDIS_PORT}"
REDIS_CMDLINE="redis-cli -h ${REDIS_HOST} -p ${REDIS_PORT} -a ${REDISPASS} --no-auth-warning"
until [[ $(${REDIS_CMDLINE} PING) == "PONG" ]]; do
echo "Waiting for Redis..."
@ -26,7 +26,7 @@ done
# Set redis session store
echo -n '
session.save_handler = redis
session.save_path = "tcp://'${REDIS_HOST}':'${REDIS_PORT}'"
session.save_path = "tcp://'${REDIS_HOST}':'${REDIS_PORT}'?auth='${REDISPASS}'"
' > /usr/local/etc/php/conf.d/session_store.ini
# Check mysql_upgrade (master and slave)
@ -81,7 +81,7 @@ if [ ${SQL_CHANGED} -eq 1 ]; then
fi
# Check mysql tz import (master and slave)
TZ_CHECK=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT CONVERT_TZ('2019-11-02 23:33:00','Europe/Berlin','UTC') AS time;" -BN 2> /dev/null)
TZ_CHECK=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT CONVERT_TZ('2019-11-02 23:33:00','Europe/Berlin','UTC') AS time;" -BN 2> /dev/null)
if [[ -z ${TZ_CHECK} ]] || [[ "${TZ_CHECK}" == "NULL" ]]; then
SQL_FULL_TZINFO_IMPORT_RETURN=$(curl --silent --insecure -XPOST https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${CONTAINER_ID}/exec -d '{"cmd":"system", "task":"mysql_tzinfo_to_sql"}' --silent -H 'Content-type: application/json')
echo "MySQL mysql_tzinfo_to_sql - debug output:"
@ -120,11 +120,11 @@ if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
while read line
do
DOMAIN_ARR+=("$line")
done < <(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain FROM domain" -Bs)
done < <(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain FROM domain" -Bs)
while read line
do
DOMAIN_ARR+=("$line")
done < <(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT alias_domain FROM alias_domain" -Bs)
done < <(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT alias_domain FROM alias_domain" -Bs)
if [[ ! -z ${DOMAIN_ARR} ]]; then
for domain in "${DOMAIN_ARR[@]}"; do
@ -146,13 +146,13 @@ if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
VALIDATED_IPS=$(array_by_comma ${VALIDATED_API_ALLOW_FROM_ARR[*]})
if [[ ! -z ${VALIDATED_IPS} ]]; then
if [[ ${API_KEY} != "invalid" ]] && [[ ! -z ${API_KEY} ]]; then
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
DELETE FROM api WHERE access = 'rw';
INSERT INTO api (api_key, active, allow_from, access) VALUES ("${API_KEY}", "1", "${VALIDATED_IPS}", "rw");
EOF
fi
if [[ ${API_KEY_READ_ONLY} != "invalid" ]] && [[ ! -z ${API_KEY_READ_ONLY} ]]; then
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
DELETE FROM api WHERE access = 'ro';
INSERT INTO api (api_key, active, allow_from, access) VALUES ("${API_KEY_READ_ONLY}", "1", "${VALIDATED_IPS}", "ro");
EOF
@ -161,7 +161,7 @@ EOF
fi
# Create events (master only, STATUS for event on slave will be SLAVESIDE_DISABLED)
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
DROP EVENT IF EXISTS clean_spamalias;
DELIMITER //
CREATE EVENT clean_spamalias

View File

@ -395,7 +395,7 @@ EOF
if [ ! -f /opt/postfix/conf/dns_blocklists.cf ]; then
cat <<EOF > /opt/postfix/conf/dns_blocklists.cf
# This file can be edited.
# This file can be edited.
# Delete this file and restart postfix container to revert any changes.
postscreen_dnsbl_sites = wl.mailspike.net=127.0.0.[18;19;20]*-2
hostkarma.junkemailfilter.com=127.0.0.1*-2
@ -403,7 +403,6 @@ postscreen_dnsbl_sites = wl.mailspike.net=127.0.0.[18;19;20]*-2
list.dnswl.org=127.0.[0..255].1*-4
list.dnswl.org=127.0.[0..255].2*-6
list.dnswl.org=127.0.[0..255].3*-8
ix.dnsbl.manitu.net*2
bl.spamcop.net*2
bl.suomispam.net*2
hostkarma.junkemailfilter.com=127.0.0.2*3
@ -417,6 +416,10 @@ postscreen_dnsbl_sites = wl.mailspike.net=127.0.0.[18;19;20]*-2
bl.mailspike.net=127.0.0.[10;11;12]*4
EOF
fi
# Remove discontinued DNSBLs from existing dns_blocklists.cf
sed -i '/ix\.dnsbl\.manitu\.net\*2/d' /opt/postfix/conf/dns_blocklists.cf # Nixspam
DNSBL_CONFIG=$(grep -v '^#' /opt/postfix/conf/dns_blocklists.cf | grep '\S')
if [ ! -z "$DNSBL_CONFIG" ]; then
@ -507,6 +510,11 @@ chgrp -R postdrop /var/spool/postfix/public
chgrp -R postdrop /var/spool/postfix/maildrop
postfix set-permissions
# Checking if there is a leftover of a crashed postfix container before starting a new one
if [ -e /var/spool/postfix/pid/master.pid ]; then
rm -rf /var/spool/postfix/pid/master.pid
fi
# Check Postfix configuration
postconf -c /opt/postfix/conf > /dev/null

View File

@ -18,6 +18,7 @@ stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=true
startsecs=10
[eventlistener:processes]
command=/usr/local/sbin/stop-supervisor.sh

View File

@ -20,6 +20,7 @@ destination d_redis_ui_log {
host("`REDIS_SLAVEOF_IP`")
persist-name("redis1")
port(`REDIS_SLAVEOF_PORT`)
auth("`REDISPASS`")
command("LPUSH" "POSTFIX_MAILLOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n")
);
};
@ -28,6 +29,7 @@ destination d_redis_f2b_channel {
host("`REDIS_SLAVEOF_IP`")
persist-name("redis2")
port(`REDIS_SLAVEOF_PORT`)
auth("`REDISPASS`")
command("PUBLISH" "F2B_CHANNEL" "$(sanitize $MESSAGE)")
);
};

View File

@ -20,6 +20,7 @@ destination d_redis_ui_log {
host("redis-mailcow")
persist-name("redis1")
port(6379)
auth("`REDISPASS`")
command("LPUSH" "POSTFIX_MAILLOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n")
);
};
@ -28,6 +29,7 @@ destination d_redis_f2b_channel {
host("redis-mailcow")
persist-name("redis2")
port(6379)
auth("`REDISPASS`")
command("PUBLISH" "F2B_CHANNEL" "$(sanitize $MESSAGE)")
);
};

View File

@ -2,11 +2,11 @@ FROM debian:bookworm-slim
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
ARG DEBIAN_FRONTEND=noninteractive
ARG RSPAMD_VER=rspamd_3.10.2-1~b8a232043
ARG RSPAMD_VER=rspamd_3.11.1-1~ab0b44951
ARG CODENAME=bookworm
ENV LC_ALL=C
RUN apt-get update && apt-get install -y \
RUN apt-get update && apt-get install -y --no-install-recommends \
tzdata \
ca-certificates \
gnupg2 \

View File

@ -56,27 +56,50 @@ if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then
cat <<EOF > /etc/rspamd/local.d/redis.conf
read_servers = "redis:6379";
write_servers = "${REDIS_SLAVEOF_IP}:${REDIS_SLAVEOF_PORT}";
password = "${REDISPASS}";
timeout = 10;
EOF
until [[ $(redis-cli -h redis-mailcow PING) == "PONG" ]]; do
until [[ $(redis-cli -h redis-mailcow -a ${REDISPASS} --no-auth-warning PING) == "PONG" ]]; do
echo "Waiting for Redis @redis-mailcow..."
sleep 2
done
until [[ $(redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} PING) == "PONG" ]]; do
until [[ $(redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} --no-auth-warning PING) == "PONG" ]]; do
echo "Waiting for Redis @${REDIS_SLAVEOF_IP}..."
sleep 2
done
redis-cli -h redis-mailcow SLAVEOF ${REDIS_SLAVEOF_IP} ${REDIS_SLAVEOF_PORT}
redis-cli -h redis-mailcow -a ${REDISPASS} --no-auth-warning SLAVEOF ${REDIS_SLAVEOF_IP} ${REDIS_SLAVEOF_PORT}
else
cat <<EOF > /etc/rspamd/local.d/redis.conf
servers = "redis:6379";
password = "${REDISPASS}";
timeout = 10;
EOF
until [[ $(redis-cli -h redis-mailcow PING) == "PONG" ]]; do
until [[ $(redis-cli -h redis-mailcow -a ${REDISPASS} --no-auth-warning PING) == "PONG" ]]; do
echo "Waiting for Redis slave..."
sleep 2
done
redis-cli -h redis-mailcow SLAVEOF NO ONE
redis-cli -h redis-mailcow -a ${REDISPASS} --no-auth-warning SLAVEOF NO ONE
fi
if [[ "${SKIP_OLEFY}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
if [[ -f /etc/rspamd/local.d/external_services.conf ]]; then
rm /etc/rspamd/local.d/external_services.conf
fi
else
cat <<EOF > /etc/rspamd/local.d/external_services.conf
oletools {
# default olefy settings
servers = "olefy:10055";
# needs to be set explicitly for Rspamd < 1.9.5
scan_mime_parts = true;
# mime-part regex matching in content-type or filename
# block all macros
extended = true;
max_size = 3145728;
timeout = 20.0;
retransmits = 1;
}
EOF
fi
# Provide additional lua modules

View File

@ -4,7 +4,7 @@ LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
ARG DEBIAN_FRONTEND=noninteractive
ARG DEBIAN_VERSION=bookworm
ARG SOGO_DEBIAN_REPOSITORY=http://www.axis.cz/linux/debian
ARG SOGO_DEBIAN_REPOSITORY=https://packagingv2.sogo.nu/sogo-nightly-debian/
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
ARG GOSU_VERSION=1.17
ENV LC_ALL=C
@ -33,9 +33,8 @@ RUN echo "Building from repository $SOGO_DEBIAN_REPOSITORY" \
&& gosu nobody true \
&& mkdir /usr/share/doc/sogo \
&& touch /usr/share/doc/sogo/empty.sh \
&& wget http://www.axis.cz/linux/debian/axis-archive-keyring.deb -O /tmp/axis-archive-keyring.deb \
&& apt install -y /tmp/axis-archive-keyring.deb \
&& echo "deb [trusted=yes] ${SOGO_DEBIAN_REPOSITORY} ${DEBIAN_VERSION} sogo-v5" > /etc/apt/sources.list.d/sogo.list \
&& wget -O- https://keys.openpgp.org/vks/v1/by-fingerprint/74FFC6D72B925A34B5D356BDF8A27B36A6E2EAE9 | gpg --dearmor | apt-key add - \
&& echo "deb [trusted=yes] ${SOGO_DEBIAN_REPOSITORY} ${DEBIAN_VERSION} main" > /etc/apt/sources.list.d/sogo.list \
&& apt-get update && apt-get install -y --no-install-recommends \
sogo \
sogo-activesync \
@ -48,6 +47,7 @@ COPY syslog-ng.conf /etc/syslog-ng/syslog-ng.conf
COPY syslog-ng-redis_slave.conf /etc/syslog-ng/syslog-ng-redis_slave.conf
COPY supervisord.conf /etc/supervisor/supervisord.conf
COPY acl.diff /acl.diff
COPY navMailcowBtns.diff /navMailcowBtns.diff
COPY stop-supervisor.sh /usr/local/sbin/stop-supervisor.sh
COPY docker-entrypoint.sh /

View File

@ -14,120 +14,16 @@ do
done
# Wait for updated schema
DBV_NOW=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT version FROM versions WHERE application = 'db_schema';" -BN)
DBV_NOW=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT version FROM versions WHERE application = 'db_schema';" -BN)
DBV_NEW=$(grep -oE '\$db_version = .*;' init_db.inc.php | sed 's/$db_version = //g;s/;//g' | cut -d \" -f2)
while [[ "${DBV_NOW}" != "${DBV_NEW}" ]]; do
echo "Waiting for schema update..."
DBV_NOW=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT version FROM versions WHERE application = 'db_schema';" -BN)
DBV_NOW=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT version FROM versions WHERE application = 'db_schema';" -BN)
DBV_NEW=$(grep -oE '\$db_version = .*;' init_db.inc.php | sed 's/$db_version = //g;s/;//g' | cut -d \" -f2)
sleep 5
done
echo "DB schema is ${DBV_NOW}"
# Recreate view
if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "We are master, preparing sogo_view..."
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP VIEW IF EXISTS sogo_view"
while [[ ${VIEW_OK} != 'OK' ]]; do
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
CREATE VIEW sogo_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, ext_acl, kind, multiple_bookings) AS
SELECT
mailbox.username,
mailbox.domain,
mailbox.username,
IF(JSON_UNQUOTE(JSON_VALUE(attributes, '$.force_pw_update')) = '0', IF(JSON_UNQUOTE(JSON_VALUE(attributes, '$.sogo_access')) = 1, password, '{SSHA256}A123A123A321A321A321B321B321B123B123B321B432F123E321123123321321'), '{SSHA256}A123A123A321A321A321B321B321B123B123B321B432F123E321123123321321'),
mailbox.name,
mailbox.username,
IFNULL(GROUP_CONCAT(ga.aliases ORDER BY ga.aliases SEPARATOR ' '), ''),
IFNULL(gda.ad_alias, ''),
IFNULL(external_acl.send_as_acl, ''),
mailbox.kind,
mailbox.multiple_bookings
FROM
mailbox
LEFT OUTER JOIN
grouped_mail_aliases ga
ON ga.username REGEXP CONCAT('(^|,)', mailbox.username, '($|,)')
LEFT OUTER JOIN
grouped_domain_alias_address gda
ON gda.username = mailbox.username
LEFT OUTER JOIN
grouped_sender_acl_external external_acl
ON external_acl.username = mailbox.username
WHERE
mailbox.active = '1'
GROUP BY
mailbox.username;
EOF
if [[ ! -z $(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sogo_view'") ]]; then
VIEW_OK=OK
else
echo "Will retry to setup SOGo view in 3s..."
sleep 3
fi
done
else
while [[ ${VIEW_OK} != 'OK' ]]; do
if [[ ! -z $(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sogo_view'") ]]; then
VIEW_OK=OK
else
echo "Waiting for SOGo view to be created by master..."
sleep 3
fi
done
fi
# Wait for static view table if missing after update and update content
if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "We are master, preparing _sogo_static_view..."
while [[ ${STATIC_VIEW_OK} != 'OK' ]]; do
if [[ ! -z $(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '_sogo_static_view'") ]]; then
STATIC_VIEW_OK=OK
echo "Updating _sogo_static_view content..."
# If changed, also update init_db.inc.php
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "REPLACE INTO _sogo_static_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, ext_acl, kind, multiple_bookings) SELECT c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, ext_acl, kind, multiple_bookings from sogo_view;"
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "DELETE FROM _sogo_static_view WHERE c_uid NOT IN (SELECT username FROM mailbox WHERE active = '1')"
else
echo "Waiting for database initialization..."
sleep 3
fi
done
else
while [[ ${STATIC_VIEW_OK} != 'OK' ]]; do
if [[ ! -z $(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '_sogo_static_view'") ]]; then
STATIC_VIEW_OK=OK
else
echo "Waiting for database initialization by master..."
sleep 3
fi
done
fi
# Recreate password update trigger
if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "We are master, preparing update trigger..."
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP TRIGGER IF EXISTS sogo_update_password"
while [[ ${TRIGGER_OK} != 'OK' ]]; do
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
DELIMITER -
CREATE TRIGGER sogo_update_password AFTER UPDATE ON _sogo_static_view
FOR EACH ROW
BEGIN
UPDATE mailbox SET password = NEW.c_password WHERE NEW.c_uid = username;
END;
-
DELIMITER ;
EOF
if [[ ! -z $(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_NAME = 'sogo_update_password'") ]]; then
TRIGGER_OK=OK
else
echo "Will retry to setup SOGo password update trigger in 3s"
sleep 3
fi
done
fi
# cat /dev/urandom seems to hang here occasionally and is not recommended anyway, better use openssl
RAND_PASS=$(openssl rand -base64 16 | tr -dc _A-Z-a-z-0-9)
@ -213,10 +109,10 @@ while read -r line gal
</dict>" >> /var/lib/sogo/GNUstep/Defaults/sogod.plist
# Generate alternative LDAP authentication dict, when SQL authentication fails
# This will nevertheless read attributes from LDAP
line=${line} envsubst < /etc/sogo/plist_ldap >> /var/lib/sogo/GNUstep/Defaults/sogod.plist
/etc/sogo/plist_ldap.sh ${line} ${gal} >> /var/lib/sogo/GNUstep/Defaults/sogod.plist
echo " </array>
</dict>" >> /var/lib/sogo/GNUstep/Defaults/sogod.plist
done < <(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain, CASE gal WHEN '1' THEN 'YES' ELSE 'NO' END AS gal FROM domain;" -B -N)
done < <(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain, CASE gal WHEN '1' THEN 'YES' ELSE 'NO' END AS gal FROM domain;" -B -N)
# Generate footer
echo ' </dict>
@ -240,8 +136,12 @@ chmod 600 /var/lib/sogo/GNUstep/Defaults/sogod.plist
# fi
#fi
# Copy logo, if any
[[ -f /etc/sogo/sogo-full.svg ]] && cp /etc/sogo/sogo-full.svg /usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-full.svg
if patch -R -sfN --dry-run /usr/lib/GNUstep/SOGo/Templates/UIxTopnavToolbar.wox < /navMailcowBtns.diff > /dev/null; then
patch -R /usr/lib/GNUstep/SOGo/Templates/UIxTopnavToolbar.wox < /navMailcowBtns.diff;
fi
# Rename custom logo, if any
[[ -f /etc/sogo/sogo-full.svg ]] && mv /etc/sogo/sogo-full.svg /etc/sogo/custom-fulllogo.svg
# Rsync web content
echo "Syncing web content with named volume"

View File

@ -0,0 +1,15 @@
60,65d58
< var:ng-click="navButtonClick"
< ng-href="/user">
< <md-icon>build</md-icon>
< <md-tooltip>mailcow <var:string label:value="Preferences"/></md-tooltip>
< </md-button>
< <md-button class="md-icon-button"
83c76
< onclick="mc_logout();"
---
> ng-show="::activeUser.path.logoff.length"
85c78
< ng-href="#">
---
> ng-href="{{::activeUser.path.logoff}}">

View File

@ -22,6 +22,7 @@ destination d_redis_ui_log {
host("`REDIS_SLAVEOF_IP`")
persist-name("redis1")
port(`REDIS_SLAVEOF_PORT`)
auth("`REDISPASS`")
command("LPUSH" "SOGO_LOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n")
);
};
@ -30,6 +31,7 @@ destination d_redis_f2b_channel {
host("`REDIS_SLAVEOF_IP`")
persist-name("redis2")
port(`REDIS_SLAVEOF_PORT`)
auth("`REDISPASS`")
command("PUBLISH" "F2B_CHANNEL" "$(sanitize $MESSAGE)")
);
};

View File

@ -22,6 +22,7 @@ destination d_redis_ui_log {
host("redis-mailcow")
persist-name("redis1")
port(6379)
auth("`REDISPASS`")
command("LPUSH" "SOGO_LOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n")
);
};
@ -30,6 +31,7 @@ destination d_redis_f2b_channel {
host("redis-mailcow")
persist-name("redis2")
port(6379)
auth("`REDISPASS`")
command("PUBLISH" "F2B_CHANNEL" "$(sanitize $MESSAGE)")
);
};

View File

@ -1,31 +0,0 @@
FROM solr:7.7-slim
USER root
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=(?<version>.*)$
ARG GOSU_VERSION=1.17
COPY solr.sh /
COPY solr-config-7.7.0.xml /
COPY solr-schema-7.7.0.xml /
RUN dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')" \
&& wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch" \
&& chmod +x /usr/local/bin/gosu \
&& gosu nobody true \
&& apt-get update && apt-get install -y --no-install-recommends \
tzdata \
curl \
bash \
zip \
&& apt-get autoclean \
&& rm -rf /var/lib/apt/lists/* \
&& chmod +x /solr.sh \
&& sync \
&& bash /solr.sh --bootstrap
RUN zip -q -d /opt/solr/server/lib/ext/log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
RUN apt remove zip -y
CMD ["/solr.sh"]

View File

@ -1,289 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!-- This is the default config with stuff non-essential to Dovecot removed. -->
<config>
<!-- Controls what version of Lucene various components of Solr
adhere to. Generally, you want to use the latest version to
get all bug fixes and improvements. It is highly recommended
that you fully re-index after changing this setting as it can
affect both how text is indexed and queried.
-->
<luceneMatchVersion>7.7.0</luceneMatchVersion>
<!-- A 'dir' option by itself adds any files found in the directory
to the classpath, this is useful for including all jars in a
directory.
When a 'regex' is specified in addition to a 'dir', only the
files in that directory which completely match the regex
(anchored on both ends) will be included.
If a 'dir' option (with or without a regex) is used and nothing
is found that matches, a warning will be logged.
The examples below can be used to load some solr-contribs along
with their external dependencies.
-->
<lib dir="${solr.install.dir:../../../..}/contrib/extraction/lib" regex=".*\.jar" />
<lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-cell-\d.*\.jar" />
<lib dir="${solr.install.dir:../../../..}/contrib/clustering/lib/" regex=".*\.jar" />
<lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-clustering-\d.*\.jar" />
<lib dir="${solr.install.dir:../../../..}/contrib/langid/lib/" regex=".*\.jar" />
<lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-langid-\d.*\.jar" />
<lib dir="${solr.install.dir:../../../..}/contrib/velocity/lib" regex=".*\.jar" />
<lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-velocity-\d.*\.jar" />
<!-- Data Directory
Used to specify an alternate directory to hold all index data
other than the default ./data under the Solr home. If
replication is in use, this should match the replication
configuration.
-->
<dataDir>${solr.data.dir:}</dataDir>
<!-- The default high-performance update handler -->
<updateHandler class="solr.DirectUpdateHandler2">
<!-- Enables a transaction log, used for real-time get, durability, and
and solr cloud replica recovery. The log can grow as big as
uncommitted changes to the index, so use of a hard autoCommit
is recommended (see below).
"dir" - the target directory for transaction logs, defaults to the
solr data directory.
"numVersionBuckets" - sets the number of buckets used to keep
track of max version values when checking for re-ordered
updates; increase this value to reduce the cost of
synchronizing access to version buckets during high-volume
indexing, this requires 8 bytes (long) * numVersionBuckets
of heap space per Solr core.
-->
<updateLog>
<str name="dir">${solr.ulog.dir:}</str>
<int name="numVersionBuckets">${solr.ulog.numVersionBuckets:65536}</int>
</updateLog>
<!-- AutoCommit
Perform a hard commit automatically under certain conditions.
Instead of enabling autoCommit, consider using "commitWithin"
when adding documents.
http://wiki.apache.org/solr/UpdateXmlMessages
maxDocs - Maximum number of documents to add since the last
commit before automatically triggering a new commit.
maxTime - Maximum amount of time in ms that is allowed to pass
since a document was added before automatically
triggering a new commit.
openSearcher - if false, the commit causes recent index changes
to be flushed to stable storage, but does not cause a new
searcher to be opened to make those changes visible.
If the updateLog is enabled, then it's highly recommended to
have some sort of hard autoCommit to limit the log size.
-->
<autoCommit>
<maxTime>${solr.autoCommit.maxTime:15000}</maxTime>
<openSearcher>false</openSearcher>
</autoCommit>
<!-- softAutoCommit is like autoCommit except it causes a
'soft' commit which only ensures that changes are visible
but does not ensure that data is synced to disk. This is
faster and more near-realtime friendly than a hard commit.
-->
<autoSoftCommit>
<maxTime>${solr.autoSoftCommit.maxTime:-1}</maxTime>
</autoSoftCommit>
<!-- Update Related Event Listeners
Various IndexWriter related events can trigger Listeners to
take actions.
postCommit - fired after every commit or optimize command
postOptimize - fired after every optimize command
-->
</updateHandler>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Query section - these settings control query time things like caches
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<query>
<!-- Solr Internal Query Caches
There are two implementations of cache available for Solr,
LRUCache, based on a synchronized LinkedHashMap, and
FastLRUCache, based on a ConcurrentHashMap.
FastLRUCache has faster gets and slower puts in single
threaded operation and thus is generally faster than LRUCache
when the hit ratio of the cache is high (> 75%), and may be
faster under other scenarios on multi-cpu systems.
-->
<!-- Filter Cache
Cache used by SolrIndexSearcher for filters (DocSets),
unordered sets of *all* documents that match a query. When a
new searcher is opened, its caches may be prepopulated or
"autowarmed" using data from caches in the old searcher.
autowarmCount is the number of items to prepopulate. For
LRUCache, the autowarmed items will be the most recently
accessed items.
Parameters:
class - the SolrCache implementation LRUCache or
(LRUCache or FastLRUCache)
size - the maximum number of entries in the cache
initialSize - the initial capacity (number of entries) of
the cache. (see java.util.HashMap)
autowarmCount - the number of entries to prepopulate from
and old cache.
maxRamMB - the maximum amount of RAM (in MB) that this cache is allowed
to occupy. Note that when this option is specified, the size
and initialSize parameters are ignored.
-->
<filterCache class="solr.FastLRUCache"
size="512"
initialSize="512"
autowarmCount="0"/>
<!-- Query Result Cache
Caches results of searches - ordered lists of document ids
(DocList) based on a query, a sort, and the range of documents requested.
Additional supported parameter by LRUCache:
maxRamMB - the maximum amount of RAM (in MB) that this cache is allowed
to occupy
-->
<queryResultCache class="solr.LRUCache"
size="512"
initialSize="512"
autowarmCount="0"/>
<!-- Document Cache
Caches Lucene Document objects (the stored fields for each
document). Since Lucene internal document ids are transient,
this cache will not be autowarmed.
-->
<documentCache class="solr.LRUCache"
size="512"
initialSize="512"
autowarmCount="0"/>
<!-- custom cache currently used by block join -->
<cache name="perSegFilter"
class="solr.search.LRUCache"
size="10"
initialSize="0"
autowarmCount="10"
regenerator="solr.NoOpRegenerator" />
<!-- Lazy Field Loading
If true, stored fields that are not requested will be loaded
lazily. This can result in a significant speed improvement
if the usual case is to not load all stored fields,
especially if the skipped fields are large compressed text
fields.
-->
<enableLazyFieldLoading>true</enableLazyFieldLoading>
<!-- Result Window Size
An optimization for use with the queryResultCache. When a search
is requested, a superset of the requested number of document ids
are collected. For example, if a search for a particular query
requests matching documents 10 through 19, and queryWindowSize is 50,
then documents 0 through 49 will be collected and cached. Any further
requests in that range can be satisfied via the cache.
-->
<queryResultWindowSize>20</queryResultWindowSize>
<!-- Maximum number of documents to cache for any entry in the
queryResultCache.
-->
<queryResultMaxDocsCached>200</queryResultMaxDocsCached>
<!-- Use Cold Searcher
If a search request comes in and there is no current
registered searcher, then immediately register the still
warming searcher and use it. If "false" then all requests
will block until the first searcher is done warming.
-->
<useColdSearcher>false</useColdSearcher>
</query>
<!-- Request Dispatcher
This section contains instructions for how the SolrDispatchFilter
should behave when processing requests for this SolrCore.
-->
<requestDispatcher>
<httpCaching never304="true" />
</requestDispatcher>
<!-- Request Handlers
http://wiki.apache.org/solr/SolrRequestHandler
Incoming queries will be dispatched to a specific handler by name
based on the path specified in the request.
If a Request Handler is declared with startup="lazy", then it will
not be initialized until the first request that uses it.
-->
<!-- SearchHandler
http://wiki.apache.org/solr/SearchHandler
For processing Search Queries, the primary Request Handler
provided with Solr is "SearchHandler" It delegates to a sequent
of SearchComponents (see below) and supports distributed
queries across multiple shards
-->
<requestHandler name="/select" class="solr.SearchHandler">
<!-- default values for query parameters can be specified, these
will be overridden by parameters in the request
-->
<lst name="defaults">
<str name="echoParams">explicit</str>
<int name="rows">10</int>
</lst>
</requestHandler>
<initParams path="/update/**,/select">
<lst name="defaults">
<str name="df">_text_</str>
</lst>
</initParams>
<!-- Response Writers
http://wiki.apache.org/solr/QueryResponseWriter
Request responses will be written using the writer specified by
the 'wt' request parameter matching the name of a registered
writer.
The "default" writer is the default and will be used if 'wt' is
not specified in the request.
-->
<queryResponseWriter name="xml"
default="true"
class="solr.XMLResponseWriter" />
</config>

View File

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema name="dovecot-fts" version="2.0">
<fieldType name="string" class="solr.StrField" omitNorms="true" sortMissingLast="true"/>
<fieldType name="long" class="solr.LongPointField" positionIncrementGap="0"/>
<fieldType name="boolean" class="solr.BoolField" sortMissingLast="true"/>
<fieldType name="text" class="solr.TextField" autoGeneratePhraseQueries="true" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.EdgeNGramFilterFactory" minGramSize="3" maxGramSize="20"/>
<filter class="solr.StopFilterFactory" words="stopwords.txt" ignoreCase="true"/>
<filter class="solr.WordDelimiterGraphFilterFactory" catenateNumbers="1" generateNumberParts="1" splitOnCaseChange="1" generateWordParts="1" splitOnNumerics="1" catenateAll="1" catenateWords="1"/>
<filter class="solr.FlattenGraphFilterFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
<filter class="solr.PorterStemFilterFactory"/>
</analyzer>
<analyzer type="query">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.SynonymGraphFilterFactory" expand="true" ignoreCase="true" synonyms="synonyms.txt"/>
<filter class="solr.FlattenGraphFilterFactory"/>
<filter class="solr.StopFilterFactory" words="stopwords.txt" ignoreCase="true"/>
<filter class="solr.WordDelimiterGraphFilterFactory" catenateNumbers="1" generateNumberParts="1" splitOnCaseChange="1" generateWordParts="1" splitOnNumerics="1" catenateAll="1" catenateWords="1"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
<filter class="solr.PorterStemFilterFactory"/>
</analyzer>
</fieldType>
<field name="id" type="string" indexed="true" required="true" stored="true"/>
<field name="uid" type="long" indexed="true" required="true" stored="true"/>
<field name="box" type="string" indexed="true" required="true" stored="true"/>
<field name="user" type="string" indexed="true" required="true" stored="true"/>
<field name="hdr" type="text" indexed="true" stored="false"/>
<field name="body" type="text" indexed="true" stored="false"/>
<field name="from" type="text" indexed="true" stored="false"/>
<field name="to" type="text" indexed="true" stored="false"/>
<field name="cc" type="text" indexed="true" stored="false"/>
<field name="bcc" type="text" indexed="true" stored="false"/>
<field name="subject" type="text" indexed="true" stored="false"/>
<!-- Used by Solr internally: -->
<field name="_version_" type="long" indexed="true" stored="true"/>
<uniqueKey>id</uniqueKey>
</schema>

View File

@ -1,75 +0,0 @@
#!/bin/bash
if [[ "${FLATCURVE_EXPERIMENTAL}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "FLATCURVE_EXPERIMENTAL=y, skipping Solr but enabling Flatcurve as FTS for Dovecot!"
echo "Solr will be removed in the future!"
sleep 365d
exit 0
elif [[ "${SKIP_SOLR}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "SKIP_SOLR=y, skipping Solr..."
echo "HINT: You could try the newer FTS Backend Flatcurve, which is currently in experimental state..."
echo "Simply set FLATCURVE_EXPERIMENTAL=y inside your mailcow.conf and restart the stack afterwards!"
echo "Solr will be removed in the future!"
sleep 365d
exit 0
fi
MEM_TOTAL=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
if [[ "${1}" != "--bootstrap" ]]; then
if [ ${MEM_TOTAL} -lt "2097152" ]; then
echo "System memory less than 2 GB, skipping Solr..."
sleep 365d
exit 0
fi
fi
set -e
# run the optional initdb
. /opt/docker-solr/scripts/run-initdb
# fixing volume permission
[[ -d /opt/solr/server/solr/dovecot-fts/data ]] && chown -R solr:solr /opt/solr/server/solr/dovecot-fts/data
if [[ "${1}" != "--bootstrap" ]]; then
sed -i '/SOLR_HEAP=/c\SOLR_HEAP="'${SOLR_HEAP:-1024}'m"' /opt/solr/bin/solr.in.sh
else
sed -i '/SOLR_HEAP=/c\SOLR_HEAP="256m"' /opt/solr/bin/solr.in.sh
fi
if [[ "${1}" == "--bootstrap" ]]; then
echo "Creating initial configuration"
echo "Modifying default config set"
cp /solr-config-7.7.0.xml /opt/solr/server/solr/configsets/_default/conf/solrconfig.xml
cp /solr-schema-7.7.0.xml /opt/solr/server/solr/configsets/_default/conf/schema.xml
rm /opt/solr/server/solr/configsets/_default/conf/managed-schema
echo "Starting local Solr instance to setup configuration"
gosu solr start-local-solr
echo "Creating core \"dovecot-fts\""
gosu solr /opt/solr/bin/solr create -c "dovecot-fts"
# See https://github.com/docker-solr/docker-solr/issues/27
echo "Checking core"
while ! wget -O - 'http://localhost:8983/solr/admin/cores?action=STATUS' | grep -q instanceDir; do
echo "Could not find any cores, waiting..."
sleep 3
done
echo "Created core \"dovecot-fts\""
echo "Stopping local Solr"
gosu solr stop-local-solr
exit 0
fi
echo "Starting up Solr..."
echo -e "\e[31mSolr is deprecated! You can try the new FTS System now by enabling FLATCURVE_EXPERIMENTAL=y inside mailcow.conf and restarting the stack\e[0m"
echo -e "\e[31mSolr will be removed completely soon!\e[0m"
sleep 15
exec gosu solr solr-foreground

View File

@ -1,4 +1,4 @@
FROM alpine:3.20
FROM alpine:3.21
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"

View File

@ -1,4 +1,4 @@
FROM alpine:3.20
FROM alpine:3.21
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"

View File

@ -49,7 +49,7 @@
# 2013101601 Optical clean up #
# 2013101602 Rewrite help output #
# 2013101700 Handle Slave IO in 'Connecting' state #
# 2013101701 Minor changes in output, handling UNKWNON situations now #
# 2013101701 Minor changes in output, handling UNKNOWN situations now #
# 2013101702 Exit CRITICAL when Slave IO in Connecting state #
# 2013123000 Slave_SQL_Running also matched Slave_SQL_Running_State #
# 2015011600 Added 'moving' check to catch possible connection issues #
@ -131,10 +131,10 @@ elif [[ -n "${socket}" && (-z "${user}" || -z "${password}") ]]; then
fi
# Connect to the DB server and store output in vars
if [[ -n $socket ]]; then
ConnectionResult=$(mysql ${optfile} ${socket} ${user} -e "show slave ${connection} status\G" 2>&1)
if [[ -n $socket ]]; then
ConnectionResult=$(mariadb --skip-ssl ${optfile} ${socket} ${user} -e "show slave ${connection} status\G" 2>&1)
else
ConnectionResult=$(mysql ${optfile} ${host} ${port} ${user} -e "show slave ${connection} status\G" 2>&1)
ConnectionResult=$(mariadb --skip-ssl ${optfile} ${host} ${port} ${user} -e "show slave ${connection} status\G" 2>&1)
fi
if [ -z "`echo "${ConnectionResult}" |grep Slave_IO_State`" ]; then
@ -178,33 +178,33 @@ if [ ${check} = ${ok} ] && [ ${checkio} = ${ok} ]; then
then echo "CRITICAL: Slave is ${delayinfo} seconds behind Master | delay=${delayinfo}s"; exit ${STATE_CRITICAL}
elif [[ ${delayinfo} -ge ${warn_delay} ]]
then echo "WARNING: Slave is ${delayinfo} seconds behind Master | delay=${delayinfo}s"; exit ${STATE_WARNING}
else
else
# Everything looks OK here but now let us check if the replication is moving
if [[ -n ${moving} ]] && [[ -n ${tmpfile} ]] && [[ $readpos -eq $execpos ]]
then
#echo "Debug: Read pos is $readpos - Exec pos is $execpos"
then
#echo "Debug: Read pos is $readpos - Exec pos is $execpos"
# Check if tmp file exists
curtime=`date +%s`
if [[ -w $tmpfile ]]
then
if [[ -w $tmpfile ]]
then
tmpfiletime=`date +%s -r $tmpfile`
if [[ `expr $curtime - $tmpfiletime` -gt ${moving} ]]
then
exectmp=`cat $tmpfile`
#echo "Debug: Exec pos in tmpfile is $exectmp"
if [[ $exectmp -eq $execpos ]]
then
then
# The value read from the tmp file and from db are the same. Replication hasnt moved!
echo "WARNING: Slave replication has not moved in ${moving} seconds. Manual check required."; exit ${STATE_WARNING}
else
else
# Replication has moved since the tmp file was written. Delete tmp file and output OK.
rm $tmpfile
echo "OK: Slave SQL running: ${check} Slave IO running: ${checkio} / master: ${masterinfo} / slave is ${delayinfo} seconds behind master | delay=${delayinfo}s"; exit ${STATE_OK};
fi
else
else
echo "OK: Slave SQL running: ${check} Slave IO running: ${checkio} / master: ${masterinfo} / slave is ${delayinfo} seconds behind master | delay=${delayinfo}s"; exit ${STATE_OK};
fi
else
else
echo "$execpos" > $tmpfile
echo "OK: Slave SQL running: ${check} Slave IO running: ${checkio} / master: ${masterinfo} / slave is ${delayinfo} seconds behind master | delay=${delayinfo}s"; exit ${STATE_OK};
fi

View File

@ -40,9 +40,9 @@ done
# Do not attempt to write to slave
if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then
REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT}"
REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} --no-auth-warning"
else
REDIS_CMDLINE="redis-cli -h redis -p 6379"
REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS} --no-auth-warning"
fi
until [[ $(${REDIS_CMDLINE} PING) == "PONG" ]]; do
@ -234,7 +234,7 @@ external_checks() {
diff_c=0
THRESHOLD=${EXTERNAL_CHECKS_THRESHOLD}
# Reduce error count by 2 after restarting an unhealthy container
GUID=$(mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT version FROM versions WHERE application = 'GUID'" -BN)
GUID=$(mariadb --skip-ssl -u${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT version FROM versions WHERE application = 'GUID'" -BN)
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
while [ ${err_count} -lt ${THRESHOLD} ]; do
err_c_cur=${err_count}
@ -330,7 +330,7 @@ redis_checks() {
touch /tmp/redis-mailcow; echo "$(tail -50 /tmp/redis-mailcow)" > /tmp/redis-mailcow
host_ip=$(get_container_ip redis-mailcow)
err_c_cur=${err_count}
/usr/lib/nagios/plugins/check_tcp -4 -H redis-mailcow -p 6379 -E -s "PING\n" -q "QUIT" -e "PONG" 2>> /tmp/redis-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
/usr/lib/nagios/plugins/check_tcp -4 -H redis-mailcow -p 6379 -E -s "AUTH ${REDISPASS}\nPING\n" -q "QUIT" -e "PONG" 2>> /tmp/redis-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
[ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1
[ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} ))
progress "Redis" ${THRESHOLD} $(( ${THRESHOLD} - ${err_count} )) ${diff_c}
@ -503,12 +503,12 @@ dovecot_repl_checks() {
err_count=0
diff_c=0
THRESHOLD=${DOVECOT_REPL_THRESHOLD}
D_REPL_STATUS=$(redis-cli -h redis -r GET DOVECOT_REPL_HEALTH)
D_REPL_STATUS=$(redis-cli -h redis -a ${REDISPASS} --no-auth-warning -r GET DOVECOT_REPL_HEALTH)
# Reduce error count by 2 after restarting an unhealthy container
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
while [ ${err_count} -lt ${THRESHOLD} ]; do
err_c_cur=${err_count}
D_REPL_STATUS=$(redis-cli --raw -h redis GET DOVECOT_REPL_HEALTH)
D_REPL_STATUS=$(redis-cli --raw -h redis -a ${REDISPASS} --no-auth-warning GET DOVECOT_REPL_HEALTH)
if [[ "${D_REPL_STATUS}" != "1" ]]; then
err_count=$(( ${err_count} + 1 ))
fi
@ -578,19 +578,19 @@ ratelimit_checks() {
err_count=0
diff_c=0
THRESHOLD=${RATELIMIT_THRESHOLD}
RL_LOG_STATUS=$(redis-cli -h redis LRANGE RL_LOG 0 0 | jq .qid)
RL_LOG_STATUS=$(redis-cli -h redis -a ${REDISPASS} --no-auth-warning LRANGE RL_LOG 0 0 | jq .qid)
# Reduce error count by 2 after restarting an unhealthy container
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
while [ ${err_count} -lt ${THRESHOLD} ]; do
err_c_cur=${err_count}
RL_LOG_STATUS_PREV=${RL_LOG_STATUS}
RL_LOG_STATUS=$(redis-cli -h redis LRANGE RL_LOG 0 0 | jq .qid)
RL_LOG_STATUS=$(redis-cli -h redis -a ${REDISPASS} --no-auth-warning LRANGE RL_LOG 0 0 | jq .qid)
if [[ ${RL_LOG_STATUS_PREV} != ${RL_LOG_STATUS} ]]; then
err_count=$(( ${err_count} + 1 ))
echo 'Last 10 applied ratelimits (may overlap with previous reports).' > /tmp/ratelimit
echo 'Full ratelimit buckets can be emptied by deleting the ratelimit hash from within mailcow UI (see /debug -> Protocols -> Ratelimit):' >> /tmp/ratelimit
echo >> /tmp/ratelimit
redis-cli --raw -h redis LRANGE RL_LOG 0 10 | jq . >> /tmp/ratelimit
redis-cli --raw -h redis -a ${REDISPASS} --no-auth-warning LRANGE RL_LOG 0 10 | jq . >> /tmp/ratelimit
fi
[ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1
[ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} ))
@ -673,7 +673,7 @@ acme_checks() {
err_count=0
diff_c=0
THRESHOLD=${ACME_THRESHOLD}
ACME_LOG_STATUS=$(redis-cli -h redis GET ACME_FAIL_TIME)
ACME_LOG_STATUS=$(redis-cli -h redis -a ${REDISPASS} --no-auth-warning GET ACME_FAIL_TIME)
if [[ -z "${ACME_LOG_STATUS}" ]]; then
${REDIS_CMDLINE} SET ACME_FAIL_TIME 0
ACME_LOG_STATUS=0
@ -685,7 +685,7 @@ acme_checks() {
ACME_LOG_STATUS_PREV=${ACME_LOG_STATUS}
ACME_LC=0
until [[ ! -z ${ACME_LOG_STATUS} ]] || [ ${ACME_LC} -ge 3 ]; do
ACME_LOG_STATUS=$(redis-cli -h redis GET ACME_FAIL_TIME 2> /dev/null)
ACME_LOG_STATUS=$(redis-cli -h redis -a ${REDISPASS} --no-auth-warning GET ACME_FAIL_TIME 2> /dev/null)
sleep 3
ACME_LC=$((ACME_LC+1))
done
@ -994,6 +994,7 @@ PID=$!
echo "Spawned cert_checks with PID ${PID}"
BACKGROUND_TASKS+=(${PID})
if [[ "${SKIP_OLEFY}" =~ ^([nN][oO]|[nN])+$ ]]; then
(
while true; do
if ! olefy_checks; then
@ -1005,6 +1006,7 @@ done
PID=$!
echo "Spawned olefy_checks with PID ${PID}"
BACKGROUND_TASKS+=(${PID})
fi
(
while true; do

View File

@ -1,130 +0,0 @@
map $http_x_forwarded_proto $client_req_scheme_nc {
default $scheme;
https https;
}
server {
include /etc/nginx/conf.d/listen_ssl.active;
include /etc/nginx/conf.d/listen_plain.active;
include /etc/nginx/mime.types;
charset utf-8;
override_charset on;
ssl_certificate /etc/ssl/mail/cert.pem;
ssl_certificate_key /etc/ssl/mail/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_ecdh_curve X25519:X448:secp384r1:secp256k1;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Download-Options "noopen" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "noindex, nofollow" always;
add_header X-XSS-Protection "1; mode=block" always;
fastcgi_hide_header X-Powered-By;
server_name NC_SUBD;
root /web/nextcloud/;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location = /.well-known/carddav {
return 301 $client_req_scheme_nc://$host/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $client_req_scheme_nc://$host/remote.php/dav;
}
location = /.well-known/webfinger {
return 301 $client_req_scheme_nc://$host/index.php/.well-known/webfinger;
}
location = /.well-known/nodeinfo {
return 301 $client_req_scheme_nc://$host/index.php/.well-known/nodeinfo;
}
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /web;
}
fastcgi_buffers 64 4K;
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
set_real_ip_from fc00::/7;
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
location / {
rewrite ^ /index.php$uri;
}
location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ {
deny all;
}
location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}
location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+)\.php(?:$|\/) {
fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
fastcgi_param HTTPS on;
# Avoid sending the security headers twice
fastcgi_param modHeadersAvailable true;
# Enable pretty urls
fastcgi_param front_controller_active true;
fastcgi_pass phpfpm:9002;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
client_max_body_size 0;
fastcgi_read_timeout 1200;
}
location ~ ^\/(?:updater|ocs-provider)(?:$|\/) {
try_files $uri/ =404;
index index.php;
}
location ~ \.(?:css|js|woff2?|svg|gif|map)$ {
try_files $uri /index.php$request_uri;
add_header Cache-Control "public, max-age=15778463";
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Download-Options "noopen" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "none" always;
add_header X-XSS-Protection "1; mode=block" always;
access_log off;
}
location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap)$ {
try_files $uri /index.php$request_uri;
access_log off;
}
}

View File

@ -1,2 +0,0 @@
#!/bin/bash
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) php /web/nextcloud/occ ${@}

View File

@ -1,8 +1,8 @@
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA9iHB0CRDhV8wfBgqnmvuJpl0fzL3qL75R4ZvQHlfMNLrxuIz2x9D
9zcDhPcBTVzV5Ay0AAkke4wP6r6wDQqXqBP4Y8IOkYAyLh3jM40jfHQzQt+5JdQl
ond3kiscBsFOch/vMfSLMu3lAb0YhPNTvrxhMz7LcVAWYl82swASupdiKR+MgaQr
XsugpmDKsHW60VmIM9B7K9Y+rNHwvMWkmISd0KxA8oOy1WJvsVEissMALZDE3c4w
2xHmO2lXxgEx3aez28736t4m/KW3g9Zr31a1M0KusmfY//fGkPk4NUrLBOS2xrgp
Y/rG1qSBdcVyerM0Ki93qCyHKYu4ene0OwIBAg==
MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----

View File

@ -0,0 +1,115 @@
<?php
ini_set('error_reporting', 0);
header('Content-Type: application/json');
$post = trim(file_get_contents('php://input'));
if ($post) {
$post = json_decode($post, true);
}
$return = array("success" => false);
if(!isset($post['username']) || !isset($post['password']) || !isset($post['real_rip'])){
error_log("MAILCOWAUTH: Bad Request");
http_response_code(400); // Bad Request
echo json_encode($return);
exit();
}
require_once('../../../web/inc/vars.inc.php');
if (file_exists('../../../web/inc/vars.local.inc.php')) {
include_once('../../../web/inc/vars.local.inc.php');
}
require_once '../../../web/inc/lib/vendor/autoload.php';
// Init Redis
$redis = new Redis();
try {
if (!empty(getenv('REDIS_SLAVEOF_IP'))) {
$redis->connect(getenv('REDIS_SLAVEOF_IP'), getenv('REDIS_SLAVEOF_PORT'));
}
else {
$redis->connect('redis-mailcow', 6379);
}
$redis->auth(getenv("REDISPASS"));
}
catch (Exception $e) {
error_log("MAILCOWAUTH: " . $e . PHP_EOL);
http_response_code(500); // Internal Server Error
echo json_encode($return);
exit;
}
// Init database
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
error_log("MAILCOWAUTH: " . $e . PHP_EOL);
http_response_code(500); // Internal Server Error
echo json_encode($return);
exit;
}
// Load core functions first
require_once 'functions.inc.php';
require_once 'functions.auth.inc.php';
require_once 'sessions.inc.php';
require_once 'functions.mailbox.inc.php';
require_once 'functions.ratelimit.inc.php';
require_once 'functions.acl.inc.php';
$isSOGoRequest = $post['real_rip'] == getenv('IPV4_NETWORK') . '.248';
$result = false;
if ($isSOGoRequest) {
// This is a SOGo Auth request. First check for SSO password.
$sogo_sso_pass = file_get_contents("/etc/sogo-sso/sogo-sso.pass");
if ($sogo_sso_pass === $post['password']){
error_log('MAILCOWAUTH: SOGo SSO auth for user ' . $post['username']);
set_sasl_log($post['username'], $post['real_rip'], "SOGO");
$result = true;
}
}
if ($result === false){
// If it's a SOGo Request, don't check for protocol access
$service = ($isSOGoRequest) ? false : array($post['service'] => true);
$result = apppass_login($post['username'], $post['password'], $service, array(
'is_internal' => true,
'remote_addr' => $post['real_rip']
));
if ($result) {
error_log('MAILCOWAUTH: App auth for user ' . $post['username']);
set_sasl_log($post['username'], $post['real_rip'], $post['service']);
}
}
if ($result === false){
// Init Identity Provider
$iam_provider = identity_provider('init');
$iam_settings = identity_provider('get');
$result = user_login($post['username'], $post['password'], array('is_internal' => true));
if ($result) {
error_log('MAILCOWAUTH: User auth for user ' . $post['username']);
set_sasl_log($post['username'], $post['real_rip'], $post['service']);
}
}
if ($result) {
http_response_code(200); // OK
$return['success'] = true;
} else {
error_log("MAILCOWAUTH: Login failed for user " . $post['username']);
http_response_code(401); // Unauthorized
}
echo json_encode($return);
session_destroy();
exit;

View File

@ -0,0 +1,57 @@
function auth_password_verify(request, password)
if request.domain == nil then
return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, "No such user"
end
local json = require "cjson"
local ltn12 = require "ltn12"
local https = require "ssl.https"
https.TIMEOUT = 30
local req = {
username = request.user,
password = password,
real_rip = request.real_rip,
service = request.service
}
local req_json = json.encode(req)
local res = {}
local b, c = https.request {
method = "POST",
url = "https://nginx:9082",
source = ltn12.source.string(req_json),
headers = {
["content-type"] = "application/json",
["content-length"] = tostring(#req_json)
},
sink = ltn12.sink.table(res),
insecure = true
}
-- Returning PASSDB_RESULT_PASSWORD_MISMATCH will reset the user's auth cache entry.
-- Returning PASSDB_RESULT_INTERNAL_FAILURE keeps the existing cache entry,
-- even if the TTL has expired. Useful to avoid cache eviction during backend issues.
if c ~= 200 and c ~= 401 then
dovecot.i_info("HTTP request failed with " .. c .. " for user " .. request.user)
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Upstream error"
end
local response_str = table.concat(res)
local is_response_valid, response_json = pcall(json.decode, response_str)
if not is_response_valid then
dovecot.i_info("Invalid JSON received: " .. response_str)
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Invalid response format"
end
if response_json.success == true then
return dovecot.auth.PASSDB_RESULT_OK, ""
end
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Failed to authenticate"
end
function auth_passdb_lookup(req)
return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, ""
end

View File

@ -0,0 +1,37 @@
# mailcow FTS Flatcurve Settings, change them as you like.
plugin {
fts_autoindex = yes
fts_autoindex_exclude = \Junk
fts_autoindex_exclude2 = \Trash
# Tweak this setting if you only want to ensure big and frequent folders are indexed, not all.
fts_autoindex_max_recent_msgs = 20
fts = flatcurve
# Maximum term length can be set via the 'maxlen' argument (maxlen is
# specified in bytes, not number of UTF-8 characters)
fts_tokenizer_email_address = maxlen=100
fts_tokenizer_generic = algorithm=simple maxlen=30
# These are not flatcurve settings, but required for Dovecot FTS. See
# Dovecot FTS Configuration link above for further information.
fts_languages = en es de
fts_tokenizers = generic email-address
# OPTIONAL: Recommended default FTS core configuration
fts_filters = normalizer-icu snowball stopwords
fts_filters_en = lowercase snowball english-possessive stopwords
fts_index_timeout = 300s
}
### THIS PART WILL BE CHANGED BY MODIFYING mailcow.conf AUTOMATICALLY DURING RUNTIME! ###
service indexer-worker {
# Max amount of simultaniously running indexer jobs.
process_limit=1
# Max amount of RAM used by EACH indexer process.
vsz_limit=128 MB
}
### THIS PART WILL BE CHANGED BY MODIFYING mailcow.conf AUTOMATICALLY DURING RUNTIME! ###

View File

@ -53,7 +53,7 @@ mail_shared_explicit_inbox = yes
mail_prefetch_count = 30
passdb {
driver = lua
args = file=/etc/dovecot/lua/passwd-verify.lua blocking=yes
args = file=/etc/dovecot/auth/passwd-verify.lua blocking=yes cache_key=%s:%u:%w
result_success = return-ok
result_failure = continue
result_internalfail = continue
@ -69,7 +69,7 @@ passdb {
# a return of the following passdb is mandatory
passdb {
driver = lua
args = file=/etc/dovecot/lua/passwd-verify.lua blocking=yes
args = file=/etc/dovecot/auth/passwd-verify.lua blocking=yes
}
# Set doveadm_password=your-secret-password in data/conf/dovecot/extra.conf (create if missing)
service doveadm {
@ -125,6 +125,7 @@ service managesieve-login {
}
service imap-login {
service_count = 1
process_min_avail = 2
process_limit = 10000
vsz_limit = 1G
user = dovenull
@ -140,6 +141,7 @@ service imap-login {
}
service pop3-login {
service_count = 1
process_min_avail = 1
vsz_limit = 1G
inet_listener pop3_haproxy {
port = 10110
@ -239,7 +241,7 @@ plugin {
mail_crypt_global_public_key = </mail_crypt/ecpubkey.pem
mail_crypt_save_version = 2
# Enable compression while saving, lz4 Dovecot v2.2.11+
# Enable compression while saving, lz4 Dovecot v2.3.17+
zlib_save = lz4
mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename
@ -274,10 +276,11 @@ service stats {
}
}
imap_max_line_length = 2 M
#auth_cache_verify_password_with_worker = yes
#auth_cache_negative_ttl = 0
#auth_cache_ttl = 30 s
#auth_cache_size = 2 M
auth_cache_verify_password_with_worker = yes
auth_cache_negative_ttl = 60s
auth_cache_ttl = 300s
auth_cache_size = 10M
auth_verbose_passwords = sha1:6
service replicator {
process_min_avail = 1
}
@ -301,7 +304,6 @@ replication_dsync_parameters = -d -l 30 -U -n INBOX
!include_try /etc/dovecot/sni.conf
!include_try /etc/dovecot/sogo_trusted_ip.conf
!include_try /etc/dovecot/extra.conf
!include_try /etc/dovecot/sogo-sso.conf
!include_try /etc/dovecot/shared_namespace.conf
!include_try /etc/dovecot/conf.d/fts.conf
# </Includes>

View File

@ -1,7 +1,7 @@
[mysqld]
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
collation-server = utf8mb4_general_ci
#innodb_file_per_table = TRUE
#innodb_file_format = barracuda
#innodb_large_prefix = TRUE
@ -20,7 +20,7 @@ thread_cache_size = 8
query_cache_type = 0
query_cache_size = 0
max_heap_table_size = 48M
thread_stack = 128K
thread_stack = 256K
skip-host-cache
skip-name-resolve
log-warnings = 0

View File

@ -1,3 +0,0 @@
map_hash_max_size 256;
map_hash_bucket_size 256;

View File

@ -1,19 +0,0 @@
server {
listen 8081;
listen [::]:8081;
index index.php index.html;
server_name _;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /dynmaps;
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9001;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

View File

@ -1,242 +0,0 @@
include /etc/nginx/mime.types;
charset utf-8;
override_charset on;
server_tokens off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_ecdh_curve X25519:X448:secp384r1:secp256k1;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=15768000;";
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies none;
add_header Referrer-Policy strict-origin;
index index.php index.html;
client_max_body_size 0;
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied off;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon;
location ~ ^/(fonts|js|css|img)/ {
expires max;
add_header Cache-Control public;
}
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
fastcgi_hide_header X-Powered-By;
absolute_redirect off;
root /web;
location / {
try_files $uri $uri/ @strip-ext;
}
location /qhandler {
rewrite ^/qhandler/(.*)/(.*) /qhandler.php?action=$1&hash=$2;
}
location /edit {
rewrite ^/edit/(.*)/(.*) /edit.php?$1=$2;
}
location @strip-ext {
rewrite ^(.*)$ $1.php last;
}
location ~ ^/api/v1/(.*)$ {
try_files $uri $uri/ /json_api.php?query=$1&$args;
}
location ^~ /.well-known/acme-challenge/ {
allow all;
default_type "text/plain";
}
# If behind reverse proxy, forwards the correct IP
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
set_real_ip_from fc00::/7;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
rewrite ^/.well-known/caldav$ /SOGo/dav/ permanent;
rewrite ^/.well-known/carddav$ /SOGo/dav/ permanent;
location ^~ /principals {
return 301 /SOGo/dav;
}
location ^~ /inc/lib/ {
deny all;
return 403;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9002;
fastcgi_index index.php;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_read_timeout 3600;
fastcgi_send_timeout 3600;
}
location /rspamd/ {
location /rspamd/auth {
# proxy_pass is not inherited
proxy_pass http://rspamd:11334/auth;
proxy_intercept_errors on;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_redirect off;
error_page 401 /_rspamderror.php;
}
proxy_pass http://rspamd:11334/;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_redirect off;
}
location ~* ^/Autodiscover/Autodiscover.xml {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9002;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
try_files /autodiscover.php =404;
}
location ~* ^/Autodiscover/Autodiscover.json {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9002;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
try_files /autodiscover-json.php =404;
}
location ~ /(?:m|M)ail/(?:c|C)onfig-v1.1.xml {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9002;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
try_files /autoconfig.php =404;
}
location /sogo-auth-verify {
internal;
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header Content-Length "";
proxy_pass http://127.0.0.1:65510/sogo-auth;
proxy_pass_request_body off;
}
location ^~ /Microsoft-Server-ActiveSync {
include /etc/nginx/conf.d/includes/sogo_proxy_auth.conf;
include /etc/nginx/conf.d/sogo_eas.active;
proxy_connect_timeout 75;
proxy_send_timeout 3600;
proxy_read_timeout 3600;
proxy_buffer_size 128k;
proxy_buffers 64 512k;
proxy_busy_buffers_size 512k;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
client_body_buffer_size 512k;
client_max_body_size 0;
}
location ^~ /SOGo {
location ~* ^/SOGo/so/.*\.(xml|js|html|xhtml)$ {
include /etc/nginx/conf.d/includes/sogo_proxy_auth.conf;
include /etc/nginx/conf.d/sogo.active;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header x-webobjects-server-protocol HTTP/1.0;
proxy_set_header x-webobjects-remote-host $remote_addr;
proxy_set_header x-webobjects-server-name $server_name;
proxy_set_header x-webobjects-server-url $client_req_scheme://$http_host;
proxy_set_header x-webobjects-server-port $server_port;
proxy_hide_header Content-Type;
add_header Content-Type text/plain;
break;
}
include /etc/nginx/conf.d/includes/sogo_proxy_auth.conf;
include /etc/nginx/conf.d/sogo.active;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header x-webobjects-server-protocol HTTP/1.0;
proxy_set_header x-webobjects-remote-host $remote_addr;
proxy_set_header x-webobjects-server-name $server_name;
proxy_set_header x-webobjects-server-url $client_req_scheme://$http_host;
proxy_set_header x-webobjects-server-port $server_port;
proxy_buffer_size 128k;
proxy_buffers 64 512k;
proxy_busy_buffers_size 512k;
proxy_send_timeout 3600;
proxy_read_timeout 3600;
client_body_buffer_size 128k;
client_max_body_size 0;
break;
}
location ~* /sogo$ {
return 301 $client_req_scheme://$http_host/SOGo;
}
location /SOGo.woa/WebServerResources/ {
alias /usr/lib/GNUstep/SOGo/WebServerResources/;
}
location /.woa/WebServerResources/ {
alias /usr/lib/GNUstep/SOGo/WebServerResources/;
}
location /SOGo/WebServerResources/ {
alias /usr/lib/GNUstep/SOGo/WebServerResources/;
}
location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$) {
alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2;
}
include /etc/nginx/conf.d/site.*.custom;
error_page 502 @awaitingupstream;
location @awaitingupstream {
rewrite ^(.*)$ /_status.502.html break;
}
location ~ ^/cache/(.*)$ {
try_files $uri $uri/ /resource.php?file=$1;
}

View File

@ -1,8 +0,0 @@
auth_request /sogo-auth-verify;
auth_request_set $user $upstream_http_x_user;
auth_request_set $auth $upstream_http_x_auth;
auth_request_set $auth_type $upstream_http_x_auth_type;
proxy_set_header x-webobjects-remote-user "$user";
proxy_set_header Authorization "$auth";
proxy_set_header x-webobjects-auth-type "$auth_type";

View File

@ -1,19 +0,0 @@
server {
listen 9081;
index index.php index.html;
server_name _;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /meta_exporter;
client_max_body_size 10M;
location ~ \.php$ {
client_max_body_size 10M;
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9001;
fastcgi_index pipe.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

View File

@ -1,10 +0,0 @@
proxy_cache_path /tmp levels=1:2 keys_zone=sogo:10m inactive=24h max_size=1g;
server_names_hash_max_size 512;
server_names_hash_bucket_size 128;
map $http_x_forwarded_proto $client_req_scheme {
default $scheme;
https https;
}
include /etc/nginx/conf.d/sites.active;

View File

@ -1,2 +0,0 @@
listen ${HTTP_PORT};
listen [::]:${HTTP_PORT};

View File

@ -1,3 +0,0 @@
listen ${HTTPS_PORT} ssl;
listen [::]:${HTTPS_PORT} ssl;
http2 on;

View File

@ -0,0 +1,211 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
# map-size.conf:
map_hash_max_size 256;
map_hash_bucket_size 256;
# site.conf:
proxy_cache_path /tmp levels=1:2 keys_zone=sogo:10m inactive=24h max_size=1g;
server_names_hash_max_size 512;
server_names_hash_bucket_size 128;
map $http_x_forwarded_proto $client_req_scheme {
default $scheme;
https https;
}
{% if HTTP_REDIRECT %}
# HTTP to HTTPS redirect
server {
root /web;
listen {{ HTTP_PORT }} default_server;
listen [::]:{{ HTTP_PORT }} default_server;
server_name {{ MAILCOW_HOSTNAME }} autodiscover.* autoconfig.* {{ ADDITIONAL_SERVER_NAMES | join(' ') }};
if ( $request_uri ~* "%0A|%0D" ) { return 403; }
location ^~ /.well-known/acme-challenge/ {
allow all;
default_type "text/plain";
}
location / {
return 301 https://$host$uri$is_args$args;
}
}
{%endif%}
# Default Server Name
server {
listen 127.0.0.1:65510; # sogo-auth verify internal
{% if not HTTP_REDIRECT %}
listen {{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
{%endif%}
listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
{% if not DISABLE_IPv6 %}
{% if not HTTP_REDIRECT %}
listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
{%endif%}
listen [::]:{{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
{%endif%}
http2 on;
ssl_certificate /etc/ssl/mail/cert.pem;
ssl_certificate_key /etc/ssl/mail/key.pem;
server_name {{ MAILCOW_HOSTNAME }} autodiscover.* autoconfig.*;
include /etc/nginx/includes/sites-default.conf;
}
# Additional Server Names
{% for SERVER_NAME in ADDITIONAL_SERVER_NAMES %}
server {
listen 127.0.0.1:65510; # sogo-auth verify internal
{% if not HTTP_REDIRECT %}
listen {{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
{%endif%}
listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
{% if not DISABLE_IPv6 %}
{% if not HTTP_REDIRECT %}
listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
{%endif%}
listen [::]:{{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
{%endif%}
http2 on;
ssl_certificate /etc/ssl/mail/cert.pem;
ssl_certificate_key /etc/ssl/mail/key.pem;
server_name {{ SERVER_NAME }};
include /etc/nginx/includes/sites-default.conf;
}
{% endfor %}
# rspamd dynmaps:
server {
listen 8081;
{% if not DISABLE_IPv6 %}
listen [::]:8081;
{%endif%}
index index.php index.html;
server_name _;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /dynmaps;
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass {{ PHPFPMHOST }}:9001;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
# rspamd meta_exporter:
server {
listen 9081;
index index.php index.html;
server_name _;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /meta_exporter;
client_max_body_size 10M;
location ~ \.php$ {
client_max_body_size 10M;
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass {{ PHPFPMHOST }}:9001;
fastcgi_index pipe.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
server {
listen 9082 ssl http2;
ssl_certificate /etc/ssl/mail/cert.pem;
ssl_certificate_key /etc/ssl/mail/key.pem;
index mailcowauth.php;
server_name _;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /mailcowauth;
client_max_body_size 10M;
location ~ \.php$ {
client_max_body_size 10M;
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9001;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
include /etc/nginx/conf.d/*.conf;
{% for cert in valid_cert_dirs %}
server {
{% if not HTTP_REDIRECT %}
listen {{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
{%endif%}
listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
{% if not DISABLE_IPv6 %}
{% if not HTTP_REDIRECT %}
listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
{%endif%}
listen [::]:{{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
{%endif%}
http2 on;
ssl_certificate {{ cert.cert_path }}cert.pem;
ssl_certificate_key {{ cert.cert_path }}key.pem;
server_name {{ cert.domains }};
include /etc/nginx/includes/sites-default.conf;
}
{% endfor %}
}

View File

@ -1 +0,0 @@
echo "server_name ${MAILCOW_HOSTNAME} autodiscover.* autoconfig.* $(echo ${ADDITIONAL_SERVER_NAMES} | tr ',' ' ');"

View File

@ -0,0 +1,287 @@
include /etc/nginx/mime.types;
charset utf-8;
override_charset on;
server_tokens off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_ecdh_curve X25519:X448:secp384r1:secp256k1;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=15768000;";
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies none;
add_header Referrer-Policy strict-origin;
index index.php index.html;
client_max_body_size 0;
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied off;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon;
location ~ ^/(fonts|js|css|img)/ {
expires max;
add_header Cache-Control public;
}
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
fastcgi_hide_header X-Powered-By;
absolute_redirect off;
root /web;
# If behind reverse proxy, forwards the correct IP
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
set_real_ip_from fc00::/7;
{% for TRUSTED_PROXY in TRUSTED_PROXIES %}
set_real_ip_from {{ TRUSTED_PROXY }};
{% endfor %}
{% if not NGINX_USE_PROXY_PROTOCOL %}
real_ip_header X-Forwarded-For;
{% else %}
real_ip_header proxy_protocol;
{% endif %}
real_ip_recursive on;
location @strip-ext {
rewrite ^(.*)$ $1.php last;
}
location ^~ /inc/lib/ {
deny all;
return 403;
}
location ^~ /.well-known/acme-challenge/ {
allow all;
default_type "text/plain";
}
rewrite ^/.well-known/caldav$ /SOGo/dav/ permanent;
rewrite ^/.well-known/carddav$ /SOGo/dav/ permanent;
location / {
try_files $uri $uri/ @strip-ext;
}
location /qhandler {
rewrite ^/qhandler/(.*)/(.*) /qhandler.php?action=$1&hash=$2;
}
location /edit {
rewrite ^/edit/(.*)/(.*) /edit.php?$1=$2;
}
location ~ ^/api/v1/(.*)$ {
try_files $uri $uri/ /json_api.php?query=$1&$args;
}
location ~ ^/cache/(.*)$ {
try_files $uri $uri/ /resource.php?file=$1;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass {{ PHPFPMHOST }}:9002;
fastcgi_index index.php;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_read_timeout 3600;
fastcgi_send_timeout 3600;
}
location ~* ^/Autodiscover/Autodiscover.xml {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass {{ PHPFPMHOST }}:9002;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
try_files /autodiscover.php =404;
}
location ~* ^/Autodiscover/Autodiscover.json {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass {{ PHPFPMHOST }}:9002;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
try_files /autodiscover-json.php =404;
}
location ~ /(?:m|M)ail/(?:c|C)onfig-v1.1.xml {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass {{ PHPFPMHOST }}:9002;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
try_files /autoconfig.php =404;
}
{% if not SKIP_RSPAMD %}
location /rspamd/ {
location /rspamd/auth {
# proxy_pass is not inherited
proxy_pass http://{{ RSPAMDHOST }}:11334/auth;
proxy_intercept_errors on;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For {% if not NGINX_USE_PROXY_PROTOCOL %}$proxy_add_x_forwarded_for{% else %}$proxy_protocol_addr{%endif%};
proxy_set_header X-Real-IP {% if not NGINX_USE_PROXY_PROTOCOL %}$remote_addr{% else %}$proxy_protocol_addr{%endif%};
proxy_redirect off;
error_page 401 /_rspamderror.php;
}
proxy_pass http://{{ RSPAMDHOST }}:11334/;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For {% if not NGINX_USE_PROXY_PROTOCOL %}$proxy_add_x_forwarded_for{% else %}$proxy_protocol_addr{%endif%};
proxy_set_header X-Real-IP {% if not NGINX_USE_PROXY_PROTOCOL %}$remote_addr{% else %}$proxy_protocol_addr{%endif%};
proxy_redirect off;
}
{% endif %}
{% if not SKIP_SOGO %}
location ^~ /principals {
return 301 /SOGo/dav;
}
location /sogo-auth-verify {
internal;
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header Content-Length "";
proxy_pass http://127.0.0.1:65510/sogo-auth;
proxy_pass_request_body off;
}
location ^~ /Microsoft-Server-ActiveSync {
auth_request /sogo-auth-verify;
auth_request_set $user $upstream_http_x_user;
auth_request_set $auth $upstream_http_x_auth;
auth_request_set $auth_type $upstream_http_x_auth_type;
proxy_set_header x-webobjects-remote-user "$user";
proxy_set_header Authorization "$auth";
proxy_set_header x-webobjects-auth-type "$auth_type";
proxy_pass http://{{ SOGOHOST }}:20000/SOGo/Microsoft-Server-ActiveSync;
proxy_set_header X-Forwarded-For {% if not NGINX_USE_PROXY_PROTOCOL %}$proxy_add_x_forwarded_for{% else %}$proxy_protocol_addr{%endif%};
proxy_set_header X-Real-IP {% if not NGINX_USE_PROXY_PROTOCOL %}$remote_addr{% else %}$proxy_protocol_addr{%endif%};
proxy_connect_timeout 75;
proxy_send_timeout 3600;
proxy_read_timeout 3600;
proxy_buffer_size 128k;
proxy_buffers 64 512k;
proxy_busy_buffers_size 512k;
proxy_set_header Host $http_host;
client_body_buffer_size 512k;
client_max_body_size 0;
}
location ^~ /SOGo {
location ~* ^/SOGo/so/.*\.(xml|js|html|xhtml)$ {
auth_request /sogo-auth-verify;
auth_request_set $user $upstream_http_x_user;
auth_request_set $auth $upstream_http_x_auth;
auth_request_set $auth_type $upstream_http_x_auth_type;
proxy_set_header x-webobjects-remote-user "$user";
proxy_set_header Authorization "$auth";
proxy_set_header x-webobjects-auth-type "$auth_type";
proxy_pass http://{{ SOGOHOST }}:20000;
proxy_set_header X-Forwarded-For {% if not NGINX_USE_PROXY_PROTOCOL %}$proxy_add_x_forwarded_for{% else %}$proxy_protocol_addr{%endif%};
proxy_set_header X-Real-IP {% if not NGINX_USE_PROXY_PROTOCOL %}$remote_addr{% else %}$proxy_protocol_addr{%endif%};
proxy_set_header Host $http_host;
proxy_set_header x-webobjects-server-protocol HTTP/1.0;
proxy_set_header x-webobjects-remote-host $remote_addr;
proxy_set_header x-webobjects-server-name $server_name;
proxy_set_header x-webobjects-server-url $client_req_scheme://$http_host;
proxy_set_header x-webobjects-server-port $server_port;
proxy_hide_header Content-Type;
add_header Content-Type text/plain;
break;
}
auth_request /sogo-auth-verify;
auth_request_set $user $upstream_http_x_user;
auth_request_set $auth $upstream_http_x_auth;
auth_request_set $auth_type $upstream_http_x_auth_type;
proxy_set_header x-webobjects-remote-user "$user";
proxy_set_header Authorization "$auth";
proxy_set_header x-webobjects-auth-type "$auth_type";
proxy_pass http://{{ SOGOHOST }}:20000;
proxy_set_header X-Forwarded-For {% if not NGINX_USE_PROXY_PROTOCOL %}$proxy_add_x_forwarded_for{% else %}$proxy_protocol_addr{%endif%};
proxy_set_header X-Real-IP {% if not NGINX_USE_PROXY_PROTOCOL %}$remote_addr{% else %}$proxy_protocol_addr{%endif%};
proxy_set_header Host $http_host;
proxy_set_header x-webobjects-server-protocol HTTP/1.0;
proxy_set_header x-webobjects-remote-host $remote_addr;
proxy_set_header x-webobjects-server-name $server_name;
proxy_set_header x-webobjects-server-url $client_req_scheme://$http_host;
proxy_set_header x-webobjects-server-port $server_port;
proxy_buffer_size 128k;
proxy_buffers 64 512k;
proxy_busy_buffers_size 512k;
proxy_send_timeout 3600;
proxy_read_timeout 3600;
client_body_buffer_size 128k;
client_max_body_size 0;
break;
}
location ~* /sogo$ {
return 301 $client_req_scheme://$http_host/SOGo;
}
location /SOGo.woa/WebServerResources/ {
alias /usr/lib/GNUstep/SOGo/WebServerResources/;
}
location /.woa/WebServerResources/ {
alias /usr/lib/GNUstep/SOGo/WebServerResources/;
}
location /SOGo/WebServerResources/ {
alias /usr/lib/GNUstep/SOGo/WebServerResources/;
}
location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$) {
alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2;
}
{% endif %}
include /etc/nginx/conf.d/site.*.custom;
error_page 502 @awaitingupstream;
location @awaitingupstream {
rewrite ^(.*)$ /_status.502.html break;
}
location ~* \.php$ {
return 404;
}
location ~* \.twig$ {
return 404;
}

View File

@ -1,38 +0,0 @@
echo '
server {
listen 127.0.0.1:65510;
include /etc/nginx/conf.d/listen_plain.active;
include /etc/nginx/conf.d/listen_ssl.active;
ssl_certificate /etc/ssl/mail/cert.pem;
ssl_certificate_key /etc/ssl/mail/key.pem;
include /etc/nginx/conf.d/server_name.active;
include /etc/nginx/conf.d/includes/site-defaults.conf;
}
';
for cert_dir in /etc/ssl/mail/*/ ; do
if [[ ! -f ${cert_dir}domains ]] || [[ ! -f ${cert_dir}cert.pem ]] || [[ ! -f ${cert_dir}key.pem ]]; then
continue
fi
# do not create vhost for default-certificate. the cert is already in the default server listen
domains="$(cat ${cert_dir}domains | sed -e 's/^[[:space:]]*//')"
case "${domains}" in
"") continue;;
"${MAILCOW_HOSTNAME}"*) continue;;
esac
echo -n '
server {
include /etc/nginx/conf.d/listen_ssl.active;
ssl_certificate '${cert_dir}'cert.pem;
ssl_certificate_key '${cert_dir}'key.pem;
';
echo -n '
server_name '${domains}';
include /etc/nginx/conf.d/includes/site-defaults.conf;
}
';
done

View File

@ -1 +0,0 @@
proxy_pass http://${IPV4_NETWORK}.248:20000;

View File

@ -1,5 +0,0 @@
if printf "%s\n" "${SKIP_SOGO}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then
echo "return 410;"
else
echo "proxy_pass http://${IPV4_NETWORK}.248:20000/SOGo/Microsoft-Server-ActiveSync;"
fi

View File

@ -0,0 +1,231 @@
<?php
require_once(__DIR__ . '/../web/inc/vars.inc.php');
if (file_exists(__DIR__ . '/../web/inc/vars.local.inc.php')) {
include_once(__DIR__ . '/../web/inc/vars.local.inc.php');
}
require_once __DIR__ . '/../web/inc/lib/vendor/autoload.php';
// Init database
//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
logMsg("err", $e->getMessage());
session_destroy();
exit;
}
// Init Redis
$redis = new Redis();
try {
if (!empty(getenv('REDIS_SLAVEOF_IP'))) {
$redis->connect(getenv('REDIS_SLAVEOF_IP'), getenv('REDIS_SLAVEOF_PORT'));
}
else {
$redis->connect('redis-mailcow', 6379);
}
$redis->auth(getenv("REDISPASS"));
}
catch (Exception $e) {
echo "Exiting: " . $e->getMessage();
session_destroy();
exit;
}
function logMsg($priority, $message, $task = "Keycloak Sync") {
global $redis;
$finalMsg = array(
"time" => time(),
"priority" => $priority,
"task" => $task,
"message" => $message
);
$redis->lPush('CRON_LOG', json_encode($finalMsg));
}
// Load core functions first
require_once __DIR__ . '/../web/inc/functions.inc.php';
require_once __DIR__ . '/../web/inc/functions.auth.inc.php';
require_once __DIR__ . '/../web/inc/sessions.inc.php';
require_once __DIR__ . '/../web/inc/functions.mailbox.inc.php';
require_once __DIR__ . '/../web/inc/functions.ratelimit.inc.php';
require_once __DIR__ . '/../web/inc/functions.acl.inc.php';
$_SESSION['mailcow_cc_username'] = "admin";
$_SESSION['mailcow_cc_role'] = "admin";
$_SESSION['acl']['tls_policy'] = "1";
$_SESSION['acl']['quarantine_notification'] = "1";
$_SESSION['acl']['quarantine_category'] = "1";
$_SESSION['acl']['ratelimit'] = "1";
$_SESSION['acl']['sogo_access'] = "1";
$_SESSION['acl']['protocol_access'] = "1";
$_SESSION['acl']['mailbox_relayhost'] = "1";
$_SESSION['acl']['unlimited_quota'] = "1";
$iam_settings = identity_provider('get');
if ($iam_settings['authsource'] != "keycloak" || (intval($iam_settings['periodic_sync']) != 1 && intval($iam_settings['import_users']) != 1)) {
session_destroy();
exit;
}
// Set pagination variables
$start = 0;
$max = 100;
// lock sync if already running
$lock_file = '/tmp/iam-sync.lock';
if (file_exists($lock_file)) {
$lock_file_parts = explode("\n", file_get_contents($lock_file));
$pid = $lock_file_parts[0];
if (count($lock_file_parts) > 1){
$last_execution = $lock_file_parts[1];
$elapsed_time = (time() - $last_execution) / 60;
if ($elapsed_time < intval($iam_settings['sync_interval'])) {
logMsg("warning", "Sync not ready (".number_format((float)$elapsed_time, 2, '.', '')."min / ".$iam_settings['sync_interval']."min)");
session_destroy();
exit;
}
}
if (posix_kill($pid, 0)) {
logMsg("warning", "Sync is already running");
session_destroy();
exit;
} else {
unlink($lock_file);
}
}
$lock_file_handle = fopen($lock_file, 'w');
fwrite($lock_file_handle, getmypid());
fclose($lock_file_handle);
// Init Keycloak Provider
$iam_provider = identity_provider('init');
// Loop until all users have been retrieved
while (true) {
// Get admin access token
$admin_token = identity_provider("get-keycloak-admin-token");
// Make the API request to retrieve the users
$url = "{$iam_settings['server_url']}/admin/realms/{$iam_settings['realm']}/users?first=$start&max=$max";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
"Authorization: Bearer " . $admin_token
]);
$response = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($code != 200){
logMsg("err", "Received HTTP {$code}");
session_destroy();
exit;
}
try {
$response = json_decode($response, true);
} catch (Exception $e) {
logMsg("err", $e->getMessage());
break;
}
if (!is_array($response)){
logMsg("err", "Received malformed response from keycloak api");
break;
}
if (count($response) == 0) {
break;
}
// Process the batch of users
foreach ($response as $user) {
if (empty($user['email'])){
logMsg("warning", "No email address in keycloak found for user " . $user['name']);
continue;
}
// try get mailbox user
$stmt = $pdo->prepare("SELECT
mailbox.*,
domain.active AS d_active
FROM `mailbox`
INNER JOIN domain on mailbox.domain = domain.domain
WHERE `kind` NOT REGEXP 'location|thing|group'
AND `username` = :user");
$stmt->execute(array(':user' => $user['email']));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
// check if matching attribute mapping exists
$user_template = $user['attributes']['mailcow_template'][0];
$mapper_key = array_search($user_template, $iam_settings['mappers']);
$_SESSION['access_all_exception'] = '1';
if (!$row && intval($iam_settings['import_users']) == 1){
if ($mapper_key === false){
if (!empty($iam_settings['default_template'])) {
$mbox_template = $iam_settings['default_template'];
logMsg("warning", "Using default template for user " . $user['email']);
} else {
logMsg("warning", "No matching attribute mapping found for user " . $user['email']);
continue;
}
} else {
$mbox_template = $iam_settings['templates'][$mapper_key];
}
// mailbox user does not exist, create...
logMsg("info", "Creating user " . $user['email']);
$create_res = mailbox('add', 'mailbox_from_template', array(
'domain' => explode('@', $user['email'])[1],
'local_part' => explode('@', $user['email'])[0],
'name' => $user['firstName'] . " " . $user['lastName'],
'authsource' => 'keycloak',
'template' => $mbox_template
));
if (!$create_res){
logMsg("err", "Could not create user " . $user['email']);
continue;
}
} else if ($row && intval($iam_settings['periodic_sync']) == 1 && $row['authsource'] == "keycloak") {
if ($mapper_key === false){
logMsg("warning", "No matching attribute mapping found for user " . $user['email']);
continue;
}
$mbox_template = $iam_settings['templates'][$mapper_key];
// mailbox user does exist, sync attribtues...
logMsg("info", "Syncing attributes for user " . $user['email']);
mailbox('edit', 'mailbox_from_template', array(
'username' => $user['email'],
'name' => $user['firstName'] . " " . $user['lastName'],
'template' => $mbox_template
));
} else {
// skip mailbox user
logMsg("info", "Skipping user " . $user['email']);
}
$_SESSION['access_all_exception'] = '0';
sleep(0.025);
}
// Update the pagination variables for the next batch
$start += $max;
sleep(1);
}
logMsg("info", "DONE!");
// add last execution time to lock file
$lock_file_handle = fopen($lock_file, 'w');
fwrite($lock_file_handle, getmypid() . "\n" . time());
fclose($lock_file_handle);
session_destroy();

View File

@ -0,0 +1,198 @@
<?php
require_once(__DIR__ . '/../web/inc/vars.inc.php');
if (file_exists(__DIR__ . '/../web/inc/vars.local.inc.php')) {
include_once(__DIR__ . '/../web/inc/vars.local.inc.php');
}
require_once __DIR__ . '/../web/inc/lib/vendor/autoload.php';
// Init database
//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
logMsg("err", $e->getMessage());
session_destroy();
exit;
}
// Init Redis
$redis = new Redis();
try {
if (!empty(getenv('REDIS_SLAVEOF_IP'))) {
$redis->connect(getenv('REDIS_SLAVEOF_IP'), getenv('REDIS_SLAVEOF_PORT'));
}
else {
$redis->connect('redis-mailcow', 6379);
}
$redis->auth(getenv("REDISPASS"));
}
catch (Exception $e) {
echo "Exiting: " . $e->getMessage();
session_destroy();
exit;
}
function logMsg($priority, $message, $task = "LDAP Sync") {
global $redis;
$finalMsg = array(
"time" => time(),
"priority" => $priority,
"task" => $task,
"message" => $message
);
$redis->lPush('CRON_LOG', json_encode($finalMsg));
}
// Load core functions first
require_once __DIR__ . '/../web/inc/functions.inc.php';
require_once __DIR__ . '/../web/inc/functions.auth.inc.php';
require_once __DIR__ . '/../web/inc/sessions.inc.php';
require_once __DIR__ . '/../web/inc/functions.mailbox.inc.php';
require_once __DIR__ . '/../web/inc/functions.ratelimit.inc.php';
require_once __DIR__ . '/../web/inc/functions.acl.inc.php';
$_SESSION['mailcow_cc_username'] = "admin";
$_SESSION['mailcow_cc_role'] = "admin";
$_SESSION['acl']['tls_policy'] = "1";
$_SESSION['acl']['quarantine_notification'] = "1";
$_SESSION['acl']['quarantine_category'] = "1";
$_SESSION['acl']['ratelimit'] = "1";
$_SESSION['acl']['sogo_access'] = "1";
$_SESSION['acl']['protocol_access'] = "1";
$_SESSION['acl']['mailbox_relayhost'] = "1";
$_SESSION['acl']['unlimited_quota'] = "1";
$iam_settings = identity_provider('get');
if ($iam_settings['authsource'] != "ldap" || (intval($iam_settings['periodic_sync']) != 1 && intval($iam_settings['import_users']) != 1)) {
session_destroy();
exit;
}
// Set pagination variables
$start = 0;
$max = 100;
// lock sync if already running
$lock_file = '/tmp/iam-sync.lock';
if (file_exists($lock_file)) {
$lock_file_parts = explode("\n", file_get_contents($lock_file));
$pid = $lock_file_parts[0];
if (count($lock_file_parts) > 1){
$last_execution = $lock_file_parts[1];
$elapsed_time = (time() - $last_execution) / 60;
if ($elapsed_time < intval($iam_settings['sync_interval'])) {
logMsg("warning", "Sync not ready (".number_format((float)$elapsed_time, 2, '.', '')."min / ".$iam_settings['sync_interval']."min)");
session_destroy();
exit;
}
}
if (posix_kill($pid, 0)) {
logMsg("warning", "Sync is already running");
session_destroy();
exit;
} else {
unlink($lock_file);
}
}
$lock_file_handle = fopen($lock_file, 'w');
fwrite($lock_file_handle, getmypid());
fclose($lock_file_handle);
// Init Provider
$iam_provider = identity_provider('init');
// Get ldap users
$ldap_query = $iam_provider->query();
if (!empty($iam_settings['filter'])) {
$ldap_query = $ldap_query->rawFilter($iam_settings['filter']);
}
$response = $ldap_query->where($iam_settings['username_field'], "*")
->where($iam_settings['attribute_field'], "*")
->select([$iam_settings['username_field'], $iam_settings['attribute_field'], 'displayname'])
->paginate($max);
// Process the users
foreach ($response as $user) {
// try get mailbox user
$stmt = $pdo->prepare("SELECT
mailbox.*,
domain.active AS d_active
FROM `mailbox`
INNER JOIN domain on mailbox.domain = domain.domain
WHERE `kind` NOT REGEXP 'location|thing|group'
AND `username` = :user");
$stmt->execute(array(':user' => $user[$iam_settings['username_field']][0]));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
// check if matching attribute mapping exists
$user_template = $user[$iam_settings['attribute_field']][0];
$mapper_key = array_search($user_template, $iam_settings['mappers']);
if (empty($user[$iam_settings['username_field']][0])){
logMsg("warning", "Skipping user " . $user['displayname'][0] . " due to empty LDAP ". $iam_settings['username_field'] . " property.");
continue;
}
$_SESSION['access_all_exception'] = '1';
if (!$row && intval($iam_settings['import_users']) == 1){
if ($mapper_key === false){
if (!empty($iam_settings['default_template'])) {
$mbox_template = $iam_settings['default_template'];
} else {
logMsg("warning", "No matching attribute mapping found for user " . $user[$iam_settings['username_field']][0]);
continue;
}
} else {
$mbox_template = $iam_settings['templates'][$mapper_key];
}
// mailbox user does not exist, create...
logMsg("info", "Creating user " . $user[$iam_settings['username_field']][0]);
$create_res = mailbox('add', 'mailbox_from_template', array(
'domain' => explode('@', $user[$iam_settings['username_field']][0])[1],
'local_part' => explode('@', $user[$iam_settings['username_field']][0])[0],
'name' => $user['displayname'][0],
'authsource' => 'ldap',
'template' => $mbox_template
));
if (!$create_res){
logMsg("err", "Could not create user " . $user[$iam_settings['username_field']][0]);
continue;
}
} else if ($row && intval($iam_settings['periodic_sync']) == 1 && $row['authsource'] == "ldap") {
if ($mapper_key === false){
logMsg("warning", "No matching attribute mapping found for user " . $user[$iam_settings['username_field']][0]);
continue;
}
$mbox_template = $iam_settings['templates'][$mapper_key];
// mailbox user does exist, sync attribtues...
logMsg("info", "Syncing attributes for user " . $user[$iam_settings['username_field']][0]);
mailbox('edit', 'mailbox_from_template', array(
'username' => $user[$iam_settings['username_field']][0],
'name' => $user['displayname'][0],
'template' => $mbox_template
));
} else {
// skip mailbox user
logMsg("info", "Skipping user " . $user[$iam_settings['username_field']][0]);
}
$_SESSION['access_all_exception'] = '0';
sleep(0.025);
}
logMsg("info", "DONE!");
// add last execution time to lock file
$lock_file_handle = fopen($lock_file, 'w');
fwrite($lock_file_handle, getmypid() . "\n" . time());
fclose($lock_file_handle);
session_destroy();

View File

@ -162,10 +162,9 @@ transport_maps = pcre:/opt/postfix/conf/custom_transport.pcre,
proxy:mysql:/opt/postfix/conf/sql/mysql_relay_ne.cf,
proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf
smtp_sasl_auth_soft_bounce = no
postscreen_discard_ehlo_keywords = silent-discard, dsn, chunking
smtpd_discard_ehlo_keywords = chunking, silent-discard
postscreen_discard_ehlo_keywords = chunking, silent-discard, smtputf8, dsn
smtpd_discard_ehlo_keywords = chunking, silent-discard, smtputf8
compatibility_level = 3.7
smtputf8_enable = no
# Define protocols for SMTPS and submission service
submission_smtpd_tls_mandatory_protocols = >=TLSv1.2
smtps_smtpd_tls_mandatory_protocols = >=TLSv1.2

View File

@ -1,6 +1,6 @@
# Whitelist generated by Postwhite v3.4 on Fri Nov 1 00:18:49 UTC 2024
# Whitelist generated by Postwhite v3.4 on Tue Jul 1 00:22:55 UTC 2025
# https://github.com/stevejenkins/postwhite/
# 2013 total rules
# 2105 total rules
2a00:1450:4000::/36 permit
2a01:111:f400::/48 permit
2a01:111:f403:8000::/50 permit
@ -8,37 +8,69 @@
2a01:111:f403::/49 permit
2a01:111:f403:c000::/51 permit
2a01:111:f403:f000::/52 permit
2a01:b747:3000:200::/56 permit
2a01:b747:3001:200::/56 permit
2a01:b747:3002:200::/56 permit
2a01:b747:3003:200::/56 permit
2a01:b747:3004:200::/56 permit
2a01:b747:3005:200::/56 permit
2a01:b747:3006:200::/56 permit
2a02:a60:0:5::/64 permit
2c0f:fb50:4000::/36 permit
2.207.151.53 permit
2.207.217.30 permit
3.64.237.68 permit
3.65.3.180 permit
3.70.123.177 permit
3.72.182.33 permit
3.74.81.189 permit
3.75.33.185 permit
3.93.157.0/24 permit
3.94.40.108 permit
3.129.120.190 permit
3.210.190.0/24 permit
3.211.80.218 permit
3.216.221.67 permit
3.221.209.22 permit
8.20.114.31 permit
8.25.194.0/23 permit
8.25.196.0/23 permit
8.36.116.0/24 permit
8.39.54.0/23 permit
8.39.54.250/31 permit
8.39.144.0/24 permit
8.40.222.0/23 permit
8.40.222.250/31 permit
12.130.86.238 permit
13.107.246.51 permit
13.110.208.0/21 permit
13.110.209.0/24 permit
13.110.216.0/22 permit
13.110.224.0/20 permit
13.111.0.0/16 permit
13.111.191.0/24 permit
13.216.7.111 permit
13.216.54.180 permit
15.200.21.50 permit
15.200.44.248 permit
15.200.201.185 permit
17.41.0.0/16 permit
17.57.155.0/24 permit
17.57.156.0/24 permit
17.58.0.0/16 permit
17.143.234.140/30 permit
17.142.0.0/15 permit
18.97.0.8/30 permit
18.97.1.184/29 permit
18.97.2.64/26 permit
18.156.89.250 permit
18.156.205.64 permit
18.157.243.190 permit
18.158.153.154 permit
18.194.95.56 permit
18.197.217.180 permit
18.198.96.88 permit
18.199.210.3 permit
18.207.52.234 permit
18.208.124.128/25 permit
18.216.232.154 permit
18.235.27.253 permit
@ -51,8 +83,6 @@
20.59.80.4/30 permit
20.63.210.192/28 permit
20.69.8.108/30 permit
20.70.246.20 permit
20.76.201.171 permit
20.83.222.104/30 permit
20.88.157.184/30 permit
20.94.180.64/28 permit
@ -61,14 +91,11 @@
20.98.194.68/30 permit
20.105.209.76/30 permit
20.107.239.64/30 permit
20.112.250.133 permit
20.118.139.208/30 permit
20.141.10.196 permit
20.185.214.0/27 permit
20.185.214.32/27 permit
20.185.214.64/27 permit
20.231.239.246 permit
20.236.44.162 permit
23.103.224.0/19 permit
23.249.208.0/20 permit
23.251.224.0/19 permit
@ -80,6 +107,7 @@
23.253.183.147 permit
23.253.183.148 permit
23.253.183.150 permit
24.110.64.0/18 permit
27.123.204.128/30 permit
27.123.204.132/31 permit
27.123.204.148/30 permit
@ -93,18 +121,52 @@
27.123.206.76/30 permit
27.123.206.80/28 permit
31.25.48.222 permit
31.47.251.17 permit
31.186.239.0/24 permit
34.2.64.0/22 permit
34.2.68.0/23 permit
34.2.70.0/23 permit
34.2.71.64/26 permit
34.2.72.0/22 permit
34.2.75.0/26 permit
34.2.78.0/23 permit
34.2.80.0/23 permit
34.2.82.0/23 permit
34.2.84.0/24 permit
34.2.84.64/26 permit
34.2.85.0/24 permit
34.2.85.64/26 permit
34.2.86.0/23 permit
34.2.88.0/23 permit
34.2.90.0/23 permit
34.2.92.0/23 permit
34.2.94.0/23 permit
34.70.158.162 permit
34.74.74.140 permit
34.83.159.189 permit
34.141.160.224 permit
34.193.58.168 permit
34.195.217.107 permit
34.197.10.50 permit
34.197.254.9 permit
34.198.94.229 permit
34.198.218.121 permit
34.212.163.75 permit
34.215.104.144 permit
34.218.115.239 permit
34.218.116.3 permit
34.225.212.172 permit
35.83.148.184 permit
35.155.198.111 permit
35.158.23.94 permit
35.161.32.253 permit
35.162.73.231 permit
35.167.93.243 permit
35.176.132.251 permit
35.190.247.0/24 permit
35.191.0.0/16 permit
35.205.92.9 permit
35.228.216.85 permit
35.242.169.159 permit
37.188.97.188 permit
37.218.248.47 permit
37.218.249.47 permit
37.218.251.62 permit
@ -116,11 +178,15 @@
40.233.64.216 permit
40.233.83.78 permit
40.233.88.28 permit
43.228.184.0/22 permit
44.206.138.57 permit
44.210.169.44 permit
44.217.45.156 permit
44.236.56.93 permit
44.238.220.251 permit
44.245.243.92 permit
44.246.1.125 permit
44.246.68.102 permit
44.246.77.92 permit
45.14.148.0/22 permit
46.19.170.16 permit
46.226.48.0/21 permit
@ -173,6 +239,7 @@
46.243.88.177 permit
46.243.95.179 permit
46.243.95.180 permit
50.16.246.183 permit
50.18.45.249 permit
50.18.121.236 permit
50.18.121.248 permit
@ -186,14 +253,23 @@
50.56.130.220 permit
50.56.130.221 permit
50.56.130.222 permit
50.112.246.219 permit
52.1.14.157 permit
52.5.230.59 permit
52.12.53.23 permit
52.13.214.179 permit
52.26.1.71 permit
52.27.5.72 permit
52.27.28.47 permit
52.28.63.81 permit
52.28.197.132 permit
52.34.181.151 permit
52.35.192.45 permit
52.36.138.31 permit
52.37.142.146 permit
52.42.203.116 permit
52.50.24.208 permit
52.57.120.243 permit
52.58.216.183 permit
52.59.143.3 permit
52.60.41.5 permit
@ -236,15 +312,16 @@
54.174.63.0/24 permit
54.186.193.102 permit
54.191.223.56 permit
54.211.126.101 permit
54.213.20.246 permit
54.214.39.184 permit
54.240.0.0/18 permit
54.240.64.0/19 permit
54.240.96.0/19 permit
54.240.64.0/18 permit
54.241.16.209 permit
54.244.54.130 permit
54.244.242.0/24 permit
54.255.61.23 permit
56.124.6.228 permit
57.103.64.0/18 permit
62.13.128.0/24 permit
62.13.129.128/25 permit
@ -260,6 +337,9 @@
63.128.21.0/24 permit
63.143.57.128/25 permit
63.143.59.128/25 permit
63.176.194.123 permit
63.178.132.221 permit
63.178.143.178 permit
64.18.0.0/20 permit
64.20.241.45 permit
64.69.212.0/24 permit
@ -328,8 +408,8 @@
65.154.166.0/24 permit
65.212.180.36 permit
66.102.0.0/20 permit
66.102.0.0/21 permit
66.119.150.192/26 permit
66.162.193.226/31 permit
66.163.184.0/24 permit
66.163.185.0/24 permit
66.163.186.0/24 permit
@ -445,6 +525,7 @@
69.171.244.0/23 permit
70.37.151.128/25 permit
70.42.149.35 permit
72.3.185.0/24 permit
72.14.192.0/18 permit
72.21.192.0/19 permit
72.21.217.142 permit
@ -505,6 +586,9 @@
72.30.239.228/31 permit
72.30.239.244/30 permit
72.30.239.248/31 permit
72.32.154.0/24 permit
72.32.217.0/24 permit
72.32.243.0/24 permit
72.52.72.32/28 permit
74.6.128.0/24 permit
74.6.129.0/24 permit
@ -531,7 +615,6 @@
74.86.241.250/31 permit
74.112.67.243 permit
74.125.0.0/16 permit
74.202.227.40 permit
74.208.4.200 permit
74.208.4.201 permit
74.208.4.220 permit
@ -620,6 +703,7 @@
89.22.108.0/24 permit
91.211.240.0/22 permit
94.169.2.0/23 permit
94.236.119.0/26 permit
94.245.112.0/27 permit
94.245.112.10/31 permit
95.131.104.0/21 permit
@ -1114,12 +1198,12 @@
98.139.245.212/31 permit
99.78.197.208/28 permit
99.83.190.102 permit
103.2.140.0/22 permit
103.9.96.0/22 permit
103.28.42.0/24 permit
103.47.204.0/22 permit
103.122.78.238 permit
103.151.192.0/23 permit
103.168.172.128/27 permit
103.237.104.0/22 permit
104.43.243.237 permit
104.44.112.128/25 permit
104.47.0.0/17 permit
@ -1251,6 +1335,7 @@
106.50.16.0/28 permit
107.20.18.111 permit
107.20.210.250 permit
107.22.191.150 permit
108.174.0.0/24 permit
108.174.0.215 permit
108.174.3.0/24 permit
@ -1336,7 +1421,8 @@
129.213.195.191 permit
130.61.9.72 permit
130.162.39.83 permit
130.211.0.0/22 permit
130.248.172.0/24 permit
130.248.173.0/24 permit
131.253.30.0/24 permit
131.253.121.0/26 permit
132.145.13.209 permit
@ -1356,6 +1442,8 @@
136.143.160.0/24 permit
136.143.161.0/24 permit
136.143.162.0/24 permit
136.143.176.0/24 permit
136.143.177.0/24 permit
136.143.178.49 permit
136.143.182.0/23 permit
136.143.184.0/24 permit
@ -1410,6 +1498,8 @@
146.20.215.0/24 permit
146.20.215.182 permit
146.88.28.0/24 permit
146.148.116.76 permit
147.154.32.0/25 permit
147.243.1.47 permit
147.243.1.48 permit
147.243.1.153 permit
@ -1450,7 +1540,6 @@
157.151.208.65 permit
157.255.1.64/29 permit
158.101.211.207 permit
158.120.80.0/21 permit
158.247.16.0/20 permit
159.92.154.0/24 permit
159.92.155.0/24 permit
@ -1473,20 +1562,31 @@
159.135.224.0/20 permit
159.135.228.10 permit
159.183.0.0/16 permit
159.183.68.71 permit
159.183.79.38 permit
160.1.62.192 permit
161.38.192.0/20 permit
161.38.204.0/22 permit
161.71.32.0/19 permit
161.71.64.0/20 permit
162.88.4.0/23 permit
162.88.8.0/24 permit
162.88.24.0/24 permit
162.88.25.0/24 permit
162.88.36.0/24 permit
162.247.216.0/22 permit
163.47.180.0/22 permit
163.114.130.16 permit
163.114.132.120 permit
163.114.134.16 permit
163.114.135.16 permit
163.116.128.0/17 permit
164.152.23.32 permit
164.152.25.241 permit
164.177.132.168/30 permit
165.173.128.0/24 permit
165.173.180.250/31 permit
165.173.182.250/31 permit
166.78.68.0/22 permit
166.78.68.221 permit
166.78.69.169 permit
@ -1511,6 +1611,7 @@
168.138.5.36 permit
168.138.73.51 permit
168.138.77.31 permit
168.138.237.153 permit
168.245.0.0/17 permit
168.245.12.252 permit
168.245.46.9 permit
@ -1520,7 +1621,8 @@
169.148.142.10 permit
169.148.144.0/25 permit
169.148.144.10 permit
170.10.68.0/22 permit
169.148.146.0/23 permit
169.148.188.182 permit
170.10.128.0/24 permit
170.10.129.0/24 permit
170.10.132.56/29 permit
@ -1561,6 +1663,7 @@
183.240.219.64/29 permit
185.4.120.0/22 permit
185.12.80.0/22 permit
185.28.196.0/22 permit
185.58.84.93 permit
185.80.93.204 permit
185.80.93.227 permit
@ -1626,6 +1729,8 @@
192.18.139.154 permit
192.18.145.36 permit
192.18.152.58 permit
192.28.128.0/18 permit
192.29.103.128/25 permit
192.30.252.0/22 permit
192.161.144.0/20 permit
192.162.87.0/24 permit
@ -1643,6 +1748,21 @@
193.123.56.63 permit
194.19.134.0/25 permit
194.64.234.129 permit
194.97.196.0/24 permit
194.97.196.3 permit
194.97.196.4 permit
194.97.196.11 permit
194.97.196.12 permit
194.97.204.0/24 permit
194.97.204.3 permit
194.97.204.4 permit
194.97.204.11 permit
194.97.204.12 permit
194.97.212.0/24 permit
194.97.212.3 permit
194.97.212.4 permit
194.97.212.11 permit
194.97.212.12 permit
194.106.220.0/23 permit
194.113.24.0/22 permit
194.154.193.192/27 permit
@ -1651,14 +1771,6 @@
195.234.109.226 permit
195.245.230.0/23 permit
198.2.128.0/18 permit
198.2.128.0/24 permit
198.2.132.0/22 permit
198.2.136.0/23 permit
198.2.145.0/24 permit
198.2.177.0/24 permit
198.2.178.0/23 permit
198.2.180.0/24 permit
198.2.186.0/23 permit
198.21.0.0/21 permit
198.37.144.0/20 permit
198.37.152.186 permit
@ -1667,12 +1779,21 @@
198.61.254.231 permit
198.178.234.57 permit
198.244.48.0/20 permit
198.244.56.107 permit
198.244.56.108 permit
198.244.56.109 permit
198.244.56.111 permit
198.244.56.112 permit
198.244.56.113 permit
198.244.56.114 permit
198.244.56.115 permit
198.244.59.30 permit
198.244.59.33 permit
198.244.59.35 permit
198.244.60.0/22 permit
198.245.80.0/20 permit
198.245.81.0/24 permit
199.15.212.0/22 permit
199.15.213.187 permit
199.15.226.37 permit
199.16.156.0/22 permit
@ -1687,6 +1808,7 @@
199.67.84.0/24 permit
199.67.86.0/24 permit
199.67.88.0/24 permit
199.67.90.0/24 permit
199.101.161.130 permit
199.101.162.0/25 permit
199.122.120.0/21 permit
@ -1698,7 +1820,6 @@
202.165.102.47 permit
202.177.148.100 permit
202.177.148.110 permit
203.31.36.0/22 permit
203.32.4.25 permit
203.55.21.0/24 permit
203.81.17.0/24 permit
@ -1744,19 +1865,15 @@
204.92.114.187 permit
204.92.114.203 permit
204.92.114.204/31 permit
204.141.32.0/23 permit
204.141.42.0/23 permit
204.216.164.202 permit
204.220.160.0/21 permit
204.220.168.0/21 permit
204.220.176.0/20 permit
204.220.181.105 permit
204.232.168.0/24 permit
205.139.110.0/24 permit
205.201.128.0/20 permit
205.201.131.128/25 permit
205.201.134.128/25 permit
205.201.136.0/23 permit
205.201.137.229 permit
205.201.139.0/24 permit
205.207.104.0/22 permit
205.220.167.17 permit
205.220.167.98 permit
@ -1784,7 +1901,6 @@
207.46.132.128/27 permit
207.46.198.0/25 permit
207.46.200.0/27 permit
207.58.147.64/28 permit
207.67.38.0/24 permit
207.67.98.192/27 permit
207.68.176.0/26 permit
@ -1831,6 +1947,8 @@
208.74.204.5 permit
208.74.204.9 permit
208.75.120.0/22 permit
208.76.62.0/24 permit
208.76.63.0/24 permit
208.82.237.96/29 permit
208.82.237.104/31 permit
208.82.238.96/29 permit
@ -1930,8 +2048,8 @@
213.199.177.0/26 permit
216.17.150.242 permit
216.17.150.251 permit
216.22.15.224/27 permit
216.24.224.0/20 permit
216.27.86.152/31 permit
216.39.60.154/31 permit
216.39.60.156/30 permit
216.39.60.160/30 permit
@ -1969,11 +2087,16 @@
216.99.5.68 permit
216.109.114.32/27 permit
216.109.114.64/29 permit
216.113.162.65 permit
216.113.163.65 permit
216.128.126.97 permit
216.136.162.65 permit
216.136.162.120/29 permit
216.136.168.80/28 permit
216.139.64.0/19 permit
216.145.221.0/24 permit
216.146.32.0/24 permit
216.146.33.0/24 permit
216.198.0.0/18 permit
216.203.30.55 permit
216.203.33.178/31 permit
@ -1993,6 +2116,21 @@
2001:0868:0100:0600::/64 permit
2001:4860:4000::/36 permit
2001:748:100:40::2:0/112 permit
2001:748:400:1300::3 permit
2001:748:400:1300::4 permit
2001:748:400:1301::0/64 permit
2001:748:400:1301::3 permit
2001:748:400:1301::4 permit
2001:748:400:2300::3 permit
2001:748:400:2300::4 permit
2001:748:400:2301::0/64 permit
2001:748:400:2301::3 permit
2001:748:400:2301::4 permit
2001:748:400:3300::3 permit
2001:748:400:3300::4 permit
2001:748:400:3301::0/64 permit
2001:748:400:3301::3 permit
2001:748:400:3301::4 permit
2404:6800:4000::/36 permit
2603:1010:3:3::5b permit
2603:1020:201:10::10f permit
@ -2001,6 +2139,7 @@
2603:1030:c02:8::14 permit
2607:13c0:0001:0000:0000:0000:0000:7000/116 permit
2607:13c0:0002:0000:0000:0000:0000:1000/116 permit
2607:13c0:0004:0000:0000:0000:0000:0000/116 permit
2607:f8b0:4000::/36 permit
2620:109:c003:104::/64 permit
2620:109:c003:104::215 permit

12
data/conf/redis/redis-conf.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh
cat <<EOF > /redis.conf
requirepass $REDISPASS
user quota_notify on nopass ~QW_* -@all +get +hget +ping
EOF
if [ -n "$REDISMASTERPASS" ]; then
echo "masterauth $REDISMASTERPASS" >> /redis.conf
fi
exec redis-server /redis.conf

View File

@ -24,7 +24,6 @@
/.+\.guru$/i
/.+\.icu$/i
/.+\.id$/i
/.+\.info$/i
/.+\.in.net$/i
/.+\.ir$/i
/.+\.jetzt$/i

View File

@ -25,6 +25,7 @@ catch (PDOException $e) {
// Init Redis
$redis = new Redis();
$redis->connect('redis-mailcow', 6379);
$redis->auth(getenv("REDISPASS"));
function parse_email($email) {
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;

View File

@ -4,6 +4,7 @@ ini_set('error_reporting', 0);
$redis = new Redis();
$redis->connect('redis-mailcow', 6379);
$redis->auth(getenv("REDISPASS"));
function in_net($addr, $net) {
$net = explode('/', $net);

View File

@ -8,7 +8,7 @@ VIRUS_FOUND {
}
# Bad policy from free mail providers
FREEMAIL_POLICY_FAILURE {
expression = "FREEMAIL_FROM & !DMARC_POLICY_ALLOW & !MAILLIST& !WHITELISTED_FWD_HOST & -g+:policies";
expression = "FREEMAIL_FROM & !DMARC_POLICY_ALLOW & !MAILLIST & !WHITELISTED_FWD_HOST & -g+:policies";
score = 16.0;
}
# Applies to freemail with undisclosed recipients

View File

@ -1,12 +0,0 @@
oletools {
# default olefy settings
servers = "olefy:10055";
# needs to be set explicitly for Rspamd < 1.9.5
scan_mime_parts = true;
# mime-part regex matching in content-type or filename
# block all macros
extended = true;
max_size = 3145728;
timeout = 20.0;
retransmits = 1;
}

View File

@ -3,6 +3,7 @@ dns {
}
map_watch_interval = 30s;
task_timeout = 30s;
enable_mime_utf = true;
disable_monitoring = true;
# In case a task times out (like DNS lookup), soft reject the message
# instead of silently accepting the message without further processing.

View File

@ -1,2 +0,0 @@
servers = "redis:6379";
timeout = 10;

View File

@ -24,6 +24,7 @@ catch (PDOException $e) {
// Init Redis
$redis = new Redis();
$redis->connect('redis-mailcow', 6379);
$redis->auth(getenv("REDISPASS"));
// Functions
function parse_email($email) {
@ -96,10 +97,10 @@ $rcpt_final_mailboxes = array();
foreach (json_decode($rcpts, true) as $rcpt) {
// Remove tag
$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
// Break rcpt into local part and domain part
$parsed_rcpt = parse_email($rcpt);
// Skip if not a mailcow handled domain
try {
if (!$redis->hGet('DOMAIN_MAP', $parsed_rcpt['domain'])) {
@ -243,7 +244,7 @@ foreach ($rcpt_final_mailboxes as $rcpt_final) {
WHERE `rcpt` = :rcpt2
ORDER BY id DESC
LIMIT :retention_size
) x
) x
);');
$stmt->execute(array(
':rcpt' => $rcpt_final,

View File

@ -14,6 +14,7 @@ try {
else {
$redis->connect('redis-mailcow', 6379);
}
$redis->auth(getenv("REDISPASS"));
}
catch (Exception $e) {
exit;

View File

@ -24,6 +24,7 @@ catch (PDOException $e) {
// Init Redis
$redis = new Redis();
$redis->connect('redis-mailcow', 6379);
$redis->auth(getenv("REDISPASS"));
// Functions
function parse_email($email) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY st0 "fill:#50BD37;">
]>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="640px" height="350px" viewBox="78.712 58.488 640 350" style="enable-background:new 78.712 58.488 640 350;"
xml:space="preserve">
<path style="&st0;" d="M648.541,145.679c-9.947,0-17.009-7.278-17.009-17.048c0-9.777,7.062-17.057,17.009-17.057
c10.024,0,17.086,7.279,17.086,17.057C665.627,138.401,658.565,145.679,648.541,145.679z M648.511,94.893
c-19.693,0-33.679,14.4-33.679,33.738c0,19.33,13.985,33.729,33.679,33.729c19.822,0,33.808-14.4,33.808-33.729
C682.318,109.293,668.333,94.893,648.511,94.893z M648.482,179.843c-29.889,0-51.123-21.868-51.123-51.212
c0-29.353,21.234-51.209,51.123-51.209c30.082,0,51.307,21.856,51.307,51.209C699.789,157.975,678.564,179.843,648.482,179.843z
M648.442,58.488c-40.929,0-69.995,29.946-69.995,70.143c0,40.189,29.066,70.125,69.995,70.125c41.194,0,70.27-29.937,70.27-70.125
C718.712,88.434,689.637,58.488,648.442,58.488z M158.166,183.902l-21.018-5.008c-19.131-4.396-28.849-9.413-28.849-23.21
c0-15.684,15.99-21.965,30.419-21.965c14.667,0,25.382,7.329,31.693,18.737c0.02,0.048,0.051,0.097,0.09,0.157
c0.127,0.247,0.276,0.484,0.403,0.731l0.03-0.02c1.985,3.002,5.323,5.008,8.919,5.008c6.122,0,10.558-4.425,10.558-10.547
c0-2.341-0.504-4.82-1.601-6.688c-10.764-18.302-28.513-26.192-48.838-26.192c-27.594,0-54.262,13.797-54.262,44.218
c0,27.921,27.605,36.079,37.64,38.578l20.069,4.71c15.368,3.763,27.912,8.791,27.912,23.517c0,16.938-17.561,23.943-34.499,23.943
c-17.245,0-30.015-9.37-38.814-22.37h-0.01c-1.956-3-4.988-4.328-8.702-4.328c-5.984,0-10.805,5.185-10.587,11.162
c0.098,2.438,0.909,4.637,2.153,6.405c13.787,20.633,33.728,28.41,55.96,28.41c28.543,0,57.085-13.143,57.085-45.132
C193.918,203.325,178.551,188.613,158.166,183.902z M298.479,250.312c-33.866,0-55.199-25.403-55.199-58.331
c0-32.939,21.333-58.343,55.199-58.343c34.192,0,55.516,25.403,55.516,58.343C353.996,224.91,332.672,250.312,298.479,250.312z
M298.479,114.823c-45.471,0-77.777,32.93-77.777,77.158c0,44.217,32.306,77.146,77.777,77.146
c45.786,0,78.093-32.929,78.093-77.146C376.572,147.753,344.266,114.823,298.479,114.823z M518.715,234.312
c-0.771,0.74-1.549,1.472-2.399,2.175c-1.106,1.014-2.391,2.112-3.854,3.208c-8.829,6.391-19.979,10.094-33.017,10.094
c-33.876,0-55.198-25.402-55.198-58.332c0-32.939,21.322-58.342,55.198-58.342c34.183,0,55.506,25.403,55.506,58.342
C534.951,208.653,529.135,223.774,518.715,234.312z M468.097,317.938c2.528,0,5.146-0.168,7.863-0.504
c5.018-0.631,9.588-0.909,13.729-0.909c19.24,0.109,29.036,5.7,34.943,12.158c5.895,6.499,8.168,15.311,8.158,22.796
c0.01,3.586-0.555,6.795-1.177,8.721c-2.944,8.93-8.888,15.002-17.996,19.576c-9.035,4.484-21.095,6.777-33.707,6.757
c-4.514,0-9.105-0.288-13.639-0.831c-8.573-0.987-19.911-4.671-28.13-11.093c-4.138-3.199-6.458-6.991-8.858-11.485
c-2.379-4.514-2.783-9.748-2.783-16.442v-0.742c0-12.346,4.84-20.544,11.051-26.5c3.07-2.904,5.69-5.064,7.99-6.438
c0.366-0.218,0.438-0.416,0.755-0.593C452.39,316.014,459.684,317.968,468.097,317.938z M479.445,114.301
c-45.471,0-77.786,32.929-77.786,77.157c0,29.887,14.765,54.598,38.378,67.489c-0.314,0.314-0.621,0.641-0.916,0.966
c-6.104,6.687-9.226,15.25-9.236,23.913c-0.008,3.821,0.624,7.741,1.977,11.494c-3.062,1.956-6.717,4.634-10.46,8.147
c-9.026,8.408-18.734,22.541-19.021,42.097c-0.01,0.454-0.01,0.829-0.01,1.118c-0.01,10.071,2.379,19.157,6.459,26.774
c6.133,11.466,15.683,19.445,25.539,24.77c9.917,5.334,20.257,8.166,29.273,9.274c5.373,0.643,10.826,0.988,16.268,0.988
c15.151-0.02,30.261-2.578,43.409-9.019c13.085-6.34,24.333-17.253,29.192-32.562c1.443-4.553,2.212-9.719,2.231-15.428
c-0.02-11.595-3.349-25.759-13.767-37.452c-10.421-11.734-27.654-19.566-51.288-19.459c-5.138,0-10.606,0.356-16.426,1.078
c-1.877,0.227-3.596,0.334-5.166,0.334c-7.239-0.048-10.872-2.053-13.036-4.098c-2.133-2.084-3.2-4.839-3.229-8.058
c-0.01-3.28,1.284-6.727,3.467-9.078c2.231-2.332,5.008-3.91,9.846-3.97c0.436,0,0.9,0.01,1.374,0.05
c3.101,0.216,6.112,0.325,9.037,0.325c24.188,0.047,42.38-7.448,54.756-17.759c12.415-10.312,18.971-22.854,22.071-32.76l-0.04-0.01
c3.37-8.899,5.197-18.715,5.197-29.166C557.539,147.229,525.234,114.301,479.445,114.301z"/>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY st0 "fill:#50BD37;">
]>
<svg version="1.1" id="SOGo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="140.263px" height="140.269px" viewBox="499.737 0 140.263 140.269"
style="enable-background:new 499.737 0 140.263 140.269;" xml:space="preserve">
<path style="&st0;" d="M569.697,87.024c-9.928,0-16.975-7.264-16.975-17.017c0-9.757,7.047-17.022,16.975-17.022
c10.006,0,17.054,7.265,17.054,17.022C586.751,79.76,579.703,87.024,569.697,87.024z M569.667,36.335
c-19.657,0-33.614,14.372-33.614,33.673c0,19.294,13.955,33.667,33.614,33.667c19.787,0,33.745-14.372,33.745-33.667
C603.411,50.707,589.454,36.335,569.667,36.335z M569.639,121.123c-29.833,0-51.025-21.825-51.025-51.115
c0-29.296,21.192-51.111,51.025-51.111c30.025,0,51.213,21.815,51.213,51.111C620.852,99.298,599.664,121.123,569.639,121.123z
M569.602,0c-40.854,0-69.864,29.889-69.864,70.007c0,40.112,29.01,69.993,69.864,69.993c41.116,0,70.136-29.88,70.136-69.993
C639.737,29.889,610.719,0,569.602,0z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,3 +1,21 @@
// redirect to mailcow login form
document.addEventListener('DOMContentLoaded', function () {
var loginForm = document.forms.namedItem("loginForm");
if (loginForm) {
window.location.href = '/user';
}
});
// logout function
function mc_logout() {
fetch("/", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: "logout=1"
}).then(() => window.location.href = '/');
}
// Custom SOGo JS
// Change the visible font-size in the editor, this does not change the font of a html message by default
@ -5,3 +23,4 @@ CKEDITOR.addCss("body {font-size: 16px !important}");
// Enable scayt by default
//CKEDITOR.config.scayt_autoStartup = true;

View File

@ -1,28 +1,34 @@
<!--
<example>
<key>canAuthenticate</key>
<string>YES</string>
<key>id</key>
<string>${line}_ldap</string>
<key>isAddressBook</key>
<string>NO</string>
<key>IDFieldName</key>
<string>mail</string>
<key>UIDFieldName</key>
<string>uid</string>
<key>bindFields</key>
<array>
<string>mail</string>
</array>
<key>type</key>
<string>ldap</string>
<key>bindDN</key>
<string>cn=admin,dc=example,dc=local</string>
<key>bindPassword</key>
<string>password</string>
<key>baseDN</key>
<string>ou=People,dc=example,dc=local</string>
<key>hostname</key>
<string>ldap://1.2.3.4:389</string>
</example>
-->
#!/bin/bash
domain="$1"
gal_status="$2"
echo "
<!--
<example>
<key>canAuthenticate</key>
<string>YES</string>
<key>id</key>
<string>"${domain}"_ldap</string>
<key>isAddressBook</key>
<string>"${gal_status}"</string>
<key>IDFieldName</key>
<string>mail</string>
<key>UIDFieldName</key>
<string>uid</string>
<key>bindFields</key>
<array>
<string>mail</string>
</array>
<key>type</key>
<string>ldap</string>
<key>bindDN</key>
<string>cn=admin,dc=example,dc=local</string>
<key>bindPassword</key>
<string>password</string>
<key>baseDN</key>
<string>ou=People,dc=example,dc=local</string>
<key>hostname</key>
<string>ldap://1.2.3.4:389</string>
</example>
-->"

View File

@ -16,6 +16,12 @@
SOGoFoldersSendEMailNotifications = YES;
SOGoForwardEnabled = YES;
// Added with SOGo 5.12 - Allows users to cleanup there maildirectories by deleting mails oder than X
SOGoEnableMailCleaning = YES;
// Fixes "MODIFICATION_FAILED" error (HTTP 412) in Clients when accepting invitations from external services
SOGoDisableOrganizerEventCheck = YES;
// Option to set Users as admin to globally manage calendar permissions etc. Disabled by default
// SOGoSuperUsernames = ("moo@example.com");
@ -88,7 +94,7 @@
//SoDebugBaseURL = YES;
//ImapDebugEnabled = YES;
//SOGoEASDebugEnabled = YES;
SOGoEASSearchInBody = YES; // Experimental. Enabled since 2023-10
SOGoEASSearchInBody = YES;
//LDAPDebugEnabled = YES;
//PGDebugEnabled = YES;
//MySQL4DebugEnabled = YES;

Some files were not shown because too many files have changed in this diff Show More