![Como posso encaminhar portas no modo de usuário QEMU sem conflitos?](https://rvso.com/image/756237/Como%20posso%20encaminhar%20portas%20no%20modo%20de%20usu%C3%A1rio%20QEMU%20sem%20conflitos%3F.png)
Estou executando o Linux no QEMU como parte de um conjunto de testes para um script Python que realiza algumas operações privilegiadas. Executar uma máquina virtual completa é importante para mim porque:
- Não quero exigir acesso sudo para executar testes
- mais tarde será mais fácil executar os testes em diferentes distribuições e versões de kernel
- Tenho menos probabilidade de quebrar meu computador
Minha abordagem é iniciar a VM QEMU com uma porta host encaminhada para SSH na VM, enviar e executar o script por SSH, executar o script e afirmar coisas sobre o sistema remoto. O irritante dessa configuração é a porta SSH e portas para outros aplicativos de rede.
Minha pergunta:Como posso encaminhar portas no modo de usuário QEMU sem conflitos?
Meu objetivo aqui é poder executar vários testes simultaneamente sem qualquer configuração prévia e sem sudo, conforme mencionado acima. A rede de modo de usuário no QEMU oferece suporte ao encaminhamento de porta e, quando passo 0
pela porta do host na declaração hostfwd ( hostfwd=tcp:127.0.0.1:0-:22
), o sistema operacional aloca uma dinamicamente. Incrível! O problema é obter essa porta de forma confiável no processo pai.
Estou fazendo o equivalente a:
#!/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
# ...
O sistema operacional convidado funciona bem.
Coisas que tentei:
brinquei um pouco comQMP, mas não foi possível encontrar um comando que fornecesse as informações
considerou vincular uma porta aleatória no processo pai
SO_REUSEPORT
e especificá-la para o comando filho, mas o QEMU não usa esse sinalizador, portanto não funcionanetstat -nalp | grep $child_pid
, mas isso não poderá ser usado ao encaminhar várias portasescolhendo uma porta não vinculada aleatória, tentando iniciar o qemu com ela e tentando novamente se falhar com uma mensagem correspondente
Could not set up host forwarding rule 'tcp:...'
. Isso funciona, mas- é mais provável que falhe com mais portas
- pode obscurecer outros problemas pelos quais não vale a pena tentar novamente
É uma boa alternativa, mas tenho esperança de uma solução mais direta.
Responder1
Depois de mais um pouco de pesquisa: o QEMU expõe as informações via info usernet
comando. Esta é a aparência da saída:
(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
Não está disponível via QMP, mas foi enviado um patch para adicionar um equivalentequery-usernet
aquialgum tempo atrás.
No meu caso, é simples expor o monitor usando -monitor unix:$PWD/mon.sock,server,nowait
, conectar, enviar o comando, ler a saída e analisá-la para obter os valores desejados.
Então, em resumo, para usar o QEMU para testar aplicativos de rede sem exigir sudo ou outros pré-requisitos (além do próprio QEMU) e evitando conflitos de porta no host:
- Para cada aplicação, crie um mapeamento de porta através da linha de comando (
-netdev
) ou monitor (netdev_add
), fornecendo0
a porta do host - Exponha o
monitor
como um soquete unix para o aplicativo de consumo - Execute
info usernet
através do soquete e extraia a porta de origem para cada mapeamento, que é a porta real alocada pelo sistema operacional