Linux Installation Server
Standard Paket installieren
Nach der Installation bzw. Cloud Einrichtung des Debian Servers, installieren wir uns noch folgende Pakete.
root:~# apt update && \
apt install -y sudo rsync vim screen bc curl lynx build-essential mailutils ssh dpkg-repack ntp ntpdate telnet
Unix Benutzer server, nessus und sysadmin erstellen
Unsere Standard Benutzer sind server, für unsere Keyverteilung, nessus zum Checken des Systems und sysadmin. Diese sollten auf jeden neu installierten Server eingerichtet werden.
root:~# groupadd -g 1002 sysadmin && \
groupadd -g 1003 server && \
groupadd -g 1999 nessus && \
adduser --quiet --disabled-password --gecos "sysadmin" --uid 1002 --gid 1002 sysadmin && \
adduser --quiet --disabled-password --gecos "server" --uid 1003 --gid 1003 server && \
adduser --quiet --disabled-password --gecos "nessus" --uid 1999 --gid 1999 nessus && \
usermod -a -G adm sysadmin && \
usermod -a -G sudo nessus
sudo Rechte ohne Passwort
Dem Benutzer nessus müssen wir noch sudo Rechte ohne Passwort geben. Dazu erstellen wir die Datei /etc/sudoers.d/nessus mit folgendem Inhalt.
root:~# ( cat <<HERE11
nessus ALL=(ALL) NOPASSWD: ALL
HERE11
) >/etc/sudoers.d/nessus
root:~# chmod 440 /etc/sudoers.d/nessus
SSH Umebung einrichten
Als Voraussetzung für unser SSH Keyverteilungs Tool SSHKeymgr benötigen wir die Option PermitUserEnvironment. Die ist aber auch für unser Bash History Logging wichtig, da wir dort die Variable $ORIGINAL_USER auslesen.
Dazu setzen wir die SSH Option PermitUserEnvironment wie folgt auf yes.
root:~# sed -i -e '/^#PermitUserEnvironment/s/^.*$/PermitUserEnvironment yes/' /etc/ssh/sshd_config
Mit dem Eintrag in der /etc/ssh/sshrc setzen wir die Environment Variable $ORIGINAL_USER, die wir später für das Loggen der Bash History verwenden.
root:~# (cat <<HERE1
#!/bin/bash
# xauth handling, code adapted from sshd(8)
if read proto cookie && [ -n "$DISPLAY" ]; then
if [ `echo $DISPLAY | cut -c1-10` = 'localhost:' ]; then
# X11UseLocalhost=yes
echo add unix:`echo $DISPLAY | cut -c11-` $proto $cookie
else
# X11UseLocalhost=no
echo add $DISPLAY $proto $cookie
fi | /usr/bin/xauth -q -
fi
if [ "$ORIGINAL_USER" = "" ]; then
logger -pdaemon.notice "Login by \"unknown user\" as \"$USER\" \$SSH_CLIENT=$SSH_CLIENT."
else
logger -pdaemon.notice "Login by \"$ORIGINAL_USER\" as \"$USER\" \$SSH_CLIENT=$SSH_CLIENT."
fi
# vim: syntax=bash ts=4 sw=4 sts=4 sr noet
# EOF
HERE1
) >/etc/ssh/sshrc
Damit die Einstellungen wirksam werden, starten wir den SSH Dienst neu.
root:~# systemctl restart ssh
Loggen der Bash History einrichten
Da mehrere Administratoren das System verwalten, loggen wir alle in der Bash eingegebenen Befehle in einer Datei, um Änderungen am System nachvollziehen zu können.
Wir erstellen uns in der Datei /etc/profile.d/history.sh eine ReadOnly Environment Variable und loggen die Bash history nach local5.info.
root:~# ( cat <<HERE2
# $File: history.sh
if [ -n "$ORIGINAL_USER" ]; then
readonly export PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND ; }"'echo "USER:" $USER "("$ORIGINAL_USER")" \
"COMMAND:" "$(history 1 | cut -c8-)" | /usr/bin/logger -plocal5.info'
else
readonly export PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND ; }"'echo "USER:" $USER \
"COMMAND:" "$(history 1 | cut -c8-)" | /usr/bin/logger -plocal5.info'
fi
HERE2
) >/etc/profile.d/history.sh
Dann definieren wir local5.info eine Logdatei für rsyslogd. Alles was an local5.info geschrieben wird, wird dann in die Datei /var/log/bash/history.log geschrieben.
root:~# ( cat <<HERE3
# Bash history logging
local5.info /var/log/bash/history.log
& stop
HERE3
) >/etc/rsyslog.d/history.conf
Da das Verzeichnis /var/log/bash noch nicht existiert, erstellen wir dieses.
root:~# mkdir -p /var/log/bash
Damit diese Datei nicht endlos groß wird, definieren wir noch, dass diese täglich rotiert wird.
root:~# ( cat <<HERE4
/var/log/bash/history.log {
rotate 8
daily
compress
missingok
notifempty
delaycompress
create 640 root adm
}
HERE4
) >/etc/logrotate.d/history
Damit die Einstellungen wirksam werden, wechseln wir in eine neue Bash, damit die /etc/profile.d/history.sh neu geladen wird und starten den rsyslog Daemon neu.
root:~# bash && systemctl restart rsyslog
Benachrichtigung über neue Debian Pakete
Damit wir über die neusten Debian Pakete informiert werden richten wir uns das folgende Skript ein und aktivieren ein Cronjob für täglich 01:00.
root:~# ( cat <<HERE5
#!/bin/bash
# $File: debpackages.sh $ - $Issue: OP-3563 $
# $Author: mschulz $ - $Date: 2018-02-21 12:10:59 +0100 (Mi, 21 Feb 2018) $
# $HeadURL: http://svn.tuxnet24.de/operations-001/linux/scripts/debpackages.sh $ - $Revision: 55 $
# $Description: Script to get a list of debian packages, witch has to be updated. $
# ______________________________________________________
# Get the real hostname
hostname=$( [ -x /home/sysadmin/bin/hostname ] && /home/sysadmin/bin/hostname || hostname )
# The path to the temporary logfile for this program
logfile="/tmp/debpackages.log"
# The notify mail address
notify="it-monitoring@tuxnet24.de"
# The subject of the notify message
subject="[${LOGNAME}@$hostname] Debian Package Report"
# ##
# This function send a notify message and exit the program.
#
# @param string $1 The state (ERROR|OK)
# @param string $2 The notify mail address
# @param string $3 The notify subject
# @param string $4 The path to the report logfile
# @param string $5 The notify message
# @return void
#
function f_notify () {
local count="$1"
local notify="$2"
local subject="$3"
local logfile="$4"
local msg="$5"
# Create content of the mail body
local header="$( sed -n -e '2,7p' $0 | sed 's/^# //g' )"
local logtrace=$( [ -f "$logfile" ] && cat $logfile 2>/dev/null || echo -e "$logfile" )
# Send notify message
echo -e "${header}\n\n*${msg}*\n\n${logtrace}\n\n-- \n" | mail -s "$subject [${count}]" $notify
}
# ##
# MAIN
# Get a clean exit
trap "rm -rf ${logfile} 2>/dev/null" EXIT
touch ${logfile}
# Write a temporary upgrade list by executing the command apt-get -s dist-upgrade
apt-get update >/dev/null 2>&1 && apt-get -s dist-upgrade >${logfile}
# Get only the upgrade list
upgrades="$(perl -ne 'print $_ if ( $_ =~ m/(?:he following packages will|ie folgenden Pakete werden)/io .. $_ =~ m/(?:upgraded|aktualisiert)\./io );' ${logfile})"
if [ -n "${upgrades}" ]; then
# Get the count of the packages witch will be upgraded
count="$(perl -ne 'print $1 if ($_ =~ m/^(\d+).+(?:upgraded|aktualisiert)\./ig)' ${logfile})"
# Sent a notify message
f_notify "${count}" "${notify}" "${subject}" "${upgrades}" "On ${hostname} are currently ${count} packages pending an upgrade"
fi
# vim: syntax=bash ts=4 sw=4 sts=4 sr noet
# EOF
HERE5
) >/usr/local/sbin/debpackages.sh
Nachdem wir das Skript erstellt haben, mache wir es ausführbar.
root:~# chmod 744 /usr/local/sbin/debpackages.sh
Nun tragen wir noch den Cronjob für täglich 01:00 Uhr ein.
root:~# cat <(crontab -l) <(echo -e "# OP-3320 - mschulz - $(date +%Y%m%d) - Notify about new debian packages\n0 1 * * * /usr/local/sbin/debpackages.sh >/dev/null >&1") | crontab -
Tripwire installieren
Mit tripwire überwachen wir unser Dateisystem nach veränderten Dateien (md5sum). Zuerst installieren wir tripwire aus den Debian Paketquellen. Dabei wird nach ein Passwort (Passphrase) für tripwire gefragt (steht im Keypass). Dann sichern wir uns die Original twpol.txt und ändern dann die Policy darin.
root:~# apt update && apt install -y tripwire
root:~# cp /etc/tripwire/twpol.txt /etc/tripwire/twpol.txt.dist
Wir erstellen uns nun die twpol.txt. Wir überwachen dabei die Verzeichnisse /etc, /bin, /sbin, und /usr. Die Verzeichnisse /etc/apache2 und /etc/mtab nehmen wir aus der Überprüfung raus.
root:~# ( cat <<HERE6
# Tripwire Policy File
#
!/etc/mtab ;
!/etc/apache2 ;
/etc -> +ugisMS (emailto="operations@tuxnet24.de");
/bin -> +ugisMS (emailto="operations@tuxnet24.de");
/sbin -> +ugisMS (emailto="operations@tuxnet24.de");
/usr -> +ugisMS (emailto="operations@tuxnet24.de");
HERE6
) >/etc/tripwire/twpol.txt
Nun erstellen wir uns das Skript twctl, was nur ein Wrapper für tripwire ist. Es spart uns im täglichen arbeiten aber ein Haufen Tipparbeit.
root:~# ( cat <<HERE7
#!/bin/bash
# *************************************************************
# $File: twctl $
# $Author: mschulz $ - $Date: 2013-04-17 12:44:23 +0200 (Mi, 17 Apr 2013) $
# $HeadURL: http://svn.tuxnet24.de/operation-001/linux/scripts/twctl $ - $Revision: 8 $
# $Description: Wrapper script to work with tripwire. $
# *************************************************************
tw_bindir="/usr/sbin"
tw_config="/etc/tripwire/tw.cfg"
tw_policy="/etc/tripwire/tw.pol"
tw_sitekey="/etc/tripwire/site.key"
tw_reportdir="/var/lib/tripwire/report"
tw_localkey="/etc/tripwire/$(hostname)-local.key"
# MAIN
# Get the program action
action=$1
# Get the raw config & policy file (text/plain)
tw_config_raw=$(echo ${tw_config} | sed 's/\.cfg/cfg\.txt/g')
tw_policy_raw=$(echo ${tw_policy} | sed 's/\.pol/pol\.txt/g')
case "${action}" in
config)
# Konfigurieren aktualisieren
${tw_bindir}/twadmin -m F -c ${tw_config} -S ${tw_sitekey} ${tw_config_raw}
;;
policy)
# Policy aktualisieren
${tw_bindir}/twadmin -m P -c ${tw_config} -S ${tw_sitekey} ${tw_policy_raw}
;;
init)
# Datenbank erstellen
${tw_bindir}/tripwire -m i -c ${tw_config} -S ${tw_sitekey} -p ${tw_policy} -L ${tw_localkey}
;;
update)
# Datenbank aktualisieren
${tw_bindir}/tripwire -m u -r ${tw_reportdir}/$(ls -1rt ${tw_reportdir} | tail -1)
;;
check)
# System Prüfen und report mail generieren
${tw_bindir}/tripwire --check --quiet --email-report
;;
*)
echo -e "\aUsage: $0 {config|policy|init|update|check}"; exit 1
;;
esac
exit $?
# vim: syntax=bash ts=4 sw=4 sts=4 sr noet
# EOF
HERE7
) >/usr/local/sbin/twctl
Damit wir das Skript auch nutzen können, werden wir es noch ausführbar machen.
root:~# chmod 744 /usr/local/sbin/twctl
Unter /etc/cron.daily/tripwire ist ein Cronjob bei der Installation eingerichtet wurden. Dabei wird ein tripwire --check ausgeführt. Wir nutzen dafür ein eigenes Skript, welches den Email Subject an unsere Bedürfnisse anpasst. Im Subject kann man auch gleichzeig sehen, wie viel Dateien verändert wurden. Es wird auch nur dann ein Report gesendet, wenn sich Dateien geändert haben.
Damit wir dieses Skript installieren/aktivieren, löschen wir zunächst den installieren Cronjob.
root:~# rm -r /etc/cron.daily/tripwire
Nun installieren wir unser eigenes tripwire.sh Skript wie folgt.
root:~# ( cat <<HERE8
#!/bin/bash
# $File: tripwire.sh $ - $Issue: OP-0000 $
# $Author: mschulz $ - $Date: 2018-02-09 14:00:37 +0100 (Fr, 09 Feb 2018) $
# $HeadURL: http://svn.tuxnet24.de/operations-001/linux/scripts/tripwire.sh $ - $Revision: 45 $
# $Description: Script to run a tripwire check and notify when files where detected. $
# ______________________________________________________
notify_mailaddr="operations@tuxnet24.de"
notify_subject="[${LOGNAME}@$(hostname)] Tripwire Check Report"
notify_onsuccess="Off" # On|Off
cmd_options="--check --quiet"
outfile=/tmp/twcheck.log
trap "rm $outfile" EXIT
# execute the tripwire check command
/usr/sbin/tripwire ${cmd_options} >$outfile 2>/dev/null
# get the count of total violations founds
count=$(perl -ne 'print $1 if ($_ =~ m/total violations found\:\s+(\d+)/ig)' $outfile)
if [ -z "${count}" ]; then
count=$(perl -ne 'print $1 if ($_ =~ m/(No violations)\./ig)' $outfile)
[ "${count}" = "No violations" ] && count=0
fi
# Sent notify message
if [ "${count}" -gt 0 ]; then
cat $outfile | /usr/bin/mail -s "${notify_subject} WARN[${count}]" ${notify_mailaddr}
else
if [ "${notify_onsuccess}" = "On" ]; then
cat $outfile | /usr/bin/mail -s "${notify_subject} OK" ${notify_mailaddr}
fi
fi
# vim: syntax=bash ts=4 sw=4 sts=4 sr noet
# EOF
HERE8
) >/usr/local/sbin/tripwire.sh
Dann machen wir das Skript noch ausführbar.
root:~# chmod 755 /usr/local/sbin/tripwire.sh
Als letztes richten wir noch einen täglichen Cronjob um 01:00 Uhr für dieses Skript ein.
root:~# cat <(crontab -l) <(echo -e "# OP-0000 - mschulz - $(date +%Y%m%d) - daily tripwire report\n0 1 * * * /usr/local/sbin/tripwire.sh >/dev/null >&1") | crontab -
Einrichten des SSHKeymgr
Damit wir unseren SSHKeymgr zum ausrollen der SSH Keys nutzen können, muss dazu ein Unix Benutzer server erstellt werden (siehe weiter oben) der dann folgende Befehle als root ohne Passwort mit sudo ausführen kann. Dazu erstellen wir die Datei /etc/sudoers.d/sshkeymgr. Dies setzt natürlich voraus, dass das Paket sudo installiert ist.
root:~# ( cat <<HERE9
Defaults !requiretty
server ALL=NOPASSWD: /bin/chown
server ALL=NOPASSWD: /bin/mv
server ALL=NOPASSWD: /bin/rm
server ALL=NOPASSWD: /bin/tar
server ALL=NOPASSWD: /bin/mkdir
server ALL=NOPASSWD: /bin/chmod
HERE9
) >/etc/sudoers.d/sshkeymgr
root:~# chmod 440 /etc/sudoers.d/sshkeymgr
Als nächstes erstellen wir im Homeverzeihnis des Benutzers server ein ~/.ssh Verzeichnis und erstellen darin eine ~/.ssh/authorized_keys Datei, die den Public SSH Key vom SSHKeymgr (https://sshkey.tuxnet24.de/) enthält.
root:~# mkdir -p /home/server/.ssh
root:~# chmod 700 /home/server/.ssh
root:~# ( cat <<HERE10
# THIS FILE IS MAINTAINED BY SSH key://management. MANUAL CHANGES WILL BE OVERWRITTEN
# ON THE NEXT CONFIGURATION RUN. CHANGE CONTENT ON http://sshkey.tuxnet24.de/.
environment="ORIGINAL_USER=Systemuser - server@ser001" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4zJr1rWg/XUDEkNa2riO93W7y/5hgwFbebWZF19MtbyTUwp77L4/aW3op7YvdngiQgghGmZfJwmz60NcEg0R8owxck6G3tvn8atnlucN7anztmpnnVHd9rTj10Fbma3xWhoLGYjxeLuUSDHbH4pQx8qWXiugtRl0UxTxT7TUbR/+uYLwt2frYCH4XCQJSJweYQXsGF5/j4fGeMK399gFXLcxflZGHEuIzQtMxHOv8Z0mlkaTGn1NaOwGYsOifd9HaWeuL4eDCZ0QDjmFoIwpw2oD5K06+6eClv3vsdSEsJSYxznaN9IDV78iJYtnhQvH5V1s9WehRi8srhWU0+kDv server@ser001.tuxnet24.de
HERE10
) >/home/server/.ssh/authorized_keys
Apache2 mit php-cgi installieren
Für unsere Wordpress Instanzen müssen wir öfter mal ein Webserver mit Apache2 uns php-cgi einrichten. Zuerst installieren wir die benötigten Pakete.
root:~# apt update && apt install -y apache2 apache2-utils apache2-suexec-custom libapache2-mod-fcgid php-cgi
Nun erstellen wir im Verzeichnis /etc/apache2/conf-available/ eine setenv.conf in der wir Remote_Addr und weitere Variablen definieren.
root:~# ( cat <<HERE12
<IfModule mod_setenvif.c>
# Define the timezone for PHP
SetEnv TZ Europe/Berlin
# OP-4602 - mschulz - 20151006
SetEnvIf Remote_Addr "^217\.199\.67\.137$" IP_TUXNET24
SetEnvIf Remote_Addr "^217\.199\.67\.135$" IP_TUXNET24
# Define variable DONTLOG to define Request
# witch have to no log into the access logfile.
SetEnvIf Request_URI "^/favicon.ico$" DONTLOG
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
HERE12
) >/etc/apache2/conf-available/setenv.conf
Dann ändern wir die Standard Einstellung des Apache von ServerSignature On zu ServerSignature Off und ServerTokens OS zu ServerTokens Prod.
root:~# perl -pi -e 's/^ServerSignature On/ServerSignature Off/g' /etc/apache2/conf-available/security.conf
root:~# perl -pi -e 's/^ServerTokens OS/ServerTokens Prod/g' /etc/apache2/conf-available/security.conf
Damit beim apache2ctl keine Warnings auftauchen, setzen wir in der Datei /etc/apache2/apache2.conf den ServerName auf localhost.
root:~# perl -pi -e 's/^# Global configuration/# Global configuration\nServerName localhost/g' /etc/apache2/apache2.conf
Wir nutzen bei tuxnet24 ein eigenes Logformat. Warum ein eigenes Logformat, weiß ich auch nicht mehr. Dazu erstellen wir die Datei /etc/apache2/conf-available/logformat.conf.
root:~# ( cat <<HERE13
# OP-2906 - mschulz - 20120709
LogFormat "%t %{HOST}i %h \"%u\" \"%r\" \"%{Content-Type}o\" %>s %b \"%T\" \"%{User-Agent}i\" \"%{Referer}i\" \"%{cookie}i\"" tuxnet24common
HERE13
) >/etc/apache2/conf-available/logformat.conf
Wir ändern anschließend die Deault index.html zu Hello World, da die Original Default index.html verrät, dass es sich um einen Apache Webserver auf Debian handelt.
root:~# ( cat <<HERE14
<html>
<head>
<title>Hello World</title>
</head>
<body>
<p>Hello World</p>
</body>
</html>
HERE14
) >/var/www/html/index.html
Nun erweitern wir die PHP Fcgid Konfiguration.
root:~# ( cat <<HERE15
<IfModule mod_fcgid.c>
# FcgidIdleTimeout n (300 seconds)
# An idle fastcgi application will be terminated after IdleTimeout seconds.
FcgidIdleTimeout 600
# FcgidIdleScanInterval n (120 seconds)
# The scan interval for idle fastcgi applications.
FcgidIdleScanInterval 240
# FcgidBusyTimeout n (300 seconds)
# A fastcgi application will be terminated if handing a single request
# longer than busy timeout.
FcgidBusyTimeout 300
# FcgidBusyScanInterval n (120 seconds)
# The scan interval for busy timeout fastcgi applications.
FcgidBusyScanInterval 120
# FcgidErrorScanInterval n (3 seconds)
# The scan interval for exit pending fastcgi applications. fastcgi
# applications will be terminated within this scanning.
FcgidErrorScanInterval 6
# FcgidZombieScanInterval n (3 seconds)
# The scan interval for zombie process.
FcgidZombieScanInterval 6
# FcgidProcessLifeTime n (3600 seconds)
# A fastcgi application will be terminated if lifetime expired,
# even no error is detected.
FcgidProcessLifeTime 3600
# FcgidIPCDir path (logs/fcgidsock)
# The directory to put the UNIX domain socket. (UNIX only)
# This directory should be writable only by apache user
FcgidIPCDir /var/lib/apache2/fcgid/sock
# FcgidProcessTableFile path (logs/fcgid_shm)
# The share memory file path. (UNIX only) (version >= 2.1 only)
FcgidProcessTableFile /var/lib/apache2/fcgid/shm
# FcgidSpawnScoreUpLimit n (10)
# The spawn-speed control score up water limit. Score increases while
# a process is spawned or terminated, and decreases as time progresses;
# while the score is higher than SpawnScoreUpLimit, the spawning will be
# held for a while. The higher this number is, the higher speed of the
# spawning can be.
FcgidSpawnScoreUpLimit 150
# FcgidSpawnScore n (1)
# The weight of spawning. This weight will be plused to the spawn-control
# score on every spawn. The higher this number is, the lower speed of
# spawning can be.
FcgidSpawnScore 1
# FcgidTerminationScore n (2)
# The weight of termination. This weight will be plused to the score while
# fastcgi process terminates. The higher this number is, the lower speed
# of spawning can be.
FcgidTerminationScore 1
# FcgidMaxProcesses n (1000)
# The max count of total fastcgi process count.
FcgidMaxProcesses 300
# FcgidMaxProcessesPerClass n (100)
# The maximum number of fastcgi application instances allowed to run for
# particular one fastcgi application.
FcgidMaxProcessesPerClass 10
# FcgidMinProcessesPerClass n (3)
# The minimum number of fastcgi application instances for any one fastcgi
# application.
# Idle fastcgi will not be killed if their count is less than n
# Set this to 0, and tweak IdleTimeout
FcgidMinProcessesPerClass 0
# DefaultInitEnv env_name env_value
# The default environment variables before a fastcgi application
# is spawned. You can set this configuration more than once.
# FcgidConnectTimeout n (3 seconds)
# The connect timeout to a fastcgi application.
FcgidConnectTimeout 60
# FcgidIOTimeout n (20 seconds)
# The communication timeout to a fastcgi application. Please increase this
# value if your CGI have a slow initialization or slow respond.
FcgidIOTimeout 120
# OutputBufferSize n (64k bytes)
# CGI output cache buffer size.
FcgidOutputBufferSize 65536
# PHP_Fix_Pathinfo_Enable n(n=0/1, default 0)
# If you are using PHP and set cgi.fix_pathinfo=1 in php.ini, set PHP_Fix_Pathinfo_Enable 1.
# From php.ini:
# cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI. PHP's
# previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok
# what PATH_INFO is. For more information on PATH_INFO, see the cgi specs. Setting
# this to 1 will cause PHP CGI to fix it's paths to conform to the spec. A setting
# of zero causes PHP to behave as before. Default is zero. You should fix your scripts
# to use SCRIPT_FILENAME rather than PATH_TRANSLATED.
# cgi.fix_pathinfo=1
FcgidFixPathinfo 1
# FcgidMaxRequestsPerProcess n (0)
# FastCGI application processes will be terminated after handling the specified number of requests.
# A value of 0 disables the check.
FcgidMaxRequestsPerProcess 10000
# FcgidMaxRequestLen n (131072 bytes)
# If the size of the request body exceeds this amount, the request will fail with
# 500 Server Error. Administrators should change this to an appropriate value
# for their site based on application requirements.
FcgidMaxRequestLen 67108864
# FcgidMaxRequestInMem n (64k bytes)
# This module reads the entire request body from the client before sending it to the application.
# Normally the request body will be stored in memory. Once the amount of request body read
# from the client exceeds FcgidMaxRequestInMem bytes, the remainder of the request body
# will be stored in a temporary file.
FcgidMaxRequestInMem 65536
<IfModule mod_mime.c>
AddHandler fcgid-script .php
AddHandler fcgid-script .fcgi
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps
</IfModule>
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
HERE15
) >/etc/apache2/mods-enabled/fcgid.conf
Als nächstes laden wir folgende Module im Apache Webserver.
root:~# a2enmod proxy
root:~# a2enmod proxy_http
root:~# a2enmod rewrite
root:~# a2enmod ssl
root:~# a2enmod suexec
Die beiden erstellten Konfigurationsdateien aktivieren wir global für jeden Vhost.
root:~# a2enconf logformat.conf
root:~# a2enconf setenv.conf
Die CGI Konfiguration entfernen wir.
root:~# a2disconf serve-cgi-bin.conf
Nun erstellen wir uns ein Unix Benutzer tuxnet24 mit der UID/GID 1000.
root:~# groupadd -g 1000 tuxnet24 && \
adduser --quiet --disabled-password --gecos "tuxnet24" --uid 1000 --gid 1000 tuxnet24
Dann erstellen wir für den Benutzer tuxnet24 die Verzeichnisstruktur.
root:~# mkdir -p /home/tuxnet24/etc
root:~# mkdir -p /home/tuxnet24/usr/share/fcgid
root:~# mkdir -p /home/tuxnet24/var/{log,www}
root:~# cp /etc/php/7.4/cli/php.ini /home/tuxnet24/etc/
Anschließend erstellen wir für den Benutzer den php-cgi Starter.
root:~# ( cat <<HERE16
#!/bin/sh
# Shell Script To Run PHP using mod_fcgid under Apache 2
umask 022
# The PREFIX variable will no be exported
PREFIX="/home/tuxnet24"
PHPRC="${PREFIX}/etc"
PHP_FCGI_CHILDREN=4
PHP_FCGI_MAX_REQUESTS=1000
FCGI_SERVER_MAX_STDERR_LINE_LEN=2047
TMPDIR="/var/tmp"
export PHPRC TMPDIR PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS FCGI_SERVER_MAX_STDERR_LINE_LEN
exec /usr/bin/php-cgi -c "${PHPRC}/php.ini"
HERE16
) >/home/tuxnet24/usr/share/fcgid/php-cgi
Den Starter machen wir noch ausführbar mit folgendem Befehl.
root:~# chmod 755 /home/tuxnet24/usr/share/fcgid/php-cgi
Dann erstellen wir in der DocumentRoot eine index.php. Diese soll nur anzeigen, das der Apache mit PHP korrekt installiert ist.
root:~# ( cat <<HERE17
<?php
phpinfo();
?>
HERE17
) >/home/tuxnet24/var/www/index.php
Da die DocumentRoot der Benutzers im Homeverzeichnis liegt, ändern wir die suexec Konfiguration ebenfalls in /home.
perl -pi -e 's/^\/var\/www/\/home/g' /etc/apache2/suexec/www-data
Nun ändern wir die Berechtigungen für den tuxnet24 Benutzer in seine User/Group.
root:~# chown -R tuxnet24.tuxnet24 /home/tuxnet24/{etc,usr,var}
Wir setzen eine Environment Variable, welche wir für den ServerNamen des Vhosts verwenden. In diesem Beipiel habe ich bubu.tuxnet24.de verwendet.
root:~# SERVERNAME="bubu.tuxnet24.de"
Nun erstellen wir die Vhost Datei. Dabei wird die Variable $SERVERNAME ersetzt.
root:~# ( cat <<HERE18
# http
<VirtualHost *:80>
ServerAdmin operations@tuxnet24.de
ServerName ${SERVERNAME}
DocumentRoot /var/www/html
# Redirect all http request of SERVER_NAME to https
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteCond %{SERVER_PORT} !^443$
RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [L,R=301]
</IfModule>
</VirtualHost>
# https
<VirtualHost *:443>
ServerAdmin operations@tuxnet24.de
ServerName ${SERVERNAME}
DocumentRoot /home/tuxnet24/var/www
# Suexec settings (userame, group)
SuexecUserGroup tuxnet24 tuxnet24
# Save access to our wordpress login/backend
Include conf-available/wp-security.conf
# Directory settings
<Directory />
Options +FollowSymLinks
AllowOverride None
</Directory>
<Directory /home/tuxnet24/var/www>
Options -Indexes +FollowSymLinks +MultiViews
DirectoryIndex index.htm index.html index.php
AllowOverride All
Require all granted
</Directory>
# Load php settings
Include "conf-available/php-cgi_tuxnet24.conf"
# Include ssl configuration of letsencrypt certificates
Include conf-available/ssl_tuxnet24.de.conf
CustomLog "/home/tuxnet24/var/log/${SERVERNAME}_acc.log" tuxnet24common env=!DONTLOG
ErrorLog /home/tuxnet24/var/log/${SERVERNAME}_err.log
LogLevel warn
</VirtualHost>
HERE18
) >/etc/apache2/sites-available/010-${SERVERNAME}.conf
Für die Vhost Datei definieren wir noch die PHP Konfiguration des Benutzers tuxnet24. Diese speichern wir nach /etc/apache2/conf-available/php-cgi_tuxnet24.conf.
root:~# ( cat <<HERE19
# PHP configuration for apache user 'tuxnet24'
<IfModule mod_fcgid.c>
<Files ~ "\.php">
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps
FCGIWrapper /home/tuxnet24/usr/share/fcgid/php-cgi .php
AddHandler fcgid-script .php
AddHandler fcgid-script .fcgi
Options +ExecCGI
</Files>
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
HERE19
) >/etc/apache2/conf-available/php-cgi_tuxnet24.conf
Bei unseren Wordpress Instanzen sichern wir uns unser Backend /wp-admin bzw. /wp-login.php. Dazu erstellen wir eine Datei /etc/apache2/conf-available/wp-security.conf welche ebenfalls in der Vhost Datei includiert wird.
root:~# ( cat <<HERE20
# Save access to our wordpesss backend
# Access to login page
<Files wp-login.php>
Require env IP_TUXNET24
</Files>
# Access to backend
<Location /wp-admin>
Require env IP_TUXNET24
</Location>
# Allow access to admin-ajax.php
<Files admin-ajax.php>
Satisfy Any
Allow from all
AuthType None
Require all granted
</Files>
# Deactivate xmlrpc.php
<Files xmlrpc.php>
Order Allow,Deny
Deny from all
Require all denied
</Files>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
HERE20
) >/etc/apache2/conf-available/wp-security.conf
Nun setzen wir eine Environment Variable für unsere Letsencrypt Zertifikate. Diese Variable wird in der Datei /etc/apache2/conf-available/ssl_tuxnet24.de.conf verwendet.
root:~# CERTNAME="tuxnet24.de"
Nun erstellen wir die Datei /etc/apache2/conf-available/ssl_tuxnet24.de.conf. Diese wird im https Vhost includiert.
root:~# ( cat <<HERE21
<IfModule mod_ssl.c>
SSLEngine on
# Apache < 2.4.8
#SSLCertificateFile /etc/letsencrypt/live/${CERTNAME}/cert.pem
#SSLCertificateChainFile /etc/letsencrypt/live/${CERTNAME}/chain.pem
#SSLCertificateKeyFile /etc/letsencrypt/live/${CERTNAME}/privkey.pem
# Apache >= 2.4.8
SSLCertificateKeyFile /etc/letsencrypt/live/${CERTNAME}/privkey.pem
SSLCertificateFile /etc/letsencrypt/live/${CERTNAME}/fullchain.pem
# Intermediate configuration, tweak to your needs
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
SSLSessionTickets off
SSLCompression off
SSLOptions +StrictRequire
# Enable HSTS (HTTP Strict Transport Security)
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=63072000;"
</IfModule>
BrowserMatch "MSIE [2-6]" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0
# MSIE 7 and newer should be able to use keepalive
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
HERE21
) >/etc/apache2/conf-available/ssl_tuxnet24.de.conf
Als letztes aktivieren wir die Vhost Datei und starten den Apache graceful.
root:~# a2ensite 010-${SERVERNAME}.conf
root:~# apache2ctl configtest && apache2ctl graceful
MySQL Server [mariadb] installieren
Zuerst installieren wir die Paktete mysql-server und mysql-client. Die Pakete default-mysql-* sind Meta Packages und verwenden die neuste mariadb-server Version.
root:~# apt update && apt install -y default-mysql-server default-mysql-client
Dann führen wir mysql_secure_installation aus. Dieses Programm ist interaktive und kann daher nicht als Batch ausgeführt werden. Wir setzen dabei das root Passwort, alle anderen Fragen beantworten wir mit Y (Yes). Es werden dabei anonymous Users und test Datenbanken gelöscht. Weiterhin wird Remote Login für root deaktiviert.
root:~# mysql_secure_installation
Nun setzen wir in der Environment Variable das root Passwort (DBADMINPASSWORD), welches für den admin Benutzer verwenden, der ebenfalls wie root, alle Privilegien hat. Das Passwort DBBACKUPPASSWORD ist für unseren Backup Benutzer, der via dem Skript mysqldump.sh täglich die Datenbanken sichert.
root:~# adminpass="${DBADMINPASSWORD}"
root:~# backuppass="${DBBACKUPPASSWORD}"
Die folgenden 4 Befehle, erstellen einen admin Benutzer mit den Rechten wie root. Weiterhin wird ein Backup Benutzer erstellt und die Permissions SELECT, LOCK TABLES, EVENT, SHOW VIEW gesetzt. Zum Schluss werden die Benutzer Einstellungen nochmal eingelesen bzw. neu geladen.
root:~# mysql -u root --password=${adminpass} -e "GRANT ALL ON *.* TO 'admin'@'localhost' IDENTIFIED BY '${adminpass}' WITH GRANT OPTION;"
root:~# mysql -u root --password=${adminpass} -e "CREATE USER 'backup'@'localhost' IDENTIFIED BY '${backuppass}';"
root:~# mysql -u root --password=${adminpass} -e "GRANT SELECT, LOCK TABLES, EVENT, SHOW VIEW ON *.* TO 'backup'@'localhost';"
root:~# mysql -u root --password=${adminpass} -e "FLUSH PRIVILEGES;"
Zeit mit NTP syncronisieren
Zur Zeitsynchronisation nutzen wir den ntpd Daemon. Diesen installieren wir wie folgt aus den Debian Paket quellen.
root:~# apt update && apt install -y ntp ntpdate
Die Konfiguration belassen wir soweit, bis auf die ipv6 Konfiguration, die wir deaktivieren. Das machen wir mit folgendem Befehl, womit wir den String restrict ::1 kommentieren.
perl -pi -e 's/^restrict ::1/# restrict ::1/g' /etc/ntp.conf
Außerdem wird in der /etc/default/ntp Datei die Option -4, für ipv4 gesetzt.
root:~# ( cat <<HERE22
NTPD_OPTS='-4 -g'
HERE22
) >/etc/default/ntp
Zu Schluss starten wir den ntpd Daemon neu.
root:~# systemctl restart ntp
Zeitzone Europe/Berlin einstellen
Wir nutzen bei unseren Servern die Zeitzone Europe/Berlin. Diese kann man interaktive mit dpkg-reconfigure tzdata einstellen.
root:~# dpkg-reconfigure tzdata
Besser für den Batch Modus wäre der Befehl timedatectl set-timezone Europe/Berlin.
root:~# timedatectl set-timezone Europe/Berlin
Mit dem Befehl timedatectl können wir uns die aktuelle Zeitzone anzeigen lassen.
root:~# timedatectl
Local time: Mon 2022-08-22 11:18:40 CEST
Universal time: Mon 2022-08-22 09:18:40 UTC
RTC time: Mon 2022-08-22 09:18:41
Time zone: Europe/Berlin (CEST, +0200)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
Docker Engine installieren
Zuerst installieren wir die benötigten Pakete damit apt HTTPS Repositorys nutzen kann.
root:~# apt update && \apt install -y \
ca-certificates \
curl \
gnupg \
lsb-release
Dann fügen wir den offiziellen Docker’s GPG Key hinzu.
root:~# mkdir -p /etc/apt/keyrings
root:~# curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
Mit dem folgenden Befehlt tragen wir das Docker Repository zu unseren Paket Quellen ein.
root:~# echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
$(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
Zum Schluss installieren wir Docker Enigne mit folgendem Befehl.
root:~# apt update && apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
Prüfen können wir die Installation, wenn wir docker run hello-world ausführen.
root:~# docker run hello-world
Wenn nach der Installation von Docker noch keine Gruppe docker existiert, legen wir diese an.
root:~# if ! grep -q docker /etc/group ; then groupadd docker; fi
In dieser Gruppe werden die Benutzer als Zweitgruppe hinzugefügt, welche Docker benutzen sollen.
root:~# usermod -aG docker <USERNAME>
Docker Compose installieren
Hier wählen wir die manuelle Installation nach /usr/local/bin/. In diesem Fall laden wir uns die Version 2.3.0 herunter.
root:~# curl -L "https://github.com/docker/compose/releases/download/v2.3.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
Für die docker-compose Version 1.29.2, verwenden wir folgenden Befehl.
root:~# curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
Mit folgenden Befehl werden wir das heruntergeladene docker-compose ausführbar machen.
root:~# chmod +x /usr/local/bin/docker-compose
Nun testen wir docker-compose, indem wir uns die installierte Version anzeigen lassen.
root:~# docker-compose --version
Docker Compose version v2.3.0