Bhyve es el hypervisor de virtualización oficial de FreeBSD a partir de la versión 10.0-RELEASE, en este artículo utilizaremos vm-bhyve, un wrapper que nos facilitará mucho la gestion de máquinas y de la red.
Instalación
Instalamos todo lo necesario:
Creamos un pool ZFS donde se almacenarán las máquinas virtuales:
Habilitamos el servicio y le indicamos donde estarán alojadas las máquinas virtuales:
sysrc vm_dir="zfs:zroot/vm"
Inicializamos vm-bhyve:
Copiamos los templates de ejemplo:
Creamos un bridge para conectar las máquinas virtuales con la interfaz de red física:
Añadimos la interfaz física al switch:
Podemos ver los bridges disponibles:
NAME TYPE IFACE ADDRESS PRIVATE MTU VLAN PORTS
public standard vm-public - no - - bge0
Cuando arranquemos una máquina virtual la interfaz tap correspondiente a la máquina se añadirá de forma automática al bridge.
Nos bajamos la imágenes de los sistemas operativos que queramos instalar, en mi caso Kali-linux:
-rw-r--r-- 1 root wheel 2994323456 Feb 7 19:46 /zroot/vm/.iso/kali-linux-2022.1-installer-amd64.iso
Una VM Linux puede arrancar con distintos loaders grub/uefi, según el que utilicemos accederemos a la VM de un modo u otro, mi consejo es utilizar grub y acceder mediante puerto serie a las VMs ya que no precisa de ningún software adicional, además si necesitamos arrancar alguna aplicación gráfica siempre podremos forwardear las X.
Puerto serie
Creamos la máquina virtual:
Instalamos desde la ISO:
Veremos el instalador en la propia consola:
Podemos consultar información de la máquina virtual mediante el siguiente comando:
------------------------
Virtual Machine: kali
------------------------
state: running (99075)
datastore: default
loader: grub
uuid: 05ced6c1-d3a7-11ec-875e-5065f37a62c0
uefi: default
cpu: 2
memory: 2G
memory-resident: 215412736 (205.433M)
network-interface
number: 0
emulation: virtio-net
virtual-switch: public
fixed-mac-address: 58:9c:fc:00:81:dd
fixed-device: -
active-device: tap0
desc: vmnet-kali-0-public
mtu: 1500
bridge: vm-public
bytes-in: 0 (0.000B)
bytes-out: 0 (0.000B)
virtual-disk
number: 0
device-type: sparse-zvol
emulation: virtio-blk
options: -
system-path: /dev/zvol/zroot/vm/kali/disk0
bytes-size: 16106127360 (15.000G)
bytes-used: 57344 (56.000K)
Si listamos las máquinas virtuales veremos que está corriendo:
NAME DATASTORE LOADER CPU MEMORY VNC AUTOSTART STATE
kali default grub 2 2G - No Running (99075)
Para salir de la consola debemos presionar:
~ + CtrDerecha + d
VNC
Creamos la máquina virtual:
La configuramos para que el loader sea uefi y definimos los parámetros de configuración VNC:
loader="grub" -> loader="uefi"
graphics="yes"
graphics_listen="192.168.69.180"
graphics_port="5900"
graphics_res="1600x900"
graphics_wait="yes" --> Deja la VM en pausa hasta que conectemos por VNC, brutal
Iniciamos la instalación:
Arrancamos la máquina:
Al consultar las máquinas virtuales veremos que esta última tiene asociado un socket VNC:
NAME DATASTORE LOADER CPU MEMORY VNC AUTOSTART STATE
kali default grub 2 2G - No Running (42962)
kali2 default uefi 2 2G 192.168.69.180:5900 No Locked (odyssey.alfaexploit.com)
Si tenemos el tráfico de red filtrado por IPFW las reglas de acceso al socket VNC serían:
# BHYVE VNC installation
$cmd 01311 allow tcp from $LAN to me 5900 in via $wanif
$cmd 01311 allow tcp from me 5900 to $LAN out via $wanif
Accedemos al instalador vía VNC:

