FreeIPA 4.13.2 auf FreeBSD

Die VM antwortete auf ping, war also im Netz. Nur SSH sagte Connection refused, und die CPU-Last lag bei null. Kein Panic, kein volles Dateisystem. Eine Maschine, die genug lebt, um auf Pakete zu antworten, aber nicht genug, um sich bedienen zu lassen, ist die unangenehmste Sorte Störung. Vorausgegangen war ein ganz normaler Neustart.

Angefangen hatte die Sache freundlicher. Am 18. August ist net/freeipa-server im offiziellen FreeBSD-Ports-Baum gelandet, Commit 35e4879, einen Tag später kam das Update auf 4.13.2. FreeIPA ist kein Programm, sondern ein Verbund: 389 Directory Server als LDAP, MIT-Kerberos-KDC, Dogtag PKI als CA, davor ein Apache mit mod_wsgi. Vier Dienste, die gemeinsam hochkommen müssen und gemeinsam wieder herunter.

Schaubild mit fünf Diensten: Client, krb5kdc, httpd, 389-ds und pki-tomcatd, dazu die Wege dazwischen
Sechs Wege durch fünf Dienste. Das Ticket kommt vom KDC, die Anmeldung prüft httpd gegen das HTTP-Keytab, die Daten liegen im Directory, und die Zertifikate macht Dogtag, das seinerseits im selben Directory ablegt.

Acht Fehler sind dabei zusammengekommen, und ihre Fundorte sagen mehr über Portierungsarbeit aus als die Fehler selbst. Software gilt als portiert, wenn sie startet. Dort endet das Protokoll. Die Hälfte meiner Funde lag dahinter, und zwar beim Neustart, beim Herunterfahren und beim Upgrade.

Was vor dem ersten Neustart lag

Fair ist fair, vier Fehler lagen vor diesen drei Punkten, zwei davon gehören nicht einmal mir, sondern sssd2. autoconf ersetzt @datadir@ nicht durch einen Pfad, sondern durch die Shell-Referenz ${prefix}/share. In einem Makefile löst die Shell das auf, in einer Python-Datei nie, und der Port übergab weder --datadir noch --sysconfdir:

[Errno 2] No such file or directory: '${prefix}/share/sssd/sssd.api.conf'

Getroffen hat es auch sss_obfuscate aus dem Port selbst, der Fix ging als PR 297667 an arrowd@ und ist committet. Daneben der kleine Bruder: Der @sample-Eintrag in der pkg-plist trug keinen Modus, pkg legte die sssd.conf mit 0644 an, und sssd startet dann nicht, sondern sagt Can't read config: 'File ownership and permissions check failed'.

Danach stand FreeIPA. Das Ticket kam, LDAP antwortete, die Web-UI war erreichbar, nur die Anmeldung per GSSAPI nicht. Der Browser bekam ein knappes No valid Negotiate header in server response, was jeden erst einmal zum Service Principal schickt. Das httpd-Log war ehrlicher:

GSS ERROR gss_acquire_cred[_from]() failed to get server creds:
[No credentials were supplied ... SPNEGO cannot find mechanisms to negotiate]

Keine Anmeldedaten. Nicht die falschen, gar keine. Das HTTP-Keytab gehörte root. Der Port fährt bewusst mit GSS_USE_PROXY=no, weil die S4U2-Delegation von gssproxy im langlebigen httpd-Worker unzuverlässig ist, und wer keinen Vermittler hat, muss selbst an das Material. httpd läuft als www. Ein Kerberos-Problem, das eine Frage der Dateirechte war.

Dazu drei ACME-Vorlagen des Ports mit hart einkodiertem Linux-Pfad, Unable to start ACME engine: Cannot find config file: /etc/pki/pki-tomcat/ca/CS.cfg, während die Instanz hier unter /var/db/pki/pki-tomcat/conf liegt.

Der Neustart

Zurück zur VM. Ohne SSH bleibt der Bildschirm, und den gibt der QEMU-Monitor als Screendump heraus. Darauf stand eine Zeile, die sich nur in einer Ziffer änderte:

Error connecting to bus for "com.redhat.oddjob" (attempt 17)!

Der Zähler lief, das System nicht. dbus war nicht aktiviert. Es fehlt auf FreeBSD nicht, es ist ein gewöhnlicher rc-Dienst und ab Werk aus, während unter Linux der System-Bus per Socket-Aktivierung immer da ist. certmonger startete deshalb gar nicht erst, oddjobd versuchte es blockierend weiter, an einer Stelle der Startkette vor sshd. Deshalb war die Kiste im Netz und trotzdem stumm: Das Interface hatte der Kernel längst oben, das Userland kam nie bis zur Shell. Hinein bin ich über den Single-User-Modus, per sendkey am QEMU-Monitor: blind tippen, Bild ziehen, nachsehen, ob der Buchstabe angekommen ist.

Der zweite Fund war leiser. /etc/rc.d/kadmind definiert kein eigenes command, es lebt allein von kadmind_program aus der rc.conf, und der Deaktivierungspfad des Ports hatte diese Variable entfernt. Also meldete service kadmind onestatus tapfer kadmind is not running, während der Prozess munter lief. ipactl status übernahm das ungeprüft, ipactl restart wartete endlos. Der Dienst war in Ordnung, nur wusste rc(8) nichts von ihm, und alles darüber glaubt rc(8).

