Jeder hatte sicher schon einmal diese “mal eben etwas machen”-Momente. Momente in denen aus einer Kleinigkeit ein tagesfüllendes Problem wird. Vor einiger Zeit wollte ich eine Änderungen an unserer Webserver-Konfiguration vornehmen und war mir absolut sicher, dass ich ein Backup der Datei habe.
Es gab natürlich kein Backup. Dafür viele Stunden der Recherche, des Zitterns, dass der Server nicht neustartet, des explorativen Programmierens und am Ende den einen Moment des Erfolgs.
Etwas anders als ein Apache, lädt der NGINX seine Konfiguration komplett in den Speicher. Das hat ein paar Nachteile, auf die ich hier nicht näher eingehen möchte, aber auch einen Vorteil: Die Konfiguration im Speicher ist, solange der Webserver läuft, nicht neugestartet oder neu geladen wurde, auf dem Stand des letzten Neustarts und man kann sie auslesen.
Mithilfe vom GNU debugger kann man den Speicher einzelner Prozesse zu Laufzeit mit einem kleinen Skript auslesen und wegschreiben.
pid=12598
cat /proc/$pid/maps | awk '$6 !~ "^/" {split ($1,addrs,"-"); print "dump memory /tmp/nginx_dump/mem_" addrs[1] " 0x" addrs[1] " 0x" addrs[2] ;}END{print "quit"}' > gdb-commands
gdb -p $pid -x gdb-commands
Dieses kleine Skript braucht lediglich die Prozess-Id, des Prozesses der ausgelesen werden soll. In meinem Fall der NGINX Hauptprozess. Dann schreibt es die Speicherbereiche, die genutzt werden als Kommando für gdb in die Datei gdb-commands und führt sie am Ende aus. Hier die Liste meiner verwendeten Speicherbereiche
dump memory /tmp/nginx_dump/mem_41f85000 0x41f85000 0x41fa5000
dump memory /tmp/nginx_dump/mem_557986c39000 0x557986c39000 0x557986c59000
dump memory /tmp/nginx_dump/mem_557987fb4000 0x557987fb4000 0x557988202000
dump memory /tmp/nginx_dump/mem_557988202000 0x557988202000 0x55798832f000
dump memory /tmp/nginx_dump/mem_7f3209091000 0x7f3209091000 0x7f3209093000
dump memory /tmp/nginx_dump/mem_7f320993b000 0x7f320993b000 0x7f3209b8b000
dump memory /tmp/nginx_dump/mem_7f320a336000 0x7f320a336000 0x7f320a33a000
dump memory /tmp/nginx_dump/mem_7f320c0d1000 0x7f320c0d1000 0x7f320c0d2000
dump memory /tmp/nginx_dump/mem_7f320c462000 0x7f320c462000 0x7f320c466000
dump memory /tmp/nginx_dump/mem_7f320cc73000 0x7f320cc73000 0x7f320cc74000
dump memory /tmp/nginx_dump/mem_7f320e156000 0x7f320e156000 0x7f320e158000
dump memory /tmp/nginx_dump/mem_7f32104a8000 0x7f32104a8000 0x7f32104ac000
dump memory /tmp/nginx_dump/mem_7f3211950000 0x7f3211950000 0x7f321195a000
dump memory /tmp/nginx_dump/mem_7f321217b000 0x7f321217b000 0x7f3212181000
dump memory /tmp/nginx_dump/mem_7f32125a4000 0x7f32125a4000 0x7f32125a6000
dump memory /tmp/nginx_dump/mem_7f3212b75000 0x7f3212b75000 0x7f3212b79000
dump memory /tmp/nginx_dump/mem_7f32131d5000 0x7f32131d5000 0x7f32131d8000
dump memory /tmp/nginx_dump/mem_7f32138bc000 0x7f32138bc000 0x7f32138ea000
dump memory /tmp/nginx_dump/mem_7f3213b03000 0x7f3213b03000 0x7f3213b07000
dump memory /tmp/nginx_dump/mem_7f3213f23000 0x7f3213f23000 0x7f3213f29000
dump memory /tmp/nginx_dump/mem_7f3213f32000 0x7f3213f32000 0x7f3213f33000
dump memory /tmp/nginx_dump/mem_7ffde6890000 0x7ffde6890000 0x7ffde68b1000
dump memory /tmp/nginx_dump/mem_7ffde6913000 0x7ffde6913000 0x7ffde6916000
dump memory /tmp/nginx_dump/mem_7ffde6916000 0x7ffde6916000 0x7ffde6918000
dump memory /tmp/nginx_dump/mem_ffffffffff600000 0xffffffffff600000 0xffffffffff601000
quit
Das Ergebnis ist eine Vielzahl von Dateien im Ordner /tmp/nginx_dump/, die jeweils den Inhalt des Speicherbereichs enthalten. Viele der Zeilen sind binär kodiert, zwischen den binären Teilen gibt es Klartext. Da ich wusste wonach ich in etwa suche, war grep das Mittel der Wahl, um nicht alle Dateien händisch zu durchsuchen: grep 'server {' ./*
Die Liste der Treffer war überschaubar
root@Website:/tmp/nginx_dump# grep 'server {' ./*
Binary file ./mem_557987fb4000 matches
Binary file ./mem_55be6952d000 matches
Zwischen den binären Teilen wurde ich schnell fündig und konnte die gesamte Konfiguration wiederherstellen.
^@^@^@^@^@^@^@ ^ ^E^ yU^@^@^@^B^@^@^@^@^@^@^@^P^@^@^@^@^@^@^@^D^@^@^@^@^@^@^D^@^@^@^@^@^@^@^@ ^@^@^@^@^@^@` ^@^@^@^@^@^@^A^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@$
$@^@^@^@^ ^ ^C^ yU^@^@^ ^ ^C^ yU^@^@ ^ ^C^ yU^@^@^F^@^@^@^@^@^@^@server {
listen 80;
listen [::]:80;
server_name www.wamoco.de;
include /etc/nginx/snippets/acme-challenge.conf;
location / {
return 301 https://www.wamoco.de$request_uri;
}
}
[...]
Also keine Angst, wenn ihr eure Konfiguration ohne Backup verändert. Solange der Server läuft, ist sie noch nicht verloren und lässt sich in wenigen Augenblicken wiederherstellen. Nichtsdestotrotz lohnt es sich die Konfigurationen unter Versionskontrolle zu haben oder noch besser, Server mit beispielsweise Ansible oder Puppet zu provisionieren. Das erspart einem am Ende eine solche Zitterpartie.