Dedicated Server mit LUKS Root Filesystem

Submitted by admin on So, 11.12.2016 - 12:01

1463574861938-luks-encryption.png

Vorbereitung

Im Admin Tool wird zu nächst das Rescue System für Linux gestartet. Auf den Festplatten werden zwei Partitionen vom Typ 0xFD (Linux raid autodetect) eingerichtet. Danach wird mit

mdadm --create -l 1 -n 2 /dev/md0 /dev/sd[ab]1** 
mdadm --create -l 1 -n 2 /dev/md1 /dev/sd[ab]5** 

der RAID Verbund erstellt. Mit mkfs.ext4 /dev/md0 wird jetzt schon die /boot Partition formatiert.

EFI: Manche Hoster verwenden EFI. In diesem Fall muss man zuerst eine ca. 200MByte große EFI Partition erstellen und diese dann als /target/boot/efi einbinden.

LUKS Partition erstellen

cryptsetup luksFormat /dev/md1** 

erstellt ein LUKS Device, dabei ist ein Passwort festgelegt, das für alle weiteren Bootvorgänge benötigt wird. Mit

blkid /dev/md1

wird die UUID ermittelt. In den folgenden Beispielen wird die UUID 4bb3dd2c-cad4-4dd4-8ff4-a2e944baf090 verwendet.

Das LUKS Device wird mit

cryptsetup luksOpen /dev/md1 crypt-4bb3dd2c-cad4-4dd4-8ff4-a2e944baf090

geöffnet und kann danach verwendet werden. Dabei ist das eben erstellte Passwort zu verwenden.

In diesem Beitrag wird davon ausgegangen, dass alles verschlüsselt werden soll, daher wird die LUKS Partition mit dem LVM2 betrieben.

Logical Volumes erstellen

pvcreate /dev/mapper/crypt-4bb3dd2c-cad4-4dd4-8ff4-a2e944baf090
vgcreate vg_myvol /dev/mapper/crypt-4bb3dd2c-cad4-4dd4-8ff4-a2e944baf090
lvcreate -n swap -L4G vg_myvol
lvcreate -n root -L100G vg_myvol

erstellt alle notwendigen Komponenten mit LVM im Crypto Device. Mit

mkfs.ext4 /dev/vg_myvol/root
mkswap /dev/vg_myvol/swap

werden die LVs formatiert.

Installation des Basis Systems

Mit

mkdir /target
mount /dev/vg_myvol/root /target
mkdir /target/boot
mount /dev/md0 /target/boot

werden die LVs eingebunden, so dass mit dem Rescue System eine Installation erfolgen kann. Falls das Rescue System etwas veraltet ist, dann muss man ihm noch etwas nach helfen, damit sich Ubuntu 16.04 (aktuell Stand Mai/2016) installieren lässt:

wget http://archive.ubuntu.com/ubuntu/pool/main/d/debootstrap/debootstrap_1.0.78+nmu1ubuntu1_all.deb
wget http://archive.ubuntu.com/ubuntu/pool/main/u/ubuntu-keyring/ubuntu-keyring_2012.05.19_all.deb
dpkg -i debootstrap_1.0.78+nmu1ubuntu1_all.deb
dpkg -i ubuntu-keyring_2012.05.19_all.deb

Danach hat man ein aktuelles debootstrap.

debootstrap --variant=minbase --arch=amd64 xenial /target http://ca.archive.ubuntu.com/ubuntu/

startet jetzt die Installation. JETZT NOCH NICHT BOOTEN!!! Es müssen mit chroot noch ein paar Dinge getan werden, damit das System bootfähig ist und man das LUKS Device auch remote entsperren kann.

mount --bind /proc /target/proc
mount --bind /sys /target/sys
mount --bind /dev /target/dev

bereitet eine chroot Umgebung vor, in der sich auch GRUB sowie Kernel installieren lassen. Mit chroot /target springen wir in unser neues Zielsystem. Jetzt noch

