So vermeiden Sie Befehle auf dem falschen Server

Machen Sie Produktion unverwechselbar: Setzen Sie auf jedem Produktionsserver einen roten Prompt in PS1 und färben Sie auch die tmux-Statusleiste und das Terminal. Bauen Sie dann Gewohnheiten auf, die den Ausrutscher abfangen: zuerst einen Dry Run, einen Glob vor dem Löschen auflisten, in Skripten "${DIR:?}" schreiben und Bracketed Paste an lassen. Das verringert Fehler. Nichts davon macht sie unmöglich, Backups bleiben also wichtig.

Lieber ein Schalter als ein Prompt auf jedem Server? In Wardterm markieren Sie einen Server oder eine Gruppe einmal als Produktion. Zeigen

Aktualisiert am . Prompt, rm-Schutz, Dry Runs, tmux-Statusfarben und Bracketed Paste wurden unter Debian 13 in einem Wegwerf-Container (bash 5.2, GNU coreutils 9.7, tmux 3.5) per SSH von macOS aus ausgeführt.

Ein roter Prompt auf Produktion

Die meisten Verwechslungen des Servers passieren, weil zwei Terminals gleich aussehen. Der Prompt ist das, was Sie kurz vor dem Drücken von Return lesen, ändern Sie ihn also dort. Fügen Sie dies am Ende von ~/.bashrc auf dem Server hinzu. Es muss ans Ende, weil die .bashrc der Distribution PS1 früher setzt und sonst überschreiben würde:

PS1='\[\e[1;37;41m\] PRODUCTION \[\e[0m\] \u@\h:\w\$ '

Das zeigt PRODUCTION in Weiß auf Rot, danach den normalen Benutzer, Host und Ordner. \[ und \] sagen bash, dass die Farbcodes keine Breite haben, lange Befehle werden also weiterhin richtig umbrochen.

