diff --git a/data/Dockerfiles/clamav/Dockerfile b/data/Dockerfiles/clamav/Dockerfile index 5fc44d9a..170b7d8f 100755 --- a/data/Dockerfiles/clamav/Dockerfile +++ b/data/Dockerfiles/clamav/Dockerfile @@ -1,8 +1,8 @@ -FROM debian:latest +FROM debian:stretch-slim MAINTAINER https://m-ko.de Markus Kosmal # Debian Base to use -ENV DEBIAN_VERSION jessie +ENV DEBIAN_VERSION stretch # initial install of av daemon RUN echo "deb http://http.debian.net/debian/ $DEBIAN_VERSION main contrib non-free" > /etc/apt/sources.list && \ @@ -13,15 +13,14 @@ RUN echo "deb http://http.debian.net/debian/ $DEBIAN_VERSION main contrib non-fr clamav-daemon \ clamav-freshclam \ libclamunrar7 \ - wget && \ + curl && \ 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 +COPY dl_files.sh /dl_files.sh +RUN chmod +x /dl_files.sh +RUN /dl_files.sh # permission juggling RUN mkdir /var/run/clamav && \ @@ -33,9 +32,6 @@ 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 diff --git a/data/Dockerfiles/clamav/bootstrap.sh b/data/Dockerfiles/clamav/bootstrap.sh index 635e93ea..bc5d1b32 100755 --- a/data/Dockerfiles/clamav/bootstrap.sh +++ b/data/Dockerfiles/clamav/bootstrap.sh @@ -1,35 +1,7 @@ #!/bin/bash -# bootstrap clam av service and clam av database updater shell script -# presented by mko (Markus Kosmal) -set -m +trap "kill 0" SIGINT -# 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 +sleep inf diff --git a/data/Dockerfiles/clamav/dl_files.sh b/data/Dockerfiles/clamav/dl_files.sh new file mode 100755 index 00000000..09d61241 --- /dev/null +++ b/data/Dockerfiles/clamav/dl_files.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +declare -a DB_MIRRORS=( + "switch.clamav.net" + "clamavdb.heanet.ie" + "clamav.iol.cz" + "clamav.univ-nantes.fr" + "clamav.easynet.fr" + "clamav.begi.net" +) +declare -a DB_MIRRORS=( $(shuf -e "${DB_MIRRORS[@]}") ) + +DB_FILES=( + "bytecode.cvd" + "daily.cvd" + "main.cvd" +) + +for i in "${DB_MIRRORS[@]}"; do + for j in "${DB_FILES[@]}"; do + [[ -f "/var/lib/clamav/${j}" && -s "/var/lib/clamav/${j}" ]] && continue; + if [[ $(curl -o /dev/null --connect-timeout 1 \ + --max-time 1 \ + --silent \ + --head \ + --write-out "%{http_code}\n" "${i}/${j}") == 200 ]]; then + curl "${i}/${j}" -o "/var/lib/clamav/${j}" -# + fi + done +done + +chown clamav:clamav /var/lib/clamav/*.cvd diff --git a/data/Dockerfiles/dovecot/Dockerfile b/data/Dockerfiles/dovecot/Dockerfile index 90f7352d..1d3bfbef 100644 --- a/data/Dockerfiles/dovecot/Dockerfile +++ b/data/Dockerfiles/dovecot/Dockerfile @@ -4,8 +4,8 @@ MAINTAINER Andre Peters ENV DEBIAN_FRONTEND noninteractive ENV LC_ALL C -ENV DOVECOT_VERSION 2.2.28 -ENV PIGEONHOLE_VERSION 0.4.17 +ENV DOVECOT_VERSION 2.2.29.1 +ENV PIGEONHOLE_VERSION 0.4.18 RUN apt-get update \ && apt-get -y install libpam-dev \ diff --git a/data/Dockerfiles/postfix/Dockerfile b/data/Dockerfiles/postfix/Dockerfile index 0fcdc893..9da92ad6 100644 --- a/data/Dockerfiles/postfix/Dockerfile +++ b/data/Dockerfiles/postfix/Dockerfile @@ -23,6 +23,7 @@ RUN apt-get install -y --no-install-recommends supervisor \ gnupg \ python-gpgme \ sudo \ + curl \ dirmngr RUN addgroup --system --gid 600 zeyple @@ -34,6 +35,7 @@ COPY zeyple.py /usr/local/bin/zeyple.py COPY zeyple.conf /etc/zeyple.conf COPY supervisord.conf /etc/supervisor/supervisord.conf COPY postfix.sh /opt/postfix.sh +COPY whitelist_forwardinghosts.sh /usr/local/bin/whitelist_forwardinghosts.sh EXPOSE 588 diff --git a/data/Dockerfiles/postfix/whitelist_forwardinghosts.sh b/data/Dockerfiles/postfix/whitelist_forwardinghosts.sh new file mode 100755 index 00000000..4ad5ab32 --- /dev/null +++ b/data/Dockerfiles/postfix/whitelist_forwardinghosts.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +while read QUERY; do + QUERY=($QUERY) + if [ "${QUERY[0]}" != "get" ]; then + echo "500 dunno" + continue + fi + result=$(curl -s http://nginx:8081/forwardinghosts.php?host=${QUERY[1]}) + logger -t whitelist_forwardinghosts -p mail.info "Look up ${QUERY[1]} on whitelist, result $result" + echo ${result} +done diff --git a/data/conf/nginx/site.conf b/data/conf/nginx/site.conf index a78c483f..685e1fc7 100644 --- a/data/conf/nginx/site.conf +++ b/data/conf/nginx/site.conf @@ -18,10 +18,9 @@ server { access_log /var/log/nginx/access.log; root /web; - location /api/v1/ { - try_files $uri $uri/ /json_api.php?$args; + location ~ ^/api/v1/(.*)$ { + try_files $uri $uri/ /json_api.php?query=$1; } - rewrite ^/api/v1/([^/]+)/([^/]+)/?$ /json_api.php?action=$1&object=$2? last; location ^~ /.well-known/acme-challenge/ { allow all; @@ -64,10 +63,6 @@ server { 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; @@ -171,10 +166,9 @@ server { access_log /var/log/nginx/access.log; root /web; - location /api/v1/ { - try_files $uri $uri/ /json_api.php?$args; + location ~ ^/api/v1/(.*)$ { + try_files $uri $uri/ /json_api.php?query=$1; } - rewrite ^/api/v1/([^/]+)/([^/]+)/?$ /json_api.php?action=$1&object=$2? last; location ^~ /.well-known/acme-challenge/ { allow all; diff --git a/data/conf/postfix/main.cf b/data/conf/postfix/main.cf index b28f6eb9..52e86681 100644 --- a/data/conf/postfix/main.cf +++ b/data/conf/postfix/main.cf @@ -24,7 +24,7 @@ milter_default_action = accept milter_protocol = 6 minimal_backoff_time = 300s plaintext_reject_code = 550 -postscreen_access_list = permit_mynetworks, cidr:/opt/postfix/conf/postscreen_access.cidr +postscreen_access_list = permit_mynetworks, cidr:/opt/postfix/conf/postscreen_access.cidr, tcp:127.0.0.1:10027 postscreen_bare_newline_enable = no postscreen_blacklist_action = drop postscreen_cache_cleanup_interval = 24h diff --git a/data/conf/postfix/master.cf b/data/conf/postfix/master.cf index 3802c9a0..955728d1 100644 --- a/data/conf/postfix/master.cf +++ b/data/conf/postfix/master.cf @@ -55,3 +55,5 @@ zeyple unix - n n - - pipe -o smtpd_recipient_restrictions=permit_mynetworks,reject -o mynetworks=127.0.0.0/8 -o smtpd_authorized_xforward_hosts=127.0.0.0/8 + +127.0.0.1:10027 inet n n n - 0 spawn user=nobody argv=/usr/local/bin/whitelist_forwardinghosts.sh diff --git a/data/conf/rmilter/rmilter.conf b/data/conf/rmilter/rmilter.conf index f84408cd..d957935c 100644 --- a/data/conf/rmilter/rmilter.conf +++ b/data/conf/rmilter/rmilter.conf @@ -28,7 +28,6 @@ redis { }; tempdir = /tmp; tempfiles_mode = 00600; -max_size = 20M; strict_auth = yes; use_dcc = no; limits { diff --git a/data/conf/rspamd/dynmaps/forwardinghosts.php b/data/conf/rspamd/dynmaps/forwardinghosts.php new file mode 100644 index 00000000..377c5e7e --- /dev/null +++ b/data/conf/rspamd/dynmaps/forwardinghosts.php @@ -0,0 +1,56 @@ + 1) + $mask = $net[1]; + $net = inet_pton($net[0]); + $addr = inet_pton($addr); + + $length = strlen($net); // 4 for IPv4, 16 for IPv6 + if (strlen($net) != strlen($addr)) + return FALSE; + if (!isset($mask)) + $mask = $length * 8; + + $addr_bin = ''; + $net_bin = ''; + for ($i = 0; $i < $length; ++$i) + { + $addr_bin .= str_pad(decbin(ord(substr($addr, $i, $i+1))), 8, '0', STR_PAD_LEFT); + $net_bin .= str_pad(decbin(ord(substr($net, $i, $i+1))), 8, '0', STR_PAD_LEFT); + } + + return substr($addr_bin, 0, $mask) == substr($net_bin, 0, $mask); +} + +$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 host FROM `forwarding_hosts`"); + $networks = $stmt->fetchAll(PDO::FETCH_COLUMN); + foreach ($networks as $network) + { + if (in_net($_GET['host'], $network)) + { + echo '200 permit'; + exit; + } + } + echo '200 dunno'; +} +catch (PDOException $e) { + echo '200 dunno'; + exit; +} +?> diff --git a/data/conf/rspamd/dynmaps/settings.php b/data/conf/rspamd/dynmaps/settings.php index 9be1f696..098ffbd9 100644 --- a/data/conf/rspamd/dynmaps/settings.php +++ b/data/conf/rspamd/dynmaps/settings.php @@ -32,6 +32,35 @@ catch (PDOException $e) { ?> settings { query("SELECT `host` FROM `forwarding_hosts`"); + $rows = $stmt->fetchAll(PDO::FETCH_COLUMN); +} +catch (PDOException $e) { + $rows = array(); +} + +if ($rows) +{ +?> + whitelist_forwarding_hosts { + priority = high; + + apply "default" { + actions { + reject = 999.9; + } + } + symbols [ + "WHITELIST_FORWARDING_HOST" + ] + } +query("SELECT DISTINCT `object` FROM `filterconf` WHERE `option` = 'highspamlevel' OR `option` = 'lowspamlevel'"); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); @@ -207,8 +236,11 @@ while ($row = array_shift($rows)) { } ?> apply "default" { - MAILCOW_MOO = -999.0; + MAILCOW_WHITE = -999.0; } + symbols [ + "MAILCOW_WHITE" + ] } apply "default" { - MAILCOW_MOO = 999.0; + MAILCOW_BLACK = 999.0; } + symbols [ + "MAILCOW_BLACK" + ] } -} \ No newline at end of file +} diff --git a/data/conf/rspamd/local.d/antivirus.conf b/data/conf/rspamd/local.d/antivirus.conf new file mode 100644 index 00000000..92ba684c --- /dev/null +++ b/data/conf/rspamd/local.d/antivirus.conf @@ -0,0 +1,7 @@ +clamav { + attachments_only = false; + symbol = "CLAM_VIRUS"; + type = "clamav"; + log_clean = true; + servers = "clamd:3310"; +} diff --git a/data/conf/rspamd/local.d/dkim.conf b/data/conf/rspamd/local.d/dkim.conf deleted file mode 100644 index c199c6ae..00000000 --- a/data/conf/rspamd/local.d/dkim.conf +++ /dev/null @@ -1,34 +0,0 @@ -sign_condition =<
- +
diff --git a/data/web/admin.php b/data/web/admin.php index e0e04aee..ca20af8a 100644 --- a/data/web/admin.php +++ b/data/web/admin.php @@ -183,9 +183,11 @@ $tfa_data = get_tfa();

