• This forum has a zero tolerance policy regarding spam. If you register here to publish advertising, your user account will be deleted without further questions.

Bashscript sicher als Root ausführen

sbr2d2

Registered User
Hallo alle zusammen,
ich möchte auf einem Testserver für eine Testumgebung ein Script haben, in welchen Optional aus einer Quelle die Inhalte der Testumgebung, neu angelegt werden können. Das Script soll später von den Programmierern genutzt werden, ohne das diese Zugriff auf Rootebene haben sollen.
Erst dachte ich an ein Textfile, in welchen verschiedene .sh Scripte aufgerufen werden, welches über einen Cronejob minütlich angesteuert wird. Das hätte aber zufolge das man dort beliebigen Code eintragen könnte und dann das System angreifbar wird. Da das File später in einem gesicherten Webordner liegen soll, scheidet der Weg also aus. Ich denke nun an folgendes. Ein File im Webordner welches ungefähr so ausschaut.
Code:
## Hier soll stehen was möglich ist
## damit die Leute später wissen
## was sie zu tun haben
option web=off
option db=off
option web-db=on
option server-reboot=off

Die Programmierer sollen nun die Möglichkeit haben das Textfile um die jeweilige Option zu ändern. Danach soll das Script das Textfile auslesen und anhand der gewählten Option ein Script x ausführen. Ist das so machbar und wenn ja wie gehe ich das am besten an. Ziel ist es, das man von dem Ort wo das Textfile liegt, nicht x-beliebigen Code einschleusen kann.
Für ein paar Denkanstöße oder andere Lösungen bin ich wie immer dankbar :)
 
Das File per grep/sed/etc Option für Option parsen und ausschlisslich auf die zugelassenen Werte (on/off) prüfen und alles andere rigeros verwerfen.
Und selbst das kann schiefgehen, wenn die RegEx nicht hundertprozentig passen...

Etwas sicherer wäre eine Datenbank (SQLite reicht hier völlig) mit einfachen BOOL-Feldern.
 
Vielen Dank,
ich sehe ich muss unbedingt noch mal die Grundlagen durch gehen. Das was ich bisher gebraucht habe, waren simple Dateioptionen wie Datanbankdump erstellen, den Webspace sichern, per ssh auf den Backupserver schieben und andere kleine Spielereien die mir Arbeit abnehmen. Das hier wird etwas komplizierter, aber das sollte am Ende nicht das Problem sein.
 
Wenn du Dateien parsen willst, dann erfinde nicht dein eigenes Format, sondern verwende Formate wie z.B. JSON, XML, YAML oder ini.
Um die Syntaxprüfung kümmert sich die Implementierung. Bevor du jetzt aber losrennst und direkt einfach irgend eine Implementierung verwendest, informiere dich auch, welche Gefahren von diesen Spezifikationen ausgehen. Umgangssprachlich ausgedrückt: Du brauchst einen "dummen" Parser, der nicht zuviel kann. Beispiele findest du z.B. bei Ubuntu Server. Dort wurde z.B. auf netplan gesetzt, dass sich mit YAML-Dateien konfigurieren lässt. Vom Syntax her finde ich YAML noch am besten.

Die Lösung mit SQLlite ist noch besser, da du dann mit definierten Datenstrukturen arbeiten kannst und ungültige Einträge von vorne herein ausgeschlossen werden können.
 
Die Lösung mit SQLlite ist noch besser, da du dann mit definierten Datenstrukturen arbeiten kannst und ungültige Einträge von vorne herein ausgeschlossen werden können.
Dazu bräuchte es einer SQL-Umgebung mit Rechtevergabe (INSERT only). Bei SQLite kann jeder Benutzer die Datenbank umbauen, das Root-Skript müsste also trotzdem bei jedem Lesevorgang eine Schemaüberprüfung oder Inhaltsüberprüfung durchführen.

Für solche Einsatzzwecke geeignet finde ich SQL-Server (bspw Mysql) oder schema-basierte Konfigurationsdateien. In XML oder Jsonschema bspw kann man reguläre Ausdrücke in der Validierungsdatei hinterlegen. Solange kein direkter Angriff auf den Parser möglich ist und die Schemadatei korrekt ist, wird somit nie eine ungültige Konfiguration geladen werden können.

Bei aktiverer Kommunikation oder Dringlichkeit der Befehle könnte man auch über Queue-Worker nachdenken. Auch Queue-Nachrichten kann man gut filtern und validieren, Vorteil wäre eine nahezu Echtzeitausführung.
 
