Kubernetes Workshop Teil 3: Wireguard

Nachdem wir in Teil 2 die Server vorbereitet haben, wollen wir nun Wireguard installieren und konfigurieren.

Wie gesagt, sollen die Server und die Kubernetes-Komponenten verschlüsselt über wireguard miteinander kommunizieren. Eine schöne Anleitung dazu gibt es z.B. hier:

https://vitobotta.com/2019/07/17/kubernetes-wireguard-vpn-rancheros/

Im ersten Schritt installieren wir Wireguard. Vorher machen wir uns natürlich zum root.

sudo -s
add-apt-repository ppa:wireguard/wireguard
apt update
apt install wireguard

Es gibt diverse Anleitungen, wie man unter Ubuntu ein Wireguard-Setup aufbauen kann, wie z.B.

https://www.thomas-krenn.com/de/wiki/Ubuntu_18.04_als_WireGuard_VPN_Client_konfigurieren

Die Schritte sind im Prinzip für jeden Server das Gleiche. Wir erzeugen auf jedem Server jeweils einen privaten, einen öffentlichen Schlüssel und einen sogenannten preshared-key. Hier als Beispiel für meinen ersten Server defiant:

sudo -s
cd /etc/wireguard
umask 077
wg genkey > private-key
wg pubkey > public-key < private-key
wg genpsk > defiant.psk

Im Verzeichnis sollten nun die jeweiligen Dateien liegen. Insbesondere auf den privaten Key sollte man gut aufpassen, daher lohnt sich noch ein:

chmod 600 /etc/wireguard/privatekey

Im nächsten Schritt legen wir für jeden Server eine Konfigurationsdatei unter /etc/wireguard/wg0.conf an. Hier ist das Beispiel für den ersten Server defiant. Dieser soll mit allen anderen 3 Servern und meinem Raspberry-Pi reden können. Daher tragen wir die 4 Kommunikationspartner mit in die Datei ein. Das gleiche gilt dann jeweils für die anderen 3 Server auch, nur dass dort der jeweilige private Schlüssel und eine andere IP vergeben werden müssen.

In meinem Setup vergebe ich folgende IPs:

defiant10.0.0.1
discovery10.0.0.2
reliant10.0.0.3
yorktown10.0.0.4
pi10.0.0.10
IP-Addressen der Server

Der Rest der Datei ist selbsterklärend:

[Interface]
ListenPort = 51871
PrivateKey = <privater Schlüssel Server defiant>
Address = 10.0.0.1/32

PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
# pi 
PublicKey = <öffentlicher Schlüssel raspberrypi>
PresharedKey = <preshared key raspberrypi>
AllowedIPs = 10.0.0.10/32, 192.168.0.0/24

[Peer]
# Discovery 
PublicKey = <öffentlicher Schlüssel discovery>
PresharedKey = <preshared key discovery>
AllowedIPs = 10.0.0.2/32
Endpoint = <öffentliche IP discovery>:51871

[Peer]
# reliant 
PublicKey = <öffentlicher Schlüssel reliant>
PresharedKey = <preshared key reliant>
AllowedIPs = 10.0.0.3/32
Endpoint = <öffentliche IP reliant>:51871

[Peer]
#yorktown 
PublicKey = <öffentlicher Schlüssel yorktown>
PresharedKey = <preshared key yorktown>
AllowedIPs = 10.0.0.4/32
Endpoint = <öffentliche IP yorktown>:51871

Zur Erklärung:

  • ListenPort: Port (UDP) auf dem Wireguard hört
  • PrivateKey: privater Schlüssel
  • Adress: Die IP-Adresse des Servers für das wireguard-Interface
  • PostUp: mit den Befehlen aktiviert man das packet-forwarding auf dem wireguard interface
  • PostDown: Damit deaktiviert man es wieder

Für jeden Peer trägt man folgende Dinge ein:

  • PublicKey: Der öffentliche Schlüssel des Peers
  • PresharedKey: der preshared-key, den ihr für den jeweiligen Server erzeugt habt.
  • AllowedIPs: Das ist die IP des Partners
  • Endpoint: Die öffentliche IP-Adresse des Servers, auf der dann wireguard erreichbar ist.

Eine Besonderheit gibt es bei dem Eintrag für den RaspberryPI. Dort lasse ich neben der IP-Adresse 10.0.0.10 auch das Netz 192.168.0.0/24 zu. Dabei handelt es sich um mein Heimnetzwerk. Damit kann ich nachher die Geräte in meinem Heimnetz erreichen und vom Heimnetz aus die Server über Wireguard ansprechen.

Anschließend erlauben wir noch in der ufw-Firewall auf den Servern die Kommunikation über den UDP-Port 51871:

ufw allow 51871/udp

Mit dem Befehl wg-quick kann man wireguard starten.

wg-quick up wg0

