In diesem Beitrag beschreibe ich, wie unter Proxmox ein NFS-Server in einem LXC-basierten Container aufgesetzt werden kann. NFS soll dabei durch Kerberos auf Grundlage von FreeIPA abgesichert werden.

Weitere Beiträge in der Reihe:

Voraussetzungen

Zielsetzungen im Detail

Ziel wird sein, via NFS Shares bereitzustellen, die anhand von FreeIPA und mit Hilfe von Kerberos zugriffsbeschränkt sind. Das ist leider nicht ganz so einfach wie einen TCP-Dienst bereitzustellen, den man dann einfach mit Hilfe eines Clients irgendwo einbindet. Man muss mehrere Rahmenbedingungen herstellen, die passen.

Die Einrichtung von Samba als Alternative, sofern sie auf die User aus der zentralen Verwaltung bei FreeIPA zugreifen soll, ist dabei nicht viel einfacher. Mitunter muss man auch hier dann Kerberos verwenden oder das LDAP-Schema im Hintergrund modifizieren, damit Samba hier ordentlich funktioniert. Die Entscheidung fällt also zu Gunsten von NFS. NFS ist sehr Linux-spezifisch. Wer Windows-Clients bedienen möchte, sollte sich dennoch die Verwendung von Samba überlegen.

Diese Anleitung bezieht sich ausdrücklich auf die Einrichtung auf Grundlage von LXC. NFS findet hauptsächlich im Kernel statt und LXC-basierte Container verwenden den Kernel des Host-Systems, dadurch ist die Trennung zwischen virtualisierter Umgebung und Host-System hier nicht so groß wie bei getrennten Systemen. Ist diese notwendig und sind erhöhte Sicherheitsanforderungen zu beachten, bietet das Anlegen einer virtuellen Maschine an dieser Stelle eine deutlichere Trennung.

Vorbereitungen im Proxmox

Nachdem NFS ohnehin für gewöhnlich vom Kernel bedient wird und die Container den Kernel des Host-Systems verwenden, könnte auch direkt das Host-System die NFS-Shares bereitstellen. Ich möchte das Host-System allerdings unabhängig von den dort laufenden Containern halten. Das schließt zum einen die Verwaltung des Host-Systems mit Puppet, zum anderen auch die Integration in die Rechteverwaltung mit FreeIPA und Kerberos aus. Nachdem ich aber beides für die Datenfreigabe nutzen möchte, bleibt keine andere Lösung als eben einen weiteren Container für Dateifreigaben zu nutzen.

Container-Erstellung

Während es sehr sinnvoll ist, Linux-Container unprivilegiert zu starten und dadurch etwas mehr Sicherheit zu gewinnen, ist das allerdings beim Aufsetzen von NFS nicht so leicht möglich. Ich habe mich also dafür entschieden, einen minimalen Container aufzusetzen, auf dem außer NFS nichts passiert.

Dieser Container muss also privilegiert sein. Ansonsten können die Einstellungen beim Erstellen des Containers nach Belieben getroffen werden.

Nach Aufsetzend es Containers sollte dieser noch nicht gestartet werden, damit noch sein AppArmor-Profil festgelegt werden kann.

Apparmor

Die Abgrenzung der Container zum Host-System wird bei Proxmox mit Hilfe von AppArmor durchgesetzt. Dort werden verschiedene sicherheitsrelevante Rechte beschränkt. Dazu gehört auch die Einschränkung von mounts. Für NFS werden einige Mounts gebraucht, also müssen die wieder freigegeben werden.

Bei der Recherche im Internet zum Thema NFS im Container unter Proxmox wird oft geraten, man solle den ganzen Container auf unconfined stellen. Dadurch ist dann allerdings so gut wie kein Schutz mehr vorhanden, der das Host-System vom Gast abtrennen würde. Insbesondere weil für NFS ein privilligierter Contianer gebraucht wird, entspricht UID 0 im Container der UID 0 auf dem Host! Bricht man also mit Hilfe der Root-Rechte aus dem Container aus, hat man sie auch auf dem Host! Ein paar Sicherheitsmaßnahmen sollte man also durchaus gültig lassen.

Im Proxmox-Forum gibt es ein AppArmor-Profil, was die notwendigen Mounts ermöglicht. Die folgende Datei sollte also unter /etc/apparmor.d/lxc/lxc-default-with-nfsd angelegt werden:

# Do not load this file.  Rather, load /etc/apparmor.d/lxc-containers, which
# will source all profiles under /etc/apparmor.d/lxc