apt update
apt install vim
vi /etc/apt/sources.list # hier von einer bestehenden Ubuntu Version die sources.list kopieren
groupadd myadm
useradd -md /home/myadm -g myadm myadm
mkdir /home/myadm/.ssh

Jetzt noch einen SSH Public Key kopieren, denn man wird sich ja wohl nicht mit einem Passwort als Admin einloggen wollen…

cat > /home/myadm/.ssh/authorized_keys <<EOF
ssh-rsa AAAA...
...

Jetzt noch die Rechte setzen:

chown -R myadm:myadm /home/myadm/.ssh
chmod -R og-rwx /home/myadm/.ssh

Ganz wichtig: Es werden noch ein paar Pakete gebraucht, die mit

apt install sudo mdadm linux-generic cryptsetup iproute2 iptables bridge-utils dropbear ssh language-pack-en lvm net-tools netbase psmisc ubuntu-standard apt-utils

installiert werden. Nun müssen mit blkid die UUIDs der Filesysteme bestimmt werden, die für den Boot notwendig sind und in die /etc/fstab eingetragen werden:

cat > /etc/fstab <<EOF
UUID=11a032dd-313d-4680-9ab9-189a4b117470 /               ext4    noatime,nodiratime,errors=remount-ro 0       1
UUID=4dd5b46a-6f48-4554-9107-132438d7cc45 /boot           ext4    discard,noatime,nodiratime 0       2
UUID=cc1530f9-eb4c-4bd8-a365-b5e3300eab56 none            swap    sw              0       0
EOF**

Für die /etc/crypttab wird noch einmal die UUID der LUKS Partion benötigt, damit Linux weiß was es beim Boot entschlüsseln muss.

cat > /etc/crypttab <<EOF
crypt-4bb3dd2c-cad4-4dd4-8ff4-a2e944baf090 UUID=4bb3dd2c-cad4-4dd4-8ff4-a2e944baf090 none luks
EOF

Der User myadm sollte mit vigr in die Gruppe sudo aufgenommen werden, damit dieser ein sudo machen kann.

Boot Parameter

In /etc/default/grub ist der Parameter GRUB_CMDLINE_LINUX=“net.ifnames=0 biosdevname=0” zu setzen. Damit wird sichergestellt, dass eth0 auch nach dem Systemstart noch eth0 heißt.

Unlock Tool compilieren

Das unlock Tool dient dazu nach dem ssh Login während der initrd Phase im Bootvorgang die Passphrase entgegen zu nehmen, damit nach der Eingabe der Bootvorgang fortgesetzt werden kann.

unlock.c

#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>

void stuff(int fd, const char * str) {
  for (; *str; ++str) {
    int rv = ioctl(fd, TIOCSTI, str);
    if (rv < 0) perror("ioctl(TIOCSTI)");
  }
}

int main (int argc, const char * argv[]) {

  char *password; // password string pointer
  password = getpass("Enter Password: "); // get a password
  char target[]="/dev/console";

  int fd = open(target, O_RDONLY);
  if (fd < 0) {
    perror("open");
    return 2;
  }

  stuff(fd,password);
  stuff(fd, "\r");

  close(fd);
  return 0;
}

Makefile

unlock:	unlock.c
	gcc -std=gnu99 -O2 -Wall unlock.c -o unlock

unlock dann nach /usr/local/sbin kopieren.

Bootfähigkeit herstellen

Der GRUB sollte auf beide Platten installiert werden, damit RAID1 auch da einen Sinn hat.

  • grub-install /dev/sda
  • grub-install /dev/sdb

initrd erstellen