+ +
+
-

+ +
+
+
+

+
+
+ + + + + + + + + + source; + $host = $host->host; + ?> + + + + + + + + + + + +
+
+ +
+
+
+
+ +

+
+
+ +
+ +
+
+
+
+ +
+
+
+
+ +
+
-getRegisterData(getRegs($username)); - list($req, $sigs) = $data; - $_SESSION['regReq'] = json_encode($req); -?> - -getMessage(); - } - break; - - case 'authenticate': - try { - $reqs = json_encode($u2f->getAuthenticateData(getRegs($username))); - $_SESSION['authReq'] = $reqs; -?> - -getMessage(); - } - break; - } - } - if (!empty($_POST['u2f_register_data'])) { - try { - $reg = $u2f->doRegister(json_decode($_SESSION['regReq']), json_decode($_POST['u2f_register_data'])); - addReg($username, $reg); - } - catch (Exception $e) { - echo "U2F error: " . $e->getMessage(); - } - finally { - echo "Success"; - $_SESSION['regReq'] = null; - } - } - if (!empty($_POST['u2f_auth_data'])) { - try { - $reg = $u2f->doAuthenticate(json_decode($_SESSION['authReq']), getRegs($username), json_decode($_POST['u2f_auth_data'])); - updateReg($reg); - } - catch (Exception $e) { - echo "U2F error: " . $e->getMessage(); - } - finally { - echo "Success"; - $_SESSION['authReq'] = null; - } - } - } -?> - - -
-
- - -
-
-
- -
-Username:

