====== Konfiguration Server ======

===== Vorbereitung =====

Geänderte Konfigurationsdateien ansehen:

<code>
sudo debsums -ce
diff /etc/whatever{,.dpkg-dist}
</code>


===== Basis-Setup =====

Setup eines Befragungsservers auf Basis eines Debian oder Ubuntu Servers.

SSH KeepAlive, vgl. [[https://patrickmn.com/aside/how-to-keep-alive-ssh-sessions/|How to Keep Alive SSH Sessions]]

<code bash>
sudo vim /etc/ssh/ssh_config
Host *
    ServerAliveInterval 300
    ServerAliveCountMax 2
</code>

<code bash>
sudo vim /etc/ssh/sshd_config
ClientAliveInterval 60
ClientAliveCountMax 2
</code>

Key-Fingerprints für SSH notieren (''ssh_host_rsa_key'' und alle anderen, mal als md5, mal ohne):

<code bash>
sudo ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key -E md5
</code>

Benutzer einrichten, Kennwörter in KeePass. ''s2access'' bekommt ein 32-stelliges Passwort, sosci erlaubt den Login nur via KeyFile.

<code>
sudo useradd -m sosci
sudo useradd -m s2access
sudo passwd sosci
sudo passwd s2access
sudo usermod -a -G sudo sosci
sudo usermod -a -G sudo s2access
</code>

Noch die Shell ändern:

<code>
sudo vim /etc/passwd
sosci:x:1001:1001::/home/sosci:/bin/bash
s2access:x:1002:1002::/home/s2access:/bin/bash
</code>

Kein Passwort für ''root''

<code>
sudo vim /etc/shadow
</code>
  
Zugang via Keyfile ([[https://vorkbaard.nl/using-putty-and-keyfiles-to-ssh-into-your-ubuntu-12-04-server/|Using PuTTY and keyfiles to SSH...]]), dafür Login als ''sosci''.

<code>
mkdir ~/.ssh
chmod -R 700 ~/.ssh
vim ~/.ssh/authorized_keys
# Inhalt aus ''puttygen''
chmod 600 ~/.ssh/authorized_keys

# Edit /etc/ssh/sshd_config so it contains
sudo vim /etc/ssh/sshd_config
AuthorizedKeysFile %h/.ssh/authorized_keys
PermitRootLogin no

sudo service ssh restart
</code>

Passwort-Login für alles außer s2access deaktivieren, vgl. [[https://serverfault.com/q/285800/100194|How to disable SSH login with password for some users?]]

<code>
sudo vim /etc/ssh/sshd_config
# Am Ende der Datei einfügen
PasswordAuthentication no
Match User s2access
PasswordAuthentication yes

sudo service ssh restart
</code>

Eventuell noch früheren Nutzer löschen.

<code>
sudo deluser whatever
</code>


===== Software =====

  * Einige kleine Dienstprogramme
    * Browser ''elinks'' zum Abruf des Server-Status
  * Einige Sicherheitsfeatures
    * [[http://munin-monitoring.org/wiki/munin.conf|Munin]] zur Überwachung der Server-Leistung -- [[http://www.darkcoding.net/software/setting-up-munin-on-ubuntu/|Setup-Tipps]]
    * Monit zur Überwachung der Erreichbarkeit (inkl. Neustart des Servers bei Problemen)
    * iptables für die Firewall ([[http://www.debian-administration.org/articles/445|Getting IPTables to survive a reboot]])
  * Basis-Software für den Webserver
    * MySQL Datenbank
    * Apache Webserver ([[http://library.linode.com/web-servers/apache/installation/debian-6-squeeze|Apache 2 Web Server on Debian 6]]) oder ''nginx''
    * ''apachetop'' Zur schnellen Kontrolle der Server-Last
    * PHP
    * Certbot zur Aufschaltung von Domains und HTTPS via Let's Encrypt

<code>
sudo apt update
sudo apt dist-upgrade
sudo apt remove apache2
sudo apt autoremove

sudo apt install less vim elinks debsums fail2ban munin libwww-perl monit unattended-upgrades duplicity duply tmux ncdu
sudo apt install iptables rkhunter chkrootkit mailutils apache2-utils
sudo apt install nginx mysql-server apachetop php php-fpm php-mysql php-json php-xml php-gd php-mbstring php-zip php-intl dovecot-pop3d opendkim opendkim-tools 

sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot

sudo apt-get update
sudo apt-get install python-certbot-nginx
</code>


===== Sicherheit =====

==== Sicherheitsupdates ====

''unattended-upgrades'' muss noch aktiviert werden, vgl [[https://wiki.debian.org/UnattendedUpgrades|UnattendedUpgrades]]

<code>
sudo dpkg-reconfigure -plow unattended-upgrades
</code>

<file conf  /etc/apt/apt.conf.d/50unattended-upgrades>
Unattended-Upgrade::Mail "info@soscisurvey.de";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "05:00";
</file>

==== Wöchentliche Updates ====

<code>
sudo vim /root/mailinfo.update.sh
</code>

<file bash /root/mailinfo.update.sh>
#!/bin/sh
printf "Subject:[Server] s2survey.net Update\n\n%s\n." "`/root/update.sh`" | /usr/sbin/sendmail -t info@soscisurvey.de
</file>

<code>
sudo vim /root/update.sh
</code>

<file bash /root/update.sh>
#!/bin/bash
sudo apt-get update
sudo apt-get --quiet --assume-yes --show-upgraded dist-upgrade
sudo apt-get autoremove
</file>

<file bash /root/kernel-clean.sh>
#! /bin/bash
printf "\n\nAktueller Kernel\n"
uname -a

printf "\n\nZu entfernen\n"
dpkg -l 'linux-[ihs]*' | sed '/^ii/!d;/'"$(uname -r | sed "s/\([-0-9]*\)-\([^0-9]\+\)/\1/")"'/d;s/^[^ ]* [^ ]* \([^ ]*\).*/\1/;/[0-9]/!d' | tee kernel-clean.list

printf "\n"
read -p "Press enter to continue"

cat kernel-clean.list | xargs sudo apt-get -y purge
rm kernel-clean.list
</file>

<file bash /root/mailinfo.reboot.sh>
#!/bin/sh
printf "Subject:[Server] s2survey.net (2019) Reboot\n\nServer rebootet\n." | /usr/sbin/sendmail -t info@soscisurvey.de
</file>

<file bash /root/postfix.sh>
#!/bin/bash
# Update the postfix databases and reload
postmap /etc/postfix/virtual
postmap /etc/postfix/senders
postalias /etc/aliases
postfix reload
</file>

<code>
sudo chmod u+x /root/kernel-clean.sh
sudo chmod u+x /root/update.sh
sudo chmod u+x /root/mailinfo.update.sh
sudo chmod u+x /root/mailinfo.reboot.sh
sudo chmod u+x /root/postfix.sh
</code>


==== Backup-Scripte ====

Siehe auch [[https://www.thomas-krenn.com/de/wiki/Backup_unter_Linux_mit_duply|Backup unter Linux mit duply]].

<code bash>
sudo vim /etc/fstab
mkdir /mnt/backup
touch /mnt/backup/NOT\ MOUNTED
touch /mnt/backup/s2survey2019
</code>

<code bash>
nas01.internetwire.de:/home/sosci /mnt/backup nfs nfsvers=3,soft,timeo=30,addr=95.130.16.146 0 0
</code>

<file bash /root/backup/backup-daily.sh>
#!/bin/bash
echo "



********** Backup (Daily) **********" >> /var/log/backup-daily.log
date >> /var/log/backup-daily.log
mount /mnt/backup >> /var/log/backup-daily.log
echo "
" >> /var/log/backup-daily.log

/usr/bin/duply database backup_purge --force >> /var/log/backup-daily.log
#/usr/bin/duply sosci-database-A backup_purge --force >> /var/log/backup-daily.log
#/usr/bin/duply sosci-database-B backup_purge --force >> /var/log/backup-daily.log
#/usr/bin/duply server backup_purge --force >> /var/log/backup-daily.log
/usr/bin/duply files backup_purge --force >> /var/log/backup-daily.log

sleep 20
umount /mnt/backup >> /var/log/backup-daily.log
</file>


<file bash /root/backup/backup-weekly.sh>
#!/bin/bash
echo "


***** Backup (Weekly) *****" >> /var/log/backup-weekly.log
date >> /var/log/backup-weekly.log
mount /mnt/backup >> /var/log/backup-weekly.log
echo "
" >> /var/log/backup-weekly.log

/usr/bin/duply server backup_purge --force >> /var/log/backup-weekly.log
# /usr/bin/duply s2server-etc backup_purge --force >> /var/log/backup-weekly.log
# /usr/bin/duply s2server-root backup_purge --force >> /var/log/backup-weekly.log

# Disk space information sent to standard output

echo "***** Backup Weekly Stats *****
"
/usr/local/nagios/libexec/check_disk -w 15% -c 8% -p /mnt/backup
echo ""
du /mnt/backup --max-depth=2 -h

sleep 20

umount /mnt/backup >> /var/log/backup-weekly.log
</file>

<code bash>
sudo chmod u+x /root/backup/backup-daily.sh
sudo chmod u+x /root/backup/backup-weekly.sh
sudo duply server create
sudo duply database create
sudo duply files create
sudo mkdir /root/backup/
sudo mkdir /root/backup/mysql/
</code>

<code bash>
sudo su
cd ~/.duply/server
gpg --gen-key
mv conf conf.dist
vim conf
vim exclude
</code>

<file bash /root/.duply/server/conf>
GPG_KEY='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
GPG_PW='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
GPG_OPTS="--pinentry-mode loopback"
TARGET='file:///mnt/backup/s2survey2019/server'
TARGET_USER=''
TARGET_PASS=''
SOURCE='/'
MAX_AGE=3M
MAX_FULLBKP_AGE=2M
DUPL_PARAMS="$DUPL_PARAMS --full-if-older-than $MAX_FULLBKP_AGE "
</file>

<file bash /root/.duply/server/exclude>
/initrd.img
/lib64
/vmlinuz
/dev/
/proc/
/sys/
/tmp/
/run/
/mnt/
/media/
/lost+found/

/home/sosci/backup/
/usr/share/doc/
/usr/src/
/var/backup/
/var/cache/
/var/www/
/var/tmp/
/var/lib/mysql/
/var/lib/lxcfs/cgroup/
/root/.cache/
/root/backup/mysql/
</file>

<code bash>
cd ~/.duply/database
mv conf conf.dist
vim conf
vim pre
vim post
chmod u+x pre
chmod u+x post
</code>

<file bash /root/.duply/database/conf>
GPG_KEY='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
GPG_PW='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
GPG_OPTS="--pinentry-mode loopback"
TARGET='file:///mnt/backup/s2survey2019/database'
SOURCE='/root/backup/mysql'
MAX_AGE=1M
MAX_FULL_BACKUPS=31
MAX_FULLBKP_AGE=23h
DUPL_PARAMS="$DUPL_PARAMS --full-if-older-than $MAX_FULLBKP_AGE "
</file>

<file bash /root/.duply/database/pre>
#!/bin/sh
datetime=`date +%Y-%m-%d_%H-%M-%S`
mysqldump -ubackup -pXXXXXXXXXXXXXXXXXXXX --databases s2survey --ignore-table s2survey.s2_receivers --quick --single-transaction | gzip -9 > /root/backup/mysql/s2survey-$datetime.sql.gz
mysqldump -ubackup -pXXXXXXXXXXXXXXXXXXXX --quick --single-transaction s2survey s2_receivers | gzip -9 > /root/backup/mysql/s2survey-receivers-$datetime.sql.gz
</file>

<file bash /root/.duply/database/post>
#!/bin/sh
rm /root/backup/mysql/s2survey-*
</file>

<code bash>
cd ~/.duply/files
mv conf conf.dist
vim conf
vim exclude
</code>

Das Backup kann entweder auf das lokale Laufwerk (gemountetet Backup-Speicher) oder vis SFTP erfolgen. Eventuell ist dafür noch das Modul ''python-paramiko'' (via ''apt'')) erforderlich.

<file bash /root/.duply/files/conf>
GPG_KEY='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
GPG_PW='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
GPG_OPTS="--pinentry-mode loopback"
TARGET='file:///mnt/backup/s2survey2019/files'
SOURCE='/var/www/s2survey/html'
MAX_AGE=1M
MAX_FULL_BACKUPS=2
MAX_FULLBKP_AGE=14D
DUPL_PARAMS="$DUPL_PARAMS --full-if-older-than $MAX_FULLBKP_AGE "
</file>

<file bash /root/.duply/files/conf>
GPG_KEY='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
GPG_PW=''
TARGET='sftp://s2backup@207.180.212.193/sosci/files'
TARGET_USER='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
TARGET_PASS='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
SOURCE='/var/www/soscisurvey/html'
MAX_AGE=13D
MAX_FULL_BACKUPS=1
MAX_FULLBKP_AGE=1M
DUPL_PARAMS="$DUPL_PARAMS --full-if-older-than $MAX_FULLBKP_AGE "
</file>

<file bash /root/.duply/files/exclude>
/var/www/s2survey/html/view/
/var/www/s2survey/html/system/cache/
/var/www/s2survey/html/system/temp/
/var/www/s2survey/html/system/session/
/var/www/s2survey/html/system/lock/

/var/www/s2survey/html/files/protected/000000/
/var/www/s2survey/html/CUSTOM_PROJECT/
</file>

**Wichtig:** Schlüssel auf RAM-Disk übertragen und in KeePass sichern.


==== Noch nicht eingearbeitet ====

  * [[http://blog.bodhizazen.net/linux/prevent-dos-with-iptables/|Verbindungen limitieren gegen DoS-Angriffe]]


==== NTP-Server einstellen ====

s. [[https://help.ubuntu.com/lts/serverguide/NTP.html|Time Synchronization]]

<code>
sudo vim /etc/systemd/timesyncd.conf
NTP=ptbtime1.ptb.de
FallbackNTP=ntp1.lrz.de
</code>

==== fail2ban ====

Den anderen Befragungsserver als vertrauenswürdig akzeptieren.

<code bash>
sudo vim /etc/fail2ban/jail.local
</code>

<file conf /etc/fail2ban/jail.local>
[DEFAULT]
ignoreip = 127.0.0.1/8 95.130.22.98
findtime = 600
bantime  = 600
maxretry = 3
destemail = info@soscisurvey.de
chain = INPUT

[serial-fail]
# SoSci Surveys locks IPs on its own, this is only the upper limit
enabled  = true
port     = http,https
filter   = serial-fail
action   = iptables-allports
           mail-whois[name=Serial Fail, dest=info@soscisurvey.de]
logpath  = /var/www/s2survey/html/system/logfiles/token-fail.log
maxretry = 50
bantime  = 7200

[sshd]
enabled = true
mode    = aggressive

[php-url-fopen]
enabled = true

# Nur wenn installiert
[owncloud]
enabled = true
</file>

<code bash>
sudo vim /etc/fail2ban/filter.d/serial-fail.conf
</code>

<file conf /etc/fail2ban/filter.d/serial-fail.conf>
[Definition]
failregex = <HOST>\s+(invalid|serial|deliveryToken)
ignoreregex =
</file>


==== rkhunter ====

''xinet '' wird für Nagios erlaubt.

<code>
sudo vim /etc/rkhunter.conf
PKGMGR=DPKG
XINETD_ALLOWED_SVC=/etc/xinetd.d/nrpe

sudo rkhunter --propupd

sudo vim /etc/default/rkhunter
REPORT_EMAIL="info@soscisurvey.de"
</code>


==== Cronjobs ====

<code>
sudo crontab -e

# m     h       dom     mon     dow     command
@reboot                                 /root/mailinfo.reboot.sh
15      00      *       *       *       /etc/cron.daily/chkrootkit
30      00      *       *       *       /etc/cron.daily/rkhunter
00      01      *       *       *       /root/backup/backup-daily.sh
00      02      *       *       sun     /root/backup/backup-weekly.sh
30      04      *       *       *       /root/mailinfo.update.sh
00      05      *       *       sun     /usr/bin/certbot renew --post-hook "service nginx reload"
55      05      *       *       sun     /sbin/reboot
</code>

<code>
sudo crontab -e -uwww-data

# m     h       dom     mon     dow     command
*/1     *       *       *       *       /var/www/s2survey/script/crontask.sh >/dev/null 2>&1
0      3       *       *       *       /var/www/s2survey/script/cronjob.sh
</code>


===== Einstellungen =====

    * Tägliches Backup (MySQL und Dateien) via Cronjob
      * [[http://www.howtoforge.de/anleitung/verschlusselte-ftp-backups-mit-duplicity-und-duply-erstellen-debian-squeeze/|Verschlüsselte FTP Backups mit duplicity und duply erstellen]]
    *  Abruf des Server-Status ermöglichen [[http://httpd.apache.org/docs/2.2/mod/mod_status.html|Apache Module mod_status]]



===== Firewall =====

<code>
sudo mkdir /etc/iptables
sudo bash -c "iptables-save > /etc/iptables/rules.v4"
sudo bash -c "ip6tables-save > /etc/iptables/rules.v6"
</code>

  * iptables
  * Regeln werden in /etc/iptables/rules.* gespeichert

<file conf rules.v4>
## Generated by iptables-save v1.4.8
## Modified manually!
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [11:777]
#
## Loopback
-A INPUT -i lo -j ACCEPT
-A INPUT -s localhost -j ACCEPT
#-A OUTPUT -o lo -j ACCEPT
#
## Monitoring-Server
-A INPUT -s 95.130.16.86 -j ACCEPT
-A INPUT -s 95.130.16.67 -j ACCEPT
#
## Test performance
#-A INPUT -s 92.75.27.206 -j ACCEPT
#
## Accept established connections
#-A INPUT -m state --state RELATED,ESTABLISHED -m limit --limit 50/second --limit-burst 50 -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
#
## Record new incoming connections per IP
-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --name ssh --set
-A INPUT -p tcp --dport 80 -m state --state NEW -m recent --name http --set
# -A INPUT -p tcp --dport 443 -m state --state NEW -m recent --name https --set
#
## Limit numbers of new connections/time per IP
-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --name ssh --rcheck --seconds 10 --hitcount 6 -j LOG --log-prefix SSH-DROP:
-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --name ssh --rcheck --seconds 10 --hitcount 6 -j REJECT --reject-with tcp-reset
-A INPUT -p tcp --dport 80 -m state --state NEW -m recent --name http --rcheck --seconds 2 --hitcount 20 -j REJECT --reject-with tcp-reset
# Okay, this is a problem for RTR mobile as there may be more than 20 new connections in 2 seconds
# -A INPUT -p tcp --dport 443 -m state --state NEW -m recent --name https --rcheck --seconds 1 --hitcount 20 -j LOG --log-prefix HTTPS-DROP:
# -A INPUT -p tcp --dport 443 -m state --state NEW -m recent --name https --rcheck --seconds 1 --hitcount 20 -j REJECT --reject-with tcp-reset
#
## Limit number of concurrent connections per IP
## Easy, because university networks may require some connections
#-A INPUT -p tcp --syn --dport 80 -m connlimit --connlimit-above 100 -j REJECT --reject-with tcp-reset
#-A INPUT -p tcp --syn --dport 443 -m connlimit --connlimit-above 100 -j REJECT --reject-with tcp-reset
#
#
## SSH (only from certain IPs)
#
# Allow from www.onlineforschung.org 178.238.229.119
-A INPUT -s 178.238.229.119 -p tcp -m tcp --dport 22 -j ACCEPT
# Allow from s2survey.net 95.130.22.100
-A INPUT -s 95.130.22.100 -p tcp -m tcp --dport 22 -j ACCEPT
# LRZ/Arbeitsplatz
-A INPUT -s 138.246.2.0/24 -p tcp -m tcp --dport 22 -j ACCEPT
# Kabelnetz: 46.128.22.170
-A INPUT -s 46.128.21.0/23 -p tcp -m tcp --dport 22 -j ACCEPT
# Temporär
# -A INPUT -s 93.233.109.195 -p tcp -m tcp --dport 22 -j ACCEPT
#
# NUL Packages (Portscanner) blockieren
-A INPUT -p tcp --tcp-flags ALL NONE -j DROP
# SYN flood blockieren
-A INPUT -p tcp ! --syn -m state --state NEW -j DROP
# XMAS blockieren
-A INPUT -p tcp --tcp-flags ALL ALL -j DROP
#
#
## SMTP
-A INPUT -p tcp -m tcp --dport 25 -j ACCEPT
#
## HTTP/S
#-A INPUT -p tcp -m tcp --dport 80 -m state --state NEW -m limit --limit 50/min --limit-burst 200 -j ACCEPT
#-A INPUT -p tcp -m tcp --dport 443 -m state --state NEW -m limit --limit 50/min --limit-burst 200 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
#
#
## Test Port
#-A INPUT -p tcp -m tcp --dport 8080 -j ACCEPT
#-A INPUT -p tcp -m tcp --dport 8081 -j ACCEPT
#
#
## Ping (limited)
-A INPUT -p icmp --icmp-type timestamp-request -j DROP
-A INPUT -p icmp --icmp-type timestamp-reply -j DROP
-A INPUT -p icmp -m limit --limit 1/s --limit-burst 1 -j ACCEPT
-A INPUT -p icmp -j LOG --log-prefix PING-DROP:
-A INPUT -p icmp -j DROP
#
## Ping (outgoing)
-A OUTPUT -p icmp -j ACCEPT
-A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# -A OUTPUT -j ACCEPT
# -A OUTPUT -o lo -j ACCEPT
# -A OUTPUT -o eth0 -j ACCEPT
#
#
## Monit (port 2812)
-A INPUT -p tcp -m tcp --dport 2812 -m state --state NEW -m limit --limit 5/min --limit-burst 20 -j ACCEPT
# -A INPUT -p tcp -m tcp --dport 2812 -j ACCEPT
#
# Final action
-A INPUT -j DROP
#
COMMIT
</file>

  * Der Port 25 ist nur auf ''mail'' freigegeben.


Die Konfiguration beim Neustart laden (bis Ubuntu 16).

<code>
sudo vim /etc/network/if-up.d/firewall
#!/bin/bash
/sbin/iptables-restore < /etc/iptables/rules.v4
/sbin/ip6tables-restore < /etc/iptables/rules.v6

sudo chmod +x /etc/network/if-up.d/firewall
</code>

Die Konfiguration beim Neustart laden (ab Ubuntu 18), vgl. [[https://nucco.org/2018/05/ubuntu-18-04-chronicles-applying-firewall-rules-on-startup-pre-network.html|sudo systemctl enable iptables-load.service]]

<code bash>
sudo vim /etc/iptables/restore.sh
sudo vim /etc/systemd/system/iptables-load.service
sudo chmod u+x /etc/iptables/restore.sh
</code>

<file bash /etc/iptables/restore.sh>
#!/bin/bash
/sbin/iptables-restore < /etc/iptables/rules.v4
/sbin/ip6tables-restore < /etc/iptables/rules.v6
</file>

<file bash /etc/systemd/system/iptables-load.service>
[Unit]
Description = Load firewall rules on startup

[Service]
Type=oneshot
ExecStart=/etc/iptables/restore.sh

[Install]
WantedBy=network-pre.target
</file>

Den Service aktivieren und zum Testen gleich noch starten (''--now'').

<code bash>
sudo systemctl enable iptables-load.service --now
</code>


===== SFTP ======

FTP-Benutzer einrichten, der nur auf SoSci Survey zugreifen kann.

<code bash>
sudo groupadd www-ftp
sudo useradd -g www-ftp -m -d /home/www-ftp -s /sbin/nologin www-ftp
sudo mkdir /home/www-ftp/.ssh
sudo vim /home/www-ftp/.ssh/authorized_keys
# Schlüssel aus PuttyGen einsetzen
sudo chown -R www-ftp:www-ftp /home/www-ftp/.ssh
sudo chmod 700 /home/www-ftp/.ssh/
sudo chmod 600 /home/www-ftp/.ssh/authorized_keys

sudo chown root:root /var/www/s2survey
sudo chown -R www-ftp:www-data /var/www/s2survey/html
sudo chmod 770 /var/www/s2survey/html
</code>

<file conf /etc/passwd>
www-ftp:x:1003:1003::/html:/usr/sbin/nologin
</file>

<file conf /etc/ssh/sshd_config>
#Subsystem       sftp    /usr/libexec/openssh/sftp-server
Subsystem       sftp    internal-sftp

# am Ende (!) der Datei noch
Match User www-ftp
    AuthorizedKeysFile /home/www-ftp/.ssh/authorized_keys
    ChrootDirectory /var/www/s2survey
    ForceCommand internal-sftp
    AllowTcpForwarding no
    X11Forwarding no

Match all
</file>

<code bash>
sudo systemctl reload sshd
</code>

Wenn es Probleme gibt, ''sshd'' im Debug-Mode laufen lassen:

<code bash>
sudo /usr/sbin/sshd -d -p 3321
</code>


===== nginx =====

Das Erstellen eines Schlüssels für ''dhparam'' dauert eine gute Weile.

<code bash>
sudo mkdir /var/www/ssl/
sudo openssl dhparam -out /var/www/ssl/dhparam.pem 4096
</code>

Dann müssen eventuell noch gültige Zertifikate und Schlüssel nach ''/var/www/ssl/'' kopiert werden.

<code bash>
sudo vim /etc/nginx/conf.d/s2survey.conf
</code>

Die eigentliche Konfiguration wird dann in ''/etc/nginx/conf.d'' untergebracht.

<file conf /etc/nginx/conf.d/ssl.conf>
# SSL Settings

ssl_protocols TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE, and TLSv1
ssl_prefer_server_ciphers on;
ssl_ciphers "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS";
ssl_ecdh_curve secp384r1;
ssl_dhparam /var/www/ssl/dhparam.pem;
## Use a SSL/TLS cache for SSL session resume.
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_stapling on;
ssl_stapling_verify on;
# HTTP-Strict Transport Security (HSTS)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
</file>

<file conf /etc/nginx/conf.d/logs.conf>
# Logging Settings

log_format noip '0.0.0.0 - $remote_user [$time_local]  '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "$http_user_agent"';

log_format noagent '0.0.0.0 - $remote_user [$time_local]  '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "na"';
</file>

<file conf /etc/nginx/conf.d/gzip.conf>
gzip_disable "msie6";
gzip_vary on;
</file>

<file conf /etc/nginx/fastcgi_params>
fastcgi_connect_timeout 65;
fastcgi_send_timeout    180;
fastcgi_read_timeout    900;
</file>

<file conf /etc/logrotate.d/nginx>
/var/log/nginx/*.log
/var/www/s2survey/log/*.log {
        weekly
        missingok
        rotate 28
        dateext
        compress
        delaycompress
        notifempty
        create 0640 www-data adm
        sharedscripts
        prerotate
                if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
                        run-parts /etc/logrotate.d/httpd-prerotate; \
                fi \
        endscript
        postrotate
                # [ -s /run/nginx.pid ] && kill -USR1 `cat /run/nginx.pid`
                invoke-rc.d nginx rotate >/dev/null 2>&1
        endscript
}
</file>


==== Let's Encrypt ====

Und eventuell muss auch die Konfiguration den ''letsencrypt'' kopiert werden. Dafür muss auf dem einen Server erstmal das Verzeichnis auf den Nutzer ''sosci'' übereignet werden:

<code bash>
sudo chown -R sosci:sosci /etc/letsencrypt/
</code>

Dann muss auf dem anderen Server mit Symlinks kopiert werden:

<code bash>
sudo rsync -avz -e ssh sosci@s2survey.net:/etc/letsencrypt /etc/
</code>

Und dann die Dateieigentümer wiederhergestellt auf dem einen Server

<code bash>
sudo chown -R root:root /etc/letsencrypt/
</code>

Zum Testen dann...

<code bash>
sudo /usr/bin/certbot renew --post-hook "service nginx reload"
</code>


==== Websites ====

<file conf /etc/nginx/sites-available/01.s2survey.conf>
server {
        #listen         8080;
        listen          80;
        server_name     s2survey.net www.s2survey.net s2survey.com www.s2survey.com www.s2survey.de;
        rewrite         ^ https://$server_name$request_uri? permanent;
}

# PHP-FPM processes to be used by SoSci Survey
upstream sosci {
    server unix:/run/php/php-fpm.sock max_fails=5 fail_timeout=30s;
    server 127.0.0.1:9002 backup;
}

# Limitation for requets per second
# Expect no more than 5 PHP requests (not images) per second = 300 per minute = 30 people doing 10 pages per minute
limit_req_zone $binary_remote_addr zone=php:10m rate=5r/s;
# (Much) less login attempts - for the SoSci Panel at the moment
limit_req_zone $binary_remote_addr zone=login:10m rate=15r/m;

# Main domain(s)
server {
        #listen   [::]:80 default ipv6only=on; ## listen for ipv6
        listen 443 ssl http2;

        server_name s2survey.net www.s2survey.net s2survey.com www.s2survey.com s2survey.de www.s2survey.de;

        ssl on;
        # The server certificate must appear before the chained certificates in the combined file
        # cat www.example.com.crt bundle.crt > www.example.com.chained.crt
        # ssl_certificate /var/www/ssl/s2survey.2017.cert;
        ssl_certificate_key /var/www/ssl/s2survey.2017.key;
        ssl_certificate /var/www/ssl/s2survey.2018.cert;
        # ssl_certificate_key /var/www/ssl/s2survey.2018.key;

        # Content
        root /var/www/s2survey/html;
        index index.php index.html index.htm;
        error_page 404 = /spellcheck.php;
        client_max_body_size    80M;

        access_log /var/www/s2survey/log/n-access.log noagent;
        error_log /var/www/s2survey/log/n-error.log;

        # Forbidden directories
        location ~ ^/(system|inc)/ {
                deny all;
                return 403;
        }
        location ~ ^/files/(protected|upload|share)/ {
                deny all;
                return 403;
        }
        location ~ ^/download/(release|system|working)/ {
                deny all;
                return 403;
        }
        
        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        location ~ /\.ht {
                deny all;
                return 403;
        }

        # Munin
        location ^~ /munin/static/ {
                alias /etc/munin/static/;
                expires modified +1w;
        }

        location ^~ /munin/ {
                alias /var/cache/munin/www/;
                expires modified +310s;
                try_files $uri $uri/ =404;
                auth_basic "Munin";
                auth_basic_user_file /etc/munin/munin-htpasswd;
        }
        
        # PHP FPM status
        location ~ ^/(status\.primary|ping\.primary)$ {
                include fastcgi_params;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_pass unix:/run/php/php-fpm.sock;
                log_not_found off;
                allow 95.130.22.98;
                allow 127.0.0.1;
                deny all;
        }
        location ~ ^/(status\.secondary|ping\.secondary)$ {
                include fastcgi_params;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_pass 127.0.0.1:9002;
                log_not_found off;
                allow 95.130.22.98;
                allow 127.0.0.1;
                deny all;
        }
        
        # Different PHP settings for the sosci administration
        location ~ ^/admin/(.+\.php)$ {
                limit_req zone=php burst=50;
                try_files $uri =404;
                # No secondary pool for that
                fastcgi_pass unix:/run/php/php-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                include fastcgi_params;
                # Special
                fastcgi_param PHP_VALUE "
                        max_execution_time = 20
                        memory_limit = 256M
                        max_file_uploads = 50
                        post_max_size = 80M
                ";
        }

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        location ~ \.php$ {
                limit_req zone=php burst=50;
                try_files $uri =404;

                # First, you need to capture fastcgi errors
                fastcgi_intercept_errors      on;
                # Specifies in which cases a request should be passed to the next server
                fastcgi_next_upstream         error timeout http_500 http_503;
                # Limits the time during which a request can be passed
                # to the next server
                fastcgi_next_upstream_timeout 10s;
                # Limits the number of possible tries for passing
                # a request to the next server
                fastcgi_next_upstream_tries   2;
                # Upstream to pass request to
                fastcgi_pass sosci;

                fastcgi_index index.php;
                fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                include fastcgi_params;
                # Limitations for survey projects
                fastcgi_param PHP_VALUE "
                        max_execution_time = 5
                        memory_limit = 64M
                        max_file_uploads = 5
                        post_max_size = 16M
                ";
        }

        # Docs
        location /doc/ {
                alias /usr/share/doc/;
                autoindex on;
                allow 127.0.0.1;
                allow 95.130.22.100;
                deny all;
        }

        # Cache management
        location ~* \.(?:ico|css|js|gif|jpg|jpeg|png|flv|pdf|swf)$ {
                expires 1d;
                add_header Vary Accept-Encoding;
                # access_log off;
        }

        # Anything else
        location ~ / {
                # First attempt to serve request as file, then
                # as directory, then fall back to index.html
                try_files $uri $uri/ =404; #$uri/ /index.php /index.html =404;
        }

}
</file>

<file conf /etc/nginx/sites-available/02.customers.conf>
server {
        listen          95.130.22.101:80;
        server_name     access.lgbti-label.ch;

        # Allow certbot auth
        root     /var/www/s2survey/certbot;
        location ~ ^/.well-known/ {
        }

        # redirect everything else
        location ~ / {
                rewrite        ^ https://$server_name$request_uri? permanent;
        }

        access_log /var/www/s2survey/log/n-access-custom.log noagent;
        error_log /var/www/s2survey/log/n-error-custom.log;
}

server {
        listen 95.130.22.101:443 ssl;
        server_name access.lgbti-label.ch;

        root /var/www/s2survey/html/_$host;
        index index.php index.html index.htm;
        client_max_body_size    64M;

        access_log /var/www/s2survey/log/n-access-custom.log noagent;
        error_log /var/www/s2survey/log/n-error-custom.log;

        ssl on;
        ssl_certificate_key /etc/letsencrypt/live/access.lgbti-label.ch/privkey.pem;
        ssl_certificate /etc/letsencrypt/live/access.lgbti-label.ch/fullchain.pem;

        location ~ /\.ht {
                deny all;
                return 403;
        }

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        location ~ \.php$ {
                limit_req zone=php burst=50;
                try_files $uri =404;

                fastcgi_intercept_errors      on;
                fastcgi_next_upstream         error timeout http_500 http_503;
                fastcgi_next_upstream_timeout 10s;
                fastcgi_next_upstream_tries   2;
                fastcgi_pass sosci;

                fastcgi_index index.php;
                fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                include fastcgi_params;
                # Limitations for survey projects
                fastcgi_param PHP_VALUE "
                        max_execution_time = 5
                        memory_limit = 64M
                        max_file_uploads = 5
                        post_max_size = 16M
                ";
        }

        # Cache management
        location ~* \.(?:ico|css|js|gif|jpg|jpeg|png|flv|pdf|swf)$ {
                expires 1d;
                add_header Vary Accept-Encoding;
                # access_log off;
        }

        # Anything else
        location ~ / {
                # First attempt to serve request as file, then
                # as directory, then fall back to index.html
                try_files $uri $uri/ =404; #$uri/ /index.php /index.html =404;
        }

}
</file>

<file conf /etc/nginx/sites-available/09.nossl.s2survey.conf>
# Allows access without SSL for single projects
server {
        listen          80;
        #listen   [::]:80 default ipv6only=on; ## listen for ipv6

        root /var/www/soscisurvey/html;
        index index.php index.html index.htm;
        error_page 404 = /spellcheck.php;

        server_name nossl.s2survey.net;

        access_log /var/www/soscisurvey/log/nossl-access.log noip;
        error_log /var/www/soscisurvey/log/nossl-error.log;


        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        location ~ /\.ht {
                deny all;
        }

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        location ~ \.php$ {
                limit_req zone=php burst=50;
                try_files $uri =404;

                fastcgi_intercept_errors      on;
                fastcgi_next_upstream         error timeout http_500 http_503;
                fastcgi_next_upstream_timeout 10s;
                fastcgi_next_upstream_tries   2;
                fastcgi_pass sosci;

                fastcgi_index index.php;
                fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                include fastcgi_params;
                # Limitations for survey projects
                fastcgi_param PHP_VALUE "
                        max_execution_time = 5
                        memory_limit = 64M
                        max_file_uploads = 5
                        post_max_size = 16M
                ";
        }
        
        # Allowed directories
        location /images/ {
                try_files $uri =404;
                allow all;
        }
        location /view/ {
                try_files $uri =404;
                allow all;
        }

        # Allowed projects
        location /curtest/ {
                try_files $uri $uri/ =404;
                allow all;
        }

        location / {
                allow 127.0.0.1;
                deny all;
                try_files $uri $uri/ =404;
        }
}
</file>

<file conf /etc/nginx/sites-available/default>
server {
        listen          80;
        #listen   [::]:80 default ipv6only=on; ## listen for ipv6

        root /var/www/default/html;
        index index.php index.html index.htm;

        # Make site accessible from http://localhost/
        server_name localhost;

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        location ~ /\.ht {
                deny all;
        }

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        location ~ \.php$ {
                limit_req zone=php burst=50;
                try_files $uri =404;
                fastcgi_pass sosci;

                fastcgi_index index.php;
                fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                include fastcgi_params;
        }

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to index.html
                try_files $uri $uri/ =404;
                # Uncomment to enable naxsi on this location
                # include /etc/nginx/naxsi.rules
        }

        # Server status
        location = /server-status {
                # copied from http://blog.kovyrin.net/2006/04/29/monitoring-nginx-with-rrdtool/
                stub_status on;
                #extended_status on;
                access_log   off;
                allow 127.0.0.1;
                allow 95.130.22.98;
                deny all;
        }

        # Server status for munin
        location = /nginx_status {
                stub_status on;
                access_log   off;
                allow 127.0.0.1;
                allow 95.130.22.98;
                deny all;
        }
        
        # Also access munin via localhost
        location ^~ /munin/static/ {
                alias /etc/munin/static/;
                expires modified +1w;
        }

        location ^~ /munin/ {
                alias /var/cache/munin/www/;
                expires modified +310s;
                try_files $uri $uri/ =404;
                auth_basic "Munin";
                auth_basic_user_file /etc/munin/munin-htpasswd;
        }

}
</file>

Dann die Seiten noch aktivieren

<code bash>
sudo ln -s /etc/nginx/sites-available/01.s2survey.conf /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/02.customers.conf /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/09.nossl.s2survey.conf /etc/nginx/sites-enabled/
</code>


===== E-Mail =====

Für die eingehenden E-Mails ist ein gesonderter Server zuständig, konfiguriert nach [[https://thomas-leister.de/mailserver-debian-stretch/|thomas-leister.de]].

Die Server-SSL-Schlüssel können leider nicht direkt für TLS verwendet, werden also erstmal neue erstellen.

<code>
sudo openssl req -new -x509 -days 3650 -nodes -out /etc/ssl/certs/postfix.pem -keyout /etc/ssl/private/postfix.key

Country Name (2 letter code) [AU]:                            DE
State or Province Name (full name) [Some-State]:              Bayern
Locality Name (eg, city) []:                                  Muenchen
Organization Name (eg, company) [Internet Widgits Pty Ltd]:   SoSci Survey GmbH
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:               s2survey.net
Email Address []:                                             info@soscisurvey.de
</code>

Damit es mit ausgehenden E-Mails und der Erkennung von Mail-Rückläufern funktioniert, sind folgende Änderungen erforderlich.

<file conf /etc/postfix/main.cf>
##### TLS settings ######
# see https://thomas-leister.de/erweiterte-postfix-ssltls-konfiguration-verschluesselte-server-server-verbindungen/

tls_ssl_options = NO_COMPRESSION
tls_high_cipherlist=EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:+CAMELLIA256:+AES256:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!ECDSA:CAMELLIA256-SHA:AES256-SHA:CAMELLIA128-SHA:AES128-SHA

### outgoing connections ###
smtp_tls_security_level=may
smtp_tls_cert_file=/etc/ssl/certs/postfix.pem
smtp_tls_key_file=/etc/ssl/private/postfix.key
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtp_bind_address = 95.130.22.100

### incoming connections ###
smtpd_sasl_security_options = noanonymous
smtpd_use_tls=yes
smtpd_tls_security_level=may
smtpd_tls_cert_file=/etc/ssl/certs/postfix.pem
smtpd_tls_key_file=/etc/ssl/private/postfix.key
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache

# Allow 64 MB message size
message_size_limit = 67108864

# email accounts (hosting)
virtual_alias_domains = s2survey.de, www.s2survey.de, s2survey.com, www.s2survey.com
virtual_alias_maps = hash:/etc/postfix/virtual

# sender aliases
sender_canonical_maps=hash:/etc/postfix/senders

# Limit connections per server (default 20)
smtp_destination_concurrency_limit = 3

# Stop retries after 36 hours (to quickly delete problem-mails)
bounce_queue_lifetime = 24h
maximal_queue_lifetime = 36h

### Basic settings ###
myhostname = s2survey.net
mydestination = $myhostname, mail.s2survey.net, www.s2survey.net, soscisurvey2, localhost.localdomain, localhost
inet_protocols = ipv4
</file>

<file conf /etc/postfix/senders>
root      info@s2survey.net
www-data  info@s2survey.net
</file>

<file conf /etc/postfix/virtual>
root                         info@soscisurvey.de
webmaster                    info@soscisurvey.de
postmaster                   info@soscisurvey.de
root@mail.s2survey.net       info@soscisurvey.de

info@s2survey.net            info@soscisurvey.de
info@s2survey.com            info@soscisurvey.de
info@s2survey.de             info@soscisurvey.de

hostmaster@s2survey.net      info@soscisurvey.de
hostmaster@www.s2survey.net  info@soscisurvey.de
hostmaster@s2survey.com      info@soscisurvey.de
hostmaster@www.s2survey.com  info@soscisurvey.de
hostmaster@s2survey.de       info@soscisurvey.de
hostmaster@www.s2survey.de   info@soscisurvey.de
webmaster@s2survey.net       info@soscisurvey.de
webmaster@www.s2survey.net   info@soscisurvey.de
webmaster@s2survey.com       info@soscisurvey.de
webmaster@www.s2survey.com   info@soscisurvey.de
webmaster@s2survey.de        info@soscisurvey.de
webmaster@www.s2survey.de    info@soscisurvey.de
postmaster@s2survey.net      info@soscisurvey.de
postmaster@www.s2survey.net  info@soscisurvey.de
postmaster@s2survey.com      info@soscisurvey.de
postmaster@www.s2survey.com  info@soscisurvey.de
postmaster@s2survey.de       info@soscisurvey.de
postmaster@www.s2survey.de   info@soscisurvey.de

noreply@s2survey.net         info@soscisurvey.de
wiki@s2survey.net            info@soscisurvey.de
root@s2survey.net            info@soscisurvey.de
www-data@s2survey.net        info@soscisurvey.de

mailer@s2survey.net          mailer
survey@s2survey.net          mailer
</file>


Für die ausgehenden E-Mails sind einige Anpassungen erforderlich, damit sie durch den Spamfilter kommen. Zu Testen mit [[https://www.mail-tester.com/|mail tester]].

Die TLS-Konfiguration kann man mit [[https://www.checktls.com/checktls.com]] überprüfen.

Für die Rückläufer-Mails benötigen wir außerdem noch einen Nutzer für das Mail-Postfach mit Passwort.

<code bash>
sudo adduser mailer
sudo vim /etc/passwd

mailer:x:1000:1000:,,,:/home/mailer:/usr/sbin/nologin
</code>

Zuletzt noch DKIM einrichten, damit Mails von @s2survey.net nirgends hängen bleiben.

Hierzu folgende Anleitung: [[https://kofler.info/dkim-konfiguration-fuer-postfix/|DKIM für Postfix]].

<code bash>
sudo mkdir /etc/opendkim
sudo mkdir /etc/opendkim/keys
sudo chown -R opendkim:opendkim /etc/opendkim
sudo chmod go-rw /etc/opendkim/keys
</code>

<file conf /etc/opendkim.conf>
UserID                  opendkim:opendkim
UMask                   002
Socket                  inet:8892@localhost
AutoRestart             yes
AutoRestartRate         10/1h
# Später weniger Logging
Syslog                  yes
SyslogSuccess           yes
LogWhy                  yes
# Nicht zu streng sein
Canonicalization        relaxed/simple
# interne Mails nicht mit OpenDKIM verarbeiten
ExternalIgnoreList      refile:/etc/opendkim/trusted
InternalHosts           refile:/etc/opendkim/trusted
# Keys pro Domain
# (refile: für Dateien mit regulären Ausdrücke)        
SigningTable            refile:/etc/opendkim/signing.table
KeyTable                /etc/opendkim/key.table       
# diesen Signatur-Algorithmus verwenden
SignatureAlgorithm      rsa-sha256
# Always oversign From (sign using actual From and a null From to prevent
# malicious signatures header fields (From and/or others)
OversignHeaders         From
</file>

<file conf /etc/opendkim/trusted>
127.0.0.1
::1
localhost
soscisurvey2
s2survey
s2survey.net
</file>

<file conf /etc/opendkim/signing.table>
# Nur Mails @s2survey.net signieren
*@s2survey.net s2survey
</file>

<file conf /etc/opendkim/key.table>
s2survey s2survey.net:2019a:/etc/opendkim/keys/2019a.private
</file>

<code bash>
sudo su
cd /etc/opendkim/keys
opendkim-genkey -d s2survey.net -b 3072 -r -s 2019a
chown opendkim:opendkim *
exit
sudo systemctl restart opendkim
opendkim-testkey -d s2survey.net -s 2019a -vvv
</code>

Zuletzt noch einen DNS-Eintrag für s2survey.net erzeugen (z.B. ''2019a._domainkey.s2survey.net''), entsprechend dem Inhalt der Datei ''2019a.txt''. Dabei kommt alles zwischen den Klammern in den TXT Eintrag, aber Anführungszeichen, Leerzeichen und Zeilenumbrüche werden entfernt.

<file conf /etc/postfix/main.cf>
# OpenDKIM
milter_default_action = accept   
milter_protocol   = 6              
smtpd_milters     = inet:localhost:8892
non_smtpd_milters = inet:localhost:8892
</file>


===== MySQL =====

  * File per Table
  * Mehr RAM-Speicher für den InnoDb Cache


===== Webserver (nginx) ======

s. auch https://www.digitalocean.com/community/tutorials/how-to-set-up-nginx-with-http-2-support-on-ubuntu-16-04

==== SSL ====

Wird generell in ''nginx'' konfiguriert. Allerdings ist sicherzustellen, dass die Schlüssel unter ''/var/www/s2survey/ssl'' vorhanden sind und dass die richtigen Prozesse auf diese zugreifen können.

<code bash>
sudo chown -R root:ssl-cert /var/www/ssl
</code>


==== HTTP/2 ====

===== PHP-FPM =====

Der Standard-Pool ''www.conf'' muss gelöscht oder umbenennt werden, z.B. ''mv www.conf www.conf.dpkg-dist''.

<file conf /etc/php/7.2/fpm/pool.d/primary.conf>
[primary]
user = www-data
group = www-data
listen = /run/php/php-fpm.sock
listen.backlog = 1023
listen.owner = www-data
listen.group = www-data
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 16
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 2500
pm.status_path = /status.primary
ping.path = /ping.primary
env[PATH] = /usr/local/bin:/usr/bin:/bin
</file>

<file conf /etc/php/7.2/fpm/pool.d/secondary.conf>
[secondary]
user = www-data
group = www-data
listen = 127.0.0.1:9002
listen.backlog = 511
listen.owner = www-data
listen.group = www-data
listen.allowed_clients = 127.0.0.1
pm = ondemand
pm.max_children = 10
pm.process_idle_timeout = 10s;
pm.max_requests = 500
pm.status_path = /status.secondary
ping.path = /ping.secondary
env[PATH] = /usr/local/bin:/usr/bin:/bin
</file>

Und dann noch ein paar Änderungen in der php.ini

<file conf /etc/php/7.2/fpm/conf.d/10-sosci.ini>
display_errors = On
display_startup_errors = On
post_max_size = 76M
upload_max_filesize = 64M
max_file_uploads = 50
mail.add_x_header = On
</file>


==== SoSci Survey ====

Zwei Scripte (gehören ''www-data''), werden beim Übertragen der Inhalte evtl. nicht automatisch kopiert (permission denied).

<file bash /var/www/s2survey/script/crontask.sh>
#!/bin/bash
cd /var/www/s2survey/html/admin/
/usr/bin/php /var/www/s2survey/html/admin/crontask.php password=123456
</file>

<file bash /var/www/s2survey/script/cronjob.sh>
#!/bin/bash
cd /var/www/s2survey/html/admin/
/usr/bin/php /var/www/s2survey/html/admin/cronjob.php password=123456
</file>

<code bash>
sudo vim /var/www/s2survey/script/crontask.sh
sudo vim /var/www/s2survey/script/cronjob.sh
sudo chown www-data:www-data /var/www/s2survey/script/crontask.sh
sudo chown www-data:www-data /var/www/s2survey/script/cronjob.sh
sudo chmod u+x /var/www/s2survey/script/crontask.sh
sudo chmod u+x /var/www/s2survey/script/cronjob.sh

sudo chown www-data:www-data /var/www/s2survey/script
sudo chown www-data:www-data /var/www/s2survey/log
sudo chmod 700 /var/www/s2survey/script
sudo chmod 700 /var/www/s2survey/log
</code>

===== MySQL =====

<code bash>
sudo vim /etc/mysql/conf.d/s2survey.conf
sudo systemctl restart mysql
</code>

<file conf /etc/mysql/conf.d/s2survey.conf>
[mysqld]
key_buffer_size         = 64M
max_allowed_packet      = 32M
max_connections         = 512
query_cache_limit       = 4M
query_cache_size        = 64M

slow_query_log          = 1
slow_query_log_file     = /var/log/mysql/mysql-slow.log
long_query_time         = 5

# InnoDb configuration
innodb_file_per_table = 1
innodb_buffer_pool_size = 2G # 50% of System Memory was better
innodb_log_file_size=128M
innodb_log_buffer_size = 512M
innodb_thread_concurrency = 8
innodb_flush_method=O_DSYNC # Mixed mode for InnoDB
innodb_io_capacity=100
innodb_lock_wait_timeout=20
# innodb_additional_mem_pool_size = 20M
# innodb_data_file_path = ibdata1:50M:autoextend
</file>

<code bash>
sudo su
mysql
</code>

Einen neuen Root-Nutzer mit Passwort anlegen, vgl [[https://tableplus.io/blog/2018/10/how-to-create-a-superuser-in-mysql.html|https://tableplus.io/blog/2018/10/how-to-create-a-superuser-in-mysql.html]]

<code mysql>
CREATE USER 'master'@'localhost' IDENTIFIED BY 'XXXXXXXXXXXXXX';
GRANT ALL PRIVILEGES ON *.* TO 'master'@'localhost' WITH GRANT OPTION;
SHOW GRANTS FOR master@localhost;
FLUSH PRIVILEGES;

CREATE DATABASE s2survey;
CREATE USER 's2survey'@'localhost' IDENTIFIED BY 'XXXXXXXXXXXXXX';
GRANT ALL PRIVILEGES ON s2survey.* TO 's2survey'@'localhost';
FLUSH PRIVILEGES;
</code>


===== Überwachung =====

<code bash>
# PHP-Service herausfinden
systemctl list-unit-files | grep enabled
sudo vim /etc/monit/conf.d/s2survey.conf
sudo vim /etc/monit/conf.d/mysql
sudo vim /etc/monit/conf.d/nginx
sudo vim /etc/monit/conf.d/php-fpm
sudo vim /etc/monit/php-fpm.stop.sh
sudo vim /etc/monit/conf.d/system
sudo vim /etc/monit/conf.d/webserver
sudo vim /etc/monit/webserver.stop.sh
sudo vim /etc/monit/webserver.start.sh
sudo vim /etc/monit/webserver.restart.sh
# Nur wenn relevant
sudo vim /etc/monit/conf.d/postfix

sudo chmod u+x /etc/monit/php-fpm.stop.sh
sudo chmod u+x /etc/monit/webserver.stop.sh
sudo chmod u+x /etc/monit/webserver.start.sh
sudo chmod u+x /etc/monit/webserver.restart.sh
monit -t

sudo ln -s /usr/share/munin/plugins/nginx_request /etc/munin/plugins/
sudo ln -s /usr/share/munin/plugins/nginx_status /etc/munin/plugins/
sudo ln -s /usr/share/munin/plugins/http_loadtime /etc/munin/plugins/
sudo ln -s /usr/share/munin/plugins/iostat /etc/munin/plugins/
sudo ln -s /usr/share/munin/plugins/iostat_ios /etc/munin/plugins/
sudo ln -s /usr/share/munin/plugins/postfix_mailqueue /etc/munin/plugins/
sudo ln -s /usr/share/munin/plugins/postfix_mailvolume /etc/munin/plugins/
</code>

Noch ein Extra-PlugIn für PHP-FPM

<code bash>
cd /usr/share/munin/plugins/
sudo wget -O php-fpm https://raw.github.com/MorbZ/munin-php-fpm/master/php-fpm.php
sudo chmod +x php-fpm
sudo ln -s /usr/share/munin/plugins/php-fpm /etc/munin/plugins/php-fpm-memory
sudo ln -s /usr/share/munin/plugins/php-fpm /etc/munin/plugins/php-fpm-cpu
sudo ln -s /usr/share/munin/plugins/php-fpm /etc/munin/plugins/php-fpm-count
sudo ln -s /usr/share/munin/plugins/php-fpm /etc/munin/plugins/php-fpm-time
sudo service munin-node restart
</code>


==== Monit ====

<file conf /etc/monit/conf.d/s2survey.conf>
set daemon 20
set mail-format { from: monit@s2survey.net }
set httpd port 2812 and
        use address 0.0.0.0
        allow admin:"XXXXXXXXXXX"
</file>

<file conf /etc/monit/conf.d/mysql>
check process mysqld with pidfile /var/run/mysqld/mysqld.pid
    start program = "/usr/sbin/service mysql start"
    stop program = "/usr/sbin/service mysql stop"
    if failed host 127.0.0.1 port 3306
        for 3 cycles
        then restart
    if 3 restarts within 20 cycles
        then timeout
</file>

<file conf /etc/monit/conf.d/nginx>
check process nginx with pidfile /var/run/nginx.pid
   start program = "/usr/sbin/service nginx start"
   stop program  = "/usr/sbin/service nginx stop"
   if failed port 80 for 2 cycles
     then restart
   if cpu > 50% for 2 cycles
     then alert
</file>

<file conf /etc/monit/conf.d/php-fpm>
check process php-fpm with pidfile /var/run/php/php7.2-fpm.pid
        group php-www
        start program = "/bin/systemctl start php7.2-fpm"
        stop program = "/etc/monit/php-fpm.stop.sh"
        if failed unixsocket /var/run/php/php-fpm.sock
                then alert
        if failed unixsocket /var/run/php/php-fpm.sock
                for 2 times within 3 cycles
                then restart
        if failed unixsocket /var/run/php/php-fpm.sock
                for 5 cycles
                then exec "/sbin/reboot"
        if 5 restarts within 8 cycles
                then alert
</file>

<file bash /etc/monit/php-fpm.stop.sh>
#!/bin/bash

# Stop php-fpm
/bin/systemctl stop php7.2-fpm stop

# Kill any further instances of PHP-FPM
/usr/bin/pkill php7.2-fpm
</file>

<file conf /etc/monit/conf.d/system>
check system Memory
        group resources
        if memory usage > 75% for 5 cycles then alert
                else if passed within 5 cycles then alert
        if loadavg (15min) is greater than 0.9 for 2 cycles then alert
                else if passed within 5 cycles then alert

check filesystem rootfs with path /
        if space usage > 75% then alert
check device bootfs with path /boot
        if space usage > 75% then alert
</file>

<file conf /etc/monit/conf.d/webserver>
CHECK HOST webserver WITH ADDRESS 95.130.22.100
  START PROGRAM = "/etc/monit/webserver.start.sh"
  STOP PROGRAM  = "/etc/monit/webserver.stop.sh"
  # IF NOT EXIST THEN ALERT
   # if failed (url https://www.soscisurvey.de/example/?debug&password=demo and content == 'onlineFragebogen' and timeout 20 seconds)
   #   then alert
  IF FAILED (url https://s2survey.net/s2survey/ and content == 'onlineFragebogen' and timeout 20 seconds)
    FOR 2 CYCLES
    THEN RESTART
  IF FAILED (url https://s2survey.net/s2survey/ and content == 'onlineFragebogen' and timeout 20 seconds)
    FOR 4 CYCLES
    THEN EXEC "/sbin/reboot"
</file>

<file bash /etc/monit/webserver.stop.sh>
#!/bin/bash

# Stop php-fpm
/etc/monit/php-fpm.stop.sh

# Stop nginx
/bin/systemctl stop nginx
</file>

<file bash /etc/monit/webserver.start.sh>
#!/bin/bash
/bin/systemctl start php7.2-fpm
/bin/systemctl start nginx
</file>

<file bash /etc/monit/webserver.restart.sh>
#!/bin/bash
/etc/monit/webserver.stop.sh
/etc/monit/webserver.start.sh
</file>

<file conf /etc/monit/conf.d/postfix>
CHECK PROCESS postfix WITH PIDFILE /var/spool/postfix/pid/master.pid
    start program = "/usr/sbin/service postfix start"
    stop program  = "/usr/sbin/service postfix stop"
    if cpu > 60% for 2 cycles then alert
    if cpu > 80% for 5 cycles then restart
    if failed host localhost port 25 type tcp protocol smtp
       with timeout 15 seconds
      then alert
    if 3 restarts within 5 cycles then timeout
</file>


==== Munin ====

<code bash>
sudo vim /etc/munin/munin.conf

> [localhost.localdomain]
< [s2survey.net]
</code>

<code bash>
sudo htpasswd -c /etc/munin/munin-htpasswd admin
</code>


===== Reload =====

<code bash>
sudo systemctl reload php7.2-fpm nginx monit sshd
sudo systemctl restart munin-node unattended-upgrades
</code>


===== Umziehen =====

  * Monit auf beiden Servern deaktivieren
  * SoSci-Cache leeren
  * Server sperren
  * Datenbank exportieren
  * Dateien kopieren
  * Datenbank importieren
  * IP ändern


==== Inhalte kopieren ====

  * Dateien unter /var/www/
    * SoSci Survey
    * SSL-Zertifikate (sofern vorhanden)

<code bash>
# Vorab: Arbeits-Cache löschen, Schreibcache auflösen

# Dann die Dateien kopieren
sudo rsync -av -e ssh --delete sosci@s2survey.net:/var/www/s2survey /var/www/
sudo chown -R www-data:www-data /var/www/s2survey/
</code>

Eventuell auch /etc/letsencrypt kopieren. Anschließend wieder dem Benutzer ''root'' übergeben.

  * Datenbank(en)
    * MySQL-Dump **TODO**
    * MySQL einspielen **TODO**

<code bash>
sudo mysqldump -uroot -p --databases s2survey --quick --single-transaction | gzip -9 > /home/sosci/s2survey-mysql-transfer.sql.gz
</code>

Auf dem anderen Server die Daten holen und einspielen.

<code bash>
scp sosci@s2survey.net:~/s2survey-mysql-transfer.sql.gz ~/
gunzip -c s2survey-mysql-transfer.sql.gz | mysql -u s2survey -p s2survey
</code>


==== IP-Adresse ändern ====

Zum Ändern der IP-Adresse s. [[https://wiki.ubuntuusers.de/Netplan/|netplan]] und [https://www.linux.com/learn/intro-to-linux/2018/9/how-use-netplan-network-configuration-tool-linux|linux.com]].

Es kann sinnvoll sein, erstmal eine IP-Adresse umzustellen und dann die andere.

<file conf /etc/netplan/01-netcfg.yaml>
# This file describes the network interfaces available on your system
# For more information, see netplan(5).
network:
  version: 2
  renderer: networkd
  ethernets:
          ens160:
                  dhcp4: no
                  dhcp6: no
                  addresses: [95.130.22.100/28,95.130.22.101/28]
                  gateway4: 95.130.22.97
                  nameservers:
                          addresses: [95.130.16.100,95.130.16.121]
</file>

Die Konfiguration kann dann wie folgt testweise geladen werden:

<code>
sudo netplan try
</code>


==== Zuletzt ====

  * Mailversand testen
  * https://access.lgbti-label.ch/ prüfen
  * Update der SSL-Zertifikate via Let's Encrypt testen
  * Logrotation (**TODO:** Prüfen, ob das läuft, v.a. für nginx)

<code bash>
sudo certbot renew --force-renewal
</code>