cat > /etc/initramfs-tools/conf.d/network_config <<EOF
export IP=192.168.12.34::192.168.12.254:255.255.255.0:myhost:eth0:off
EOF
/usr/lib/dropbear/dropbearconvert  openssh dropbear /etc/ssh/ssh_host_rsa_key /etc/dropbear/dropbear_rsa_host_key 
/usr/lib/dropbear/dropbearconvert  openssh dropbear /etc/ssh/ssh_host_dsa_key /etc/dropbear/dropbear_dss_host_key 
/usr/lib/dropbear/dropbearconvert  openssh dropbear /etc/ssh/ssh_host_ecdsa_key /etc/dropbear/dropbear_ecdsa_host_key 

Die /etc/initramfs-tools/hooks/mount_cryptroot muss noch erstellt werden. Es muss mit

chmod 755 /etc/initramfs-tools/hooks/mount_cryptroot

ausführbar gemacht werden.

#!/bin/bash

# This script generates two scripts in the initramfs output,
# /root/mount_cryptroot.sh and /root/.profile
ALLOW_SHELL=1

# Set this to 1 before running update-initramfs if you want
# to allow authorized users to type Ctrl-C to drop to a
# root shell (useful for debugging, potential for abuse.)
#
# (Note that even with ALLOW_SHELL=0 it may still be possible
# to achieve a root shell.)
#
. /usr/share/initramfs-tools/hook-functions

copy_exec /usr/local/sbin/unlock /bin/unlock

if [ -z ${DESTDIR} ]; then
exit
fi

if [ -d ${DESTDIR}/root ]; then
  TMPROOT=root
else
  TMPROOT=root-$(find ${DESTDIR} -name "root-*" | grep 'root-' | while read r; do basename $r|awk -F - '{print $2;}'; done)
fi

SCRIPT="${DESTDIR}/${TMPROOT}/mount_cryptroot.sh"
cat > "${SCRIPT}" << 'EOF'
#!/bin/sh

unlock

EOF

chmod +x "${SCRIPT}"

# Run mount_cryptroot by default and close the login session afterwards
# If ALLOW_SHELL is set to 1, you can press Ctrl-C to get to an interactive prompt
cat > "${DESTDIR}/${TMPROOT}/.profile" << EOF
ctrl_c_exit() {
exit 1
}
ctrl_c_shell() {
# Ctrl-C during .profile appears to mangle terminal settings
reset
}
if [ "$ALLOW_SHELL" == "1" ]; then
echo "Unlocking rootfs... Type Ctrl-C for a shell."
trap ctrl_c_shell INT
else
echo "Unlocking rootfs..."
trap ctrl_c_exit INT
fi
/${TMPROOT}/mount_cryptroot.sh && exit 1 || echo "Run ./mount_cryptroot.sh to try unlocking again"
trap INT
EOF

Netz Config

Im Gegensatz zu Ubuntu 14.04 und Vorgängern muss das Netz mit den folgenden Confis explizit konfiguriert werden, ansonsten hat das System keine IP Adresse im Internet mehr.

cat > /etc/network/interfaces <<EOF
auto lo
iface lo inet loopback
	up iptables-restore < /etc/iptables/active4
	up ip6tables-restore < /etc/iptables/active6