Mit wg show sollten nun die Daten zum wireguard-Netzwerk aufgezeigt werden. Ebenfalls kann man mit ping die jeweiligen peers erreichen:

wg show

ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=3.59 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.729 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=1.34 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=0.787 ms

Wenn das alles zur Zufriedenheit funktioniert, sollte man den Dienst noch aktivieren, damit wireguard auch nach dem nächsten Serverneustart gestartet wird:

# aktivieren
systemctl enable wg-quick@wg0
# ggf. starten
systemctl start wg-quick@wg0

Ich erlaube in der Firewall noch alles, was über das Interface wg0 reinkommt, also alles aus meinem VPN:

ufw allow in on wg0

Wenn ihr euch sicher seid, dass ihr eure Server von eurem Client aus per SSH durch den wireguard-tunnel erreichen könnt, könnt ihr auch die OpenSSH-Regel aus der Firewall entfernen. Der Port muss ja nicht für das ganze Internet geöffnet sein. Wir prüfen das lokal mit:

ssh ronny@10.0.0.1

und entfernen dann die „alte“ OpenSSH Regel:

ufw delete allow OpenSSH

Nachdem nun die Kommunikation zwischen den Servern funktioniert, wollen wir im nächsten Teil Docker und Rancher installieren.

Kubernetes Workshop Teil 2: Einrichtung der Server

Wie bereits im ersten Teil angekündigt, geht es heute um die Einrichtung der Server. Wie gesagt, habe ich 4 kleine virtuelle Server bei Contabo bestellt. Die Server habe ich mit Ubuntu 18.04 LTS aufgesetzt. Die Server sollen unter meiner Domain erreichbar sein. Dazu muss ich im ersten Schritt die IP-Adressen der Server bei meinem Domain-Anbieter hinterlegen. Ich hoste meine Domains bei united-domains und lege nun DNS-Records sowohl für IPv4 als auch für IPv6 an. Die IPs könnt ihr bei Contabo im Kundenportal nachschlagen. Das sieht dann beispielsweise so aus:

Weiterhin lege ich einen CNAME-Eintrag an, der alle Sub-Domains auf einen der Server zeigen lässt:

Damit kann ich später alle Applikationen mit eigenen Sub-Domains app1.ronnywalter.de, app2.ronnywalter.de usw. versehen und muss nicht jede Sub-Domain einzeln pflegen. Um das Routing im Cluster kümmert sich Kubernetes.

Für den Aufruf der Rancher-Oberfläche konfiguriere ich noch einen CNAME-Eintrag, der auf den „kleinen“ Server (VPS-S) zeigt:

Nun loggen wir uns auf den Servern ein und konfigurieren sie ein wenig. Ich verwende auf dem Mac das kleine Tool csshX. Damit kann ich mich gleichzeitig auf allen Servern einloggen. Die eingegebenen Kommandos werden zeitgleich an alle Server geschickt. Sehr praktisch! Im ersten Schritt wird auf allen Servern das root-passwort geändert, dass euch Contabo geschickt hat. Ebenso lege ich einen User an.

csshx root@defiant.ronnywalter.de root@discovery.ronnywalter.de root@reliant.ronnywalter.de root@yorktown.ronnywalter.de
#Passwort für root ändern
passwd

#User "ronny" anlegen
useradd -m -g users -s /bin/bash ronny

#Passwort vergeben
passwd ronny

#IPv6 anktivieren (auf contabo Servern)
enable_ipv6

#Den User ronny in die sudo-group aufnehmen
gpasswd -a ronny sudo

Ich aktiviere auf gleich noch die ufw-firewall unter ubuntu und aktiviere SSH, da wir uns ja weiterhin per SSH verbinden wollen:

systemctl enable ufw
systemctl start ufw
ufw enable
ufw allow SSH

Dann setze ich auf jedem Server den gewünschten hostnamen, trage also den Namen in die /etc/hostname ein. Beispiel für den ersten Server:

echo "defiant" > /etc/hostname

Ich starte nun die Server neu.

reboot

In Zukunft möchte ich mich nur noch mit SSH-Key einloggen und will die Passwortauthentifizierung deaktivieren. Logins als root möchte ich komplett verbieten und bei der Gelegenheit den root-Account deaktivieren. Ich kann später ja alles per sudo erledigen.

Dazu erstellt man sich lokal einen SSH-Key mit ssh-keygen und kopiert den anschliessend auf alle Server.

# key erzeugen
ssh-keygen -t ed25519

# key auf den Server defiant kopieren
ssh-copy-id defiant.ronnywalter.de

# die anderen server
ssh-copy-id reliant.ronnywalter.de
ssh-copy-id discovery.ronnywalter.de
ssh-copy-id yorktown.ronnywalter.de

Jetzt testen wir mal die Verbindung per SSH und sollten uns ohne Passwort einloggen können. Anschließend testen wir, ob wir uns mit sudo zum root machen können.

