仮想化ルーター

pfSense×2の仮想マシンを準備し、とりあえず本番運用に入った。
流石に専用アプライアンスなので、設定はWebから楽々できるし、PPPoEの安定度もちゃんとしている。


多少問題はあるものの、追って解決していく。
ただ、tumbleweedが仮想化親機なので比較的再起動が必要ってのは微妙かも。
しばらく使ってみよう。


リソースを割と贅沢に割り振ったけど、ほとんど使っていないっぽいので状況を見て削減する。
ただ、メモリはつかみっぱなしだけど、CPUは不要な場合は返却しているっぽいので特に問題ないかも。

hostapdによる無線アクセスポイント

PCIeパススルーでNUC本体の無線LANユニットを接続し、hostapdによるアクセスポイントに成功した。
基本的な手順は下記を参照した。
hostapd を使用した簡単な方法で WiFi に強力な暗号化を実装する
confファイルについては、zypperでインストールする際に追加されたもののデバイス名だけ書き換えることで動いた。
上記のサイトではドライバをnl80211にすると書いてあるが、デフォルトのままで動作した。


これでMACアドレスごとに事前交換鍵が異なるアクセスポイントができた。
ルーターなどの共通鍵よりちょっとセキュアなのと、MACアドレスフィルタリング相当もできたことになる。
MACアドレスは偽造可能なので、微妙だけど…


ただ、SuSE Firewallを使っているルーター本体の方が不安定なので、専用のアプライアンスなどを検討する。

pfSense

ルーターアプライアンスを幾つか検討した結果、pfSenseを使ってみることにした。
既存ルータはそのままで、ルータ内をDMZとみなしてDMZから内部ネットワーク部分を練習がてら作ってみる。


VyOSも検討したが、こちらはどちらかというとよりエンタープライズ的かなーと。
同じ事をやる場合は性能高いらしい。


無線LANもいけるかな?と思ったが、Intelのカードの場合ホストモードは無理っぽいので、別途ブリッジ(hostapd)仮想マシンを一個作る。
802.11 Wireless — Supported Wireless Cards | pfSense Documentation

ホストデバイス接続

ネットワークなどは仮想化インターフェースでいいとして、無線LANアダプタを取り込んでアクセスポイントにするために仮想マシン側にPCIバイスを直結するPCIパススルーを試す。
PCIパススルーに必要なIOMMUは標準でONにならないので、カーネル引数に下記を追加する。

intel_iommu=on

ただ、Leapの場合上記を追加してVt-dが有効な状態で起動させるとカーネルパニックが起きてしまう。
カーネルパニックが起きるようになってしまっても、起動だけならUEFI設定でVt-dを無効化することで行えるが、それは本末転倒なので、結局Tumbleweedに戻した。
不安定だったのはKDEのせいということにして、Minimal Xで使うことにした。


上記設定を適用した後、Virt-managerでホストデバイスを選んで追加し、仮想マシンを起動させればアクセスが可能となる。
同時にホストデバイスからはデバイスが切断される。


下調べしていた時はホスト側であらかじめ切り離す処理などを入れる必要があるのかと考えていたが、特に必要なかった。
OVMF による PCI パススルー - ArchWiki
ただ、blacklistに入れてドライバがロードされないようにしておいた方が良さげ。

準仮想化ドライバ

ゲストOS側が対応していればNICやBlockデバイスを準仮想化ドライバVirtIOにすることで性能向上が図れるということなのでやってみた。
特に難しいことはなく、ディスクの場合は接続方式をデフォルトのIDEからVirtIOに変更し、NICの場合はe1000からVirtIOに変更するして再起動すれば問題なく動作した。


"準"というのは既存ハードウェアを完全に模擬するのではなく、仮想化ホストと効率よく通信できるようなデバイスを定義して効率を上げると理解した。
下記のサイトを参考にした。
virtio: Linux の I/O 仮想化フレームワーク
https://www.nic.ad.jp/ja/materials/iw/2012/proceedings/d1/d1-Asama.pdf


現時点で性能は測ってないが、悪くなる道理はないので安定するようならこのまま使う。


追記
VirtIOに変更するとディスクデバイス名がsda→vdaに変わる。
SUSEはfstabやgrubの設定はUUIDベースで行っているので、VirtIOに変更したとしても自動追従するが、カーネル引数だけresume=/dev/sda2と決め打ちで書いてある箇所があり、起動が遅くなることがある。
仮想マシンではレジュームを使わないので、この引数は削除した。

仮想OS側の設定

ルーター化はSUSE Firewall(中身はiptable)などを使えばとりあえず動くものはできた。


それ以外の設定項目として、一応アンチウィルスソフトのClamAVをインストールし、clamdとfreshclamのサービスを有効化する。
あとは仮想化PCIパススルーでWifiを直結化し、アクセスポイント化する作業。

仮想マシン起動&監視デーモン

ルーターとしての用途を考えると、電源が入った時点で仮想マシンが起動している必要がある。
virshにはautostartというオプションがあるが、これは起動順番などは制御できないようなので、自前で起動スクリプトを書いて、初期化時に呼び出すことにした。
下記を参考にした。
[ThinkIT] 第4回:virshコマンドで仮想化を管理する! (2/3)
これで作ったスクリプトをboot.localから呼び出せば自動起動…と思ったのだが、libvirtに必要なサービスが起動していないらしく、起動に失敗した。


試行錯誤の結果、作成した監視スクリプトをsystemdのサービスとして登録し、依存関係を登録してやればうまく動くことが分かった。


まず監視スクリプト

#!/bin/bash

# Must be root
if test "`/usr/bin/id -u`" != 0 ; then
    echo "$0: You must be root to run this script" >& 2
    exit 1
fi

SLEEPTIME=10

boot_vm ()
{
    if test "`virsh domstate $1`" != "running" ; then
        virsh start $1
        rc=$?
        [ $rc -ne 0 ] && exit $rc

        sleep $SLEEPTIME
    fi
}

halt_vm ()
{
    if test "`virsh domstate $1`" != "shut off" ; then
        virsh shutdown $1
        rc=$?
        [ $rc -ne 0 ] && exit $rc

        sleep $SLEEPTIME
    fi
}

exit_service ()
{
    halt_vm leap-router-dmz-int
    halt_vm leap-router-ext-dmz
}

trap "exit_service" EXIT

while true
do
    boot_vm leap-router-ext-dmz
    boot_vm leap-router-dmz-int
    sleep 120
done

pingによる監視なども入れたいと思ったが、とりあえず最低限の起動順序制御・落ちた場合の再起動・ホスト終了時のシャットダウン処理のみ。
これによって、特に操作しなくても自動的にルーター用のVMが起動するようになる。


呼び出すためのsystemdサービス定義。

[Unit]
Description = KVM router startup
After=network.target libvirt-guests.service libvirtd.service
Requires=network.target libvirt-guests.service libvirtd.service

[Service]
ExecStart = /usr/local/sbin/vm-router-service
Restart = always
Type = simple

[Install]
WantedBy = multi-user.target

これによって必要なサービスの準備が終わってから起動するようになる。