Kai Ole Hartwig
5 Min. Lesezeit
Mittel
Von

Der Kern entscheidet, Plugins führen aus

Ein Dependency-Werkzeug hat zwei gefährliche Eigenschaften zugleich. Es hält einen Schreib-Token für viele Repositories. Und es führt Paketmanager aus, die Code aus genau diesen Repositories lesen.

pinup trennt deshalb scharf: Der Kern entscheidet alles, was sich ändert. Ein Task führt nur aus, ohne die Zugangsdaten der Plattform und in einem Bereich, den er nicht verlassen darf.

01 — Die Meinung in einem Satz

Ein Task erzeugt eine Lock-Datei neu oder führt einen erlaubten Befehl aus, und sonst nichts.

Welche Abhängigkeit sich ändert, welche Version kommt und welche Datei betroffen ist, steht im Plan. Der Task bekommt einen Checkout und einen Auftrag. Ein Befehl aus einer Regel sieht so aus:

 

packageRules:
  - matchDatasources: [git-refs]
    postUpgradeTasks:
      commands: ["node tools/update-expected-commit.mjs {{{packageFile}}}"]
      fileFilters: ["*.yaml"]

 

Das steht in der .pinup.yaml des Repositories. Ob der Befehl laufen darf, entscheidet aber nicht das Repository. Das entscheidet die Liste erlaubter Befehle im Runner, dem zentralen Job, der pinup über alle Repositories laufen lässt.

02 — Die Alternative: Isolation als Betriebsaufgabe

Renovate führt Post-Upgrade-Tasks in der Umgebung aus, in der es selbst läuft. Eine Liste erlaubter Befehle legt der Betreiber fest. Die Isolation überlässt Renovate weitgehend dieser Umgebung: einem Container pro Lauf, einem eigenen Runner, einem Token mit wenig Rechten.

Das ist ein vernünftiger Schnitt. Ein Werkzeug für viele Plattformen kann nicht wissen, welche Isolation dort möglich ist. Also wird sie Aufgabe des Betriebs.

In meinem Bestand läuft ein Job über viele Repositories, mit einem Token und einem Signaturschlüssel. Ein Task aus Repository A hätte die Checkouts von B und C gesehen, kurz bevor pinup sie mit seiner Signatur committet. Das wollte ich nicht dem Betrieb überlassen.

03 — Der Preis

Nur auf Linux. Die Sandbox nutzt User-Namespaces. Wo es die nicht gibt oder ein Profil sie verbietet, verweigert pinup jeden Task und hält den Branch, statt ohne Schutz zu laufen.

Caches pro Repository. Ein gemeinsamer Paket-Cache hätte es einem Repository erlaubt, den Lock-Refresh des nächsten zu vergiften. Getrennte Caches kosten bei npm einige Sekunden pro Repository.

Das Netzwerk bleibt offen. Ein Paketmanager muss Pakete laden. Was der Job erreicht, erreicht auch der Task. Das ist die bewusst benannte Lücke.

Mehr Arbeit im Kern. Die Befehle für Lock-Refreshes setzt pinup selbst zusammen, mit Schaltern, die Skripte und Plugins der Paketmanager abschalten.

04 — Der Anlass

Zwei Vorfälle aus dem eigenen Bestand.

Der Befehl, der still wegfiel. Unter Renovate stand ein Befehl nicht auf der Liste erlaubter Befehle. Renovate übersprang ihn und pushte den Branch trotzdem. So fehlten wochenlang die Lock-Refreshes für eigene Pakete, ohne roten Job. pinup hält einen solchen Branch mit dem Grund taskRefused und nennt den Befehl.

Der Token unter falschem Namen. pinup filterte die Zugangsdaten der Plattform von Anfang an nach Namen. Trotzdem erreichte der Schreib-Token eine Zeit lang die Tasks in meinem Runner. Die Runner-Konfiguration hatte ihn unter anderen Namen an Tasks weitergereicht. Seitdem prüft pinup auch den Wert: Trägt eine Variable den Token unter irgendeinem Namen, startet der Task nicht.

05 — Die Durchsetzung

Häufige Fragen

Wie kommt ein Task dann an private Pakete?+

Über Lese-Zugangsdaten, die der Runner ausdrücklich freigibt: für Go-Module eine .netrc im temporären Home des Tasks, für Composer und npm Lese-Token als Variablen. Den Schreib-Token der Plattform enthält keine davon, und pinup prüft das am Wert.

Warum nicht einfach ein Container pro Task?+

In der Spezifikation ist das sogar die Standardvariante, mit einem per Digest gepinnten Image pro Task. Sie braucht aber Docker im Runner. In Produktion läuft die Variante ohne Container, mit der Sandbox im Prozess, und dort ist sie bewiesen.

Kann ich die Sandbox abschalten?+

Ja, mit PINUP_TASK_ISOLATION=off. Dann laufen Tasks ohne Isolation, und pinup sagt das bei jedem Lauf. Ohne diesen Schalter läuft kein Task ohne Isolation: Fehlt die Sandbox, verweigert pinup die Tasks und hält die Branches.

Fazit

Ein Task, der nur ausführt, kann weniger. Er kann aber auch weniger kaputt machen, und genau das ist der Punkt. Der Preis sind Linux, getrennte Caches und eine Lücke im Netzwerk, die offen benannt ist.

Nächste Woche geht es um die Stellen, an denen pinup bewusst nicht anders entscheidet: Kompatibilität zu Renovate. Die Übersicht aller Teile und die Begriffe, die öfter vorkommen, stehen im Auftakt der Serie.

Was sehen die Tasks in deinem Dependency-Runner? Ich prüfe es nach.

Ein Blick auf deinen Runner: welche Zugangsdaten Tasks erreichen, welche Befehle erlaubt sind und was ein Repository über das nächste erfährt.

Termin buchen →

Über den Autor

Photo of Kai Ole Hartwig.

Kai Ole Hartwig

Freelance DevSecOps consultant · OnlyOle Consulting

Programming since 2002 – self-taught, set up my own business with KO-Web in 2012. Over 100 projects, with a focus on security, performance, automation and quality. Today freelance: DevSecOps consulting, training and software development.