Tragen Sie dieselbe Zeile in /root/.bashrc ein: In unserem Test zeigte sudo -i das rote Etikett nur, weil auch diese Datei es enthielt. Für Staging nehmen Sie Gelb mit \e[1;30;43m. Es wirkt nur für interaktive bash, andere Shells brauchen ihre eigene Einstellung.

iPhone-Terminal mit einem Production-Etikett im Titel, das vor einem rekursiven erzwungenen Löschen einen Dialog Run this on production? mit den Schaltflächen Cancel und Run it zeigtiPhone-Terminal mit einem Production-Etikett im Titel, das vor einem rekursiven erzwungenen Löschen einen Dialog Run this on production? mit den Schaltflächen Cancel und Run it zeigt

tmux-Leiste und Terminal einfärben

Wenn Sie auf dem Server tmux nutzen, färben Sie dessen Statusleiste, dort in ~/.tmux.conf:

set -g status-style bg=red,fg=white,bold
set -g status-left " PRODUCTION "

Ob es geladen wurde, prüfen Sie mit tmux show -g status-style.

Auf Ihrem eigenen Rechner können die meisten Terminal-Apps einen Host in einem eigenen Profil öffnen. Geben Sie Produktion ein Fenster mit dunkelrotem Hintergrund. Sie können die Farbe auch von Ihrer SSH-Konfiguration setzen lassen, in Terminals, die die Sequenz OSC 11 unterstützen:

Host prod-*
    PermitLocalCommand yes
    LocalCommand printf '\033]11;#400000\007'

Die Farbe geht beim Abmelden nicht von selbst zurück, und die Unterstützung ist unterschiedlich, probieren Sie es also zuerst in Ihrem Terminal aus.

Bei vielen Servern helfen klare Namen und Gruppen so viel wie Farben: siehe viele SSH-Server verwalten.

Dateien sicher löschen

GNU rm verweigert den schlimmsten Fall bereits:

$ rm -rf /
rm: it is dangerous to operate recursively on '/'
rm: use --no-preserve-root to override this failsafe

Das schützt nur das buchstäbliche /. Bei rm -rf /* erweitert die Shell zuerst den Stern, rm erhält also /bin, /etc und den Rest, und die Prüfung greift nicht. Ein verirrtes Leerzeichen in rm -rf /var/app /* bewirkt das aus Versehen. Diese Gewohnheiten helfen mehr:

  • Erst ansehen, dann löschen. Setzen Sie echo davor, um zu sehen, wozu ein Glob erweitert wird: echo rm -rf /tmp/app/*. Entfernen Sie dann das echo.
  • Mit && verbinden, nicht mit ;. Bei cd /var/app/tmp && rm -rf ./* stoppt ein fehlgeschlagenes cd dort. Mit ; läuft das Löschen in dem Ordner, in dem Sie gerade sind.
  • Variablen in Skripten absichern. rm -rf "${DIR:?}/" bricht mit einem Fehler ab, wenn DIR leer ist, wo ein einfaches "$DIR/" zu / würde. Auch set -u am Anfang eines Skripts stoppt bei nicht gesetzten Variablen.
  • Erst verschieben, später löschen. mv in einen Ablageordner lässt sich rückgängig machen. rm nicht.
  • Nutzen Sie rm -I. Es fragt einmal nach, bevor mehr als drei Dateien oder rekursiv gelöscht werden. Wer nichts antwortet, löscht nichts, wie in unserem Test.
  • Nutzen Sie --preserve-root=all. Es lehnt auch einen Ordner auf einem anderen Dateisystem als sein Elternordner ab, würde also eine eingehängte Platte ablehnen: rm: skipping '/dev/shm', since it's on a different device.

Die letzten beiden können in einen Alias auf dem Server, alias rm='rm -I --preserve-root=all'. Aliase gelten nur für interaktive Shells, nicht für Skripte oder für sudo rm, und diese Optionen gibt es nur bei GNU.

Zuerst ein Dry Run

BefehlVorschau
rsyncrsync -an --delete --itemize-changes src/ dst/
findzuerst find . -name '*.tmp' -print, dann -print durch -delete ersetzen
git cleangit clean -n
apt-getapt-get -s remove package

Bei SQL führen Sie zuerst BEGIN; aus, dann die Anweisung, prüfen die Zeilenzahl und schließen mit COMMIT; oder ROLLBACK; ab. Das gilt nicht für Anweisungen, die Ihre Datenbank selbst committet. MySQL tut das zum Beispiel bei Tabellenänderungen.

Mehrere Zeilen einfügen

In einem Terminal wirkt ein Zeilenumbruch in eingefügtem Text wie Return, ein Einfügen kann also Befehle ausführen, bevor Sie sie gelesen haben. Bracketed Paste löst das. Das Terminal markiert den eingefügten Text, und die Shell fügt ihn ein, ohne etwas auszuführen. Wir haben zwei echo-Zeilen über tmux in bash eingefügt:

  • Mit eingeschalteter Funktion standen beide Zeilen am Prompt, und nichts lief bis zum Return.
  • Mit ausgeschalteter Funktion lief die erste Zeile sofort nach dem Einfügen.

In aktuellen bash-Versionen ist sie standardmäßig an. Prüfen Sie es mit diesem Befehl in einer interaktiven Shell:

bind -v | grep bracketed-paste

Steht dort off, fügen Sie set enable-bracketed-paste on zu ~/.inputrc hinzu. Es hilft nur dort, wo das Programm darum bittet. Beim Einfügen in ein Programm, das es nicht tut, oder in eine alte Shell, läuft es weiter Zeile für Zeile.

Was das alles nicht kann

Eine Prompt-Farbe hilft nur, wenn Sie hinsehen. Sie schützt kein Skript, keinen Cronjob und keinen Befehl, der mit ssh server 'command' läuft und nie einen Prompt zeigt. Aliase lassen sich umgehen, und nichts davon stoppt einen Fehler in den Argumenten eines Befehls, den Sie ausführen wollten.

Behalten Sie also die größeren Sicherungen: getestete Backups, getrennte Konten und Schlüssel für Produktion und möglichst wenig sudo für jede Person. Für enge Regeln siehe sudo über SSH.

Fragen

Wie mache ich den Bash-Prompt auf einem Produktionsserver rot?

Setzen Sie PS1 mit einem Farbcode für roten Hintergrund ans Ende von ~/.bashrc auf diesem Server, und in /root/.bashrc für sudo -i. Ans Ende, damit das PS1 der Distribution es nicht überschreibt.

Funktioniert rm -rf / unter Linux noch?

GNU rm verweigert das wörtliche /, außer mit --no-preserve-root. rm -rf /* schützt es nicht, weil die Shell den Stern vorher in alle Ordner der obersten Ebene erweitert.

Wie sehe ich vorher, was ein Befehl löschen würde?

Setzen Sie echo davor, um zu sehen, wozu ein Muster wird, oder nutzen Sie den Probelauf: rsync -n, git clean -n oder find mit -print vor -delete.

Quellen

Mehr zu Server absichern

Alle Anleitungen