久しぶりにKVMで遊ぶ

久しぶりにKVMで遊ぶ

March 25, 2022

機材 #

  • ECS LIVA
  • Debian 11 (bullseye)を最小構成でインストール

準備 #

# apt install sudo
# usermod -aG sudo (俺)
$ echo 'Apt::Install-Recommends "false";' | sudo tee -a /etc/apt/apt.conf.d/99local
$ echo 'Apt::Install-Suggests "false";' | sudo tee -a /etc/apt/apt.conf.d/99local
$ sudo apt install qemu-system qemu-utils libvirt-clients libvirt-daemon-system virtinst
$ sudo usermod -aG libvirt (俺)
$ sudo apt install bridge-utils

ブリッジを作る #

auto br0
iface br0 inet static
  bridge_ports enp3s0
  bridge_stp off
  bridge_waitport 0
  bridge_fd 0
  address 192.168.xxx.xxx
  netmask 255.255.255.0
  gateway 192.168.xxx.xxx
  nameserver 192.168.xxx.xxx

KVMでOPNsenseを動かす #

参考: Installing OPNsense firewall on KVM

$ bunzip2 OPNsense-22.1.2-OpenSSL-nano-amd64.img.bz2
$ qemu-img convert -f raw -O qcow2 OPNsense-22.1.2-OpenSSL-nano-amd64.img opnsense.qcow2
$ qemu-img resize opnsense.qcow2 8G
$ sudo mv opnsense.qcow2 /var/lib/libvirt/images/

opnsense.xmlを作成

<domain type='kvm'>
  <name>opnsense</name>
  <memory unit='MiB'>1024</memory>
  <currentMemory unit='MiB'>1024</currentMemory>
  <vcpu>1</vcpu>
  <os>
    <type arch='x86_64'>hvm</type>
    <boot dev='hd'/>
  </os>
  <features><acpi/><apic/><pae/></features>
  <clock offset='utc'/>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>restart</on_crash>
  <devices>
    <emulator>/usr/bin/qemu-system-x86_64</emulator>
    <controller type='pci' index='0' model='pci-root'/>
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2' cache='none'/>
      <source file='/var/lib/libvirt/images/opnsense.qcow2'/>
      <target dev='vda' bus='virtio'/>
    </disk>
    <interface type='bridge'>
      <source bridge='br0'/>
      <model type='virtio'/>
      <target dev='vnet0'/>
      <alias name='net0'/>
    </interface>
    <serial type='pty'><target port='0'/></serial>
    <console type='pty'><target port='0'/></console>
    <memballoon model='none'></memballoon>
  </devices>
</domain>
$ sudo virsh define opnsense.xml
$ sudo virsh list --all
$ sudo virsh start opnsense
$ sudo virsh console opnsense
$ sudo virsh autostart opnsense

ゲストから外部へのIPv6の通信が通らない? #

症状 #

アップデートしようとすると下記のようになる

***GOT REQUEST TO CHECK FOR UPDATES***
Currently running OPNsense 22.1.4 (amd64/OpenSSL) at Thu Mar 24 22:57:56 JST 2022
Fetching changelog information, please wait... fetch: transfer timed out
fetch: /usr/local/opnsense/changelog/changelog.txz appears to be truncated: 0/244144 bytes
Updating OPNsense repository catalogue...

ゲストからDNSサーバにdrillしようとすると固まる

# drill @2001:240::13
Error: error sending query: Could not send or receive, because of network error

ホストを再起動してからしばらくは繋がっている(pingやdrillに返事がある)ように見える

ブリッジのかわりにmacvtapを使ってみる #

macvtapを使うとゲスト~ホスト間で通信できないので、内部ネットワークを作る

DHCPサーバは無効にしている(でもdnsmasqは要るみたい)

<network>
  <name>host-to-guest-manual</name>
  <ip address='192.168.254.1' netmask='255.255.255.0'>
  </ip>
</network>
$ sudo apt install dnsmasq
$ sudo virsh net-define host-to-guest-manual.xml
$ sudo virsh net-autostart host-to-guest-manual
$ sudo virsh net-start host-to-guest-manual
$ sudo virsh edit opnsense

下記を追加

    <interface type='network'>
      <source network='host-to-guest-manual'/>
      <model type='virtio'/>
    </interface>

ゲストを一旦シャットダウンしてから起動

$ sudo virsh shutdown opnsense
$ sudo virsh start opnsense

ゲスト側で固定アドレスを振る(192.168.254.x)

ゲスト起動後、IPv6で外部にアクセスできるようになるまで数分かかる

なんか遅い気がする #

$ sudo virsh edit opnsense

CPUの割り当てを増やしてみる

<vcpu placement='static'>2</vcpu>

