Verschlüsselter Docker-Server thinc
Siehe ritter.local
Hardware: HP t520 thin client mit 120GB SSD
OS: debian-bookworm
- Minimum-Installation
- Alle Updates
apt update && apt upgrade apt install \ vim \ openssh-server \ dropbear-initramfs \ apache2-utils \ mc \ rcs \ rsync \ swaks \ ufw \ inetutils-tools \ net-tools \ fuse \ at \ samba \ nullmailer \ mailutils \ hdparm \ sudo \ golang \ ethtool \ hwinfo \ hdparm \ pv \ gdu \ iw wpasupplicant wireless-tools
springm zur Gruppe sudoers hinzufügen
su - usermod -aG sudo springm
Achtung: wird erst nach ausloggen wirksam
Netzwerk: *.zalmoxis.aberdoch.de registriert
Boot-Disk verschlüsseln
ssh passwordless login
ssh-copy-id -i .ssh/id_ecdsa.pub 192.168.2.251 ssh zalmoxis sudo cp ~/.ssh/authorized_keys /root/.ssh
dropbear-initramfs
Entsprechend c't-Artikel luks_verschluesselte_linux-systeme_ueber_ssh_entsperren_c_t_magazin.pdf
Die ssh-public-keys der Benutzer, die das System per ssh entsperren sollen können, liegen in /etc/dropbear/initramfs/authorized_keys (kopieren von hermes…).
Minimum-Inhalt für die Konfigurationsdatei:
- bash
# # Command line options to pass to dropbear(8) # DROPBEAR_OPTIONS="-p 20022 -c cryptroot-unlock"
Die .ssh/authorizedkeys von springm nach /root/.ssh kopierenssh -v -p 20022 <rechnername lt. Fritzbox>
sudo cp ~springm/.ssh/authorized_keys /root/.ssh
Nach Änderung muss das initramfs aktualisiert werden:
update-initramfs -u
Entsperren mit /etc/nullmailer
Das ganze mit Wifi:
* https://www.marcfargas.com/2017/12/enable-wireless-networks-in-debian-initramfs/
* https://www.dwarmstrong.org/remote-unlock-dropbear/
===== Wifi in initramfs einschalten =====
Wird gebraucht um später die Platte nach dem Booten über WLAN entschlüsseln zu können.
Rezept von https://www.marcfargas.com/2017/12/enable-wireless-networks-in-debian-initramfs/ ( enable_wireless_networks_in_debian_initramfs_marcfargas.com.pdf )
wlan0 beim Boot bekanntmachen:
<file bash /etc/default/grub>
…
GRUBCMDLINELINUX=“net.ifnames=0 ip=:::::wlan0:dhcp”
…
</file>
Benötigte Module für das initramfs
<file bash /etc/initramfs-tools/modules>
rtlwifi
rtl8xxxu
mac80211
usbcore
rtl8192eu_nic.bin
</file>
<file bash /etc/initramfs-tools/scripts/init-premount/aenablewireless>
#!/bin/sh
PREREQ=“”
prereqs()
{
echo “$PREREQ”
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
. /scripts/functions
AUTHLIMIT=30
INTERFACE=“wlan0”
alias WPACLI=“/sbin/wpacli -p /tmp/wpa_supplicant -i $INTERFACE ”
logbeginmsg “Starting WLAN connection”
### mws
rmmod rtl8xxxu
modprobe rtl8xxxu
sleep 2
### end mws
/sbin/wpasupplicant -i $INTERFACE -c /etc/wpasupplicant.conf -P /run/initram-wpasupplicant.pid -B -f /tmp/wpasupplicant.log
# Wait for AUTHLIMIT seconds, then check the status
limit=${AUTHLIMIT}
echo -n “Waiting for connection (max ${AUTHLIMIT} seconds)”
while [ $limit -ge 0 -a WPACLI status | grep wpa_state != “wpastate=COMPLETED” ]
do
sleep 1
echo -n “.”
limit=expr $limit - 1
done
echo “”
if [ WPACLI status | grep wpa_state != “wpastate=COMPLETED” ]; then
ONLINE=0
logfailuremsg “WLAN offline after timeout”
panic
else
ONLINE=1
logsuccess_msg “WLAN online”
fi
configure_networking
</file>
<file bash /etc/initramfs-tools/scripts/local-bottom/kill_wireless>
#!/bin/sh
PREREQ=“”
prereqs()
{
echo “$PREREQ”
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
echo “Killing wpasupplicant so the system takes over later.”
kill cat /run/initram-wpa_supplicant.pid
</file>
Eintrag in /etc/network/interfaces
<file bash /etc/network/interfaces>
…
auto wlan0
iface wlan0 inet dhcp
wpa-conf /etc/wpasupplicant/wpa_supplicant.conf
…
</file>
Passwort in /etc/wpasupplicant/wpasupplicant.conf
<file bash /etc/wpasupplicant/wpasupplicant.conf>
ctrlinterface=/run/wpasupplicant
update_config=1
network={
ssid=“Your ESSID”
psk=langundverschlüsselt
}
</file>
Die wpasupplicant.conf findet sich identisch an 3 verschiedenen Stellen :(
* /etc/wpasupplicant.conf
* /etc/wpasupplicant/wpasupplicant.conf
* /etc/initramfs-tools/wpa_supplicant.conf
==== ESSID und/oder Wifi-Passwort ändern ====
wpapassphrase “Eleven Ways” wifipassword » /etc/wpasupplicant.conf
/etc/wpa_supplicant.conf editieren und alte Konfiguration auskommentieren
rm /etc/wpasupplicant/wpasupplicant.conf
cp -l /etc/wpasupplicant.conf /etc/wpasupplicant/wpasupplicant.conf
rm /etc/initramfs-tools/wpasupplicant.conf
cp -l /etc/wpasupplicant.conf /etc/initramfs-tools/wpasupplicant.conf
Kontrollieren auf Gleichheit mit
sum $(find / -name wpa_supplicant.conf -not -path “/backupdisk”)
Dann initramfs neu erstellen
update-initramfs -u -k all
==== Wlan als Dienst starten ====
Nach dem Entsperren wird das Wlan über einen Dienst gestartet ( https://www.skerit.com/en/configure-wifi-on-minimal-debian-11-server )
configure_wifi_on_a_minimal_debian_11_server_skeritcom.pdf
<file bash /etc/systemd/system/dhclient.service>
[Unit]
Description=DHCP Client
Before=network.target
After=wpa_supplicant.service
[Service]
Type=forking
ExecStart=/sbin/dhclient wlan0 -v
ExecStop=/sbin/dhclient wlan0 -r
Restart=always
[Install]
WantedBy=multi-user.target
</file>
Starten mit
systemctl enable dhclient
systemctl restart wpa_supplicant
systemctl restart networking
Kontrollieren mit
ip a
===== ufw =====
ufw allow proto tcp from any to any port 22,80,139,443,445
ufw allow proto udp from any to any port 137,138
ufw enable
===== email senden =====
Emails sollen für Statusmails versendet werden können. Nullmailer ist einfach, 3 Konfigurationsdateien stehen in
Links
* https://wiki.debian.org/nullmailer
* https://dan.langille.org/2013/10/16/using-nullmailer-instead-of-a-full-blown-mail-server/
===== debian packages sichern (und später wiederherstellen) =====
von https://unix.stackexchange.com/questions/79125/reinstalling-all-debian-packages
dpkg –get-selections | grep -v deinstall | awk '{print $1}' | awk '$1=$1' ORS=' ' > /etc/installeddebs.txt
kommt in die /etc/crontab.
Restore erfolgt dann mit
apt-get clean && apt-get update && apt-get install –reinstall $(/etc/installeddebs.txt)
===== docker =====
from https://computingforgeeks.com/how-to-install-docker-on-debian-12-bookworm/
sudo apt install -y \
lsb-release \
gnupg2 \
apt-transport-https \
ca-certificates \
curl \
software-properties-common
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg –dearmor -o /etc/apt/trusted.gpg.d/debian.gpg
add-apt-repository “deb [arch=$(dpkg –print-architecture)] https://download.docker.com/linux/debian $(lsb_release -cs) stable”
apt update
apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
usermod -aG docker springm
newgrp docker
systemctl status docker
docker.service - Docker Application Container Engine
Loaded: loaded (/lib/systemd/system/docker.service; enabled; preset: enabl>
Active: active (running) since Mon 2023-08-28 15:07:09 CEST; 27s ago
TriggeredBy: ● docker.socket
Docs: https://docs.docker.com
Main PID: 9365 (dockerd)
Tasks: 9
Memory: 29.0M
CPU: 1.391s
CGroup: /system.slice/docker.service
└─9365 /usr/bin/dockerd -H fd: –containerd=/run/containerd/cont>
Aug 28 15:07:04 debian-thinc systemd[1]: Starting docker.service - Docker Appli>
Aug 28 15:07:04 debian-thinc dockerd[9365]: time=“2023-08-28T15:07:04.749035874>
Aug 28 15:07:06 debian-thinc dockerd[9365]: time=“2023-08-28T15:07:06.254635497>
Aug 28 15:07:08 debian-thinc dockerd[9365]: time=“2023-08-28T15:07:08.360733123>
Aug 28 15:07:08 debian-thinc dockerd[9365]: time=“2023-08-28T15:07:08.565571357>
Aug 28 15:07:08 debian-thinc dockerd[9365]: time=“2023-08-28T15:07:08.566231451>
Aug 28 15:07:09 debian-thinc dockerd[9365]: time=“2023-08-28T15:07:09.476426543>
Aug 28 15:07:09 debian-thinc systemd[1]: Started docker.service - Docker Applic>
docker version
Client: Docker Engine - Community
Version: 24.0.5
API version: 1.43
Go version: go1.20.6
Git commit: ced0996
Built: Fri Jul 21 20:35:35 2023
OS/Arch: linux/amd64
Context: default
Server: Docker Engine - Community
Engine:
Version: 24.0.5
API version: 1.43 (minimum version 1.12)
Go version: go1.20.6
Git commit: a61e2b4
Built: Fri Jul 21 20:35:35 2023
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.22
GitCommit: 8165feabfdfe38c65b599c4993d227328c231fca
runc:
Version: 1.1.8
GitCommit: v1.1.8-0-g82f18fe
docker-init:
Version: 0.19.0
GitCommit: de40ad0
==== lazydocker ====
https://github.com/jesseduffield/lazydocker
go install github.com/jesseduffield/lazydocker@latest
mv /root/go /usr/local
ln -s /usr/local/go/bin/lazydocker /usr/local/bin
==== Docker-Dienste ====
=== Traefik ===
=== pihole ===
=== restic-restserver ===
=== resticinfo ===
=== mailserver ===
=== calibre ===
===== Wake-on-Lan =====
* https://wiki.debian.org/WakeOnLan
* https://www.thomas-krenn.com/de/wiki/Wake_on_LAN_unter_Linux
Datei /etc/network/interfaces.d/enp1s0 erzeugen:
auto enp1s0
iface enp1s0 inet dhcp
ethernet-wol g
booten um die Änderung wirksam zu machen.
===== Zeitgesteuert schlafen/aufwachen =====
von https://www.baeldung.com/linux/auto-suspend-wake
Achtung: Mit Verschlüsselung funktioniert nur suspend-to-memory
===== Traefik =====
Traefik ist der router für die Docker-Container. Da alle Dienste nur im LAN zur Verfügung stehen, müssen dafür keine Zertifikate zur Verfügung gestellt werden.
===== pihole =====
<code yaml docker-compose.yml>
# from https://codecaptured.com/blog/self-hosting-pi-hole-with-docker-and-traefik/
# adapted for usage of .env file
version: “3”
services:
pihole:
image: pihole/pihole:latest
containername: pihole
restart: always
ports:
- “53:53/tcp”
- “53:53/udp”
dns:
- 127.0.0.1
- 9.9.9.9
environment:
TZ: 'Europe/Berlin'
WEBPASSWORD: ${WEBPASSWORD}
PIHOLEDNS: 9.9.9.9;149.112.112.112;1.1.1.1
DNSSEC: 'true'
ServerIP: ${SERVERIP}
VIRTUALHOST: ${VIRTUALHOST}
DNSMASQLISTENING: all
DHCPACTIVE: ${DHCPACTIVE}
DHCPSTART: ${DHCPSTART}
DHCPEND: ${DHCPEND}
DHCPROUTER: ${DHCPROUTER}
DHCPLEASETIME: ${DHCPLEASETIME}
WEBTHEME: default-dark
PIHOLEDOMAIN: ${PIHOLEDOMAIN}
volumes:
- './services/pihole:/etc/pihole/'
- './services/dnsmasq.d:/etc/dnsmasq.d/'
capadd:
- NETADMIN
restart: unless-stopped
networks:
lan: {}
backend:
ipv4address: ${BACKENDIPV4ADDRESS}
labels:
- “traefik.enable=true”
- “traefik.http.routers.pihole.rule=Host(${VIRTUAL_HOST})”
- “traefik.http.services.pihole.loadbalancer.server.port=80”
- “traefik.http.routers.pihole.entrypoints=web”
dependson:
- dhcphelper
dhcphelper:
image: noamokman/dhcp-helper
containername: dhcp-helper
restart: unless-stopped
networkmode: “host”
command: -s ${BACKENDIPV4ADDRESS}
capadd:
- NETADMIN
networks:
backend:
ipam:
config:
- subnet: ${BACKEND_SUBNET}
lan:
external: true
</code>
<code bash [enablelinenumbers=“false”] .env>
PIHOLEDOMAIN=
WEBPASSWORD=
# Actual server IP. Matches DHCP conf file IP
SERVERIP=
# Same as port traefik config
VIRTUALHOST=
DHCPACTIVE=
DHCPSTART=
DHCPEND=
DHCPROUTER=
DHCPLEASETIME=
BACKENDIPV4ADDRESS=
BACKENDSUBNET=
</code>
* https://codecaptured.com/blog/self-hosting-pi-hole-with-docker-and-traefik/
* https://github.com/mirdaki/computer-config/blob/blog-pihole/services/local/README.md
* https://jurian.slui.mn/posts/pi-hole-web-interface-behind-traefik/
==== dnscrypt-proxy ====
Vorher die Versionsnummer von https://github.com/DNSCrypt/dnscrypt-proxy/releases holen
cd /opt/ && \
wget https://github.com/DNSCrypt/dnscrypt-proxy/releases/download/2.1.5/dnscrypt-proxy-linux_x86_64-2.1.5.tar.gz && \
tar xvzf dnscrypt-proxy-linuxx8664-2.1.5.tar.gz
cd linux-x86_64/
cp ~springm/dnscrypt-proxy/dnscrypt-proxy.toml .
sudo ./dnscrypt-proxy -service install
sudo ./dnscrypt-proxy -service start
===== Backup =====
Das Backup passiert mit Restic: https://restic.net
Das binary liegt in /usr/local/bin
Backups erfolgen in das Verzeichnis /backupdisk/restic-repo
Das restic-Passwort für das Restic-Repository wird in /root/resticpw gespeichert.
==== Eigenes Backup ====
<code bash /usr/local/sbin/resticbackup.sh>
#!/bin/bash
export RESTICREPOSITORY=/backupdisk/restic-repo
export RESTICPASSWORDFILE=/root/resticpw
export RESTICEXCLUDEFILE=/root/restic_exclude
export HOME=/root
/usr/local/bin/restic backup /root /etc /usr/local/bin /usr/local/sbin /home/springm/docker
</code>
Zu starten via cron, ebenso restic forget
==== Restic-Server ====
Der restic-Server läuft in einem docker-Container unter der URL http://resticserver.ritter.local
==== Restic-Info ====
Läuft in einem Docker-Containern. Liest die JSON-Datei aus /var/lib/restic/snapshots.json
/var/lib/restic/snapshots.json wird per inotify bei Änderungen im Verzeichnis /backupdisk/restic-repo/snapshots erzeugt.
Die Informationen stehen unter der URL http://resticinfo.ritter.local zur Verfügung.
=== Erzeugen der /var/lib/restic/snapshots.json ===
Ein inotify-Eintrag auf das snapshots-Verzeichnis im Restic-Repository triggert das Schreiben einer JSON-Zustandsdatei mit dem Befehl
restic snapshots -c –json > /var/lib/restic/snapshots.json
Dieser Befehl ist zusammen mit dem Restic-Passwort und dem Repository in /usr/local/sbin/resticsnapshotsjson.sh niedergelegt:
<code bash /usr/local/sbin/resticsnapshotsjson.sh>
#!/bin/bash
/usr/bin/date » /tmp/resticmonitoring.log
export RESTICREPOSITORY=/backupdisk/restic-repo
export RESTICPASSWORDFILE=/root/resticpw
export HOME=/root
#/usr/local/bin/restic snapshots -c –json
/usr/local/bin/restic snapshots -c –json > /var/lib/restic/snapshots.json 2»/tmp/restic_monitoring.log
</code>
Die Automatisierung erfolgt über einen systemd-Service (vgl. https://www.putorius.net/systemd-path-units.html)
<file bash /etc/systemd/system/restic-snapshots-mon.path>
[Unit]
Description=“Monitor restic snapshots for changes”
[Path]
PathModified=/backupdisk/restic-repo/snapshots/
Unit=restic-snapshots-mon.service
[Install]
WantedBy=multi-user.target
</file>
<file bash /etc/systemd/system/restic-snapshots-mon.service>
[Unit]
Description=“Recreate /var/lib/restic/snapshots.json when snapshots have changed”
[Service]
ExecStart=/usr/local/sbin/resticsnapshotsjson.sh
</file>
<file bash /usr/local/sbin/restic-json.sh>
#!/bin/bash
/usr/bin/date » /tmp/resticmonitoring.log
export RESTICREPOSITORY=/backupdisk/restic-repo
export RESTICPASSWORDFILE=/root/resticpw
export HOME=/root
/usr/local/bin/restic snapshots -c –json > /var/lib/restic/snapshots.json 2»/tmp/restic_monitoring.log
</file>
Analysieren mit systemd-analyze verify /etc/systemd/system/restic-snapshots-mon*
Starten mit systemctl start restic-snapshots-mon.path
Beim Systemstart hochfahren mit systemctl enable restic-snapshots-mon.path
Status überprüfen mit systemctl status restic-snapshots-mon.path
Log-Einträge ansehen mit journalctl -u restic-snapshots-mon.path
Erzeugen der /var/lib/restic/snapshots.json beim booten durch einen cron-eintrag
@reboot root /usr/local/sbin/resticsnapshotsjson.sh
==== Backuptree für Kontrolle und Restore ====
Backups werden readonly im Verzeichnis /restic_restore zur Verfügung gestellt.
Der restic-mount-Prozess wird täglich zu einer festgesetzten Zeit gekillt und neu gestartet, damit die neuesten Änderungen sichtbar werden.
/etc/crontab ruft regelmäßig folgendes Script auf:
<code bash /usr/local/sbin/mountresticrestore.sh>
#!/bin/bash
#chmod 755 /restic-restore
/usr/bin/umount /restic-restore
sleep 3
/usr/local/bin/restic -r /backupdisk/restic-repo/ \
-p /root/resticpw \
mount \
–allow-other \
–no-default-permissions \
–no-lock \
/restic-restore
</code>
Links:
* https://wiki.debian.org/Samba/ServerSimple
* https://www.thomas-krenn.com/en/wiki/Simple_Samba_Shares_in_Debian
* https://superuser.com/questions/1069044/how-to-set-up-anonymous-read-only-samba-shares