csshx ronny@defiant.ronnywalter.de ronny@discovery.ronnywalter.de ronny@reliant.ronnywalter.de ronny@yorktown.ronnywalter.de
sudo -s

Wenn das alles klappt, schalten wir die Logins per Passwort und als Root ab. Dazu muss die Datei /etc/ssh/sshd_config editiert werden. Dort werden folgende Einträge geändert:

PermitRootLogin no
PasswordAuthentication no

Danach starten wir den SSH-Server neu und deaktivieren root

systemctl restart sshd
passwd -l root

Danach dürfte man sich nicht mehr als root einloggen können. Es gibt im Netz diverse Anleitungen, um Ubuntu-Server abzusichern. Dazu einfach mal nach „ubuntu hardening guide“ suchen. Ich werd sicher mal noch fail2ban installieren. Jetzt belass ich erstmal dabei.

Ich möchte unter Ubuntu 18.04 einen aktuellen Kernel betreiben. Dazu kann man den sogenannten HWE-Kernel installieren. Eine Beschreibung gibts hier. Wir installieren bei der Gelegenheit gleich mal noch ein paar Softwareupdates, falls verfügbar.

apt-get update
apt-get upgrade
apt-get install --install-recommends linux-generic-hwe-18.04
reboot

Im nächsten Teil wollen wir wireguard konfigurieren und die Kommunikation mit dem Heimnetzwerk einrichten.

Kubernetes Workshop Teil 1: Vorüberlegungen

Ich möchte mir einen kleinen Kubernetescluster aufbauen, um dort verschiedenste Applikationen zu deployen, die ich mehr oder weniger im Alltag nutzen möchte. Weiterhin möchte ich diverse Anwendungen aus dem Bereich der Softwareentwicklung installieren, um verschiedene Aspekte insbesondere aus dem Bereich CI/CD ausprobieren zu können. Anfangs denke ich hierbei an folgende Applikationen:

Weiterhin möchte ich CI/CD-Buildstrecken mit eben dieser Infrastruktur aufsetzen. Das werde ich mit kleinen Java-Beispielen ausprobieren.

Ziel soll sein, dass alle Applikationen mit Kubernetes-Mitteln bzw. Helm installiert werden können. Ich hoffe, dabei viel zu lernen.

Um den Kubernetes-Cluster aufzusetzen, möchte ich Rancher einsetzen. Ich möchte ebenfalls einen zentrales Logging (ElasticStack) und ein Monitoring installieren, vor allem um zu lernen, wie das geht.

Es gibt ja die Möglichkeit, sich mit Tools wie minikube einen eigenen kleinen Cluster für Experimente zu erzeugen. Ich wollte mir das Ganze aber auf „richigen“ Servern im Internet installieren. Dabei muss ich dann auch einige Sachen wie Firewall-Einstellungen berücksichtigen. Ebenso soll die Kommunikation zwischen den Servern und von meinem PC aus verschlüsselt erfolgen. Dazu habe ich mich etwas mit Wireguard beschäftigt.

In meinem Heimnetz steht eine Synology Diskstation. Diese hat die wunderbare Eigenschaft, dass man darauf auch Docker-Container starten kann. Das möchte ich nutzen, um dort u.a. einen kleinen Git-Server (gitea) und ein S3-Storage (minio) zu betreiben. In dieses S3-Storage soll mein Kubernetes Cluster z.B. Backups ablegen. Weiterhin habe ich einen Raspberry PI, den ich dazu nutzen möchte, eine VPN-Verbindung zwischen meinem lokalen Netz und den Servern herzustellen.

Das Ganze soll dann schematisch so aussehen:

Für mein Experiment nutze ich virtuelle Server von contabo (VPS), da diese relativ günstig sind. Experimente in der Cloud mit AWS EKS oder GKE werde ich später machen. Ich habe mir insgesamt 4 Maschinen bestellt:

  1. VPS S – Rancher Installation
  2. VPS L – Kubernetes Node 1 (Worker, etcd, controllpane)
  3. VPS M – Kubernetes Node 2 (Worker)
  4. VPS M – Kubernetes Node 3 (Worker)

Warum ausgerechnet 4 Server? Auf dem ersten kleinen Server soll die Rancher-Installation laufen. In Rancher wiederum soll ein Kubernetes-Cluster auf den anderen 3 Nodes angelegt werden. Damit habe ich auf dem Cluster genug RAM (ca. 60GB) und auch etwas CPU-Power (20 Cores) um einige Applikationen laufen lassen zu können. Das Ganze kostet mich im Monat ca. 38€. Das bin ich aktuell bereit auszugeben. 🙂

Wie das geübte Auge sieht, ist das kein Hochverfügbarkeits-Setup. Dafür bräuchte man einige Nodes mehr. Rancher selbst soll auch „nur“ als Single-Node installiert werden. Für meine Experimente reicht das soweit aber aus.

Im nächsten Teil wollen wir die 4 Server einrichten.