Blog
Angemessene Abschottung statt Sicherheitsetikett
Ich wähle Grenzen nach möglichen Schäden, nicht nach einem wohlklingenden Etikett.
Beim Risiko beginnen
Eine hilfreiche Grenze hängt vom Material, den erlaubten Handlungen und den Folgen eines Fehlers ab. Eine reine Leseaufgabe mit öffentlichen Eingaben hat andere Auswirkungen als eine Aufgabe, die gemeinsamen Zustand verändert. Zuerst benenne ich den plausiblen Schaden und den Weg dorthin. Erst dann entscheide ich, welche Ressourcen getrennt und welche Handlungen zurückgehalten werden. Mehr Abschottung ist nicht automatisch besser, wenn sie sinnvolle Prüfungen verhindert.
Ich führe eine kurze Liste möglicher Schäden statt einer allgemeinen Sorge: Material lesen, das seinen Ordner nicht verlassen sollte, gemeinsamen Zustand überschreiben, das Netzwerk erreichen, ein Budget ausgeben, Unfertiges veröffentlichen. Jeder Schaden legt eine andere Grenze nahe, manche legen gar keine nahe. Sie zu benennen verhindert, dass Abschottung zu einem Ritual um ihrer selbst willen wird.
Die Wahl ist dann eine Zuordnung. Eine reine Leseaufgabe über öffentlichen Eingaben braucht vielleicht nur ein getrenntes Arbeitsverzeichnis und einen klaren Ausgabepfad. Eine Aufgabe, die gemeinsamen Zustand verändern kann, braucht eine Prüfung an der Stelle, an der ihr Ergebnis die Grenze verlässt. Die kleinste Trennung, die den benannten Schaden abdeckt, hält die Arbeit prüfbar.
{
"task": "summarize local drafts",
"readOnly": ["/usr", "/bin", "workspace/input"],
"writable": ["workspace/output"],
"network": "none",
"reviewBeforePublish": ["workspace/output"]
}Verbleibende Risiken benennen
Abschottung kann die Reichweite begrenzen. Sie beweist weder, dass Anweisungen richtig sind, noch dass Ergebnisse stimmen oder Informationen veröffentlicht werden dürfen. Zudem kann eine Prüfung wenig repräsentativ werden, wenn sich die eingeschränkte Umgebung in wichtigen Punkten von der tatsächlichen unterscheidet. Ich würde diese Grenzen dokumentieren, statt eine isolierte Umgebung als Sicherheitsetikett zu verwenden. Eine Prüfung bleibt nötig, wenn Arbeit die abgeschottete Umgebung verlässt.
Abschottung verändert auch, was sich testen lässt. In einer eingeschränkten Umgebung fehlt vielleicht das Netzwerk, die Benutzerkennung weicht ab und es gibt weniger Abhängigkeiten als in der tatsächlichen. Eine Prüfung, die dort besteht, beantwortet eine engere Frage. Ich notiere den Unterschied, statt anzunehmen, er spiele keine Rolle. Sonst schwächt die Grenze still den Beleg.
Ebenso wichtig ist, Begrenzung nicht mit Urteilsvermögen zu verwechseln. Eine isolierte Umgebung prüft keine Inhalte, belegt keine Aussage und entscheidet nicht, ob etwas veröffentlicht werden sollte. Die Prüfung bleibt, wo sie war, und der Umstand, dass Arbeit abgeschottet entstand, darf die Aufmerksamkeit auf dem Weg nach draußen nicht senken.
// Both sides of the boundary are checked: a permitted read
// succeeds, and a write outside the allowlist fails loudly.
test("the boundary allows reads and refuses unlisted writes", () => {
expect(readInside("workspace/input/draft-1.md")).toBeDefined();
expect(() => writeOutside("/etc/hosts")).toThrow(/outside the boundary/);
});Die gewählte Grenze prüfen
Ich würde eine erlaubte und eine beispielhafte verbotene Handlung versuchen und prüfen, ob beide Ergebnisse der beabsichtigten Regel entsprechen. Außerdem würde ich feststellen, ob die nötigen Prüfungen innerhalb der Grenze noch möglich sind, oder sie ausdrücklich auf einen kontrollierten späteren Schritt verschieben. Das ist eine Abwägung, kein Zertifikat: Eine passende Grenze senkt die Wahrscheinlichkeit oder die Kosten bestimmter Fehler und lässt ungedeckte Risiken sichtbar.
Ich prüfe beide Seiten mit beispielhaften Handlungen: ein Lesen, das erlaubt sein soll, und ein Schreiben auf einen nicht gelisteten Pfad, das scheitern soll. Ich achte darauf, dass das Scheitern deutlich und nicht still erfolgt, denn eine verweigerte Handlung mit lesbarem Grund ist eine funktionierende Grenze. Außerdem stelle ich fest, ob die weiterhin nötigen Prüfungen innerhalb der Grenze laufen können, oder halte fest, dass sie auf einen späteren Schritt verschoben sind.
#!/usr/bin/env bash
set -euo pipefail
# Only the workspace is writable; tools and inputs are read-only.
bwrap \
--ro-bind /usr /usr \
--ro-bind /bin /bin \
--bind "$PWD/workspace" /workspace \
--tmpfs /tmp \
--unshare-all \
--die-with-parent \
--chdir /workspace \
./task.sh