Verwenden von ssh und sed in einem Python-Skript mit os.system ordnungsgemäß
Ich versuche , einen SSH - Befehl in einem Python - Skript ausgeführt wird unter Verwendung os.systemein hinzuzufügen 0in einem Remote - Server am Ende eines vollständig angepassten String mit sshund sed.
Ich habe eine Datei nodelistauf einem Remote-Server aufgerufen , deren Liste so aussieht.
test-node-1
test-node-2
...
test-node-11
test-node-12
test-node-13
...
test-node-21
Ich möchte sed verwenden, um die folgende Änderung vorzunehmen, ich möchte suchen test-node-1, und wenn eine vollständige Übereinstimmung gefunden wird, möchte ich am Ende eine 0 hinzufügen, muss die Datei am Ende so aussehen.
test-node-1 0
test-node-2
...
test-node-11
test-node-12
test-node-13
...
test-node-21
Wenn ich jedoch den ersten Befehl ausführe,
hostname = 'test-node-1'
function = 'nodelist'
os.system(f"ssh -i ~/.ssh/my-ssh-key username@serverlocation \"sed -i '/{hostname}/s/$/ 0/' ~/{function}.txt\"")
Das Ergebnis wird so,
test-node-1 0
test-node-2
...
test-node-11 0
test-node-12 0
test-node-13 0
...
test-node-21
Ich habe versucht, dem Befehl wie folgt ein \ b hinzuzufügen:
os.system(f"ssh -i ~/.ssh/my-ssh-key username@serverlocation \"sed -i '/\b{hostname}\b/s/$/ 0/' ~/{function}.txt\"")
Der Befehl funktioniert überhaupt nicht.
Ich muss den Knotennamen manuell eingeben, anstatt eine Variable wie diese zu verwenden.
os.system(f"ssh -i ~/.ssh/my-ssh-key username@serverlocation \"sed -i '/\btest-node-1\b/s/$/ 0/' ~/{function}.txt\"")
damit mein Befehl funktioniert.
Was ist los mit meinem Befehl, warum kann ich nicht tun, was ich will?
Antworten
Dieser Code weist schwerwiegende Sicherheitsprobleme auf. Um sie zu reparieren, muss sie von Grund auf neu entwickelt werden. Machen wir das hier:
#!/usr/bin/env python3
import os.path
import shlex # note, quote is only here in Python 3.x; in 2.x it was in the pipes module
import subprocess
import sys
# can set these from a loop if you choose, of course
username = "whoever"
serverlocation = "whereever"
hostname = 'test-node-1'
function = 'somename'
desired_cmd = ['sed', '-i',
f'/\\b{hostname}\\b/s/$/ 0/',
f'{function}.txt']
desired_cmd_str = ' '.join(shlex.quote(word) for word in desired_cmd)
print(f"Remote command: {desired_cmd_str}", file=sys.stderr)
# could just pass the below direct to subprocess.run, but let's log what we're doing:
ssh_cmd = ['ssh', '-i', os.path.expanduser('~/.ssh/my-ssh-key'),
f"{username}@{serverlocation}", desired_cmd_str]
ssh_cmd_str = ' '.join(shlex.quote(word) for word in ssh_cmd)
print(f"Local command: {ssh_cmd_str}", file=sys.stderr) # log equivalent shell command
subprocess.run(ssh_cmd) # but locally, run without a shell
Wenn Sie dies ausführen (mit Ausnahme von subprocess.runam Ende, für das ein echter SSH-Schlüssel, ein Hostname usw. erforderlich wäre), sieht die Ausgabe folgendermaßen aus:
Remote command: sed -i '/\btest-node-1\b/s/$/ 0/' somename.txt
Local command: ssh -i /home/yourname/.ssh/my-ssh-key whoever@whereever 'sed -i '"'"'/\btest-node-1\b/s/$/ 0/'"'"' somename.txt'
Das ist die richtige / gewünschte Ausgabe; Die lustige '"'"'Redewendung ist, wie man ein wörtliches einfaches Anführungszeichen sicher in eine einfache Zeichenfolge in einer POSIX-kompatiblen Shell einfügt.
Was ist unterschiedlich? Viele:
- Wir generieren die Befehle, die wir als Arrays ausführen möchten , und lassen Python diese Arrays bei Bedarf in Zeichenfolgen konvertieren. Dies vermeidet Shell-Injection-Angriffe, eine sehr häufige Klasse von Sicherheitslücken.
- Da wir selbst Listen erstellen, können wir die Art und Weise ändern, in der wir die einzelnen zitieren: Wir können f-Strings verwenden, wenn dies angemessen ist, rohe Strings, wenn dies angemessen ist usw.
- Wir werden nicht
~an den Remote-Server weitergeleitet: Es ist redundant und unnötig, da dies~der Standardort für den Start einer SSH-Sitzung ist. und die Sicherheitsvorkehrungen, die wir verwenden (um zu verhindern, dass Werte von einer Shell als Code analysiert werden), verhindern, dass sie Auswirkungen haben (da das Ersetzen~durch den aktiven Wert vonHOMEnicht vonsedselbst erfolgt, sondern von der Shell, die sie aufruft ; da wir überhaupt keine lokale Shell aufrufen, mussten wir auch verwendenos.path.expanduser, um das~In~/.ssh/my-ssh-keyzu ehren). - Da wir keine rohe Zeichenfolge verwenden, müssen wir die Backslashes verdoppeln,
\bum sicherzustellen, dass sie von Python als wörtlich und nicht als syntaktisch behandelt werden. - Entscheidend ist, dass wir Daten niemals in einem Kontext übergeben, in dem sie von einer lokalen oder Remote-Shell als Code analysiert werden können .