Verwenden von ssh und sed in einem Python-Skript mit os.system ordnungsgemäß

Sep 12 2020

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

5 CharlesDuffy Sep 13 2020 at 01:31

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 von HOMEnicht von sedselbst erfolgt, sondern von der Shell, die sie aufruft ; da wir überhaupt keine lokale Shell aufrufen, mussten wir auch verwenden os.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 .