Das Herunterfahren

Das Herunterfahren testet kaum jemand, es erledigt sich ja scheinbar von selbst. Bei Dogtag erledigte es sich nicht. Tomcat verklemmte sich, Waiting for PIDS stand minutenlang auf der Konsole, und weil ipa-server-upgrade die Instanz vorher stoppen will, riss der hängende Prozess das Upgrade mit. Ein kill -QUIT auf die JVM liefert dann den Thread-Dump:

"main" java.lang.Thread.State: BLOCKED (on object monitor)
  at org.mozilla.jss.ssl.SSLSocket.close
  at com.netscape.cmscore.ldapconn.LdapBoundConnFactory.shutdown
  at org.dogtagpki.acme.server.ACMEEngine.shutdownRealm

Der Haupt-Thread wollte die LDAP-Verbindungen der ACME-Engine schließen, derselben, die vorhin ihre Konfiguration nicht gefunden hatte, und blieb an einem Monitor hängen. Gehalten wurde der von LDAPConnThread-8, mitten in einem blockierenden Socket-Read. Darunter lag eine zweite, unabhängige Gemeinheit: jsvc schreibt die PID seines Kindprozesses in die pidfile, auf SIGTERM reagiert aber der Elternprozess. rc(8) signalisiert zuverlässig den Falschen und wartet dann ohne Zeitlimit. Der Port bringt jetzt einen eigenen Stopp-Pfad mit, sechzig Sekunden Gnadenfrist, danach hart.

Das Upgrade

Mit 4.13.2 kam der halbe Unterbau mit: dogtag-pki von 11.7.0 auf 11.10.1, dogtag-jss von 5.7.0 auf 5.10.1, dogtag-ldap-sdk von 5.6.0 auf 5.7.0, resteasy von 3.0.26 auf 3.15.6, Dogtag läuft jetzt auf Java 21. Nebenbei ist die fest verdrahtete samba416-Abhängigkeit weg, der Port nutzt das samba-Framework der Ports, Patch von fluffy.

Dogtag 11.10.1 hat dabei den Quellbaum umgebaut, und zwei FreeBSD-Anpassungen, die vorher sauber gegriffen hatten, fielen aus dem Raster: ModuleNotFoundError: No module named 'selinux' und FileNotFoundError: [Errno 2] No such file or directory: 'runuser'. runuser stammt aus util-linux und wird es hier nie geben, der Rechtewechsel läuft jetzt über subprocess. Keine neuen Baustellen, sondern alte, die ein fremder Umbau wieder aufgerissen hat: die stille Miete für einen Port dieser Größe.

Wer schon einen Server stehen hat, muss beim Update zwei Dinge selbst erledigen, den Verweis auf das JDK (Java 21) und die ACME-Pfade. Beide betreffen eine bereits ausgerollte pki-tomcat-Instanz, deren Konfiguration kein Paket anfassen darf. Ohne den ersten bleibt Tomcat mit UnsupportedClassVersionError: class file version 65.0 stehen und ipa-server-upgrade bricht ab. Beides steht im UPDATING-Eintrag vom 19.08.2026.

Der Fehler, der keiner war

Zwischendurch verlor die Maschine ihren eigenen Namen, so gründlich, dass ipactl mit socket.gaierror: [Errno 8] Name does not resolve ausstieg. Kein Portfehler, dafür einer mit Reichweite, denn er trifft jeden FreeBSD-Gast mit aktivem cloud-init: Das schreibt /etc/hosts bei jedem Boot neu und wirft die Zeile mit FQDN und echter IP heraus. Ein Drop-in mit manage_etc_hosts: false unter cloud.cfg.d half nicht, weil Proxmox die user-data selbst erzeugt, mit manage_etc_hosts: true, und user-data schlägt alles aus cloud.cfg.d. Verlässlich ist nur touch /usr/local/etc/cloud/cloud-init.disabled.

Warum erst jetzt

Naheliegend wäre, hier sei zum ersten Mal von null aufgesetzt worden. Stimmt nicht, die Test-VM ist Verbrauchsmaterial, sie wird ständig weggeworfen und neu erzeugt. Der Unterbau hatte sich bewegt, sssd steht bei 2.13.1, Dogtag bei 11.10.1, und jede frische Version bringt neue Annahmen darüber mit, wie ein Betriebssystem auszusehen hat. Vor allem aber lief zum ersten Mal die ganze Kette am Stück: Installation, Neustart, Herunterfahren, Upgrade. Vier der acht Funde saßen an diesen Übergängen, und es sind die vier, die in keinem Installationsprotokoll stehen.

Wo der Server heute steht

Besser, als ich erwartet hätte. Auf einer frischen Maschine installiert er sich sauber durch, kommt nach einem Neustart von allein hoch und fährt sauber herunter. Kerberos, LDAP, GSSAPI über HTTP, Web-UI, CA und die Auflösung von IPA-Benutzern über SSSD funktionieren. Für den Produktiveinsatz empfehle ich ihn trotzdem nicht: Der Client-Port im Baum hat die FreeBSD-Anpassungen noch nicht, dafür läuft ein eigener PR, und ipa-getcert konnte ich mangels DNS nicht abschließend prüfen. Solange das offen ist, gehört der Server in eine VM, die man wegwerfen darf.

Schreibe einen Kommentar