Merge pull request #87 from andryyy/dev

Dev to master
master
André Peters 2017-03-04 14:46:55 +01:00 committed by GitHub
commit 700f4ac0db
35 changed files with 716 additions and 236 deletions

View File

View File

@ -0,0 +1,44 @@
FROM debian:latest
MAINTAINER https://m-ko.de Markus Kosmal <code@cnfg.io>
# Debian Base to use
ENV DEBIAN_VERSION jessie
# initial install of av daemon
RUN echo "deb http://http.debian.net/debian/ $DEBIAN_VERSION main contrib non-free" > /etc/apt/sources.list && \
echo "deb http://http.debian.net/debian/ $DEBIAN_VERSION-updates main contrib non-free" >> /etc/apt/sources.list && \
echo "deb http://security.debian.org/ $DEBIAN_VERSION/updates main contrib non-free" >> /etc/apt/sources.list && \
apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y -qq \
clamav-daemon \
clamav-freshclam \
libclamunrar7 \
wget && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# initial update of av databases
RUN wget -O /var/lib/clamav/main.cvd http://db.local.clamav.net/main.cvd && \
wget -O /var/lib/clamav/daily.cvd http://db.local.clamav.net/daily.cvd && \
wget -O /var/lib/clamav/bytecode.cvd http://db.local.clamav.net/bytecode.cvd && \
chown clamav:clamav /var/lib/clamav/*.cvd
# permission juggling
RUN mkdir /var/run/clamav && \
chown clamav:clamav /var/run/clamav && \
chmod 750 /var/run/clamav
# av configuration update
RUN sed -i 's/^Foreground .*$/Foreground true/g' /etc/clamav/clamd.conf && \
echo "TCPSocket 3310" >> /etc/clamav/clamd.conf && \
sed -i 's/^Foreground .*$/Foreground true/g' /etc/clamav/freshclam.conf
# volume provision
VOLUME ["/var/lib/clamav"]
# port provision
EXPOSE 3310
# av daemon bootstrapping
ADD bootstrap.sh /
CMD ["/bootstrap.sh"]

View File

@ -0,0 +1,35 @@
#!/bin/bash
# bootstrap clam av service and clam av database updater shell script
# presented by mko (Markus Kosmal<code@cnfg.io>)
set -m
# start clam service itself and the updater in background as daemon
freshclam -d &
clamd &
# recognize PIDs
pidlist=`jobs -p`
# initialize latest result var
latest_exit=0
# define shutdown helper
function shutdown() {
trap "" SUBS
for single in $pidlist; do
if ! kill -0 $pidlist 2>/dev/null; then
wait $pidlist
exitcode=$?
fi
done
kill $pidlist 2>/dev/null
}
# run shutdown
trap terminate SUBS
wait
# return received result
exit $latest_exit

View File

@ -1,26 +0,0 @@
FROM ubuntu:xenial
MAINTAINER Andre Peters <andre.peters@debinux.de>
ENV DEBIAN_FRONTEND noninteractive
ENV LC_ALL C
RUN dpkg-divert --local --rename --add /sbin/initctl \
&& ln -sf /bin/true /sbin/initctl \
&& dpkg-divert --local --rename --add /usr/bin/ischroot \
&& ln -sf /bin/true /usr/bin/ischroot
RUN echo 'deb http://repo.powerdns.com/ubuntu xenial-rec-40 main' > /etc/apt/sources.list.d/pdns.list
RUN echo 'Package: pdns-*\n\
Pin: origin repo.powerdns.com\n\
Pin-Priority: 600\n' > /etc/apt/preferences.d/pdns
RUN apt-key adv --fetch-keys http://repo.powerdns.com/FD380FBB-pub.asc \
&& apt-get update \
&& apt-get install -y --force-yes pdns-recursor
CMD ["/usr/sbin/pdns_recursor"]
EXPOSE 53/udp
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

View File

@ -12,13 +12,17 @@ RUN dpkg-divert --local --rename --add /sbin/initctl \
RUN apt-key adv --fetch-keys http://rspamd.com/apt-stable/gpg.key \
&& echo "deb http://rspamd.com/apt-stable/ xenial main" > /etc/apt/sources.list.d/rspamd.list \
&& apt-get update \
&& apt-get -y install rspamd ca-certificates
&& apt-get -y install rspamd ca-certificates python-pip
RUN echo '.include $LOCAL_CONFDIR/local.d/rspamd.conf.local' > /etc/rspamd/rspamd.conf.local
# "Hardcoded" - we need them
RUN echo 'settings = "http://nginx:8081/settings.php";' > /etc/rspamd/modules.d/settings.conf
CMD ["/usr/bin/rspamd","-f", "-u", "_rspamd", "-g", "_rspamd"]
ADD settings.conf /etc/rspamd/modules.d/settings.conf
ADD antivirus.conf /etc/rspamd/modules.d/antivirus.conf
RUN pip install -U oletools
# Give Nginx/PHP time to restart
CMD /bin/sleep 30; /usr/bin/rspamd -f -u _rspamd -g _rspamd
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

View File

@ -0,0 +1,4 @@
antivirus {
.include(try=true,priority=1,duplicate=merge) "$LOCAL_CONFDIR/local.d/antivirus.conf"
.include(try=true,priority=10) "$LOCAL_CONFDIR/override.d/antivirus.conf"
}

View File

@ -0,0 +1 @@
settings = "http://nginx:8081/settings.php";

View File

@ -7,9 +7,6 @@ redirect_stderr=true
autostart=true
stdout_syslog=true
[group:sogo-group]
programs=reconf-domains,sogo
[program:sogo]
command=/usr/sbin/sogod
user=sogo

View File

@ -0,0 +1,20 @@
acl internal_networks {
127.0.0.0/8;
192.168.0.0/16;
172.16.0.0/12;
10.0.0.0/8;
};
options {
directory "/var/bind";
allow-recursion { internal_networks; };
listen-on { any; };
listen-on-v6 { none; };
pid-file "/var/run/named/named.pid";
allow-transfer { none; };
dnssec-enable yes;
dnssec-validation yes;
dnssec-lookaside auto;
};
include "/etc/bind/bind.keys";

View File

@ -0,0 +1 @@
server_name logs.servercow.de autodiscover.* autoconfig.*;

View File

@ -1,6 +1,6 @@
proxy_cache_path /tmp levels=1:2 keys_zone=sogo:10m inactive=24h max_size=1g;
server {
include /etc/nginx/conf.d/listen.active;
include /etc/nginx/conf.d/listen_ssl.active;
include /etc/nginx/mime.types;
charset utf-8;
override_charset on;
@ -13,13 +13,23 @@ server {
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains";
ssl_ecdh_curve secp384r1;
index index.php index.html;
server_name _ autodiscover.* autoconfig.*;
include /etc/nginx/conf.d/server_name.active;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /web;
location ^~ /.well-known/acme-challenge/ {
allow all;
default_type "text/plain";
}
# If behind reverse proxy, forwards the correct IP
set_real_ip_from 172.22.1.1;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
location = /principals/ {
rewrite ^ https://$host/SOGo/dav;
rewrite ^ $scheme://$host:$server_port/SOGo/dav;
allow all;
}
@ -42,6 +52,7 @@ server {
proxy_pass http://172.22.1.253:11334/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options SAMEORIGIN;
@ -52,12 +63,20 @@ server {
deny all;
}
if ($host ~* autodiscover\.(.*)) {
rewrite ^(.*) /autodiscover.php last;
location ~ /(?:a|A)utodiscover/(?:a|A)utodiscover.xml {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9000;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
try_files /autodiscover.php =404;
}
if ($host ~* autoconfig\.(.*)) {
rewrite ^(.*) /autoconfig.php last;
location ~ /(?:m|M)ail/(?:c|C)onfig-v1.1.xml {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9000;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
try_files /autoconfig.php =404;
}
location ^~ /Microsoft-Server-ActiveSync {
@ -92,13 +111,6 @@ server {
proxy_set_header x-webobjects-server-name $server_name;
proxy_set_header x-webobjects-server-url $scheme://$host:$server_port;
proxy_set_header x-webobjects-server-port $server_port;
#proxy_connect_timeout 90;
#proxy_send_timeout 90;
#proxy_read_timeout 90;
#proxy_buffer_size 4k;
#proxy_buffers 4 32k;
#proxy_busy_buffers_size 64k;
#proxy_temp_file_write_size 64k;
client_body_buffer_size 128k;
client_max_body_size 100m;
break;
@ -114,6 +126,164 @@ server {
allow all;
}
location /.woa/WebServerResources/ {
proxy_pass http://172.22.1.252:9192/WebServerResources/;
proxy_set_header Host $host;
proxy_cache sogo;
proxy_cache_valid 200 1d;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
#alias /usr/lib/GNUstep/SOGo/WebServerResources/;
allow all;
}
location /SOGo/WebServerResources/ {
proxy_pass http://172.22.1.252:9192/WebServerResources/;
proxy_set_header Host $host;
proxy_cache sogo;
proxy_cache_valid 200 1d;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
#alias /usr/lib/GNUstep/SOGo/WebServerResources/;
allow all;
}
location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$ {
proxy_pass http://172.22.1.252:9192/$1.SOGo/Resources/$2;
proxy_set_header Host $host;
proxy_cache sogo;
proxy_cache_valid 200 1d;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
#alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2;
}
}
server {
include /etc/nginx/conf.d/listen_plain.active;
include /etc/nginx/mime.types;
charset utf-8;
override_charset on;
index index.php index.html;
include /etc/nginx/conf.d/server_name.active;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /web;
location ^~ /.well-known/acme-challenge/ {
allow all;
default_type "text/plain";
}
# If behind reverse proxy, forwards the correct IP
set_real_ip_from 172.22.1.1;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
location = /principals/ {
rewrite ^ $scheme://$host:$server_port/SOGo/dav;
allow all;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9000;
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_param PHP_VALUE "max_execution_time = 1200
max_input_time = 1200
memory_limit = 64M";
fastcgi_read_timeout 1200;
}
rewrite ^(/save.+)$ /rspamd$1 last;
location /rspamd/ {
proxy_pass http://172.22.1.253:11334/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options SAMEORIGIN;
add_header X-XSS-Protection "1; mode=block";
}
location ^~ /inc/init.sql {
deny all;
}
location ~ /(?:a|A)utodiscover/(?:a|A)utodiscover.xml {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9000;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
try_files /autodiscover.php =404;
}
location ~ /(?:m|M)ail/(?:c|C)onfig-v1.1.xml {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9000;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
try_files /autoconfig.php =404;
}
location ^~ /Microsoft-Server-ActiveSync {
proxy_pass http://172.22.1.252:20000/SOGo/Microsoft-Server-ActiveSync;
proxy_connect_timeout 1000;
proxy_next_upstream timeout error;
proxy_send_timeout 1000;
proxy_read_timeout 1000;
proxy_buffer_size 8k;
proxy_buffers 4 32k;
proxy_temp_file_write_size 64k;
proxy_busy_buffers_size 64k;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $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 $scheme://$host:$server_port;
proxy_set_header x-webobjects-server-port $server_port;
client_body_buffer_size 128k;
client_max_body_size 100m;
}
location ^~ /SOGo {
proxy_pass http://172.22.1.252:20000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $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 $scheme://$host:$server_port;
proxy_set_header x-webobjects-server-port $server_port;
client_body_buffer_size 128k;
client_max_body_size 100m;
break;
}
location /SOGo.woa/WebServerResources/ {
proxy_pass http://172.22.1.252:9192/WebServerResources/;
proxy_set_header Host $host;
proxy_cache sogo;
proxy_cache_valid 200 1d;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
#alias /usr/lib/GNUstep/SOGo/WebServerResources/;
allow all;
}
location /.woa/WebServerResources/ {
proxy_pass http://172.22.1.252:9192/WebServerResources/;
proxy_set_header Host $host;
proxy_cache sogo;
proxy_cache_valid 200 1d;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
#alias /usr/lib/GNUstep/SOGo/WebServerResources/;
allow all;
}
location /SOGo/WebServerResources/ {
proxy_pass http://172.22.1.252:9192/WebServerResources/;
proxy_set_header Host $host;

View File

@ -0,0 +1 @@
listen ${HTTP_PORT};

View File

@ -0,0 +1 @@
server_name ${MAILCOW_HOSTNAME} autodiscover.* autoconfig.*;

View File

@ -1 +0,0 @@
addNTA("mailcow-network", "nta for local")

View File

@ -1,41 +0,0 @@
allow-from=127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8
config-dir=/etc/powerdns
daemon=no
disable-syslog=yes
dnssec=process
dnssec-log-bogus=yes
dont-query=10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fc00::/7, fe80::/10, 0.0.0.0/8, 192.0.0.0/24, 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 240.0.0.0/4, ::/96, ::ffff:0:0/96, 100::/64, 2001:db8::/32
export-etc-hosts=off
# forward-zones=
forward-zones-recurse=mailcow-network.=127.0.0.11
local-address=0.0.0.0
local-port=53
loglevel=6
# lowercase-outgoing=no
lua-config-file=/etc/powerdns/pdns_custom.lua
# max-cache-entries=1000000
# max-cache-ttl=86400
# max-mthreads=2048
# max-negative-ttl=3600
# max-packetcache-entries=500000
# max-qperq=50
# max-tcp-clients=128
# max-tcp-per-client=0
# max-total-msec=7000
# minimum-ttl-override=0
# network-timeout=1500
# packetcache-servfail-ttl=60
# packetcache-ttl=3600
quiet=yes
# security-poll-suffix=secpoll.powerdns.com.
# serve-rfc1918=yes
# server-down-max-fails=64
# server-down-throttle-time=60
setgid=pdns
setuid=pdns
# spoof-nearmiss-max=20
# stack-size=200000
# threads=2
# trace=off
version-string=PowerDNS Recursor
webserver=no

View File

@ -83,7 +83,7 @@ virtual_alias_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_alias_maps.
virtual_gid_maps = static:5000
virtual_mailbox_base = /var/vmail/
virtual_mailbox_domains = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_domains_maps.cf
virtual_mailbox_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_alias_domain_mailbox_maps.cf
virtual_mailbox_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf
virtual_minimum_uid = 104
virtual_transport = lmtp:inet:dovecot:24
virtual_uid_maps = static:5000

View File

@ -1,5 +0,0 @@
user = mailcow
password = mysafepasswd
hosts = mysql
dbname = mailcow
query = SELECT maildir FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' and mailbox.username = CONCAT('%u', '@', alias_domain.target_domain) AND mailbox.active = 1 AND alias_domain.active='1'

View File

@ -2,4 +2,4 @@ user = mailcow
password = mysafepasswd
hosts = mysql
dbname = mailcow
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('%u', '@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
query = SELECT username FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' and mailbox.username = CONCAT('%u', '@', alias_domain.target_domain) AND mailbox.active = 1 AND alias_domain.active='1'

View File

@ -2,4 +2,4 @@ user = mailcow
password = mysafepasswd
hosts = mysql
dbname = mailcow
query = SELECT goto FROM alias WHERE address='%s' AND active='1' AND domain IN(SELECT domain FROM domain WHERE domain='%d' AND active='1') UNION SELECT logged_in_as FROM sender_acl WHERE send_as='@%d' OR send_as='%s' OR send_as IN ( SELECT CONCAT ('@',target_domain) FROM alias_domain WHERE alias_domain = '%d') OR send_as IN ( SELECT CONCAT ('%u','@',target_domain) FROM alias_domain WHERE alias_domain = '%d' ) AND logged_in_as NOT IN (SELECT goto FROM alias WHERE address='%s') UNION SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' AND alias.address = CONCAT('%u','@',alias_domain.target_domain) AND alias.active ='1' AND alias_domain.active='1'
query = SELECT goto FROM alias WHERE address='%s' AND active='1' AND domain IN(SELECT domain FROM domain WHERE domain='%d' AND active='1') UNION SELECT logged_in_as FROM sender_acl WHERE send_as='@%d' OR send_as='%s' OR send_as IN ( SELECT CONCAT ('@',target_domain) FROM alias_domain WHERE alias_domain = '%d') OR send_as IN ( SELECT CONCAT ('%u','@',target_domain) FROM alias_domain WHERE alias_domain = '%d' ) AND logged_in_as NOT IN (SELECT goto FROM alias WHERE address='%s') UNION SELECT username FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' AND mailbox.username = CONCAT('%u','@',alias_domain.target_domain) AND mailbox.active ='1' AND alias_domain.active='1'

View File

@ -1,22 +1,34 @@
<?php
ini_set('error_reporting', 0);
header('Content-Type: text/plain');
require_once "vars.inc.php";
ini_set('error_reporting', 0);
$has_object = 0;
header('Content-Type: text/plain');
$dsn = $database_type . ':host=' . $database_host . ';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);
$stmt = $pdo->query("SELECT `domain` FROM `domain`");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$has_object = 1;
echo strtolower(trim($row['domain'])) . PHP_EOL;
}
$stmt = $pdo->query("SELECT `alias_domain` FROM `alias_domain`");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$has_object = 1;
echo strtolower(trim($row['alias_domain'])) . PHP_EOL;
}
if ($has_object == 0) {
echo "dummy@domain.local";
}
}
catch (PDOException $e) {
echo "dummy@domain.local";
exit;
}
?>

View File

@ -4,6 +4,11 @@ The match section performs AND operation on different matches: for example, if y
then the rule matches only when from AND rcpt match. For similar matches, the OR rule applies: if you have multiple rcpt matches,
then any of these will trigger the rule. If a rule is triggered then no more rules are matched.
*/
function parse_email($email) {
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
$a = strrpos($email, '@');
return array('local' => substr($email, 0, $a), 'domain' => substr($email, $a));
}
header('Content-Type: text/plain');
require_once "vars.inc.php";
@ -15,7 +20,15 @@ $opt = [
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
$stmt = $pdo->query("SELECT * FROM `filterconf`");
}
catch (PDOException $e) {
echo 'settings { }';
exit;
}
?>
settings {
<?php
@ -48,13 +61,27 @@ while ($row = array_shift($rows)) {
<?php
}
}
$local = parse_email($row['object'])['local'];
$domain = parse_email($row['object'])['domain'];
if (!empty($local) && !empty($local)) {
?>
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
<?php
}
?>
rcpt = "<?=$row['object'];?>";
<?php
$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` = :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address");
$stmt->execute(array(':object_goto' => $row['object'], ':object_address' => $row['object']));
$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` LIKE :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address");
$stmt->execute(array(':object_goto' => '%' . $row['object'] . '%', ':object_address' => $row['object']));
$rows_aliases_1 = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row_aliases_1 = array_shift($rows_aliases_1)) {
$local = parse_email($row_aliases_1['address'])['local'];
$domain = parse_email($row_aliases_1['address'])['domain'];
if (!empty($local) && !empty($local)) {
?>
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
<?php
}
?>
rcpt = "<?=$row_aliases_1['address'];?>";
<?php
@ -67,6 +94,13 @@ while ($row = array_shift($rows)) {
array_filter($rows_aliases_2);
while ($row_aliases_2 = array_shift($rows_aliases_2)) {
if (!empty($row_aliases_2['aliases'])) {
$local = parse_email($row_aliases_2['aliases'])['local'];
$domain = parse_email($row_aliases_2['aliases'])['domain'];
if (!empty($local) && !empty($local)) {
?>
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
<?php
}
?>
rcpt = "<?=$row_aliases_2['aliases'];?>";
<?php
@ -123,14 +157,30 @@ while ($row = array_shift($rows)) {
else {
?>
priority = high;
<?php
$local = parse_email($row['object'])['local'];
$domain = parse_email($row['object'])['domain'];
if (!empty($local) && !empty($local)) {
?>
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
<?php
}
?>
rcpt = "<?=$row['object'];?>";
<?php
}
$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` = :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address");
$stmt->execute(array(':object_goto' => $row['object'], ':object_address' => $row['object']));
$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` LIKE :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address");
$stmt->execute(array(':object_goto' => '%' . $row['object'] . '%', ':object_address' => $row['object']));
$rows_aliases_wl_1 = $stmt->fetchAll(PDO::FETCH_ASSOC);
array_filter($rows_aliases_wl_1);
while ($row_aliases_wl_1 = array_shift($rows_aliases_wl_1)) {
$local = parse_email($row_aliases_wl_1['address'])['local'];
$domain = parse_email($row_aliases_wl_1['address'])['domain'];
if (!empty($local) && !empty($local)) {
?>
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
<?php
}
?>
rcpt = "<?=$row_aliases_wl_1['address'];?>";
<?php
@ -143,6 +193,13 @@ while ($row = array_shift($rows)) {
array_filter($rows_aliases_wl_2);
while ($row_aliases_wl_2 = array_shift($rows_aliases_wl_2)) {
if (!empty($row_aliases_wl_2['aliases'])) {
$local = parse_email($row_aliases_wl_2['aliases'])['local'];
$domain = parse_email($row_aliases_wl_2['aliases'])['domain'];
if (!empty($local) && !empty($local)) {
?>
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
<?php
}
?>
rcpt = "<?=$row_aliases_wl_2['aliases'];?>";
<?php
@ -195,14 +252,30 @@ while ($row = array_shift($rows)) {
else {
?>
priority = high;
<?php
$local = parse_email($row['object'])['local'];
$domain = parse_email($row['object'])['domain'];
if (!empty($local) && !empty($local)) {
?>
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
<?php
}
?>
rcpt = "<?=$row['object'];?>";
<?php
}
$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` = :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address");
$stmt->execute(array(':object_goto' => $row['object'], ':object_address' => $row['object']));
$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` LIKE :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address");
$stmt->execute(array(':object_goto' => '%' . $row['object'] . '%', ':object_address' => $row['object']));
$rows_aliases_bl_1 = $stmt->fetchAll(PDO::FETCH_ASSOC);
array_filter($rows_aliases_bl_1);
while ($row_aliases_bl_1 = array_shift($rows_aliases_bl_1)) {
$local = parse_email($row_aliases_bl_1['address'])['local'];
$domain = parse_email($row_aliases_bl_1['address'])['domain'];
if (!empty($local) && !empty($local)) {
?>
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
<?php
}
?>
rcpt = "<?=$row_aliases_bl_1['address'];?>";
<?php
@ -215,6 +288,13 @@ while ($row = array_shift($rows)) {
array_filter($rows_aliases_bl_2);
while ($row_aliases_bl_2 = array_shift($rows_aliases_bl_2)) {
if (!empty($row_aliases_bl_2['aliases'])) {
$local = parse_email($row_aliases_bl_2['aliases'])['local'];
$domain = parse_email($row_aliases_bl_2['aliases'])['domain'];
if (!empty($local) && !empty($local)) {
?>
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
<?php
}
?>
rcpt = "<?=$row_aliases_bl_2['aliases'];?>";
<?php

View File

@ -1,22 +1,34 @@
<?php
ini_set('error_reporting', 0);
header('Content-Type: text/plain');
require_once "vars.inc.php";
ini_set('error_reporting', 0);
$has_object = 0;
header('Content-Type: text/plain');
$dsn = $database_type . ':host=' . $database_host . ';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);
$stmt = $pdo->query("SELECT `username` FROM `mailbox` WHERE `wants_tagged_subject` = '1'");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$has_object = 1;
echo strtolower(trim($row['username'])) . PHP_EOL;
}
$stmt = $pdo->query("SELECT CONCAT(mailbox.local_part, '@', alias_domain.alias_domain) as `tag_ad` FROM `mailbox` INNER JOIN `alias_domain` ON mailbox.domain = alias_domain.target_domain WHERE mailbox.wants_tagged_subject='1';");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$has_object = 1;
echo strtolower(trim($row['tag_ad'])) . PHP_EOL;
}
if ($has_object == 0) {
echo "dummy@domain.local";
}
}
catch (PDOException $e) {
echo "dummy@domain.local";
exit;
}
?>

View File

@ -11,69 +11,85 @@ rspamd_config.MAILCOW_MOO = function (task)
return true
end
local modify_subject_map = rspamd_config:add_map({
url = 'http://nginx:8081/tags.php',
modify_subject_map = rspamd_config:add_map({
url = 'http://172.22.1.251:8081/tags.php',
type = 'map',
description = 'Map of users to use subject tags for'
})
local auth_domain_map = rspamd_config:add_map({
url = 'http://nginx:8081/authoritative.php',
auth_domain_map = rspamd_config:add_map({
url = 'http://172.22.1.251:8081/authoritative.php',
type = 'map',
description = 'Map of domains we are authoritative for'
})
rspamd_config.ADD_DELIMITER_TAG = {
callback = function(task)
tag = nil
local tag_env = nil
local tag_to = nil
local tag = nil
local util = require("rspamd_util")
local rspamd_logger = require "rspamd_logger"
local user_env_tagged = task:get_recipients(1)[1]['user']
local user_to_tagged = task:get_recipients(2)[1]['user']
local user_tagged = task:get_recipients(2)[1]['user']
local domain = task:get_recipients(1)[1]['domain']
local user_env, tag_env = user_env_tagged:match("([^+]+)+(.*)")
local user_to, tag_to = user_to_tagged:match("([^+]+)+(.*)")
local user, tag = user_tagged:match("([^+]+)+(.*)")
local authdomain = auth_domain_map:get_key(domain)
if tag_env then
tag = tag_env
user = user_env
elseif tag_to then
tag = tag_to
user = user_env
end
if tag and authdomain then
rspamd_logger.infox("Domain %s is part of mailcow, start reading tag settings", domain)
rspamd_logger.infox("domain: %1, tag: %2", domain, tag)
local user_untagged = user .. '@' .. domain
rspamd_logger.infox("Querying tag settings for user %1", user_untagged)
rspamd_logger.infox("querying tag settings for user %1", user_untagged)
if modify_subject_map:get_key(user_untagged) then
rspamd_logger.infox("User wants subject modified for tagged mail")
rspamd_logger.infox("found user in map for subject rewrite")
local sbj = task:get_header('Subject')
if tag then
rspamd_logger.infox("Found tag %1, will modify subject header", tag)
new_sbj = '=?UTF-8?B?' .. tostring(util.encode_base64('[' .. tag .. '] ' .. sbj)) .. '?='
task:set_rmilter_reply({
remove_headers = {['Subject'] = 1},
add_headers = {['Subject'] = new_sbj}
})
end
else
rspamd_logger.infox("Add X-Moo-Tag header")
rspamd_logger.infox("add X-Moo-Tag header")
task:set_rmilter_reply({
add_headers = {['X-Moo-Tag'] = 'YES'}
})
end
else
rspamd_logger.infox("Skip delimiter handling for untagged message or authenticated user")
rspamd_logger.infox("skip delimiter handling for untagged message or authenticated user")
end
return false
end
}
rspamd_config.MRAPTOR = {
callback = function(task)
local parts = task:get_parts()
local rspamd_logger = require "rspamd_logger"
local rspamd_regexp = require "rspamd_regexp"
if parts then
for _,p in ipairs(parts) do
local mtype,subtype = p:get_type()
local re = rspamd_regexp.create_cached('/(office|word|excel)/i')
if re:match(subtype) then
local content = tostring(p:get_content())
local filename = p:get_filename()
local file = os.tmpname()
f = io.open(file, "a+")
f:write(content)
f:close()
local scan = assert(io.popen('PATH=/usr/bin:/usr/local/bin mraptor ' .. file .. '> /dev/null 2>&1; echo $?', 'r'))
local result = scan:read('*all')
local exit_code = string.match(result, "%d+")
rspamd_logger.infox(exit_code)
scan:close()
if exit_code == "20" then
rspamd_logger.infox("Reject dangerous macro in office file " .. filename)
task:set_pre_result(rspamd_actions['reject'], 'Dangerous macro in office file ' .. filename)
end
end
end
end
end
}

View File

@ -6,7 +6,20 @@ if (!isset($_SESSION['mailcow_cc_role']) OR !in_array($_SESSION['mailcow_cc_role
exit();
}
if ($_GET['ACTION'] == "start") {
$request = xmlrpc_encode_request("supervisor.startProcessGroup", 'sogo-group', array('encoding'=>'utf-8'));
$request = xmlrpc_encode_request("supervisor.startProcess", 'reconf-domains', array('encoding'=>'utf-8'));
$context = stream_context_create(array('http' => array(
'method' => "POST",
'header' => "Content-Length: " . strlen($request),
'content' => $request
)));
$file = @file_get_contents("http://sogo:9191/RPC2", false, $context) or die("Cannot connect to $remote_server:$listener_port");
$response = xmlrpc_decode($file);
if (isset($response['faultString'])) {
echo '<b><span class="pull-right text-warning">' . $response['faultString'] . '</span></b>';
}
else {
sleep(4);
$request = xmlrpc_encode_request("supervisor.startProcess", 'sogo', array('encoding'=>'utf-8'));
$context = stream_context_create(array('http' => array(
'method' => "POST",
'header' => "Content-Length: " . strlen($request),
@ -21,8 +34,22 @@ if ($_GET['ACTION'] == "start") {
echo '<b><span class="pull-right text-success">OK</span></b>';
}
}
}
elseif ($_GET['ACTION'] == "stop") {
$request = xmlrpc_encode_request("supervisor.stopProcessGroup", 'sogo-group', array('encoding'=>'utf-8'));
$request = xmlrpc_encode_request("supervisor.stopProcess", 'sogo', array('encoding'=>'utf-8'));
$context = stream_context_create(array('http' => array(
'method' => "POST",
'header' => "Content-Length: " . strlen($request),
'content' => $request
)));
$file = @file_get_contents("http://sogo:9191/RPC2", false, $context) or die("Cannot connect to $remote_server:$listener_port");
$response = xmlrpc_decode($file);
if (isset($response['faultString'])) {
echo '<b><span class="pull-right text-warning">' . $response['faultString'] . '</span></b>';
}
else {
sleep(1);
$request = xmlrpc_encode_request("supervisor.stopProcess", 'reconf-domains', array('encoding'=>'utf-8'));
$context = stream_context_create(array('http' => array(
'method' => "POST",
'header' => "Content-Length: " . strlen($request),
@ -37,4 +64,5 @@ elseif ($_GET['ACTION'] == "stop") {
echo '<b><span class="pull-right text-success">OK</span></b>';
}
}
}
?>

View File

@ -416,7 +416,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
<div class="form-group">
<label class="control-label col-sm-2" for="sender_acl"><?=$lang['edit']['sender_acl'];?>:</label>
<div class="col-sm-10">
<select data-width="50%" style="width:100%" id="sender_acl" name="sender_acl[]" size="10" multiple>
<select data-width="100%" style="width:100%" id="sender_acl" name="sender_acl[]" size="10" multiple>
<?php
$sender_acl_handles = mailbox_get_sender_acl_handles($mailbox);

View File

@ -253,6 +253,8 @@ function edit_admin_account($postarray) {
}
$username = $postarray['admin_user'];
$username_now = $_SESSION['mailcow_cc_username'];
$password = $postarray['admin_pass'];
$password2 = $postarray['admin_pass2'];
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username)) {
$_SESSION['return'] = array(
@ -262,15 +264,22 @@ function edit_admin_account($postarray) {
return false;
}
if (!empty($postarray['admin_pass']) && !empty($postarray['admin_pass2'])) {
if ($postarray['admin_pass'] != $postarray['admin_pass2']) {
if (!empty($password) && !empty($password2)) {
if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['password_complexity'])
);
return false;
}
if ($password != $password2) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['password_mismatch'])
);
return false;
}
$password_hashed = hash_password($postarray['admin_pass']);
$password_hashed = hash_password($password);
try {
$stmt = $pdo->prepare("UPDATE `admin` SET
`modified` = :modified,
@ -585,9 +594,7 @@ function edit_user_account($postarray) {
);
return false;
}
if (strlen($password_new) < "6" ||
!preg_match('/[A-Za-z]/', $password_new) ||
!preg_match('/[0-9]/', $password_new)) {
if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password_new)) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['password_complexity'])
@ -1459,8 +1466,11 @@ function user_get_alias_details($username) {
}
try {
$data['address'] = $username;
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') AS `aliases` FROM `alias` WHERE `goto` = :username_goto AND `address` NOT LIKE '@%' AND `address` != :username_address");
$stmt->execute(array(':username_goto' => $username, ':username_address' => $username));
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') AS `aliases` FROM `alias`
WHERE `goto` LIKE :username_goto
AND `address` NOT LIKE '@%'
AND `address` != :username_address");
$stmt->execute(array(':username_goto' => '%' . $username . '%', ':username_address' => $username));
$run = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($run)) {
$data['aliases'] = $row['aliases'];
@ -1485,8 +1495,8 @@ function user_get_alias_details($username) {
while ($row = array_shift($run)) {
$data['aliases_send_as_all'] = $row['send_as'];
}
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') as `address` FROM `alias` WHERE `goto` = :username AND `address` LIKE '@%';");
$stmt->execute(array(':username' => $username));
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') as `address` FROM `alias` WHERE `goto` LIKE :username AND `address` LIKE '@%';");
$stmt->execute(array(':username' => '%' . $username . '%'));
$run = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($run)) {
$data['is_catch_all'] = $row['address'];
@ -1571,6 +1581,13 @@ function add_domain_admin($postarray) {
}
}
if (!empty($password) && !empty($password2)) {
if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['password_complexity'])
);
return false;
}
if ($password != $password2) {
$_SESSION['return'] = array(
'type' => 'danger',
@ -1711,6 +1728,7 @@ function get_domain_admins() {
}
function get_domain_admin_details($domain_admin) {
global $pdo;
global $lang;
$domainadmindata = array();
if (isset($domain_admin) && $_SESSION['mailcow_cc_role'] != "admin") {
@ -2169,6 +2187,13 @@ function edit_domain_admin($postarray) {
}
if (!empty($password) && !empty($password2)) {
if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['password_complexity'])
);
return false;
}
if ($password != $password2) {
$_SESSION['return'] = array(
'type' => 'danger',
@ -2262,9 +2287,7 @@ function edit_domain_admin($postarray) {
);
return false;
}
if (strlen($password_new) < "6" ||
!preg_match('/[A-Za-z]/', $password_new) ||
!preg_match('/[0-9]/', $password_new)) {
if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password_new)) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['password_complexity'])
@ -2991,6 +3014,13 @@ function mailbox_add_mailbox($postarray) {
}
if (!empty($password) && !empty($password2)) {
if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['password_complexity'])
);
return false;
}
if ($password != $password2) {
$_SESSION['return'] = array(
'type' => 'danger',
@ -3735,6 +3765,13 @@ function mailbox_edit_mailbox($postarray) {
}
}
if (!empty($password) && !empty($password2)) {
if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['password_complexity'])
);
return false;
}
if ($password != $password2) {
$_SESSION['return'] = array(
'type' => 'danger',
@ -4313,9 +4350,13 @@ function mailbox_get_mailbox_details($mailbox) {
$DomainQuota = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt = $pdo->prepare("SELECT COALESCE(SUM(`quota`), 0) as `in_use` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` = :domain AND `username` != :username");
$stmt->execute(array(':domain' => $row['domain'], ':username' => $row['username']));
$stmt->execute(array(':domain' => $row['domain'], ':username' => $mailbox));
$MailboxUsage = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt = $pdo->prepare("SELECT IFNULL(COUNT(`address`), 0) AS `sa_count` FROM `spamalias` WHERE `goto` = :address AND `validity` >= :unixnow");
$stmt->execute(array(':address' => $mailbox, ':unixnow' => time()));
$SpamaliasUsage = $stmt->fetch(PDO::FETCH_ASSOC);
$mailboxdata['max_new_quota'] = ($DomainQuota['quota'] * 1048576) - $MailboxUsage['in_use'];
if ($mailboxdata['max_new_quota'] > ($DomainQuota['maxquota'] * 1048576)) {
$mailboxdata['max_new_quota'] = ($DomainQuota['maxquota'] * 1048576);
@ -4331,6 +4372,7 @@ function mailbox_get_mailbox_details($mailbox) {
$mailboxdata['quota_used'] = intval($row['bytes']);
$mailboxdata['percent_in_use'] = round((intval($row['bytes']) / intval($row['quota'])) * 100);
$mailboxdata['messages'] = $row['messages'];
$mailboxdata['spam_aliases'] = $SpamaliasUsage['sa_count'];
if ($mailboxdata['percent_in_use'] >= 90) {
$mailboxdata['percent_class'] = "danger";
}

View File

@ -35,4 +35,6 @@ $DEFAULT_LANG = "en";
// See https://bootswatch.com/
$DEFAULT_THEME = "lumen";
// Password complexity as regular expression
$PASSWD_REGEP = '.{4,}';
?>

View File

@ -57,7 +57,7 @@ $lang['danger']['exit_code_not_null'] = 'Fehler: Exit-Code ist %d';
$lang['danger']['mailbox_not_available'] = 'Mailbox nicht verfügbar';
$lang['danger']['username_invalid'] = 'Benutzername kann nicht verwendet werden';
$lang['danger']['password_mismatch'] = 'Passwort-Wiederholung stimmt nicht überein';
$lang['danger']['password_complexity'] = 'Passwort entspricht nicht den Vorgaben (Klein- und Großschreibung und mindestens eine Ziffer, mindestens 6 Zeichen lang)';
$lang['danger']['password_complexity'] = 'Passwort entspricht nicht den Richtlinien';
$lang['danger']['password_empty'] = 'Passwort darf nicht leer sein';
$lang['danger']['login_failed'] = 'Anmeldung fehlgeschlagen';
$lang['danger']['mailbox_invalid'] = 'Mailboxname ist ungültig';
@ -92,6 +92,8 @@ $lang['danger']['spam_alias_max_exceeded'] = 'Maximale Anzahl an Spam-Alias-Adre
$lang['danger']['validity_missing'] = 'Bitte geben Sie eine Gültigkeitsdauer an';
$lang['user']['on'] = 'Ein';
$lang['user']['off'] = 'Aus';
$lang['user']['messages'] = "Nachrichten";
$lang['user']['in_use'] = "Verwendet";
$lang['user']['user_change_fn'] = '';
$lang['user']['user_settings'] = 'Benutzereinstellungen';
$lang['user']['mailbox_settings'] = 'Mailbox-Einstellungen';
@ -108,8 +110,8 @@ $lang['user']['alias'] = 'Alias';
$lang['user']['aliases'] = 'Aliasse';
$lang['user']['domain_aliases'] = 'Domain-Alias Adressen';
$lang['user']['is_catch_all'] = 'Ist Catch-All Adresse für Domain(s)';
$lang['user']['aliases_also_send_as'] = 'Darf außerdem versenden als';
$lang['user']['aliases_send_as_all'] = 'Absender für folgende Domains nicht prüfen';
$lang['user']['aliases_also_send_as'] = 'Darf außerdem versenden als Benutzer';
$lang['user']['aliases_send_as_all'] = 'Absender für folgende Domains und zugehörige Alias-Domains nicht prüfen';
$lang['user']['alias_create_random'] = 'Zufälligen Alias generieren';
$lang['user']['alias_extend_all'] = 'Gültigkeit +1h';
$lang['user']['alias_valid_until'] = 'Gültig bis';
@ -207,6 +209,7 @@ $lang['header']['logged_in_as_logout'] = 'Eingeloggt als <b>%s</b> (abmelden)';
$lang['header']['logged_in_as_logout_dual'] = 'Eingeloggt als <b>%s <span class="text-info">[%s]</span></b>';
$lang['header']['locale'] = 'Sprache';
$lang['mailbox']['domain'] = 'Domain';
$lang['mailbox']['spam_aliases'] = 'Temp. Alias';
$lang['mailbox']['alias'] = 'Alias';
$lang['mailbox']['aliases'] = 'Aliasse';
$lang['mailbox']['multiple_bookings'] = 'Mehrfachbuchen';
@ -307,7 +310,7 @@ $lang['edit']['dkim_txt_name'] = 'TXT-Record Name:';
$lang['edit']['dkim_txt_value'] = 'TXT-Record Wert:';
$lang['edit']['previous'] = 'Vorherige Seite';
$lang['edit']['unchanged_if_empty'] = 'Unverändert, wenn leer';
$lang['edit']['dont_check_sender_acl'] = 'Absender für Domain %s nicht prüfen';
$lang['edit']['dont_check_sender_acl'] = 'Absender für Domain %s u. Alias-Dom. nicht prüfen';
$lang['edit']['multiple_bookings'] = 'Mehrfaches Buchen';
$lang['edit']['kind'] = 'Art';
$lang['edit']['resource'] = 'Ressource';

View File

@ -59,7 +59,7 @@ $lang['danger']['exit_code_not_null'] = "Error: Exit code was %d";
$lang['danger']['mailbox_not_available'] = "Mailbox not available";
$lang['danger']['username_invalid'] = "Username cannot be used";
$lang['danger']['password_mismatch'] = "Confirmation password is not identical";
$lang['danger']['password_complexity'] = "Password does not meet requirements (upper and lowercase letters and at least one number, min. 6 characters long)";
$lang['danger']['password_complexity'] = "Password does not meet the policy";
$lang['danger']['password_empty'] = "Password must not be empty";
$lang['danger']['login_failed'] = "Login failed";
$lang['danger']['mailbox_invalid'] = "Mailbox name is invalid";
@ -94,6 +94,8 @@ $lang['danger']['spam_alias_max_exceeded'] = "Max. allowed spam alias addresses
$lang['danger']['validity_missing'] = 'Please assign a period of validity';
$lang['user']['on'] = "On";
$lang['user']['off'] = "Off";
$lang['user']['messages'] = "messages"; // "123 messages"
$lang['user']['in_use'] = "Used";
$lang['user']['user_change_fn'] = "";
$lang['user']['user_settings'] = 'User settings';
$lang['user']['mailbox_settings'] = 'Mailbox settings';
@ -110,8 +112,8 @@ $lang['user']['alias'] = 'Alias';
$lang['user']['aliases'] = 'Aliases';
$lang['user']['domain_aliases'] = 'Domain alias addresses';
$lang['user']['is_catch_all'] = 'Catch-all for domain/s';
$lang['user']['aliases_also_send_as'] = 'Also allowed to send as';
$lang['user']['aliases_send_as_all'] = 'Do not check sender access for following domain/s';
$lang['user']['aliases_also_send_as'] = 'Also allowed to send as user';
$lang['user']['aliases_send_as_all'] = 'Do not check sender access for the following domain(s) and its alias domains';
$lang['user']['alias_create_random'] = 'Generate random alias';
$lang['user']['alias_extend_all'] = 'Extend aliases by 1 hour';
$lang['user']['alias_valid_until'] = 'Valid until';
@ -209,6 +211,7 @@ $lang['header']['logged_in_as_logout'] = 'Logged in as <b>%s</b> (logout)';
$lang['header']['logged_in_as_logout_dual'] = 'Logged in as <b>%s <span class="text-info">[%s]</span></b>';
$lang['header']['locale'] = 'Language';
$lang['mailbox']['domain'] = 'Domain';
$lang['mailbox']['spam_aliases'] = 'Temp. alias';
$lang['mailbox']['multiple_bookings'] = 'Multiple bookings';
$lang['mailbox']['kind'] = 'Kind';
$lang['mailbox']['description'] = 'Description';
@ -245,6 +248,7 @@ $lang['mailbox']['add_domain_alias'] = 'Add domain alias';
$lang['mailbox']['add_mailbox'] = 'Add mailbox';
$lang['mailbox']['add_resource'] = 'Add resource';
$lang['mailbox']['add_alias'] = 'Add alias';
$lang['mailbox']['add_domain_record_first'] = 'Please add a domain first';
$lang['info']['no_action'] = 'No action applicable';
@ -310,7 +314,7 @@ $lang['edit']['dkim_txt_name'] = 'TXT record name:';
$lang['edit']['dkim_txt_value'] = 'TXT record value:';
$lang['edit']['previous'] = 'Previous page';
$lang['edit']['unchanged_if_empty'] = 'If unchanged leave blank';
$lang['edit']['dont_check_sender_acl'] = 'Do not check sender for domain %s';
$lang['edit']['dont_check_sender_acl'] = "Disable sender check for domain %s + alias domains";
$lang['edit']['multiple_bookings'] = 'Multiple bookings';
$lang['edit']['kind'] = 'Kind';
$lang['edit']['resource'] = 'Resource';

View File

@ -91,7 +91,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
endforeach;
else:
?>
<tr id="no-data"><td colspan="8" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['no_record_single'];?></td></tr>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['no_record_single'];?></td></tr>
<?php
endif;
?>
@ -101,7 +101,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
?>
<tfoot>
<tr id="no-data">
<td colspan="8" style="text-align: center; font-style: normal; border-top: 1px solid #e7e7e7;">
<td colspan="999" style="text-align: center; font-style: normal; border-top: 1px solid #e7e7e7;">
<a href="/add.php?domain"><?=$lang['mailbox']['add_domain'];?></a>
</td>
</tr>
@ -137,6 +137,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['fname'];?></th>
<th class="sort-table" style="min-width: 86px;"><?=$lang['mailbox']['domain'];?></th>
<th class="sort-table" style="min-width: 75px;"><?=$lang['mailbox']['quota'];?></th>
<th class="sort-table" style="min-width: 75px;"><?=$lang['mailbox']['spam_aliases'];?></th>
<th class="sort-table" style="min-width: 99px;"><?=$lang['mailbox']['in_use'];?></th>
<th class="sort-table" style="min-width: 100px;"><?=$lang['mailbox']['msg_num'];?></th>
<th class="sort-table" style="min-width: 76px;"><?=$lang['mailbox']['active'];?></th>
@ -145,6 +146,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
</thead>
<tbody>
<?php
if (!empty($domains)) {
foreach (mailbox_get_domains() as $domain) {
$mailboxes = mailbox_get_mailboxes($domain);
if (!empty($mailboxes)) {
@ -156,6 +158,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<td><?=htmlspecialchars($mailboxdata['name'], ENT_QUOTES, 'UTF-8');?></td>
<td><?=htmlspecialchars($mailboxdata['domain']);?></td>
<td><?=formatBytes($mailboxdata['quota_used'], 2);?> / <?=formatBytes($mailboxdata['quota'], 2);?></td>
<td><?=$mailboxdata['spam_aliases'];?></td>
<td style="min-width:120px;">
<div class="progress">
<div class="progress-bar progress-bar-<?=$mailboxdata['percent_class'];?>" role="progressbar" aria-valuenow="<?=$mailboxdata['percent_in_use'];?>" aria-valuemin="0" aria-valuemax="100" style="min-width:2em;width: <?=$mailboxdata['percent_in_use'];?>%;">
@ -180,15 +183,20 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
}
else {
?>
<tr id="no-data"><td colspan="8" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
<?php
}
}
} else {
?>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['add_domain_record_first'];?></td></tr>
<?php
}
?>
</tbody>
<tfoot>
<tr id="no-data">
<td colspan="8" style="text-align: center; border-top: 1px solid #e7e7e7;">
<td colspan="999" style="text-align: center; border-top: 1px solid #e7e7e7;">
<a href="/add.php?mailbox"><?=$lang['mailbox']['add_mailbox'];?></a>
</td>
</tr>
@ -227,6 +235,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
</thead>
<tbody>
<?php
if (!empty($domains)) {
foreach (mailbox_get_domains() as $domain) {
$resources = mailbox_get_resources($domain);
if (!empty($resources)) {
@ -251,15 +260,20 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
}
else {
?>
<tr id="no-data"><td colspan="8" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
<?php
}
}
} else {
?>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['add_domain_record_first'];?></td></tr>
<?php
}
?>
</tbody>
<tfoot>
<tr id="no-data">
<td colspan="8" style="text-align: center; border-top: 1px solid #e7e7e7;">
<td colspan="999" style="text-align: center; border-top: 1px solid #e7e7e7;">
<a href="/add.php?resource"><?=$lang['mailbox']['add_resource'];?></a>
</td>
</tr>
@ -296,6 +310,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
</thead>
<tbody>
<?php
if (!empty($domains)) {
foreach (mailbox_get_domains() as $domain) {
$alias_domains = mailbox_get_alias_domains($domain);
if (!empty($alias_domains)) {
@ -318,15 +333,20 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
}
else {
?>
<tr id="no-data"><td colspan="8" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
<?php
}
}
} else {
?>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['add_domain_record_first'];?></td></tr>
<?php
}
?>
</tbody>
<tfoot>
<tr id="no-data">
<td colspan="8" style="text-align: center; border-top: 1px solid #e7e7e7;">
<td colspan="999" style="text-align: center; border-top: 1px solid #e7e7e7;">
<a href="/add.php?aliasdomain"><?=$lang['mailbox']['add_domain_alias'];?></a>
</td>
</tr>
@ -365,6 +385,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
</thead>
<tbody>
<?php
if (!empty($domains)) {
foreach (array_merge(mailbox_get_domains(), mailbox_get_alias_domains()) as $domain) {
$aliases = mailbox_get_aliases($domain);
if (!empty($aliases)) {
@ -396,15 +417,20 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
}
else {
?>
<tr id="no-data"><td colspan="8" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
<?php
}
}
} else {
?>
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['add_domain_record_first'];?></td></tr>
<?php
}
?>
</tbody>
<tfoot>
<tr id="no-data">
<td colspan="8" style="text-align: center; border-top: 1px solid #e7e7e7;">
<td colspan="999" style="text-align: center; border-top: 1px solid #e7e7e7;">
<a href="/add.php?alias"><?=$lang['mailbox']['add_alias'];?></a>
</td>
</tr>

View File

@ -31,7 +31,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'doma
foreach ($tfa_data['additional'] as $key_info): ?>
<form style="display:inline;" method="post">
<input type="hidden" name="unset_tfa_key" value="<?=$key_info['id'];?>" />
<div class="label label-default">🔑 <?=$key_info['key_id'];?> <a href="#" style="font-weight:bold;color:white" onClick="$(this).closest('form').submit()">[<?=strtolower($lang['admin']['remove']);?>]</a></div>
<div class="label label-default">?? <?=$key_info['key_id'];?> <a href="#" style="font-weight:bold;color:white" onClick="$(this).closest('form').submit()">[<?=strtolower($lang['admin']['remove']);?>]</a></div>
</form>
<?php endforeach;
endif;?>
@ -63,6 +63,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
$username = $_SESSION['mailcow_cc_username'];
$get_tls_policy = get_tls_policy($_SESSION['mailcow_cc_username']);
$mailboxdata = mailbox_get_mailbox_details($username);
?>
<div class="container">
<h3><?=$lang['user']['user_settings'];?></h3>
@ -109,6 +110,18 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
</div>
</div>
<hr>
<div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['in_use'];?>:</div>
<div class="col-md-5 col-xs-7">
<div class="progress">
<div class="progress-bar progress-bar-<?=$mailboxdata['percent_class'];?>" role="progressbar" aria-valuenow="<?=$mailboxdata['percent_in_use'];?>" aria-valuemin="0" aria-valuemax="100" style="min-width:2em;width: <?=$mailboxdata['percent_in_use'];?>%;">
<?=$mailboxdata['percent_in_use'];?>%
</div>
</div>
<p><?=formatBytes($mailboxdata['quota_used'], 2);?> / <?=formatBytes($mailboxdata['quota'], 2);?>, <?=$mailboxdata['messages'];?> <?=$lang['user']['messages'];?></p>
</div>
</div>
<hr>
<?php // Show tagging options ?>
<form class="form-horizontal" role="form" method="post">
<?php $get_tagging_options = get_delimiter_action()['wants_tagged_subject'];?>
@ -402,7 +415,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<th class="sort-table" style="min-width: 35px;"><?=$lang['user']['interval'];?></th>
<th class="sort-table" style="min-width: 35px;"><?=$lang['user']['last_run'];?></th>
<th class="sort-table" style="min-width: 35px;">Log</th>
<th class="sort-table" style="max-width: 35px;"><?=$lang['user']['active'];?></th>
<th class="sort-table" style="max-width: 95px;"><?=$lang['user']['active'];?></th>
<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['user']['action'];?></th>
</tr>
</thead>

View File

@ -1,19 +1,20 @@
version: '2.1'
services:
pdns-mailcow:
image: andryyy/mailcow-dockerized:pdns
bind9-mailcow:
image: resystit/bind9
command: "named -c /etc/bind/named.conf -g -u named -4"
depends_on:
mysql-mailcow:
condition: service_healthy
volumes:
- ./data/conf/pdns/:/etc/powerdns/
- ./data/conf/bind9/named.conf:/etc/bind/named.conf
restart: always
networks:
mailcow-network:
ipv4_address: 172.22.1.254
aliases:
- pdns
- bind9
mysql-mailcow:
image: mariadb:10.1
@ -42,7 +43,7 @@ services:
redis-mailcow:
image: redis
depends_on:
- pdns-mailcow
- bind9-mailcow
volumes:
- redis-vol-1:/data/
restart: always
@ -56,8 +57,10 @@ services:
rspamd-mailcow:
image: andryyy/mailcow-dockerized:rspamd
build: ./data/Dockerfiles/rspamd
depends_on:
- nginx-mailcow
nginx-mailcow:
condition: service_healthy
volumes:
- ./data/conf/rspamd/override.d/:/etc/rspamd/override.d:ro
- ./data/conf/rspamd/local.d/:/etc/rspamd/local.d:ro
@ -76,9 +79,10 @@ services:
php-fpm-mailcow:
image: andryyy/mailcow-dockerized:phpfpm
build: ./data/Dockerfiles/php-fpm
command: "php-fpm -d date.timezone=${TZ}"
depends_on:
- pdns-mailcow
- bind9-mailcow
volumes:
- ./data/web:/web:ro
- ./data/conf/rspamd/dynmaps:/dynmaps:ro
@ -99,8 +103,9 @@ services:
sogo-mailcow:
image: andryyy/mailcow-dockerized:sogo
build: ./data/Dockerfiles/sogo
depends_on:
- pdns-mailcow
- bind9-mailcow
environment:
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
@ -121,8 +126,9 @@ services:
rmilter-mailcow:
image: andryyy/mailcow-dockerized:rmilter
build: ./data/Dockerfiles/rmilter
depends_on:
- pdns-mailcow
- bind9-mailcow
volumes:
- ./data/conf/rmilter/:/etc/rmilter.conf.d/:ro
restart: always
@ -136,8 +142,9 @@ services:
dovecot-mailcow:
image: andryyy/mailcow-dockerized:dovecot
build: ./data/Dockerfiles/dovecot
depends_on:
- pdns-mailcow
- bind9-mailcow
volumes:
- ./data/conf/dovecot:/etc/dovecot
- ./data/assets/ssl:/etc/ssl/mail/:ro
@ -165,11 +172,13 @@ services:
postfix-mailcow:
image: andryyy/mailcow-dockerized:postfix
build: ./data/Dockerfiles/postfix
depends_on:
- pdns-mailcow
- bind9-mailcow
volumes:
- ./data/conf/postfix:/opt/postfix/conf
- ./data/assets/ssl:/etc/ssl/mail/:ro
- postfix-vol-1:/var/spool/postfix
environment:
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
@ -191,7 +200,7 @@ services:
memcached-mailcow:
image: memcached
depends_on:
- pdns-mailcow
- bind9-mailcow
restart: always
dns:
- 172.22.1.254
@ -206,9 +215,19 @@ services:
- sogo-mailcow
- php-fpm-mailcow
image: nginx:mainline
command: /bin/bash -c "envsubst < /etc/nginx/conf.d/listen.template > /etc/nginx/conf.d/listen.active && nginx -g 'daemon off;'"
healthcheck:
test: ["CMD", "ping", "php-fpm-mailcow", "-c", "10"]
interval: 10s
timeout: 30s
retries: 5
command: /bin/bash -c "envsubst < /etc/nginx/conf.d/templates/listen_plain.template > /etc/nginx/conf.d/listen_plain.active &&
envsubst < /etc/nginx/conf.d/templates/listen_ssl.template > /etc/nginx/conf.d/listen_ssl.active &&
envsubst < /etc/nginx/conf.d/templates/server_name.template > /etc/nginx/conf.d/server_name.active &&
nginx -g 'daemon off;'"
environment:
- HTTPS_PORT=${HTTPS_PORT:-443}
- HTTP_PORT=${HTTP_PORT:-80}
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
volumes:
- ./data/web:/web:ro
- ./data/conf/rspamd/dynmaps:/dynmaps:ro
@ -218,10 +237,12 @@ services:
- 172.22.1.254
dns_search: mailcow-network
ports:
- "${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
- "${HTTPS_BIND:-0.0.0.0}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
- "${HTTP_BIND:-127.0.0.1}:${HTTP_PORT:-80}:${HTTP_PORT:-80}"
restart: always
networks:
mailcow-network:
ipv4_address: 172.22.1.251
aliases:
- nginx
@ -239,3 +260,4 @@ volumes:
dkim-vol-1:
redis-vol-1:
rspamd-vol-1:
postfix-vol-1:

View File

@ -16,8 +16,11 @@ if [ -z "$MAILCOW_HOSTNAME" ]; then
read -p "Hostname (FQDN): " -ei "mx.example.org" MAILCOW_HOSTNAME
fi
[[ -a /etc/timezone ]] && TZ=$(cat /etc/timezone)
if [ -z "$TZ" ]; then
read -p "Timezone: " -ei "Europe/Berlin" TZ
else
read -p "Timezone: " -ei ${TZ} TZ
fi
cat << EOF > mailcow.conf
@ -40,11 +43,23 @@ DBPASS=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28)
DBROOT=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28)
# ------------------------------
# Misc configuration
# HTTP/S Bindings
# ------------------------------
# You should use HTTPS, but in case of SSL offloaded reverse proxies:
HTTP_PORT=8080
HTTP_BIND=0.0.0.0
HTTPS_PORT=443
HTTPS_BIND=0.0.0.0
# ------------------------------
# Other bindings
# ------------------------------
# You should leave that alone
# Can also be 11.22.33.44:25 or 0.0.0.0:465 etc. for specific bindings
HTTPS_PORT=443
# Format: 11.22.33.44:25 or 0.0.0.0:465 etc.
# Do _not_ use IP:PORT in HTTPS_BIND or HTTPS_PORT
SMTP_PORT=25
SMTPS_PORT=465
SUBMISSION_PORT=587