source /etc/network/interfaces.d/*.cfg
EOF

Die Files /etc/iptables/active4 und active6 sollten mit iptables-save bzw. ip6tables-save erstellt und dann passend bearbeitet werden. Im Internet sollten nur die wirklich benötigten Ports sichtbar sein.

mkdir /etc/network/interfaces.d
cat > /etc/network/interfaces.d/eth0.cfg <<EOF
auto eth0
iface eth0 inet static
	address 192.168.12.34
	netmask 255.255.255.0
	gateway 192.168.12.254
	up ip addr add 2607:5300:60:490f::/64 dev eth0
	up ip -6 route add default via 2607:5300:60:49ff:ff:ff:ff:ff
EOF

und noch der ssh Key:

mkdir /etc/initramfs-tools/root
cp -a /root/.ssh /etc/initramfs-tools/root

Mit update-initramfs -u wird jetzt eine neue initrd erstellt.

Der Augenblick der Wahrheit: Reboot

Mit exit wird die chroot Umgebung verlassen und dann mit umount /target/proc /target/sys /target/dev /target/boot /target die Mounts gelöst.

Im Admin System muss jetzt auf boot from harddisk umgestellt werden und nun:

reboot

Nach max. 2 Min. sollte ein Ping auf die IP Adresse gehen und man sollte sich mit ssh einloggen können. Nach Eingabe der richtigen Passphrase wird die ssh Verbindung getrennt und einige Sekunden später sollte man sich ganz normal am System mit ssh anmelden können.

Ergebnis

Jetzt steht ein Dedizierter Server zur Verfügung, beim dem alles mit Ausnahme von /boot und ggf. /boot/efi verschlüsselt ist. Es gibt dann ohne Manipulation des Kernels (also Inhalt von /boot) keine Möglichkeit mehr, an die Daten heranzukommen, die unterhalb von “/” liegen.

Sicherheitsmaßnahmen

  • Auch wenn LUKS sehr ausgereift ist und eine sehr hohe Sicherheit gegen unerwünschte Zugriffe bietet, gilt immer noch der Grundsatz Absolute Sicherheit gibt es nicht
  • Alle Dateien in /boot sollten mit einer SHA256 Prüfsumme geprüft werden. Ein Cronjob sollte regelmäßig prüfen, ob sich da etwas verändert hat.
  • Im Verdachtsfall oder einfach hin und wieder mal das Rescue System starten und die SHA256 Prüfsummen testen.
  • Der Hoster hat physikalischen Zugriff auf die Hardware. Durch diese Tatsache ist eine unbemerkte Manipulation immer möglich. Da kann man auch nicht viel gegen tun.
  • Den Host Fingerprint unbedingt an einem sicheren Ort verwahren. Kommt vom ssh Client eine Fehlermeldung bzgl. des Hostkeys, dann ist diese Warnung ernst zu nehmen. Wenn jemand die LUKS Passphrase abgreift, dann ist das System als kompromittiert anzusehen.

Anwendung

  • Nach einem Reboot bleibt das System in der initrd Phase stehen und wartet darauf, dass sich root mit hinterlegtem ssh public Key anmeldet.
  • Nach dem Login erfolgt die Aufforderung, die LUKS Passphrase einzugeben
  • Die Verbindung wird getrennt, wenn die Passphrase korrekt war, dann wird der Bootvorgang normal fortgesetzt und man findet danach ein einsatzbereites System vor. Falls die Passphrase falsch war, muss man sich erneut anmelden es es noch einmal versuchen.

Gründe für ein verschlüsseltes System

Eine LUKS Verschlüsselung ist sehr effektiv, wenn man sichergehen will, dass z.B. nach einem Diebstahl der Hardware die dort gespeicherten Daten nicht in falsche Hände fallen.

Weitere Maßnahmen

Man kann LUKS auch auch mit einem TPM Modul und einem Arduino mit GPS kombinieren, wobei der LUKS Key aus den Komponenten

  • Passphrase
  • Signatur aus dem TPM (private Key steckt im TPM)
  • Signatur aus PKCS#11 Smartcard (private Key steckt in der Smartcard)
  • GPS Koordinaten + privater Schlüssel im Arduino

besteht. Das ist sehr praktisch, weil sich das Filesystem ohne Übereinstimmung der o.g. Komponenten nicht mehr starten lässt. Wird der Rechner an einen anderen Ort verbracht, dann bootet das System selbst bei korrekter Passphrase nicht. Ist der Strom auch während des Transports noch angeschlossen, dann killt der Arduino einfach seinen eigenen privaten Schlüssel und sendet einen HW Reset an das Mainboard.

Das ist natürlich noch nicht alles, aber dann wird es teuer, weil dann z.B. auch ein Gehäuse aus MU Metallblech (schirmt auch magnetische Komponente der Strahlung ab) sein muss. So etwas braucht mal allenfalls beim BND…