Wie kann ich Ports im Benutzermodus von QEMU ohne Konflikte weiterleiten?

Wie kann ich Ports im Benutzermodus von QEMU ohne Konflikte weiterleiten?

Ich verwende Linux unter QEMU als Teil einer Testsuite für ein Python-Skript, das einige privilegierte Vorgänge ausführt. Der Betrieb einer vollständigen virtuellen Maschine ist mir wichtig, weil:

  1. Ich möchte keinen Sudo-Zugriff benötigen, um Tests auszuführen
  2. später wird es einfacher sein, die Tests über verschiedene Distributionen und Kernel-Versionen hinweg durchzuführen
  3. Es ist weniger wahrscheinlich, dass mein Computer kaputt geht

Mein Ansatz besteht darin, die QEMU-VM mit einem Host-Port zu starten, der an SSH auf der VM weitergeleitet wird, das Skript über SSH zu senden und auszuführen, das Skript auszuführen und Dinge über das Remote-System zu bestätigen. Das Ärgerliche an diesem Setup sind der SSH-Port und die Ports für andere Netzwerkanwendungen.

Meine Frage:Wie kann ich Ports im Benutzermodus von QEMU ohne Konflikte weiterleiten?

Mein Ziel hier ist, mehrere Tests gleichzeitig ohne vorherige Einrichtung und ohne sudo ausführen zu können, wie oben erwähnt. Die Benutzermodus-Netzwerkfunktion in QEMU unterstützt Portweiterleitung, und wenn ich 0in der Hostfwd-Deklaration ( hostfwd=tcp:127.0.0.1:0-:22) den Host-Port übergebe, weist das Betriebssystem dynamisch einen zu. Genial! Das Problem dabei war, diesen Port im übergeordneten Prozess zuverlässig zu erhalten.

Ich mache das Äquivalent von:

#!/bin/sh
set -e

cd "$(mktemp -d)"

# Download cloud image.
image_base_url="https://cloud-images.ubuntu.com/releases/18.04/release/"
image_name="ubuntu-18.04-server-cloudimg-amd64.img"
image_url="$image_base_url$image_name"
curl --location -o linux.img $image_url

# Create SSH key.
ssh_key_path="$PWD/id_rsa"
ssh-keygen -t rsa -b 4096 -N "" -f "$ssh_key_path"

# Create cloud-init payload.
cat <<EOF > user_data
#cloud-config
password: ubuntu
chpasswd: { expire: False }
ssh_pwauth: True
ssh_authorized_keys:
- $(cat "${ssh_key_path}.pub")
EOF
cloud-localds user_data.img user_data

# Socket path for QMP.
qmp_socket="$PWD/qmp.sock"

# Start VM.
qemu-system-x86_64 \
    -drive file=linux.img,format=qcow2 \
    -drive file=user_data.img,format=raw \
    -netdev user,id=net0,hostfwd=tcp:127.0.0.1:0-:22 \
    -device rtl8139,netdev=net0 \
    -enable-kvm \
    -m 2G \
    -serial mon:stdio \
    -nographic \
    -smp 2 \
    -snapshot \
    -qmp unix:$qmp_socket,server,nowait \
    & \
;

# wait a bit, then test stuff here
# ...

Das Gastbetriebssystem wird problemlos gestartet.

Dinge, die ich versucht habe:

  • spielte ein wenig mitQMP, konnte aber keinen Befehl finden, der die Informationen lieferte

  • habe überlegt, einen zufälligen Port im übergeordneten Prozess zu binden SO_REUSEPORTund ihn dann für den untergeordneten Befehl anzugeben, aber QEMU verwendet dieses Flag nicht, also funktioniert es nicht

  • netstat -nalp | grep $child_pid, dies ist jedoch nicht verwendbar, wenn mehrere Ports weitergeleitet werden

  • einen zufälligen ungebundenen Port auswählen, versuchen, qemu damit zu starten, und es erneut versuchen, wenn es mit einer passenden Nachricht fehlschlägt Could not set up host forwarding rule 'tcp:...'. Das funktioniert, aber

    1. tritt mit größerer Wahrscheinlichkeit ein Fehler auf, wenn mehr Ports vorhanden sind
    2. kann andere Probleme verschleiern, für die es sich nicht lohnt, einen erneuten Versuch zu unternehmen

    Das ist ein akzeptabler Ersatz, aber ich hoffe auf eine einfachere Lösung.

Antwort1

Nach etwas mehr Recherche: QEMU stellt die Informationen über den info usernetBefehl zur Verfügung. So sieht die Ausgabe aus:

(qemu) info usernet
VLAN -1 (net0):
  Protocol[State]    FD  Source Address  Port   Dest. Address  Port RecvQ SendQ
  TCP[HOST_FORWARD]  13       127.0.0.1 38117       10.0.2.15    22     0     0
  UDP[236 sec]       24       10.0.2.15 35061   91.189.89.198   123     0     0
  UDP[204 sec]       26       10.0.2.15 60630   91.189.89.198   123     0     0

Es ist nicht über QMP verfügbar, aber es wurde ein Patch eingereicht, um ein Äquivalent hinzuzufügenquery-usernet Hiervor einiger Zeit.

In meinem Fall ist es unkompliziert, den Monitor verfügbar zu machen -monitor unix:$PWD/mon.sock,server,nowait, eine Verbindung herzustellen, den Befehl zu senden, die Ausgabe zu lesen und sie nach den gewünschten Werten zu analysieren.

Zusammenfassend lässt sich also sagen, wie Sie QEMU zum Testen von Netzwerkanwendungen verwenden können, ohne dass sudo oder andere Voraussetzungen (außer QEMU selbst) erforderlich sind und Portkonflikte auf dem Host vermieden werden:

  1. Erstellen Sie für jede Anwendung über die Kommandozeile ( -netdev) oder den Monitor ( netdev_add) eine Portzuordnung und geben Sie dabei 0den Host-Port an
  2. Stellen Sie es monitorals Unix-Socket für die nutzende Anwendung bereit.
  3. Führen Sie die Ausführung info usernetüber den Socket durch und extrahieren Sie den Quellport für jede Zuordnung. Dabei handelt es sich um den tatsächlichen Port, der vom Betriebssystem zugewiesen wurde.

verwandte Informationen