Kategorie: Technik

  • Ein AI Coding Agent hat alle meine Projekte gelöscht – während er versuchte Laravel zu installieren

    Ein AI Coding Agent hat alle meine Projekte gelöscht – während er versuchte Laravel zu installieren

    Ich teste derzeit verschiedene AI Coding Agents, um herauszufinden, wie autonom und effizient sie tatsächlich Software entwickeln können.

    Die Aufgabe ist immer dieselbe:

    Baue eine etwas komplexere PHP Webapplikation. – der Prompt ist natürlich etwas ausführlicher als das

    Agents entscheiden sich bei solch einer Aufgabe oft für ein Framework wie Laravel.

    Im Test habe ich aktuell:

    Alle Agenten bekommen eine möglichst realistische Entwicklerumgebung. Kein künstliches Sandbox-Setup, sondern einfach ein Projektverzeichnis und typische Developer-Tools.

    Zum Beispiel:

    • ein Workspace im Home-Verzeichnis unter ~/src
    • Git
    • Docker
    • Shell-Zugriff

    Backups existieren natürlich.

    Aber die Agenten wissen das nicht.


    Ein wichtiges Detail des Testsystems

    Ein Detail war dabei bewusst etwas ungewöhnlich:

    Auf dem Host-System gibt es noch keine PHP Installation.

    Das war Absicht. Ich wollte sehen, ob der Agent selbst erkennt, welche Laufzeitumgebung benötigt wird. Ich bin ja hier, ich kann ihm ja helfen!

    Normalerweise würde ein Entwickler einfach PHP installieren:

    sudo apt install php-cli

    Der Agent konnte das allerdings nicht.

    Er hatte keine sudo-Rechte und durfte keine Systempakete installieren.

    Damit stand er vor einem klassischen Problem:

    Laravel braucht PHP – aber PHP ist nicht vorhanden.


    Wie der Agent versucht hat, seine Umgebung zu verstehen

    Bevor der Agent überhaupt versuchte, Laravel zu installieren, begann er zunächst damit, das Hostsystem besser zu verstehen.

    Dafür wollte er mehrere Dateien unter /etc lesen, unter anderem:

    • /etc/os-release
    • /etc/issue

    Er hat dafür explizit um Erlaubnis gefragt, statt einfach auf die Dateien zuzugreifen. Das ist ein Sicherheitsmechanismus, den viele dieser Agents mitbringen – allerdings greift er nicht immer zuverlässig, wie wir gleich sehen.

    Das zeigt zwei Dinge:

    1. Der Agent versucht aktiv, seine Umgebung zu analysieren.
    2. Er ist durchaus bereit, den Workspace zu verlassen, wenn er glaubt, dass es für seine Aufgabe relevant ist.

    Die Lösung des Agents: Docker

    Nachdem der Agent erkannt hatte, dass kein PHP vorhanden ist und er es nicht installieren kann, fand er eine alternative Lösung.

    Er nutzte Docker, um eine eigene Laufzeitumgebung zu starten.

    Zum Beispiel so:

    docker run --rm \
      -v /home/kosmos/src:/workspace \
      -w /workspace \
      laravelsail/php82-composer \
      composer create-project laravel/laravel

    Der Container bringt bereits mit:

    • PHP
    • Composer
    • die notwendigen Extensions

    Damit konnte der Agent Laravel installieren, ohne etwas auf dem Host zu verändern.

    Aus Sicht eines Entwicklers ist das sogar eine ziemlich vernünftige Lösung.


    Der Incident

    Während des Runs wollte der Agent ein Laravel-Projekt im Workspace erstellen.

    Der Workspace war allerdings nicht leer, weil der Agent kurz zuvor selbst eine SPEC.md als Arbeitshilfe angelegt hatte.

    Laravel (composer create-project) startet in diesem Fall nicht.

    Ein menschlicher Entwickler würde jetzt wahrscheinlich einfach die vorhandene Datei verschieben oder temporär ein Unterverzeichnis im Workspace verwenden.

    Der Agent entschied sich für eine andere Strategie.

    Die Abfolge sah ungefähr so aus:

    1. Laravel-Projekt im übergeordneten Ordner ~/src erstellen
    2. versuchen, die Dateien von dort in den eigentlichen Workspace zu verschieben
    3. Konflikte feststellen

    Und dann:

    rm -rf *

    Keine Nachfrage.
    Keine Warnung.

    Das komplette ~/src Verzeichnis war danach leer (fast).


    Das eigentliche Risiko: Docker + root

    Beim Analysieren des Runs fiel ein wichtiges Detail auf.

    Die verwendeten Laravel-Container liefen als root.

    Das ist bei vielen Docker Images leider Standard.

    Solange ein Container isoliert bleibt, ist das kein großes Problem.

    Sobald jedoch Host-Verzeichnisse gemountet werden, sieht die Sache anders aus.

    Zum Beispiel:

    docker run -v /home/kosmos/src:/workspace laravel-container

    Der Container hat dann vollständigen Schreibzugriff auf dieses Verzeichnis.

    Wenn der Prozess im Container als root läuft, gelten diese Rechte auch für alle Dateien im Mount.

    Das führt häufig zu Nebenwirkungen wie:

    • root-owned Dateien
    • kaputte Berechtigungen
    • schwer löschbare Projektordner

    Das zuvor erstellte laravel Projekt war als einziges noch in ~/src vorhanden, Verzeichnis und Dateien gehörten nämlich root.


    Warum das theoretisch viel schlimmer hätte enden können

    Docker verhindert nicht, dass ein Container größere Teile des Hostsystems mountet.

    Zum Beispiel:

    docker run -v /:/host ubuntu

    Damit sieht der Container das komplette Dateisystem des Hosts unter /host.

    Wenn der Prozess im Container als root läuft, kann er dort praktisch alles verändern.

    Zum Beispiel:

    rm -rf /host/*

    Das würde nicht den Container zerstören.

    Es würde den Host selbst löschen.

    Das ist kein klassischer Container Escape.

    Der Container nutzt einfach nur die Rechte, die ihm gegeben wurden.

    Und wenn ein autonomer Agent:

    • Docker starten darf
    • Mounts frei setzen kann
    • Container als root laufen
    • und Shell-Befehle ausführen kann

    dann ist der Unterschied zwischen

    „Arbeite im Projektordner“

    und

    „Zerstöre das gesamte System“

    plötzlich nur noch eine Frage des Mount-Pfads.


    Selbst der Agent wusste später nicht mehr genau, was passiert ist

    Ich habe den Agent gebeten den Incident selbst zu analysieren.

    Ein interessantes Detail aus dem Incident-Report:

    Der Agent konnte später nicht mehr vollständig rekonstruieren, was im Container passiert ist.

    Der Grund war ein kleines, aber folgenreiches Detail.

    Viele seiner Docker-Kommandos sahen ungefähr so aus:

    docker run ... | tail -30

    Damit wurde die Ausgabe von Anfang an auf die letzten 30 Zeilen begrenzt.

    Das spart Tokens im Kontextfenster des Modells.

    Der Nebeneffekt:

    Der größte Teil der Container-Logs ging verloren.

    Der Agent stellte im Incident-Report selbst fest, dass er deshalb nicht mehr nachvollziehen kann, welche Schritte genau im Container ausgeführt wurden.

    Für den Incident ist dies aber nur ein nebensächliches Detail, denn das Löschen geschah in meinem Fall außerhalb des Containers.


    Fazit

    Dieser Test hat wieder mal bestätigt:

    AI Coding Agents verhalten sich weniger wie Chatbots. Sie verhalten sich eher wie:

    ein extrem schneller Junior-Developer auf Koks

    Das kann unglaublich produktiv sein.

    Aber auch gefährlich.

    Vor allem wenn sie Zugriff haben auf:

    • Docker
    • das Dateisystem
    • automatische Toolchains
    • Container mit root-Rechten

    Denn dann ist der Unterschied zwischen

    composer create-project laravel

    und

    rm -rf *

    manchmal nur eine Entscheidung im nächsten Token.

    Der Agent mit dem das geschehen ist war übrigens Open Code mit dem kostenlosen „big pickle“ Model von OpenCode Zen, aber das ist hier Nebensache.

    Und natürlich ist es ein generell bekanntes, aber gerade in Dev-Umgebungen oft akzeptiertes Sicherheitsrisiko, wenn eigentlich unprivilegierte User docker nutzen können.

  • Pesky error message during Plesk Updates “Warning: install PAM module not successed” (PAMConfig.confset.PAMServiceError: system-auth)

    Pesky error message during Plesk Updates “Warning: install PAM module not successed” (PAMConfig.confset.PAMServiceError: system-auth)

    Problem

    For the longest time Plesk updates have been showing the error “Warning: install PAM module not successed” on one of my Plesk installations. While this message is a bit scary to read, the installer always claimed that all changes had been executed successfully. And I never experienced any negative effects, indeed. So I got used to ignoring the error a while ago.

    Today I stepped over it once more and I finally took the time to investigate it further.

    FYI: this is Plesk Obsidian 18.0.61 on Ubuntu 20.04 LTS.

    This is the message appearing in the installation log:
    CRITICAL:3:a programming/runtime error (PAMConfig.confset.PAMServiceError: system-auth)
    Trying to install PAM module… PAM configuration loaded from (directory="/etc/pam.d", file="/etc/pam.conf"). Configuration contains 29 file(s)
    3:a programming/runtime error (PAMConfig.confset.PAMServiceError: system-auth)
    Warning: install PAM module not successed

    Investigation

    Google wasn’t very helpful as I found exactly one result complaining about the same error, but without any solution. The Plesk forum didn’t have anything good to tell either at this point. Even though there is no clear indication which (.deb) package exactly is causing the issue, its pretty obvious that it must be PAM related. `libpam-plesk` seems to be a good candidate. So I grabbed it from `/var/cache/apt` to have a look at its content.

    $# ar -x ../libpam-plesk_18.0-v.ubuntu.20.04+p18.0.61.0+t240426.1307_amd64.deb
    $# tar xJf control.tar.xz
    $# tar xJf data.tar.xz

    This got me a bunch of files and directories to look at.

    Unfortunately the message “install PAM module” can’t be found inside any of them, but there is a “uninstall PAM module” message, so I considered myself being on the right track.
    After some more digging I found that postinst calls /opt/psa/bootstrapper/components/libpam-plesk.sh which contains “install PAM module”. Yay!

    This bootstrapper script finally calls the binary /opt/psa/pam_plesk_config/pam_plesk_install. Uuuh, a binary?

    Remember the plesk forum? This time a search there gave me 2 hints: a) people have been calling pam_plesk_install multiple times without doing any futher harm and b) pam_plesk_install supports -v for more verbosity.

    # /usr/local/psa/pam_plesk_config/pam_plesk_install -v
    DEBUG:Loading PAM configuration from (directory="/etc/pam.d", file="/etc/pam.conf")
    INFO:PAM configuration loaded from (directory="/etc/pam.d", file="/etc/pam.conf"). Configuration contains 29 file(s)
    DEBUG:calculating changes for 'auth' stacks
    DEBUG:S3:Traceback (most recent call last):
    File "pam_plesk_install.py", line 351, in main
    File "pam_plesk_install.py", line 35, in getAuthChanges
    File "PAMConfig/analyzers.py", line 130, in selectServices
    shared = confSet.getCommonServiceNames(self.facility)
    File "PAMConfig/confset.py", line 958, in getCommonServiceNames
    if self.getConf(c, facility):
    File "PAMConfig/confset.py", line 980, in getConf
    raise PAMServiceError(service)
    PAMConfig.confset.PAMServiceError: system-auth
    
    CRITICAL:3:a programming/runtime error (PAMConfig.confset.PAMServiceError: system-auth)

    Not too helpful, but there are references to .py files? Ok… so this “binary” might be just a collection of Python scripts, stitched together with something like pyinstaller.

    Thankfully theres a python script pyinstextractor.py available at https://github.com/extremecoders-re/pyinstxtractor to extract whatever might be hidden in such a binary. Spoiler Alert: THIS is not a pyinstaller binary. Dead end.

    Sooo… back to the binary: the first two bytes of the file header read PK… maybe its a simple ZIP file?

    $# unzip pam_plesk_install
    Archive: pam_plesk_install
    inflating: .bootstrap/init.py
    inflating: .bootstrap/_markerlib/init.py
    [...]

    Indeed! Now we’re talking…

    Crawling the source code was challenging and I will not claim to understand everything or even try to document the steps, but my summary is: pam_plesk_install verifies/analyzes all files in pam.d, regardsless if it needs to touch them or not. So this might be something completely unrelated to plesk.

    BTW:

    # FIXME: the entire aproach is somewhat sick. I just don’t
    # have a better one at the moment…

    PAMConfig/analyzers.py

    Well… yeah, by now I think I know what you mean!

    Conclusion

    And heres the root cause: a completely different service (Acronis Backup Manager) referenced `system-auth` in one of its own `pam.d` files. Appearantly `system-auth` is not available in Ubuntu 20.04 (anymore?) and `pam_plesk_install` can’t get any details about it. That ultimately crashed the script!

    #%PAM-1.0
    auth include system-auth
    auth required pam_listfile.so item=user sense=allow file=/etc/security/acronisagent.conf onerr=fail
    account include system-auth

    acronisagent.system

    Since I didn’t use Acronis for a long time, my fix was easy: just get rid of BackupManager. Or: So I thought… but thats another story which will be told another day.
    Hint: Don’t trust Acronis’ uninstaller to not touch files in /usr/sbin it doesn’t own!

    Anyways, the takeaway is: The error described here is gone for good after removing any invalid/outdated entries from pam.d files.

    Plese let me know if you found this to be helpful!

    I posted a copy of this to the plesk forums here: https://talk.plesk.com/threads/error-message-during-plesk-updates-warning-install-pam-module-not-successed-pamconfig-confset-pamserviceerror-system-auth.374405/

  • Synology DiskStation auf Steroiden

    Synology DiskStation auf Steroiden

    Die Tage klagte ein Kollege das Backups zu seinem Synology NAS DS216+II schleppend langsam liefen und auch gern abbrachen. Auch die nächtliche Synchronisation der Daten mit einem anderen Synology Gerät (an einem anderen Standort) funktionierte seit einer ganzen Weile nicht mehr zuverlässig.

    Die Situation stellte sich beim Versuch eines lokalen Backups zum NAS (unter Verwendung von Synology Active Backup for Business) folgendermassen dar: Anfangs zeigten sich zwar keine herausragenden aber noch akzeptable Datenübertragungsraten von 7 MB/s. Doch über die Zeit sank diese Übertragungsrate auf unterirdische Werte weniger KB/s.

    Während die Verwaltungsoberfläche abgesehen von einer hohen Auslastung keine hilfreichen Hinweise auf die Ursache des Problems gab, konnte ein schneller Blick in die Konsole und das Tool htop einen ersten Verdacht aufzeigen.

    Während die arme kleine Intel Celeron N3060 (Dual Core) CPU des Geräts unter der Last des Backups ohnehin vollkommen ausgelastet war, wurde auch das mit 1 GB sehr knapp bemessene RAM ordentlich belastet. Der mit knapp 3 GB grosszügig bewessene Swap Space erlaubt im Normalfall dennoch einen recht performanten Betrieb, zB einiger kleiner Container wie Nextcloud, wird aber in dieser Situation zum echten Problem.

    Offenbar genehmigt sich der Backup Task relativ viel RAM, mutmasslich zum Caching – etwa um bei kleinen Aufgaben die Ausführung performanter erscheinen zu lassen als es der IO Bus bzw. die HDDs eigentlich zulassen.

    Das führt aber bei grossen Datenmengen und voll augelasteten IO zu einem Stau, denn es kommen ja immer neue Daten nach. Das System kommt nie dazu den Cache zu leeren, füllt ihn sogar immer weiter auf. Aus lauter Verzweiflung beginnt das System dann damit den RAM Inhalt in den grosszügigen Swap Space auszulagern (daran zu sehen das dieser sich kaum merklich aber stetig füllte). Was widerum, man ahnt es schon, den IO noch weiter belastet – ein Teufelskreis.

    Glücklicherweise lässt sich das RAM der DiskStation mit relativ wenig Aufwand aufrüsten, zudem hatte ich hier noch einen 4GB SO-DIMM Riegel aus einem alten McBook Pro herumliegen. 1,60 € Porto und 24h später war dieser dann auch bereits am Ort des Geschehens. Der Umbau ist in hunderten von YouTube Videos hinreichend beschrieben, exemplarisch verlinke ich hier mal eines davon.

    Die Ergebnisse sprechen für sich selbst! Ein Backup das vorher den halben Tag dauerte – wenn es überhaupt abgeschlossen wurde – ist nun innerhalb einer Stunde erledigt.

    „DS216+II on steroids!“

  • E-Mail Zustellung an T-Online…

    E-Mail Zustellung an T-Online…

    …oder: wie alles nichts nützt wenn eine IP bereits „eine schlechte Reputation“ hat.

    Ich gebe zu es sind (im Verhältnis) nicht viele Mails die so täglich über meine Server laufen, vor allem nicht viele die kein Spam sind. Letztere sind vorzugsweise eingehende EMails, obwohl es Situationen gab in denen auch ich unbeabsichtig zum Mittäter wurde.

    Doch zunächst: Warum das ganze? Und warum jetzt?

    Gestern bemerkte ich das leise weinen eines meiner Server, die Mailserver von t-online.de würden nicht mit ihm reden wollen.

    May 14 05:45:12 ********** postfix/smtp[******]: ************: host mx02.t-online.de[194.25.134.9] refused to talk to me: 554 IP=xxx.xxx.xxx.xxx - Dialup/transient IP not allowed. Use a mailgateway or contact ****@**.t-online.de if obsolete. (DIAL)

    Ehrlich gesagt hatte ich diese Meldung schon in der Vergangenheit ab und zu gesehen aber nicht wirklich wahrgenommen. Wer muss schon dringend jemanden bei T-Online erreichen?

    Aus Fehlern soll man bekanntlich lernen und vor allem sollte man sie bei sich niemals von vorne herein ausschliessen. Auch wenn die Meldung oben darauf hindeutet das meine IP fälschlicherweise als DialUp/dynamisch vergebene IP eingestuft wird, wollte ich zunächst sicher gehen das bei mir alles korrekt ist.

    Daraus ergab sich eine durchaus positive Lernkurve, und ich habe mir vorgenommen hierzu einen möglichst umfassenden Guide für Leidensgenossen zu erstellen. Jetzt aber soll es genügen zu sagen: es war nicht alles Perfekt, aber ich habe auch bis jetzt schon alle Bedingungen erfüllt die https://postmaster.t-online.de/ für eine erfolgreiche Zustellung erfordert. (für einige, nicht alle meiner Domains!)

    Es sei auch erwähnt das ich bei anderen grossen Maildiensten (zB Google, web.de, GMX, UnitedDomains/UDAG) zur gleichen Zeit keinerlei Probleme hatte.

    Dennoch habe ich die letzten 24h in grossen Teilen damit zugebracht auch alles andere „gerade zu ziehen“, und hin und wieder mit den überraschend gut verfügbaren und kommunikativen Admins von T-Online zu kommunizieren. Vornehmlich kamen von dort aber eher irreführende Hinweise, denn mein Kontakt hatte offenbar den eigentlichen Grund für sich von vorne herein ausgeschlossen: Das nützt alles nichts wenn die IP bereits „eine schlechte Reputation“ bei T-Online hat.

    Nachdem ich nun also sicher war das bei mir alles (wieder*) korrekt sein muss – weil ein anderer Server (für andere Domains) mit einer äquivalenten Konfiguration problemlos von T-Online akzeptiert wurde – war erstmal einige Stunden Funkstille auf der Seite von T-Online.

    Schlussendlich kam dann heute im frühen Nachmittag die durchaus schmallippige und erlösende Antwort:

    Wir werden veranlassen, dass die Reputation dieser IP-Adresse bei unserem System resettet wird.

    Und siehe da: nur wenige Minuten später funktioniert die Zustellung tadellos!

    Ich persönlich vermute ja das der Pool von IP Adressen in dem sich mein Server befindet irgendwann einmal „en gros“ geblockt wurde, da in ihm viele billige und schnell verfügbarte V-Server existieren – die gerne oft von Spammern genutzt werden.

    *) „wieder“ weil ich – im Zuge der Bemühungen den Hinweisen von T-Online nachzukommen – es tatsächlich erstmal verschlimmert habe!