#!/bin/bash # This file is part of Take It Easy Linux. # Take It Easy Linux is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Take It Easy Linux is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with Take It Easy Linux. If not, see . # Copyright © 2019 Ludovic Pouzenc # Fonctions utilitaires pour tracer, installer, télécharger, configurer t() { echo -n "[ ] ${BASH_LINENO[0]} $*" out=$($* 2>&1) if [ $? -eq 0 ] then echo -e "\r[\e[32m OK \e[0m]" else echo -e "\r[\e[1m\e[31mFAILED\e[0m]" echo "$out" fi } inst() { touch /tmp/installed if grep -q -- "$*" /tmp/installed then echo 'Already installed' else t apt-get install --quiet --yes $* && echo $* >> /tmp/installed fi } get() { filename=$1 baseurl=$2 if [ -f /tmp/$filename ] then echo 'Already downloaded' else t wget -O /tmp/$filename $baseurl/$filename fi } link() { target=$1 linkpath=$2 [ -L $linkpath ] || ln -sf $target $linkpath } confwrite() { file=$1 [ -s $file ] && cp -a $file $file.tmp cat > $file if [ -f $file.tmp ] then if diff -q $file $file.tmp then rm $file.tmp else mv $file.tmp $file.orig fi fi } confset() { file=$1 conf_keyword=$2 conf_valuematch=$3 conf_newvalue=$4 sed --in-place -e 's#^\(\s*\)\#\?\(\s*'$conf_keyword'\s*[ =]\+\s*\)'$conf_valuematch'\(.*\)$#\1\2'$conf_newvalue'\3#' $file } confenable() { file=$1 conf_keyword=$2 sed --in-place -e 's#^\(\s*\)\#\?\(\s*'$conf_keyword'\s*[ =]\+\s*'$conf_valuematch'.*\)$#\1\2#' $file } confappend() { file=$1 shift line=$* grep -q -- "$line" $file || echo $line >> $file } # Vérifications d'environnement if [ $UID -ne 0 ] then echo "Ce script est prévu pour être lancé par root uniquement" echo "Utilisez 'su -' ou 'sudo -i' pour obtenir un shell root" exit 1 fi # Paramètres détéectés et utilisés par la suite du script set -x host=$(hostname) domain=$(dnsdomainname) lang=${LANG%.*} set +x echo 'Saisissez un mot de passe admin pour le webmail et rspamd ou ctrl+C pour annuler' read -rsp 'Password: ' adminpass echo # Ajout des dépôts nécessaires et les clés GPG pour valider les paquets reçus t confwrite /etc/apt/sources.list.d/backports.list <<"EOT" deb http://ftp.debian.org/debian stretch-backports main EOT t confwrite /etc/apt/sources.list.d/rspamd.list <<"EOT" deb [arch=amd64] http://rspamd.com/apt-stable/ stretch main EOT t get gpg.key https://rspamd.com/apt-stable t apt-key add /tmp/gpg.key # Prise en compte des nouveaux dépots t apt-get update # Préremplissage des questions posées à l'installation de certains paquets t debconf-set-selections <<"EOT" exim4-config exim4/dc_eximconfig_configtype select internet site; mail is sent and received directly using SMTP exim4-config exim4/use_split_config boolean true EOT # Installation des paquets nécessaires # Obtention de certificat TLS gratuits t inst -t stretch-backports python-certbot-apache # Redis utilisé par rainloop et rspamd (et la version 3 buggue avec rspam, "127.0.0.1: connection refusée") t inst -t stretch-backports redis-server # Programmes pour traiter et stocker les mails t inst dovecot-lmtpd dovecot-imapd dovecot-sieve dovecot-managesieved exim4-daemon-heavy rspamd clamav-daemon # Prérequis webmail RainLoop t inst libapache2-mod-php php-curl php-iconv php-sqlite3 php-xml unzip # Système, sécurité, monitoring t inst apachetop fail2ban munin-node liblwp-useragent-determined-perl read -t10 -p 'Installation de paquets terminée, configuration dans 10 secondes (ctrl+C pour annuler)' garbage echo # Configuration munin-node rm /etc/munin/plugins/ntp_kernel_* 2>/dev/null t link /usr/share/munin/plugins/netstat /etc/munin/plugins/ t link /usr/share/munin/plugins/apache_accesses /etc/munin/plugins/ t link /usr/share/munin/plugins/apache_processes /etc/munin/plugins/ t link /usr/share/munin/plugins/apache_volume /etc/munin/plugins/ t service munin-node restart # Configuration redis (base de données mémoire simple pour webmail et rspamd) t confset /etc/redis/redis.conf maxmemory '' 500mb t confset /etc/redis/redis.conf maxmemory-policy 'noeviction' volatile-ttl t confwrite /etc/sysctl.d/50-redis-overcommit_memory.conf <<"EOT" vm.overcommit_memory = 1 EOT echo 1 > /proc/sys/vm/overcommit_memory t service redis-server restart # Configuration apache2 (serveur web) t confset /etc/apache2/mods-enabled/mpm_prefork.conf MaxRequestWorkers '[0-9]\+' 20 t confwrite /etc/apache2/sites-available/000-default.conf < ServerAdmin postmaster@$domain DocumentRoot /var/www/html ErrorLog /var/log/apache2/error.log CustomLog /var/log/apache2/access.log combined # Gère les redirections depuis http vers https sur le nom canonique RewriteEngine on RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge/ RewriteRule ^/(.*) https://$host.$domain/\$1 [L,R=301] # Accepte les requêtes challenge LetsEncrypt Options None AllowOverride None Require all granted # Ne pas publier les données du webmail (si mod_rewrite n'est pas actif) Options None AllowOverride None Require all denied EOT t confwrite /etc/apache2/sites-available/default-ssl.conf < ServerAdmin postmaster@$domain ServerName $host.$domain DocumentRoot /var/www/html ErrorLog /var/log/apache2/error.log CustomLog /var/log/apache2/access.log combined Options None AllowOverride None Require all granted # Ne pas publier les données du webmail Options None AllowOverride None Require all denied # Renvoi par défaut sur le webmail et (spam,rspamd) -> rspamd/ RewriteEngine on RewriteRule ^/$ https://$host.$domain/webmail/ [L,R=302] RewriteRule ^/spam$ https://$host.$domain/rspamd/ [L,R=302] RewriteRule ^/rspamd$ https://$host.$domain/rspamd/ [L,R=302] # Exposer l'interface de rspamd en https ProxyPass /rspamd/ http://127.0.0.1:11334/ ProxyPassReverse /rspamd/ http://127.0.0.1:11334/ ProxyRequests Off # Paramètres TLS de LetsEncrypt Include /etc/letsencrypt/options-ssl-apache.conf SSLCertificateFile /etc/letsencrypt/live/$domain/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/$domain/privkey.pem EOT t a2enmod proxy_http rewrite ssl t a2ensite default-ssl t confset /etc/php/7.0/apache2/php.ini upload_max_filesize '[0-9M]\+' 26M t confset /etc/php/7.0/apache2/php.ini post_max_size '[0-9M]\+' 30M # Pré-configuration certificats TLS (initialement : auto-signés, mais prêt pour LetsEncrypt) t mkdir -p /etc/letsencrypt/live/$domain/ t cp -a /usr/lib/python3/dist-packages/certbot_apache/options-ssl-apache.conf /etc/letsencrypt/ t link /etc/ssl/certs/ssl-cert-snakeoil.pem /etc/letsencrypt/live/$domain/fullchain.pem t link /etc/ssl/private/ssl-cert-snakeoil.key /etc/letsencrypt/live/$domain/privkey.pem t service apache2 restart # Déploiement webmail RainLoop t get rainloop-community-latest.zip https://www.rainloop.net/repository/webmail t mkdir -p /var/www/html/webmail t unzip -qnd /var/www/html/webmail /tmp/rainloop-community-latest.zip t chown -R root: /var/www/html/webmail t mkdir -p /var/www/html/webmail/data/_data_/_default_/logs/fail2ban/ t touch /var/www/html/webmail/data/_data_/_default_/logs/fail2ban/auth-empty.txt # Configuration webmail Rainloop t php -- "$domain" "$lang" "$adminpass" "(autoconfig webmail)" <<"EOT" Set('webmail', 'title', 'Webmail ' . $argv[1]); $oConfig->Set('webmail', 'language', $argv[2]); $oConfig->Set('webmail', 'attachment_size_limit', 25); $oConfig->Set('contacts', 'enable', 'On'); $oConfig->SetPassword($argv[3]); $oConfig->Set('login', 'default_domain', $argv[1]); $oConfig->Set('logs', 'enable', 'On'); $oConfig->Set('logs', 'write_on_error_only', 'On'); $oConfig->Set('logs', 'filename', 'syslog'); $oConfig->Set('logs', 'auth_logging', 'On'); $oConfig->Set('cache', 'fast_cache_driver', 'redis'); echo 'Config ' . ( $oConfig->Save() ? 'Saved' : 'Error') . "\n"; ?> EOT t confwrite /var/www/html/webmail/data/_data_/_default_/domains/$domain.ini < user=.* host=.* port=.*$ ignoreregex = EOT t confwrite /etc/fail2ban/jail.local <<"EOT" [rainloop] enabled = true banaction = iptables-multiport filter = rainloop port = http,https logpath = /var/www/html/webmail/data/_data_/_default_/logs/fail2ban/auth-*.txt maxretry = 5 findtime = 1500 bantime = 900 EOT t service fail2ban restart # Configuration Dovecot (serveur IMAP de consultation mail, filtres sieve) t confset /etc/dovecot/dovecot.conf verbose_proctitle '[a-z]\+' yes t confset /etc/dovecot/conf.d/10-auth.conf disable_plaintext_auth '[a-z]\+' yes t confset /etc/dovecot/conf.d/10-auth.conf auth_username_format '[%A-Za-z]\+' '%Ln' t confenable /etc/dovecot/conf.d/10-logging.conf mail_log_events '\S\+' t confenable /etc/dovecot/conf.d/10-logging.conf mail_log_fields '\S\+' t confset /etc/dovecot/conf.d/10-mail.conf mail_location '\S\+' 'mdbox:~/mail' t confset /etc/dovecot/conf.d/10-ssl.conf ssl 'no' required t confset /etc/dovecot/conf.d/10-ssl.conf ssl_cert '[a-z/<.]\+' " {$max_received_linelength}{998}} .endif # Deny unless the address list headers are syntactically correct. # # If you enable this, you might reject legitimate mail. .ifdef CHECK_DATA_VERIFY_HEADER_SYNTAX deny message = Message headers fail syntax check !acl = acl_local_deny_exceptions !verify = header_syntax .endif # require that there is a verifiable sender address in at least # one of the "Sender:", "Reply-To:", or "From:" header lines. .ifdef CHECK_DATA_VERIFY_HEADER_SENDER deny message = No verifiable sender address in message headers !acl = acl_local_deny_exceptions !verify = header_sender .endif # Deny if the message contains malware. Before enabling this check, you # must install a virus scanner and set the av_scanner option in the # main configuration. # # exim4-daemon-heavy must be used for this section to work. # # deny # malware = * # message = This message was detected as possible malware ($malware_name). # Actually scan message agaist spam (and malware trough rspamd) warn logwrite = spam-check: starting spam = Debian-exim:true logwrite = spam-check: ending # Defer message if spam scanner is not available defer condition = ${if eq{$spam_action}{}} message = Please try again later, spam scanner unavailable # FROM https://rspamd.com/doc/integration.html (+custom rule for classfied as virus) # use greylisting available in rspamd v1.3+ defer message = Please try again later condition = ${if eq{$spam_action}{soft reject}} # Reject message that have classified as virus deny message = Message classified as virus condition = ${if and { { eq{$spam_action}{reject} } { match {$spam_report}{CLAM_VIRUS} } } } # Reject message that have high-probability to be spam deny message = Message classified as spam condition = ${if eq{$spam_action}{reject}} # Remove foreign headers warn remove_header = x-spam-bar : x-spam-score : x-spam-report : x-spam-status # add spam-score and spam-report header when "add header" action is recommended by rspamd warn condition = ${if eq{$spam_action}{add header}} add_header = X-Spam-Score: $spam_score ($spam_bar) add_header = X-Spam-Report: $spam_report # add x-spam-status header if message is not ham warn ! condition = ${if match{$spam_action}{^no action\$|^greylist\$}} add_header = X-Spam-Status: Yes # This hook allows you to hook in your own ACLs without having to # modify this file. If you do it like we suggest, you'll end up with # a small performance penalty since there is an additional file being # accessed. This doesn't happen if you leave the macro unset. .ifdef CHECK_DATA_LOCAL_ACL_FILE .include CHECK_DATA_LOCAL_ACL_FILE .endif # accept otherwise accept EOT t confwrite /etc/exim4/conf.d/auth/50_dovecot_auth <<"EOT" dovecot_authplain: driver = dovecot public_name = PLAIN server_advertise_condition = ${if def:tls_cipher} server_socket = /var/run/dovecot/auth-client server_set_id = $auth1 EOT t confwrite /etc/exim4/conf.d/transport/50_dovecot_lmtp <<"EOT" dovecot_lmtp: driver = lmtp socket = /var/run/dovecot/lmtp #maximum number of deliveries per batch, default 1 batch_max = 200 #allow suffixes/prefixes (default unset) rcpt_include_affixes EOT t confset /etc/exim4/conf.d/main/02_exim4-config_options spamd_address '\S\+\s[0-9]\+' MAIN_SPAMD_ADDRESS t service exim4 restart # Afficher un texte explicatif en fin d'exécution iface=$(ip -o r | sed -ne 's/^default .* dev \(\S\+\).*$/\1/p') ip=$(ip -o a s dev $iface | sed -ne 's/^.*inet \([0-9.]\+\).*/\1/p') cat < URL mises en place : https://$host.$domain/webmail/ https://$host.$domain/webmail/?admin https://$host.$domain/rspamd/ Il est possible d'utiliser l'IP aussi (si le DNS n'est pas configuré) https://$ip/webmail/ EOT