public
nobgit
read
NobMail
Based on mailcow: dockerized
Languages
Repository composition by tracked source files.
PHP
49%
JavaScript
35%
HTML
9%
CSS
4%
Shell
2%
Python
1%
Lua
0%
Perl
0%
Ruby
0%
SCSS
0%
Create file
Wiki Documentation
Clone
https://nobgit.com/orgs/nobgit/nobmail.git
ssh://[email protected]:2222/orgs/nobgit/nobmail.git
Commit
[Ejabberd] More fixes for Ejabberd integration (WIP)
f2453e31
.../ejabberd/mailcowCommandExecutor.php | 8 +-
data/Dockerfiles/phpfpm/docker-entrypoint.sh | 3 +
data/web/inc/functions.mailbox.inc.php | 4 +-
data/web/inc/functions.xmpp.inc.php | 120 ++++++++++++++++-----
data/web/lang/lang.de.json | 2 +
data/web/lang/lang.en.json | 2 +
docker-compose.yml | 14 +--
7 files changed, 118 insertions(+), 35 deletions(-)
Diff
diff --git a/data/Dockerfiles/ejabberd/mailcowCommandExecutor.php b/data/Dockerfiles/ejabberd/mailcowCommandExecutor.php
index 2ff33610..390783af 100644
--- a/data/Dockerfiles/ejabberd/mailcowCommandExecutor.php
+++ b/data/Dockerfiles/ejabberd/mailcowCommandExecutor.php
@@ -98,16 +98,16 @@ class mailcowCommandExecutor implements CommandExecutorInterface {
return hash_equals(hash($scheme, $password, true), $hash);
case "SMD5":
- return verify_salted_hash($hash, $password, 'md5', 16);
+ return self::verify_salted_hash($hash, $password, 'md5', 16);
case "SSHA":
- return verify_salted_hash($hash, $password, 'sha1', 20);
+ return self::verify_salted_hash($hash, $password, 'sha1', 20);
case "SSHA256":
- return verify_salted_hash($hash, $password, 'sha256', 32);
+ return self::verify_salted_hash($hash, $password, 'sha256', 32);
case "SSHA512":
- return verify_salted_hash($hash, $password, 'sha512', 64);
+ return self::verify_salted_hash($hash, $password, 'sha512', 64);
default:
return false;
diff --git a/data/Dockerfiles/phpfpm/docker-entrypoint.sh b/data/Dockerfiles/phpfpm/docker-entrypoint.sh
index 3c9039ae..c880fbf5 100755
--- a/data/Dockerfiles/phpfpm/docker-entrypoint.sh
+++ b/data/Dockerfiles/phpfpm/docker-entrypoint.sh
@@ -172,6 +172,9 @@ fi
# Fix permissions for global filters
chown -R 82:82 /global_sieve/*
+[[ ! -f /etc/nginx/conf.d/ejabberd.conf ]] && echo '# Autogenerated by mailcow' > /etc/nginx/conf.d/ejabberd.conf
+chown 82:82 /etc/nginx/conf.d/ejabberd.conf
+
# Run hooks
for file in /hooks/*; do
if [ -x "${file}" ]; then
diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php
index 1b7eb183..a2f181c4 100644
--- a/data/web/inc/functions.mailbox.inc.php
+++ b/data/web/inc/functions.mailbox.inc.php
@@ -443,7 +443,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
}
$domain = idn_to_ascii(strtolower(trim($_data['domain'])), 0, INTL_IDNA_VARIANT_UTS46);
$description = $_data['description'];
- $xmpp_prefix = $_data['xmpp_prefix'];
+ $xmpp_prefix = preg_replace('/[^\da-z-]/i', '', $_data['xmpp_prefix']);
if (empty($description)) {
$description = $domain;
}
@@ -2115,6 +2115,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
continue;
}
+ $xmpp_prefix = preg_replace('/[^\da-z-]/i', '', $xmpp_prefix);
$stmt = $pdo->prepare("UPDATE `domain` SET
`description` = :description,
`gal` = :gal,
@@ -2167,6 +2168,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
continue;
}
+ $xmpp_prefix = preg_replace('/[^\da-z-]/i', '', $xmpp_prefix);
// todo: should be using api here
$stmt = $pdo->prepare("SELECT
COUNT(*) AS count,
diff --git a/data/web/inc/functions.xmpp.inc.php b/data/web/inc/functions.xmpp.inc.php
index c6c5133d..38ac406b 100644
--- a/data/web/inc/functions.xmpp.inc.php
+++ b/data/web/inc/functions.xmpp.inc.php
@@ -128,55 +128,104 @@ function xmpp_rebuild_configs() {
touch('/ejabberd/ejabberd_hosts.yml');
touch('/ejabberd/ejabberd_acl.yml');
+ touch('/etc/nginx/conf.d/ejabberd.conf');
$ejabberd_hosts_md5 = md5_file('/ejabberd/ejabberd_hosts.yml');
$ejabberd_acl_md5 = md5_file('/ejabberd/ejabberd_acl.yml');
+ $ejabberd_site_md5 = md5_file('/etc/nginx/conf.d/ejabberd.conf');
if (!empty($xmpp_domains)) {
// Handle hosts file
- $map_handle = fopen('/ejabberd/ejabberd_hosts.yml', 'w');
- if (!$map_handle) {
+ $hosts_handle = fopen('/ejabberd/ejabberd_hosts.yml', 'w');
+ if (!$hosts_handle) {
throw new Exception($lang['danger']['file_open_error']);
}
- fwrite($map_handle, '# Autogenerated by mailcow' . PHP_EOL);
- fwrite($map_handle, 'hosts:' . PHP_EOL);
+ fwrite($hosts_handle, '# Autogenerated by mailcow' . PHP_EOL);
+ fwrite($hosts_handle, 'hosts:' . PHP_EOL);
foreach ($xmpp_domains as $domain => $domain_values) {
- fwrite($map_handle, ' - ' . $xmpp_domains[$domain]['xmpp_host'] . PHP_EOL);
+ fwrite($hosts_handle, ' - ' . $xmpp_domains[$domain]['xmpp_host'] . PHP_EOL);
}
- fclose($map_handle);
+ fclose($hosts_handle);
// Handle ACL file
- $map_handle = fopen('/ejabberd/ejabberd_acl.yml', 'w');
- if (!$map_handle) {
+ $acl_handle = fopen('/ejabberd/ejabberd_acl.yml', 'w');
+ if (!$acl_handle) {
throw new Exception($lang['danger']['file_open_error']);
}
- fwrite($map_handle, '# Autogenerated by mailcow' . PHP_EOL);
- fwrite($map_handle, 'append_host_config:' . PHP_EOL);
+ fwrite($acl_handle, '# Autogenerated by mailcow' . PHP_EOL);
+ fwrite($acl_handle, 'append_host_config:' . PHP_EOL);
foreach ($xmpp_domains as $domain => $domain_values) {
- fwrite($map_handle, ' ' . $xmpp_domains[$domain]['xmpp_host'] . ':' . PHP_EOL);
- fwrite($map_handle, ' acl:' . PHP_EOL);
- fwrite($map_handle, ' admin:' . PHP_EOL);
- fwrite($map_handle, ' user:' . PHP_EOL);
+ fwrite($acl_handle, ' ' . $xmpp_domains[$domain]['xmpp_host'] . ':' . PHP_EOL);
+ fwrite($acl_handle, ' acl:' . PHP_EOL);
+ fwrite($acl_handle, ' admin:' . PHP_EOL);
+ fwrite($acl_handle, ' user:' . PHP_EOL);
foreach ($xmpp_domains[$domain]['xmpp_admins'] as $xmpp_admin) {
- fwrite($map_handle, ' - ' . $xmpp_admin . PHP_EOL);
+ fwrite($acl_handle, ' - ' . $xmpp_admin . PHP_EOL);
}
}
- fclose($map_handle);
+ fclose($acl_handle);
+
+ // Handle Nginx site
+ $site_handle = @fopen('/etc/nginx/conf.d/ejabberd.conf', 'r+');
+ if ($site_handle !== false) {
+ ftruncate($site_handle, 0);
+ fclose($site_handle);
+ }
+ $site_handle = fopen('/etc/nginx/conf.d/ejabberd.conf', 'w');
+ if (!$site_handle) {
+ throw new Exception($lang['danger']['file_open_error']);
+ }
+ fwrite($site_handle, '# Autogenerated by mailcow' . PHP_EOL);
+ foreach ($xmpp_domains as $domain => $domain_values) {
+ $site_config = <<<EOF
+server {
+ root /web;
+
+ listen 80;
+ listen [::]:80;
+
+ server_name *.%s %s;
+
+ if (\$request_uri ~* "%%0A|%%0D") {
+ return 403;
+ }
+
+ 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;
+
+ location / {
+ proxy_pass http://ejabberd:5281/;
+ 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;
+ }
+}
+
+EOF;
+ fwrite($site_handle, sprintf($site_config, $xmpp_domains[$domain]['xmpp_host'], $xmpp_domains[$domain]['xmpp_host']));
+ }
+ fclose($site_handle);
}
else {
// Write empty hosts file
- $map_handle = fopen('/ejabberd/ejabberd_hosts.yml', 'w');
- if (!$map_handle) {
+ $hosts_handle = fopen('/ejabberd/ejabberd_hosts.yml', 'w');
+ if (!$hosts_handle) {
throw new Exception($lang['danger']['file_open_error']);
}
- fwrite($map_handle, '# Autogenerated by mailcow' . PHP_EOL);
- fclose($map_handle);
+ fwrite($hosts_handle, '# Autogenerated by mailcow' . PHP_EOL);
+ fclose($hosts_handle);
+
// Write empty ACL file
- $map_handle = fopen('/ejabberd/ejabberd_acl.yml', 'w');
- if (!$map_handle) {
+ $acl_handle = fopen('/ejabberd/ejabberd_acl.yml', 'w');
+ if (!$acl_handle) {
throw new Exception($lang['danger']['file_open_error']);
}
- fwrite($map_handle, '# Autogenerated by mailcow' . PHP_EOL);
- fclose($map_handle);
+ fwrite($acl_handle, '# Autogenerated by mailcow' . PHP_EOL);
+ fclose($acl_handle);
}
if (md5_file('/ejabberd/ejabberd_acl.yml') != $ejabberd_acl_md5) {
@@ -196,6 +245,29 @@ function xmpp_rebuild_configs() {
);
}
+ if (md5_file('/etc/nginx/conf.d/ejabberd.conf') != $ejabberd_site_md5) {
+ $response = json_decode(docker('post', 'nginx-mailcow', 'exec', array("cmd" => "reload", "task" => "nginx"), 'Content-type: application/json'), true);
+ if (isset($response['type']) && $response['type'] == "success") {
+ $_SESSION['return'][] = array(
+ 'type' => 'success',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'nginx_reloaded'
+ );
+ }
+ else {
+ if (!empty($response['msg'])) {
+ $error = $response['msg'];
+ }
+ else {
+ $error = '-';
+ }
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('nginx_reload_failed', htmlspecialchars($error))
+ );
+ }
+ }
}
catch (Exception $e) {
$_SESSION['return'][] = array(
diff --git a/data/web/lang/lang.de.json b/data/web/lang/lang.de.json
index 3a5b9320..2f99e9b0 100644
--- a/data/web/lang/lang.de.json
+++ b/data/web/lang/lang.de.json
@@ -396,6 +396,7 @@
"max_quota_in_use": "Mailbox-Speicherplatzlimit muss größer oder gleich %d MiB sein",
"maxquota_empty": "Max. Speicherplatz pro Mailbox darf nicht 0 sein.",
"mysql_error": "MySQL-Fehler: %s",
+ "nginx_reload_failed": "Nginx Reload ist fehlgeschlagen: %s",
"network_host_invalid": "Netzwerk oder Host ungültig: %s",
"next_hop_interferes": "%s verhindert das Hinzufügen von Next Hop %s",
"next_hop_interferes_any": "Ein vorhandener Eintrag verhindert das Hinzufügen von Next Hop %s",
@@ -893,6 +894,7 @@
"mailbox_added": "Mailbox %s wurde angelegt",
"mailbox_modified": "Änderungen an Mailbox %s wurden gespeichert",
"mailbox_removed": "Mailbox %s wurde entfernt",
+ "nginx_reloaded": "Nginx wurde neu geladen",
"object_modified": "Änderungen an Objekt %s wurden gespeichert",
"pushover_settings_edited": "Pushover Konfiguration gespeichert, bitte den Zugang im Anschluss verifizieren.",
"qlearn_spam": "Nachricht ID %s wurde als Spam gelernt und gelöscht",
diff --git a/data/web/lang/lang.en.json b/data/web/lang/lang.en.json
index 41458e1f..92559969 100644
--- a/data/web/lang/lang.en.json
+++ b/data/web/lang/lang.en.json
@@ -397,6 +397,7 @@
"max_quota_in_use": "Mailbox quota must be greater or equal to %d MiB",
"maxquota_empty": "Max. quota per mailbox must not be 0.",
"mysql_error": "MySQL error: %s",
+ "nginx_reload_failed": "Nginx reload failed: %s",
"network_host_invalid": "Invalid network or host: %s",
"next_hop_interferes": "%s interferes with nexthop %s",
"next_hop_interferes_any": "An existing next hop interferes with %s",
@@ -894,6 +895,7 @@
"mailbox_added": "Mailbox %s has been added",
"mailbox_modified": "Changes to mailbox %s have been saved",
"mailbox_removed": "Mailbox %s has been removed",
+ "nginx_reloaded": "Nginx was reloaded",
"object_modified": "Changes to object %s have been saved",
"pushover_settings_edited": "Pushover settings successfully set, please verify credentials.",
"qlearn_spam": "Message ID %s was learned as spam and deleted",
diff --git a/docker-compose.yml b/docker-compose.yml
index ec004b31..77ea833c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -101,13 +101,13 @@ services:
- rspamd
php-fpm-mailcow:
- image: mailcow/phpfpm:1.70
+ image: mailcow/phpfpm:1.71
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
depends_on:
- redis-mailcow
volumes:
- ./data/hooks/phpfpm:/hooks:Z
- - ./data/web:/web:rw,z
+ - ./data/web:/web:z
- ./data/conf/rspamd/dynmaps:/dynmaps:ro,z
- ./data/conf/rspamd/custom/:/rspamd_custom_maps:z
- rspamd-vol-1:/var/lib/rspamd:z
@@ -123,6 +123,7 @@ services:
- ./data/conf/dovecot/global_sieve_after:/global_sieve/after:z
- ./data/assets/templates:/tpls:z
- ./data/conf/ejabberd/autogen:/ejabberd/:z
+ - ./data/conf/nginx/:/etc/nginx/conf.d/:z
dns:
- ${IPV4_NETWORK:-172.22.1}.254
environment:
@@ -324,6 +325,7 @@ services:
until ping sogo -c1 > /dev/null; do sleep 1; done &&
until ping redis -c1 > /dev/null; do sleep 1; done &&
until ping rspamd -c1 > /dev/null; do sleep 1; done &&
+ until ping ejabberd -c1 > /dev/null; do sleep 1; done &&
exec nginx -g 'daemon off;'"
environment:
- HTTPS_PORT=${HTTPS_PORT:-443}
@@ -337,7 +339,7 @@ services:
- ./data/web:/web:ro,z
- ./data/conf/rspamd/dynmaps:/dynmaps:ro,z
- ./data/assets/ssl/:/etc/ssl/mail/:ro,z
- - ./data/conf/nginx/:/etc/nginx/conf.d/:rw,Z
+ - ./data/conf/nginx/:/etc/nginx/conf.d/:z
- ./data/conf/rspamd/meta_exporter:/meta_exporter:ro,z
- sogo-web-vol-1:/usr/lib/GNUstep/SOGo/:z
ports:
@@ -376,8 +378,8 @@ services:
- SNAT_TO_SOURCE=${SNAT_TO_SOURCE:-n}
- SNAT6_TO_SOURCE=${SNAT6_TO_SOURCE:-n}
volumes:
- - ./data/web/.well-known/acme-challenge:/var/www/acme:rw,z
- - ./data/assets/ssl:/var/lib/acme/:rw,z
+ - ./data/web/.well-known/acme-challenge:/var/www/acme:z
+ - ./data/assets/ssl:/var/lib/acme/:z
- ./data/assets/ssl-example:/var/lib/ssl-example/:ro,Z
- mysql-socket-vol-1:/var/run/mysqld/:z
restart: always
@@ -525,7 +527,7 @@ services:
- olefy
ejabberd-mailcow:
- image: mailcow/ejabberd:1.1
+ image: mailcow/ejabberd:1.2
volumes:
- ./data/conf/ejabberd/ejabberd.yml:/home/ejabberd/conf/ejabberd.yml:z
- xmpp-vol-1:/home/ejabberd/database:z