Cloud image
Creamos un fichero con las pubkeys a autorizar en la máquina virtual:
vi pubkeys/kr0m.pub
Desgraciadamente para Kali solo hay una imagen cloud compatible con Amazon EC2 por lo tanto no arrancará, se queda en el bootloader:
kali3 default grub 2 2G - No Bootloader (42532)
Para hacer la prueba nos bajamos la versión cloud de Ubuntu:
Podemos ver la imagen descargada:
DATASTORE FILENAME
default ubuntu-22.04-server-cloudimg-amd64.img
Creamos la máquina virtual:
No sabemos que dirección ip va a recibir por DHCP por lo tanto no podemos definir reglas de IPFW, además no he sido capaz de permitir el tráfico DHCP, por lo tanto deshabilitamos el firewall durante el arranque de la máquina:
NOTA: No recomiendo este tipo de imágenes ya que el firewall estará deshabilitado un momento, pero puede llegar a ser problemático en algunos escenarios.
Arrancamos la imagen:
El problema de estas imágenes es que el cloud-ini de vm-bhyve está muy limitado tan solo nos deje meter las keys ssh, pero no asignar un ip estática, por lo tanto la VM obtendrá una por DHCP que tendremos que descubrir:
- Consultando la MAC de la VM: vm info ubuntu-cloud y accediendo al servidor DHCP para consultar los logs.
- Realizando un nmap ping antes y después de arrancar la VM para comparar si hay alguna ip nueva activa.
Una vez arrancada ya podemos volver a levantar el forewall:
Accedemos vía ssh ya que nuestra pubkey debe estar autorizada:
Consultamos la información básica:
Linux ubuntu-cloud 5.15.0-27-generic #28-Ubuntu SMP Thu Apr 14 04:55:28 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
El siguiente paso sería reconfigurar la red con la ip estática y asignar reglas de IPFW.
Firewall
Lo mas sencillo es deshabilitar el filtrado de tráfico en todos los bridges del host padre y filtrar de forma específica dentro de cada VM:
net.inet.ip.forwarding=1 # Enable IP forwarding between interfaces
net.link.bridge.pfil_onlyip=0 # Only pass IP packets when pfil is enabled
net.link.bridge.pfil_bridge=0 # Packet filter on the bridge interface
net.link.bridge.pfil_member=0 # Packet filter on the member interface
Pero si nos empeñamos en filtrar en el padre también podemos hacerlo(no haría falta modificar el /etc/sysctl.conf) pero las reglas de IPFW se convierten en un infierno, como podemos ver el tráfico de red debe atravesar distintas interfaces ya que el flujo de datos desde internet a la máquina virtual es:
INET -> IN-WAN -> IN-BRIDGE-OUT -> IN-TAP-OUT -> eth0-VM
Mientras que desde la máquina virtual a internet es:
eth0-VM -> IN-TAP-OUT -> IN-BRIDGE-OUT -> WAN-OUT -> INET
En el siguiente ejemplo vemos como permitir tráfico saliente y tráfico entrante:
wanif="bge0"
bhyvebridge="vm-public"
# INET -> IN-WAN -> IN-BRIDGE-OUT -> IN-TAP-OUT -> eth0-VM
# eth0-VM -> IN-TAP-OUT -> IN-BRIDGE-OUT -> WAN-OUT -> INET
VM_NAME=kali
VM_IP=192.168.69.170
tapif=$(/usr/local/sbin/vm info $VM_NAME | grep 'active-device'|awk '{print$2}')
# Allow outgoing HTTP
$cmd 01303 allow tcp from $VM_IP to any 80 in via $tapif
$cmd 01303 allow tcp from $VM_IP to any 80 out via $tapif
$cmd 01303 allow tcp from $VM_IP to any 80 in via $bhyvebridge
$cmd 01303 allow tcp from $VM_IP to any 80 out via $bhyvebridge
$cmd 01303 allow tcp from $VM_IP to any 80 out via $wanif
$cmd 01303 allow tcp from any 80 to $VM_IP in via $wanif
$cmd 01303 allow tcp from any 80 to $VM_IP in via $bhyvebridge
$cmd 01303 allow tcp from any 80 to $VM_IP out via $bhyvebridge
$cmd 01303 allow tcp from any 80 to $VM_IP in via $tapif
$cmd 01303 allow tcp from any 80 to $VM_IP out via $tapif
# Allow incoming 7777 port
$cmd 01310 allow tcp from any to $VM_IP 7777 in via $wanif
$cmd 01310 allow tcp from any to $VM_IP 7777 in via $bhyvebridge
$cmd 01310 allow tcp from any to $VM_IP 7777 out via $bhyvebridge
$cmd 01310 allow tcp from any to $VM_IP 7777 in via $tapif
$cmd 01310 allow tcp from any to $VM_IP 7777 out via $tapif
$cmd 01310 allow tcp from $VM_IP 7777 to any in via $tapif
$cmd 01310 allow tcp from $VM_IP 7777 to any out via $tapif
$cmd 01310 allow tcp from $VM_IP 7777 to any in via $bhyvebridge
$cmd 01310 allow tcp from $VM_IP 7777 to any out via $bhyvebridge
$cmd 01310 allow tcp from $VM_IP 7777 to any out via $wanif
Troubleshooting
En algunas ocasiones las máquinas virtuales terminan en estado “Locked”:
NAME DATASTORE LOADER CPU MEMORY VNC AUTOSTART STATE
kali00 default uefi 1 2048M 0.0.0.0:5900 No Locked (odyssey.alfaexploit.com)
Para solventarlo primero debemos eliminar el fichero run.lock:
Ahora aparecerá como “Stopped”:
NAME DATASTORE LOADER CPU MEMORY VNC AUTOSTART STATE
kali00 default uefi 1 2048M - No Stopped
Si intentamos eliminarla y nos causa problemas, tendremos que liberar los recursos desde bhyvectl:
/usr/local/sbin/vm: WARNING: kali00 appears to be running locally (vmm exists)
Liberamos los recursos:
La función de bhyvectl --destroy es:
After the VM has been shutdown, its resources can be reclaimed
Incluso con los recursos liberados puede que el dataset ZFS se resista:
NAME DATASTORE LOADER CPU MEMORY VNC AUTOSTART STATE
kali00 default uefi 1 2048M - No Stopped
Are you sure you want to completely remove this virtual machine (y/n)? y
/usr/local/sbin/vm: ERROR: failed to destroy ZFS dataset zroot/vm/kali00
En tal caso lo eliminamos manualmente:
Finalmente eliminamos la máquina virtual:
Are you sure you want to completely remove this virtual machine (y/n)? y
Comprobamos que no quede rastro de ella:
NAME DATASTORE LOADER CPU MEMORY VNC AUTOSTART STATE