Das Problem würde ich mit den Berechtigungen des Dateisystems lösen. Der Client kann ja mit sudo laufen.
Richtig konfiguriert, kannst du den Programmcode nicht ändern, sondern nur ausführen und das mit einem anderen user.

Wenn du was richtig schönes haben willst, hier: https://fastapi.tiangolo.com/
XML meide ich wie die Katze das Wasser.

Manchmal ist es aber auch ziemlich bescheuert was wir machen. Auf jede Anwendung eine RESP-API zu basteln, ist auch nicht immer die Lösung.
 
  • Haha
Reactions: d4f
Das Problem würde ich mit den Berechtigungen des Dateisystems lösen. Der Client kann ja mit sudo laufen.
"chmod u+s" wäre meine to-go Lösung bei "bekannt sicherer" Software - die Software muss dabei allerdings als binary vorliegen und nicht als Skript. In allen anderen Fällen, insbesondere wenn man die Pappenheimer nicht kennt (oder zu gut kennt...), würde ich definitiv über cronjob mit Job-Queues empfehlen, also Dateien oder SQL-Datenbanken.

XML meide ich wie die Katze das Wasser.
Es ist weder schön noch mag irgendwer es ausser eventuell Folterspezialisten. Aber, es erfüllt seine Zwecke exzellent und ist hochgradig anpassbar. Mit Json-Schema und ähnlichem stehen ja aber auch Alternativen für modernere Zeiten am Start =)

Wenn du was richtig schönes haben willst, hier: https://fastapi.tiangolo.com/
Python ist sowas von out. Heute benutzt man NodeJS oder Golang. Etwas mehr bleeding-edge wären Kotlin und Consorten. Eine etablierte Sprache mit etabliertem Ökosystem ist doch sowas von langweilig :p

Auf jede Anwendung eine RESP-API zu basteln, ist auch nicht immer die Lösung.
Mit entsprechenden Toolkits aber nicht zwingend verkehrt. API-first ist definitiv eine meiner bevorzugtesten Herangehensweisen und es ist doch deutlich flexibler einsetzbar, insbesondere wenn man plötzlich noch Docker, Isolation oder gar horizontale Skalierbarkeit in den Mix werfen will. Eine Grundübung ist da immer praktisch. Gut, hier ist das aber trotzdem vermutlich overkill.
 
Was soll denn der konkrete Job des Dings sein? Sprich "was kommt am Ende der gesamten Aktion dabei raus"?
(Vermutung: Sieht für mich so aus, als ob damit Entwickler für sich Testsysteme mit def. Konfiguration aufsetzen können sollen?)
 
Python ist sowas von out. Heute benutzt man NodeJS oder Golang. Etwas mehr bleeding-edge wären Kotlin und Consorten. Eine etablierte Sprache mit etabliertem Ökosystem ist doch sowas von langweilig :p
Man könnte ja Perl 6 nehmen (die wollen die Sprache umbenennen), aber da kommt ja wieder das Problem mit den Scripts zum Vorschein.
Zur Not macht man sich noch ein kleines C-Programm, dass dann das Script mit erhöhten Rechten ausführt. <superevilemoticon>

Mit XML kann man auch Rekursion machen. Ist schon witzig Server abzuschießen, die XML komplett interpretieren.

PS: Beim aktuellen Projekt hab ich mit der API ziemlich früh angefangen. Das diente mir aber auch als Dokumentationshilfe.
PPS: Ich sehe es schon kommen, wenn das Dingen fertig ist, kann es auch Kaffee kochen.
Computer an die Macht, macht Kaffee, macht Brötchen, macht sauber :-D
 