profile lxc-container-default-with-nfsd flags=(attach_disconnected,mediate_deleted) {
  #include <abstractions/lxc/container-base>

  # the container may never be allowed to mount devpts.  If it does, it
  # will remount the host's devpts.  We could allow it to do it with
  # the newinstance option (but, right now, we don't).
  deny mount fstype=devpts,
  mount fstype=nfsd,
  mount fstype=rpc_pipefs,
  mount fstype=cgroup -> /sys/fs/cgroup/**,
}

Nachdem die Datei angelegt wurde, muss das Profil noch eingelesen werden:

apparmor_parser -r /etc/apparmor.d/lxc-containers

Das Profil muss im Anschluss dem neu erstellten Container zugewiesen werden. Dazu muss seine Konfigurationsdatei bearbeitet werden, die unter /etc/pve/lxc/1337.conf (1337 durch die Container-ID ersetzen):

lxc.apparmor.profile = lxc-container-default-with-nfsd

Container starten und konfigurieren

Nun kann der Container gestartet werden. Das reguläre Setup kann stattfinden. Dabei sind ein paar Dinge sehr wichtig für den Betrieb als NFS-Server:

  • Der Host braucht einen FQDN, beim Aufruf von hostname -f sollte der vollständige Hostname inklusive Domain zurückgegeben werden. Mit relativ modernen Distributionen reicht dafür hostnamectl set-hostname $hostname.$domain
  • Der Host sollte ebenfalls Mitglied bei FreeIPA sein. Das macht es sehr viel einfacher, die notwendigen Kerberos-Credentials zwischen den Systemen auszutauschen.

Im FreeIPA

Auch auf der Seite von FreeIPA müssen Dinge vorbereitet werden:

Service für NFS anlegen

Damit NFS mit Kerberos geschützt werden kann, wird ein Kerberos-Prinzipal gebraucht, bei FreeIPA passiert das über das Anlegen von Services. Um einen Service anzulegen kann man entweder auf der FreeIPA-Weboberfläche unter Identity, Services einen anlegen (Service nfs, als Host Name der FQDN des NFS-Servers) oder auf der Shell eines bereits bei FreeIPA beigetretenen Hosts (mit installierten FreeIPA-Admintools):

kinit admin
ipa service-add nfs/hostnamedesnfsservers.domain.tld

Reverse-DNS - Einträge

Der FQDN der DNS-Einträge muss auch umgekehrt auflösen, für Kerberos und NFS ist DNS sehr wichtig. *Das gilt auch für alle Clients! Sollte sich aus der Infrastruktur keine bessere Methode ergeben, für alle beteiligten IP-Adressen gültige RDNS-Einträge anzulegen, kann das immerhin in den DNS-Servern von FreeIPA passieren. Dazu muss dann die notwendige DNS-Zone angelegt werden, für das Netzwerk in dem die IP-Adresse liegt, beispielsweise 192.168.0.0/24. Das kann wieder über die Weboberfläche von FreeIPA stattfinden oder auch wieder über die Shell eines bereits bei FreeIPA beigetretenen Hosts (mit installierten FreeIPA-Admintools):

kinit admin
ipa dnszone-add --name-from-ip=192.168.0.0/24

Der neuen Zone kann man dann wie exemplarisch folgt neue RDNS-Einträge hinzufügen:

kinit admin
ipa dnsrecord-add --ptr-hostname=bla.example.com. 0.168.192.in-addr.arpa 23

Am Beispiel von 192.168.0.23:

  • --ptr-hostname: Der FQDN, abgeschlossen mit einem . - der Punkt ist nicht optional, der signalisiert nämlich das Ende des DNS-Records, ansonsten wird noch die (meist falsche) Domain angehängt.
  • 0.168.192.in-addr.arpa: Hier werden die Oktette der IP-Adresse umgekehrt aufgereiht und die Sonder-Domain in-addr.arpa angehängt. Das letzte Oktett der IP-Adresse ist nicht mit anzugeben. Stattdessen wird die letzte Zahl durch Leerzeichen abgetrennt einfach dazugeschrieben.

Der Befehl legt in der Zone 0.168.192.in-addr.arpa den Eintrag 23 an, der auf bla.example.com. zeigt.

Im Container

Keytab aktualisieren

Damit der NFS-Server die Krypto-Teile von Kerberos nutzen kann, wird auf dem Host das Kerberos-Prinzipal gebraucht. Wenn der Host Mitglied bei FreeIPA ist, kann dieses Prinzipal einfach heruntergeladen werden:

ipa-getkeytab -p nfs/hostnamedesnfsservers.domain.tld -k /etc/krb5.keytab

Damit wird das Prinzipal im systemweiten Kerberos-Keytab gespeichert und kann verwendet werden.

NFS-Tools installieren

Damit NFS gesteuert und aktiviert werden kann, werden Programme benötigt:

  • Ubuntu: sudo apt install nfs-kernel-server
  • CentOS: dnf install nfs-utils

Shares anlegen

NFS-Shares werden in der /etc/exports angelegt. Am deutlichsten wird die Syntax an einem Beispiel:

/nas 192.168.0.0/24(rw,sec=krb5i,async,insecure)
  • /nas: Pfad zur Freigabe
  • 192.168.0.0/24: Netzwerk, für das die Freigabe verfügbar sein soll
  • rw: Lesen und Schreiben
  • sec=krb5i: Kerberos-Authentifizierung verwenden und die RPC-Pakete verifizieren
  • insecure: Erlaubt, dass Pakete auch von Ports >1024 kommen

Um die neuen Shares zu aktivieren und die /etc/exports neu einzulesen, gibt man ein:

exportfs -rav

Auf dem Client

Der Client sollte selbst Mitglied im FreeIPA sein damit die Autentifizierung funktioniert. Um ein Share einzuhängen genúgt für gewöhnlich:

sudo mount -t nfs hostnamedesnfsservers.domain.tld:/nas /mnt/nas -o sec=krb5i