CPUをqemu64からhost-passthroughに変えてみる

<cpu mode='host-passthrough'/>

WireGuardのスループットが20Mbpsぐらいだったのが70Mbpsぐらいまで上がったのでたぶんどちらかが効いてる

Dockerでpi-holeを動かす #

KVM関係ないけども

$ sudo apt install curl gnupg
$ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt update
$ sudo apt install docker-ce docker-ce-cli containerd.io docker-compose
$ sudo usermod -aG docker (俺)
$ exit

macvtapのために入れたdnsmasqは自動起動しないようにしておく(53/udp,53/tcpを使うので)

$ sudo systemctl stop dnsmasq.service
$ sudo systemctl disable dnsmasq.service

GitHub - pi-hole/docker-pi-hole: Pi-hole in a docker container

$ mkdir ~/pihole
$ cd ~/pihole
$ wget https://raw.githubusercontent.com/pi-hole/docker-pi-hole/master/docker-compose.yml.example -O docker-compose.yml

docker-compose.ymlを編集

  • 53/tcp, 53/udpのホスト側アドレスを指定(ホストの別のアドレスでdnsmasqが動いているので0.0.0.0:53にはbindできないため)
  • 67/udpをコメントアウト(DHCPサーバとしては使用しないため)
    ports:
      - "192.168.xxx.xxx:53:53/tcp"
      - "192.168.xxx.xxx:53:53/udp"
    #  - "67:67/udp"
      - "80:80/tcp"
$ docker-compose up -d
$ docker logs pihole | grep random
Assigning random password: XXXXXXXX

ブラウザでhttp://192.168.xxx.xxx/admin/にアクセスし、上記パスワードでログイン

OPNsenseのVMと通信させるのが面倒なので、こいつもVMにしてその中でDockerを動かすことにする 物理NIC(enp3s0)を元にしたmacvlanインターフェースを生やしてやるとよさそう

auto macvlan0
iface macvlan0 inet static
  address 192.168.***.***
  netmask 255.255.255.0
  gateway 192.168.***.***
  nameserver 192.168.***.***
  pre-up ip link set enp3s0 up
  pre-up ip link add dev $IFACE link enp3s0 type macvlan mode bridge
  post-down ip link delete dev $IFACE type macvlan

iface macvlan0 inet6 auto
  privext 2

VMでWireGuardを動かす #

参考: WireGuard VPN Road Warrior Setup – EmanuelDuss.ch

WireGuardで接続したクライアントはこのマシンのアドレスにNAT(masquerade)して外に出て行く寸法

#!/usr/bin/env bash

VPNIF="wg0"
LANIF="enp1s0"

nft add table ip filter
nft add chain ip filter FORWARD { type filter hook forward priority 0 \; policy drop \; }
nft insert rule ip filter FORWARD iifname "$VPNIF" oifname "$LANIF" accept
nft insert rule ip filter FORWARD iifname "$LANIF" oifname "$VPNIF" ct state related,established accept

nft add table ip6 filter
nft add chain ip6 filter FORWARD { type filter hook forward priority 0 \; policy drop \; }
nft insert rule ip6 filter FORWARD iifname "$VPNIF" oifname "$LANIF" accept
nft insert rule ip6 filter FORWARD iifname "$LANIF" oifname "$VPNIF" ct state related,established accept

nft add table ip wireguard-nat
nft -- add chain ip wireguard-nat prerouting { type nat hook prerouting priority -100 \; }
nft add chain ip wireguard-nat postrouting { type nat hook postrouting priority 100 \; }
nft add rule ip wireguard-nat postrouting oifname "$LANIF" masquerade

nft add table ip6 wireguard-nat
nft -- add chain ip6 wireguard-nat prerouting { type nat hook prerouting priority -100 \; }
nft add chain ip6 wireguard-nat postrouting { type nat hook postrouting priority 100 \; }
nft add rule ip6 wireguard-nat postrouting oifname "$LANIF" masquerade
#!/usr/bin/env bash

VPNIF="wg0"
LANIF="enp1s0"

nft --handle list chain ip filter FORWARD | grep "iifname.*$VPNIF" | awk '{ print $NF }' | while read handle
do
  nft delete rule ip filter FORWARD handle "$handle"
done

nft --handle list chain ip6 filter FORWARD | grep "iifname.*$VPNIF" | awk '{ print $NF }' | while read handle
do
  nft delete rule ip6 filter FORWARD handle "$handle"
done
nft delete chain ip6 filter FORWARD

nft delete table ip wireguard-nat
nft delete table ip6 wireguard-nat

/etc/network/interfacesでprivext 2にするとVPNクライアントからIPv6でインターネットに出られなくなる #