Also ich sehe man kann viel machen, aber dieser Anwendungsfall erfordert glaube ich nicht so viel Aufwand. Also kurz zu dem was gebraucht wird, bzw wo es angewendet werden soll.
Es ist ein Testserver an dem 1-3 Programmierer arbeiten. Sollte es mal vorkommen, das Jemand die Datenbanken abschießt oder ein aktueller Datensatz benötigt wird, so sollen die das ohne großen Aufwand auch in meiner Abwesenheit tun können. Dazu habe ich bereits fertige Scripte, welche die Datenbanken samt User löschen und aus einem Backup neu anlegen. Oder den Webordner komplett löschen und aus einem Backup wieder herstellen. Auch ist es möglich, ein Backup vom Produktivsystem einzuspielen um dann Tagesaktuell zu arbeiten.
Da die Programmierer keinen Zugriff per Konsole bekommen sollen, möchte ich nun einen Passwort geschützten Bereich anlegen, in welchem sich eine Datei befindet, welche sie ändern können, je nachdem was sie machen wollen. Diese wird dann jede Minute ausgelesen und dann vom Root ausgeführt. Deswegen such ich nach einer Möglichkeit wie man das relativ sicher hin bekommt. Momentan über ich dran, die ganzen Optionen in Variablen zu packen und mit if then zu verarbeiten. Aber ich muss da erst noch etwas üben, bzw lese ich mich gerade in das ganze Zeugs rein.
Kann bestimmt nicht schaden, wenn man das mal alles verinnerlicht.
Vielen Dank für die vielen Denkanstöße :)
 
In dem Fall arbeitet man meistens mit Cronjob und Mysql-Datenbank.
In der Datenbank hat man dazu priniziell eine Tabelle mit 3 Spalten: "id" (autoincrement), "task" und "details".
Der Cronjob ruft die Inhalte ab und führt die Tasks über if/else (oder "switch") Anweisung ab.
Davor kann man dann eine simple Web-GUI setzen welche die Programmierer verwenden um Tasks an zu fordern,

In der simpelsten Basis-Version
* lädt der Cron den ersten Task aus der Datenbank
* löscht ihn in der Datenbank (verhindert potentielle Endlosschleifen)
* führt ihn aus

Etwas erweitern sollte/könnte man es
* es wird nur der neuste Task einer Art berücksichtigt (warum 3x hintereinander ein backup einspielen)
* über flock() und die Prozess-ID überprüfen ob nicht noch ein Cron läuft

Es ist durchaus simpel solange man einen Grundsatz berücksichtigt: never trust user data. Also nie bspw "include $row["task"].php" sondern schön sauber über switch-case oder if/else.
 
WOW und alle schiessen wieder mit Kanonen auf Spatzen...

Hinter HTTP-BASIC-AUTH packt man ein HTML-Formular (PHP-Script) mit n-Checkboxen für die gewünschten Optionen und füttert damit die SQLite, Cronjob liest die SQLite aus und triggert die entsprechenden Tasks, fertig.


Übersehe ich irgendetwas?
 
kommt halt immer drauf an, was man schon hat - und da bei uns schon Jenkins läuft, warum dann nicht verwenden? Zugriff auf die Testsysteme hat der eh, die Entwickler haben Zugriff auf den Jenkins -> fertig.

... aber ja, wenn's nur um ein System geht und sonst keine Infrastruktur dafür da ist - QnD.
 
Hinter HTTP-BASIC-AUTH packt man ein HTML-Formular (PHP-Script) mit n-Checkboxen für die gewünschten Optionen und füttert damit die SQLite, Cronjob liest die SQLite aus und triggert die entsprechenden Tasks, fertig.
Das wird es vermutlich werden. Von den Programmieren lass ich mir die php Seite machen und um die andere Seite kümmer ich mich. Dann wird einfach von oben nach unten der Stapel abgelesen und wenn eine Option zutrifft ausgeführt. Sobald ein Script gestartet wurde, lasse ich die php Seite löschen damit die nicht 2 mal was auswählen können, und wenn das Script fertig ist, wird die php wieder neu geschrieben. Ist wie gesagt eine einmalige und individuelle Aktion die so nirgendwo wieder zum Einsatz kommen wird.
 
Statt das PHP-Script zu löschen, einfach in der Datenbank nachschauen, ob noch ein Job ansteht und falls ja, einfach keine weiteren Jobs mehr annehmen bzw. in die Datenbank eintragen. Du kannst mit der Datenbank ja sogar eine Queue aufbauen, so dass z.B. die Jobs in der Reihenfolge des Anforderns abgearbeitet werden.
Wenn du das Script einfach nur löscht, hast du eine Race-Condition von bis zu einer Minute (sofern dein Cronjob minütlich läuft, ansonsten entsprechend länger), bevor dein Cronjob das PHP-Script löscht - das ist schon eine relativ lange Zeit. Daher nicht löschen, sondern per Abfrage in der PHP-Datei abfangen
 
Vielen Dank für die ganzen Vorschläge. Die Datenbank steht und das Script was die Datenbank abfragt läuft auch einwandfrei durch. Jetzt warte ich nur noch auf die Seite und dann sollte es laufen.
 
Back
Top