-Action:
- Register
- Authenticate
- -
- - - diff --git a/docker-compose.yml b/docker-compose.yml index b3d18790..f2c80a4d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -55,6 +55,18 @@ services: aliases: - redis + clamd-mailcow: + image: mailcow/clamd + build: ./data/Dockerfiles/clamav + restart: always + dns: + - 172.22.1.254 + dns_search: mailcow-network + networks: + mailcow-network: + aliases: + - clamd + rspamd-mailcow: image: mailcow/rspamd build: ./data/Dockerfiles/rspamd diff --git a/docs/first_steps.md b/docs/first_steps.md index ab7876dc..cba908e5 100644 --- a/docs/first_steps.md +++ b/docs/first_steps.md @@ -17,9 +17,9 @@ This is just an example of how to obtain certificates with certbot. There are se wget https://dl.eff.org/certbot-auto -O /usr/local/sbin/certbot && chmod +x /usr/local/sbin/certbot ``` -2\. Make sure you set `HTTP_BIND=0.0.0.0` and `HTTP_PORT=80` in `mailcow.conf` or setup a reverse proxy to enable connections to port 80. If you changed HTTP_BIND, then restart Nginx: +2\. Make sure you set `HTTP_BIND=0.0.0.0` and `HTTP_PORT=80` in `mailcow.conf` or setup a reverse proxy to enable connections to port 80. If you changed HTTP_BIND, then rebuild Nginx: ``` bash -docker-compose restart nginx-mailcow +docker-compose up -d ``` 3\. Request the certificate with the webroot method: @@ -35,6 +35,8 @@ certbot certonly \ --email you@example.org \ --agree-tos ``` + +**Remember to replace the example.org domain with your own domain, this command will not work if you dont.** 4\. Create hard links to the full path of the new certificates. Assuming you are still in the mailcow root folder: ``` bash @@ -158,6 +160,99 @@ docker-compose exec postfix-mailcow chmod 660 /opt/postfix/conf/smarthost_passwd docker-compose exec postfix-mailcow postfix reload ``` +### Helper script + +There is a helper script `mailcow-setup-relayhost.sh` you can run to setup a relayhost. + +``` bash +Usage: + +Setup a relayhost: +./mailcow-setup-relayhost.sh relayhost port (username) (password) +Username and password are optional parameters. + +Reset to defaults: +./mailcow-setup-relayhost.sh reset +``` + +## Optional: Log to Syslog + +Enable Rsyslog to receive logs on 524/tcp: + +``` +# This setting depends on your Rsyslog version and configuration format. +# For most Debian derivates it will work like this... +$ModLoad imtcp +$TCPServerAddress 127.0.0.1 +$InputTCPServerRun 524 + +# ...while for Ubuntu 16.04 it looks like this: +module(load="imtcp") +input(type="imtcp" address="127.0.0.1" port="524") + +# No matter your Rsyslog version, you should set this option to off +# if you plan to use Fail2ban +$RepeatedMsgReduction off +``` + +Restart rsyslog after enabling the TCP listener. + +Now setup Docker daemon to start with the syslog driver. +This enables the syslog driver for all containers! + +Debian users can change the startup configuration in `/etc/default/docker` while CentOS users find it in `/etc/sysconfig/docker`: +``` +... +DOCKER_OPTS="--log-driver=syslog --log-opt syslog-address=tcp://127.0.0.1:524" +... +``` + +**Caution:** For some reason Ubuntu 16.04 and some, but not all, systemd based distros do not read the defaults file parameters. + +Just run `systemctl edit docker.service` and add the following content to fix it. + +**Note:** If "systemctl edit" is not available, just copy the content to `/etc/systemd/system/docker.service.d/override.conf`. + +The first empty ExecStart parameter is not a mistake. + +``` +[Service] +EnvironmentFile=/etc/default/docker +ExecStart= +ExecStart=/usr/bin/docker daemon -H fd:// $DOCKER_OPTS +``` + +Restart the Docker daemon and run `docker-compose down && docker-compose up -d` to recreate the containers. + +### Use Fail2ban + +**This is a subsection of "Log to Syslog", which is required for Fail2ban to work.** + +Open `/etc/fail2ban/filter.d/common.conf` and search for the prefix_line parameter, change it to ".*": + +``` +__prefix_line = .* +``` + +Create `/etc/fail2ban/jail.d/dovecot.conf`... +``` +[dovecot] +enabled = true +filter = dovecot +logpath = /var/log/syslog +chain = FORWARD +``` + +and `jail.d/postfix-sasl.conf`: +``` +[postfix-sasl] +enabled = true +filter = postfix-sasl +logpath = /var/log/syslog +chain = FORWARD +``` + +Restart Fail2ban. ## Install a local MTA diff --git a/docs/index.md b/docs/index.md index f4aea569..c48e0d17 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,8 @@ # mailcow: dockerized - 🐮 + 🐋 = 💕 -[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JWBSYHF4SMC68) +[![Servercow](https://www.servercow.de/img/cow_globe_200.svg)](https://www.servercow.de) + +If you want to support mailcow, consider hosting mailcow on a Servercow virtual machine @ Servercow! ## Screenshots @@ -8,9 +10,11 @@ You can find screenshots [on Imgur](http://imgur.com/a/oewYt). ## Overview -mailcow dockerized comes with **11 containers** linked in **one bridged network**. +mailcow dockerized comes with **12 containers** linked in **one bridged network**. +Each container represents a single application. - Dovecot +- ClamAV - Memcached - Redis - MySQL @@ -22,7 +26,7 @@ mailcow dockerized comes with **11 containers** linked in **one bridged network* - Rspamd - SOGo -**6 volumes** to keep dynamic data - take care of them! +**7 volumes** to keep dynamic data - take care of them! - vmail-vol-1 - dkim-vol-1 @@ -30,6 +34,7 @@ mailcow dockerized comes with **11 containers** linked in **one bridged network* - mysql-vol-1 - rspamd-vol-1 - postfix-vol-1 +- crypt-vol-1 The integrated **mailcow UI** allows administrative work on your mail server instance as well as separated domain administrator and mailbox user access: @@ -43,6 +48,6 @@ The integrated **mailcow UI** allows administrative work on your mail server ins - imapsync to migrate or pull remote mailboxes regularly - TFA: Yubi OTP and U2F USB (Google Chrome and derivates only) - Add domains, mailboxes, aliases, domain aliases and SOGo resources - +- Add whitelisted hosts to forward mail to mailcow *[Looking for a farm to host your cow?](https://www.servercow.de)* diff --git a/docs/u_and_e.md b/docs/u_and_e.md index c80f0618..d61b81eb 100644 --- a/docs/u_and_e.md +++ b/docs/u_and_e.md @@ -75,7 +75,7 @@ Beware that a mailbox user can login to mailcow and override a domain policy fil Make your changes in `data/Dockerfiles/$service` and build the image locally: ``` -docker build data/Dockerfiles/service -t andryyy/mailcow-dockerized:$service +docker build data/Dockerfiles/service -t mailcow/$service ``` Now auto-recreate modified containers: @@ -311,14 +311,11 @@ Running `docker-compose down -v` will **destroy all mailcow: dockerized volumes* ## Reset admin password Reset mailcow admin to `admin:moohoo`: -1\. Drop admin table ``` -source mailcow.conf -docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP TABLE admin;" +cd mailcow_path +bash reset_admin.sh ``` -2\. Open mailcow UI to auto-init the db - ## Rspamd ### Learn spam and ham @@ -521,6 +518,14 @@ map $http_upgrade $connection_upgrade { Now you can simply navigate to https://${MAILCOW_HOSTNAME}/portainer/ to view your Portainer container monitoring page. You’ll then be prompted to specify a new password for the **admin** account. After specifying your password, you’ll then be able to connect to the Portainer UI. +## Change autodiscover setup type + +This disables ActiveSync in the autodiscover service for Outlook and configures it with IMAP and SMTP instead: + +Open `data/web/autodiscover.php` and set `'useEASforOutlook' => 'yes'` to `'useEASforOutlook' => 'no'`. + +To always use IMAP and SMTP instead of EAS, set `'autodiscoverType' => 'imap'`. + ## Why Bind? For DNS blacklist lookups and DNSSEC. diff --git a/mailcow-reset-admin.sh b/mailcow-reset-admin.sh new file mode 100755 index 00000000..7ce1def8 --- /dev/null +++ b/mailcow-reset-admin.sh @@ -0,0 +1,36 @@ +#/bin/bash +if [[ ! -f mailcow.conf ]]; then + echo "Cannot find mailcow.conf, make sure this script is run from within the mailcow folder." + exit 1 +fi + +echo -n "Checking MySQL service... " +docker-compose ps -q mysql-mailcow > /dev/null 2>&1 + +if [[ $? -ne 0 ]]; then + echo "failed" + echo "MySQL (mysql-mailcow) is not up and running, exiting..." + exit 1 +fi + +echo "OK" +read -r -p "Are you sure you want to reset the mailcow administrator account? [y/N] " response +response=${response,,} # tolower +if [[ "$response" =~ ^(yes|y)$ ]]; then + echo -e "\nWorking, please wait..." + source mailcow.conf + docker-compose exec -T mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM admin;" + docker-compose exec -T mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "INSERT INTO admin (username, password, superadmin, created, modified, active) VALUES ('admin', '{SSHA256}K8eVJ6YsZbQCfuJvSUbaQRLr0HPLz5rC9IAp0PAFl0tmNDBkMDc0NDAyOTAxN2Rk', 1, NOW(), NOW(), 1);" + docker-compose exec -T mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM domain_admins WHERE username='admin';" + docker-compose exec -T mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "INSERT INTO domain_admins (username, domain, created, active) VALUES ('admin', 'ALL', NOW(), 1);" + docker-compose exec -T mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM tfa WHERE username='admin';" + echo " +Reset credentials: +--- +Username: admin +Password: moohoo +TFA: none +" +else + echo "Operation canceled." +fi diff --git a/mailcow-setup-relayhost.sh b/mailcow-setup-relayhost.sh new file mode 100755 index 00000000..283590df --- /dev/null +++ b/mailcow-setup-relayhost.sh @@ -0,0 +1,76 @@ +#/bin/bash +if [[ ! -f mailcow.conf ]]; then + echo "Cannot find mailcow.conf, make sure this script is run from within the mailcow folder." + exit 1 +fi + +echo -n "Checking Postfix service... " +docker-compose ps -q postfix-mailcow > /dev/null 2>&1 + +if [[ $? -ne 0 ]]; then + echo "failed" + echo "Postfix (postifx-mailcow) is not up and running, exiting..." + exit 1 +fi + +echo "OK" + +if [[ -z ${1} ]]; then + echo "Usage:" + echo + echo "Setup a relayhost:" + echo "${0} relayhost port (username) (password)" + echo "Username and password are optional parameters." + echo + echo "Reset to defaults:" + echo "${0} reset" + exit 1 +fi + +if [[ ${1} == "reset" ]]; then + # Reset modified values to their defaults + sed -i "s/^relayhost\ \=.*/relayhost\ \=/" data/conf/postfix/main.cf + sed -i "s/^smtp\_sasl\_password\_maps.*/smtp\_sasl\_password\_maps\ \=/" data/conf/postfix/main.cf + sed -i "s/^smtp\_sasl\_security\_options.*/smtp\_sasl\_security\_options\ \=\ noplaintext\,\ noanonymous/" data/conf/postfix/main.cf + sed -i "s/^smtp\_sasl\_auth\_enable.*/smtp\_sasl\_auth\_enable\ \=\ no/" data/conf/postfix/main.cf + # Also delete the plaintext password file + rm -f data/conf/postfix/smarthost_passwd* + docker-compose exec postfix-mailcow postfix reload + # Exit with dc exit code + exit $? +else + # Try a simple connection to host:port but don't recieve any data + # Abort after 3 seconds + if ! nc -z -v -w3 ${1} ${2} 2>/dev/null; then + echo "Connection to relayhost ${1} failed, aborting..." + exit 1 + fi + # Use exact hostname as relayhost, don't lookup the MX record of relayhost + sed -i "s/relayhost\ \=.*/relayhost\ \=\ \[${1}\]\:${2}/" data/conf/postfix/main.cf + if grep -q "smtp_sasl_password_maps" data/conf/postfix/main.cf + then + sed -i "s/^smtp\_sasl\_password\_maps.*/smtp\_sasl\_password\_maps\ \=\ hash\:\/opt\/postfix\/conf\/smarthost\_passwd/" data/conf/postfix/main.cf + else + echo "smtp_sasl_password_maps = hash:/opt/postfix/conf/smarthost_passwd" >> data/conf/postfix/main.cf + fi + if grep -q "smtp_sasl_auth_enable" data/conf/postfix/main.cf + then + sed -i "s/^smtp\_sasl\_auth\_enable.*/smtp\_sasl\_auth\_enable\ \=\ yes/" data/conf/postfix/main.cf + else + echo "smtp_sasl_auth_enable = yes" >> data/conf/postfix/main.cf + fi + if grep -q "smtp_sasl_security_options" data/conf/postfix/main.cf + then + sed -i "s/^smtp\_sasl\_security\_options.*/smtp\_sasl\_security\_options\ \=/" data/conf/postfix/main.cf + else + echo "smtp_sasl_security_options =" >> data/conf/postfix/main.cf + fi + if [[ ! -z ${3} ]]; then + echo ${1} ${3}:${4} > data/conf/postfix/smarthost_passwd + docker-compose exec postfix-mailcow postmap /opt/postfix/conf/smarthost_passwd + fi + docker-compose exec postfix-mailcow chown root:postfix /opt/postfix/conf/smarthost_passwd /opt/postfix/conf/smarthost_passwd.db + docker-compose exec postfix-mailcow chmod 660 /opt/postfix/conf/smarthost_passwd /opt/postfix/conf/smarthost_passwd.db + docker-compose exec postfix-mailcow postfix reload + exit $? +fi