Moin!
In meiner Homelab Umgebung bediene ich mich einer standardisierten Abfolge von Konfigurationsschritten, um einen LXC Container auf Basis von Ubuntu Server 24.04 LTS aufzusetzen. Dazu gehören Basiskonfigurationen wie Uhrzeit, Spracheinstellungen und ein grundlegendes Toolset an Paketen. Nach erfolgreicher Konfiguration und Installation von Agents, SSH Keys und weiterem wird aus dem Container ein Template generiert. Dieses kann unendlich oft geklont werden. Dies ist aber nicht Bestandteil des Beitrags.
Bitte prüft im Vorfeld, ob für den Container die Option „nesting“ aktiviert wurde. Diese ist seit PVE 7.x zwingend erforderlich. Des weiteren sollte der Container, aus dem Sicherheitsaspekt heraus, als „unprivileged“ erstellt worden sein.
Wir starten den Container und verbinden uns als User „root“ per SSH auf die Maschine. Um einen veralteten Softwarestand zu vermeiden, aktualisieren wir die lokalen Paketlisten, aktualisieren die installierten Pakete und installieren ein grundlegendes Toolset.
apt update && apt upgrade -y
apt install locales htop ca-certificates curl gnupg ufw nano tar zip bzip2 wget curl unzip software-properties-common ca-certificates gnupg dnsutils iproute2 net-tools iputils-ping nmap rsync sudo unattended-upgrades -y
Im nächsten Schritt konfigurieren wir zwei Systemeinstellungen. Genauer gesagt erst die Zeitzone und danach die Spracheinstellung. Zunächst setzen wir das Kommando ab und wählen im Anschluss „Europe/Berlin“ aus.
dpkg-reconfigure tzdata
Jetzt kümmern wir uns um die Spracheinstellungen.
echo "de_DE.UTF-8 UTF-8" > /etc/locale.gen
dpkg-reconfigure --frontend=noninteractive locales
echo -e "LANG=de_DE.UTF-8\nLANGUAGE=\"de_DE:de\"" >> /etc/default/locale
Nun erstellen wir einen User und verteilen Rechte für die Nutzung des „sudo“ Befehls. Der Platzhalter „username“ ist durch den von euch gewünschten username zu ersetzen.
adduser username
visudo /etc/sudoers
# User privilege specification
root ALL=(ALL:ALL) ALL
username ALL=(ALL:ALL) ALL
Damit unser System kontinuierlich die neuesten Sicherheitspatches erhält, ohne dass von unserer Seite ein großer Handlungsbedarf entsteht, bedienen wir uns des Tools „unattended upgrades“. Dies ermöglicht ein automatisiertes Aktualisieren der installierten Pakete. Hierbei kann entschieden werden, ob nur sicherheitsrelevante Patches installiert werden sollen oder sämtliche Patches. Best Practise ist hierbei in der Regel nur die Sicherheitsrelevanten automatisiert zu aktualisieren. Andere Patches sollte man manuell installieren. Zudem ist es möglich, distributionsfremde Repositorys wie z.B. das Docker Repository mit die Liste aufzunehmen.
Zunächst rufen wir uns das Configfile in den Editor. Danach editieren wir die Sektion auf die in der Box aufgelisteten Parameter.
nano /etc/apt/apt.conf.d/50unattended-upgrades
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}";
"${distro_id}:${distro_codename}-security";
// Extended Security Maintenance; doesn't necessarily exist for
// every release and this system may not have it installed, but if
// available, the policy for updates is such that unattended-upgrades
// should also install from here by default.
"${distro_id}ESMApps:${distro_codename}-apps-security";
"${distro_id}ESM:${distro_codename}-infra-security";
"${distro_id}:${distro_codename}-updates";
// "${distro_id}:${distro_codename}-proposed";
// "${distro_id}:${distro_codename}-backports";
};
Die einwandfreie Funktion unserer Config können wir in einem simulierten Testlauf prüfen.
unattended-upgrades --dry-run --debug
Checking if system is running on battery is skipped. Please install powermgmt-base package to check power status and skip installing updates when the system is running on battery.
Starting unattended upgrades script
Allowed origins are: o=Ubuntu,a=noble, o=Ubuntu,a=noble-security, o=UbuntuESMApps,a=noble-apps-security, o=UbuntuESM,a=noble-infra-security, o=Ubuntu,a=noble-updates, o=Docker,a=noble
Initial blacklist:
Initial whitelist (not strict):
Using (^linux-.*-[1-9][0-9]*\.[0-9]+\.[0-9]+-[0-9]+(-.+)?$|^kfreebsd-.*-[1-9][0-9]*\.[0-9]+\.[0-9]+-[0-9]+(-.+)?$|^gnumach-.*-[1-9][0-9]*\.[0-9]+\.[0-9]+-[0-9]+(-.+)?$|^.*-modules-[1-9][0-9]*\.[0-9]+\.[0-9]+-[0-9]+(-.+)?$|^.*-kernel-[1-9][0-9]*\.[0-9]+\.[0-9]+-[0-9]+(-.+)?$|^linux-.*-[1-9][0-9]*\.[0-9]+\.[0-9]+-[0-9]+(-.+)?$|^kfreebsd-.*-[1-9][0-9]*\.[0-9]+\.[0-9]+-[0-9]+(-.+)?$|^gnumach-.*-[1-9][0-9]*\.[0-9]+\.[0-9]+-[0-9]+(-.+)?$|^.*-modules-[1-9][0-9]*\.[0-9]+\.[0-9]+-[0-9]+(-.+)?$|^.*-kernel-[1-9][0-9]*\.[0-9]+\.[0-9]+-[0-9]+(-.+)?$) regexp to find kernel packages
Using (^linux-.*-6\.8\.12\-5\-pve$|^linux-.*-6\.8\.12\-5$|^kfreebsd-.*-6\.8\.12\-5\-pve$|^kfreebsd-.*-6\.8\.12\-5$|^gnumach-.*-6\.8\.12\-5\-pve$|^gnumach-.*-6\.8\.12\-5$|^.*-modules-6\.8\.12\-5\-pve$|^.*-modules-6\.8\.12\-5$|^.*-kernel-6\.8\.12\-5\-pve$|^.*-kernel-6\.8\.12\-5$|^linux-.*-6\.8\.12\-5\-pve$|^linux-.*-6\.8\.12\-5$|^kfreebsd-.*-6\.8\.12\-5\-pve$|^kfreebsd-.*-6\.8\.12\-5$|^gnumach-.*-6\.8\.12\-5\-pve$|^gnumach-.*-6\.8\.12\-5$|^.*-modules-6\.8\.12\-5\-pve$|^.*-modules-6\.8\.12\-5$|^.*-kernel-6\.8\.12\-5\-pve$|^.*-kernel-6\.8\.12\-5$) regexp to find running kernel packages
pkgs that look like they should be upgraded:
Fetched 0 B in 0s (0 B/s)
fetch.run() result: 0
Packages blacklist due to conffile prompts: []
No packages found that can be upgraded unattended and no pending auto-removals
The list of kept packages can't be calculated in dry-run mode.
Die Ausgabe des Kommandos kann je nach Konfiguration des Systems bzw. der Paketquellen unterschiedlich ausfallen. Die oben gezeigte stammt von einem System mit Docker Repository und automatischer Installation sämtlicher Patches. Auch wenn es nicht Best Practise ist, sämtliche Patches zu installieren, kann es dennoch auf manchen Systemen sinnvoll sein. Dieses System wird per CheckMK überwacht, startet einmal die Woche neu und wird täglich gesichert. Hier spare ich mir viel händische Arbeit. Sollte dennoch mal ein Patch fehlerhaft sein oder zu Problemen führen, kann ich das System einfach aus dem nächtlichen Backup wiederherstellen.
Um ein Mindestmaß an Sicherheit bereitstellen zu können, konfigurieren wir eine simple Firewall (UFW). Diese haben wir bereits im ersten Schritt installiert. Im Regelfall blockt eine Firewall erst einmal alles an Traffic, welcher auf dem System eingeht. Durch hinzufügen von Ausnahmen in Form von Port und Protokol zu der Firewall ist es weiterhin möglich für den gewählten Port und dem Protokol eine Verbindung zu dem System aufzubauen. Als Beispiel SSH. SSH bedient sich an dem TCP Protokol und verwendet, wenn nicht anders konfiguriert, Port 22. Diese Ausnahme muss der Firewall hinzugefügt werden.
ufw allow 22/tcp comment "SSH"
Wir haben nun eine Ausnahme der Firewall hinzugefügt. Diese muss abschließend aktiviert werden, erst dann werden eingehende Verbindungen geblockt.
ufw enable
Die korrekte Konfiguration der Firewall überprüfen mit dem untenstehende Kommando.
ufw status
Status: active
To Action From
-- ------ ----
22/tcp ALLOW Anywhere # SSH
6556/tcp ALLOW Anywhere # checkmk agent data pull
22/tcp (v6) ALLOW Anywhere (v6) # SSH
6556/tcp (v6) ALLOW Anywhere (v6) # checkmk agent data pull
Abschließend solltest du das System, vor der Weiterverwendung, noch neu starten.
Grüße gehen aus dem Archiv!
Um die sudoers zu editieren, wäre es besser visudo zu nutzen.
Hallo,
sehr gute Idee! Ich werde die Beiträge bei Gelegenheit auf die Nutzung von visudo anpassen. 🙂