VirtualBoxでブリッジ接続をする(Linuxホスト)
(2008/11/12)この記事の内容は古く、推奨できない方法についての記述も含んでいるため、「GNU/LinuxホストにおけるSun xVM VirtualBoxのブリッジ接続の設定を見直し」を参照。この記事への追記は今後行わない。
(2008/12/19)Sun xVM VirtualBoxのバージョン2.1.0からブリッジ接続が簡単になっている。「Sun xVM VirtualBoxの覚え書き(2008/12/19現在)」を参照。
TUN/TAPデバイスとブリッジを組み合わせた上で、適切な準備を行うことで、VirtualBoxでも、仮想マシンをホストOSと同じネットワークに参加させることができる。
VMware製品と比べると非常に面倒。
(2007/8/1)ゲストOSのIPアドレスはDHCPが使えることが確認できたため、固定で指定する必要はないことが分かった。iptablesの設定でブロックされていただけだったので、詳細は追記を参照。
必要なもの
- カーネルのTUN/TAPドライバとブリッジのモジュール
TUN/TAPのインターフェースを設定するtunctlコマンド。「usermode-utilities」パッケージに含まれる- bridge-utils(ブリッジの設定ツール)
- sudo(管理者や別のユーザとしてコマンドを実行できる)
(2007/10/19)TUN/TAPインターフェースの設定には、VirtualBox付属のVBoxTunctlが使える。
ブリッジとTUN/TAPドライバをビルドするためのカーネル設定項目
どちらとも、組み込みにもできるのだが、モジュールとしてビルドしておく。ディストリ付属カーネルでは、両方ともモジュールとして存在している可能性が高い。
Networking ---> Networking options ---> <M> 802.1d Ethernet Bridging Device Drivers ---> Network device support ---> <M> Universal TUN/TAP device driver support
TUN/TAPデバイスのアクセス権(udevの設定)
最終的に、一般ユーザから実行して読み書きができればよいのだが、
- 「tuntap」などの名前のグループを作成して所有グループにして、モード0660
- 既存の「vboxusers」などのグループの所有にして、モード0660
- 所有グループはデフォルトにしておいて、モードは0666
など、色々なやり方が考えられる。手元の環境では「vboxusers」というグループがあり、既に一般ユーザをメンバーにしているので、これを所有グループにしておくことにする。
ファイル名: /etc/udev/rules.d/50-udev.rules など
# 1番目(多少面倒だが無難) KERNEL=="tun", NAME="net/%k", GROUP="tuntap", MODE="0660" # 2番目(TUN/TAPのためにグループを作るのが面倒) KERNEL=="tun", NAME="net/%k", GROUP="vboxusers", MODE="0660" # 3番目(非推奨) KERNEL=="tun", NAME="net/%k", MODE="0666"
sudoで一部コマンドをパスワード入力なしで使用するための設定
仮想マシンの開始・終了時にスクリプトによりインターフェース制御を行う場合、その中で実行されるコマンドの中にroot権限が必要なものがあるため、sudoコマンドをパスワード入力無しで使用できるように設定する必要がある。
以下は、ここで使用されるコマンド群に対してのみ、wheelグループのメンバーがパスワード無しで使用できるような設定例。DHCPクライアントは、dhcpcd以外の場合、環境に合ったものに置き換える。固定でIPアドレスを割り当てている場合、DHCPクライアントの場所は書く必要がない。また、modprobeも、やり方によっては不要かも。
sudoの設定を行うには、root権限でvisudoを実行し、設定ファイルを編集する。
# Cmnd alias specification Cmnd_Alias TUNTAP_BRIDGE=/sbin/modprobe, \ /sbin/ifconfig, \ /sbin/brctl, \ /opt/VirtualBox/VBoxTunctl, \ /sbin/route, \ /sbin/dhcpcd (中略) # User privilege specification (中略) %wheel ALL=(ALL) ALL (中略) %wheel ALL = NOPASSWD: TUNTAP_BRIDGE
(2007/10/19)VirtualBoxに付属しているVBoxTunctlを使用するよう修正
TUN/TAPとブリッジ接続を設定するスクリプト
スクリプトは3つに分けてみた。メッセージ表示処理はしていないし、ifconfigの出力からIPアドレスなどを取得する処理も、環境によってうまくいかない可能性があり、コマンドの場所なども、環境に応じた調整が必要かもしれない。
- VirtualBoxの起動前に準備として実行させ、終了後に後始末として実行させる
- VirtualBoxから受け取った名前のTUN/TAPのデバイスを作成、一般ユーザの所有とし、ブリッジのポートに追加(「設定アプリケーション」に指定する)
- VirtualBoxから受け取った名前のTUN/TAPのデバイスをブリッジから外し、インターフェースも削除する(「終了アプリケーション」に指定する)
(2007/8/1)最後のスクリプトは「終了アプリケーション」に指定しなくても、該当する動作が自動的に行われるため、指定する必要がないことが分かった。
ファイル名: ~/bin/vbox-init-system
#! /bin/sh USE_DHCP=0 ETH=eth0 BRCTL=/sbin/brctl #BRCTL=/usr/sbin/brctl DHCP_CLIENT=/sbin/dhcpcd MODULES_LIST='tun bridge vboxdrv' # MODULES_LIST='tun bridge kqemu' # MODULES_LIST='tun bridge kvm-amd kvm' # MODULES_LIST='tun bridge kvm-intel kvm' DEFAULT_GW=$(/sbin/route | grep default | awk -F\ '{printf $2}') start () { ip_address=$(/sbin/ifconfig ${ETH} | head -n 2 | tail -n 1 | cut -f 2 -d : | cut -f 1 -d \ ) broadcast=$(/sbin/ifconfig ${ETH} | head -n 2 | tail -n 1 | cut -f 3 -d : | cut -f 1 -d \ ) net_mask=$(/sbin/ifconfig ${ETH} | head -n 2 | tail -n 1 | cut -f 4 -d : | cut -f 1 -d \ ) ## 必要なモジュールを読み込む for i in ${MODULES_LIST}; do sudo /sbin/modprobe -q $i; done ## ブリッジの作成 sudo ${BRCTL} addbr br0 || exit 1 ## ブリッジの設定 sudo ${BRCTL} setfd br0 0 || exit 1 sudo ${BRCTL} sethello br0 2 || exit 1 sudo ${BRCTL} stp br0 off || exit 1 ## NICのIPアドレス割り当てを解除 sudo /sbin/ifconfig ${ETH} 0.0.0.0 || exit 1 ## ブリッジにNICを追加 sudo ${BRCTL} addif br0 ${ETH} || exit 1 ## ブリッジにIPアドレスなどを設定して有効にする if [ ${USE_DHCP} = 0 ]; then sudo /sbin/ifconfig br0 ${ip_address} broadcast ${broadcast} netmask ${net_mask} up || exit 1 sudo /sbin/route add default gw ${DEFAULT_GW} || exit 1 else sudo ${DHCP_CLIENT} br0 || exit 1 fi } stop () { ip_address=$(/sbin/ifconfig br0 | head -n 2 | tail -n 1 | cut -f 2 -d : | cut -f 1 -d \ ) broadcast=$(/sbin/ifconfig br0 | head -n 2 | tail -n 1 | cut -f 3 -d : | cut -f 1 -d \ ) net_mask=$(/sbin/ifconfig br0 | head -n 2 | tail -n 1 | cut -f 4 -d : | cut -f 1 -d \ ) ## ブリッジを無効にする sudo /sbin/ifconfig br0 down || exit 1 ## ブリッジからNICを削除 sudo ${BRCTL} delif br0 ${ETH} || exit 1 ## ブリッジを削除 sudo ${BRCTL} delbr br0 || exit 1 ## NICを再設定 if [ ${USE_DHCP} = 0 ]; then sudo /sbin/ifconfig ${ETH} ${ip_address} broadcast ${broadcast} netmask ${net_mask} || exit 1 sudo /sbin/route add default gw ${DEFAULT_GW} || exit 1 else sudo ${DHCP_CLIENT} ${ETH} || exit 1 fi ## モジュールを削除 sudo /sbin/modprobe -qr ${MODULES_LIST} } case "$1" in start) start ;; stop) stop ;; *) echo "Usage: $(basename "$0") {start|stop}" exit 1 esac
(2007/7/17)上のスクリプトにおいて、コマンドの実行に失敗したら処理を停止するように修正・2度続けて実行しても安全に止まるようになった
(2007/8/1)読み込むカーネルモジュールを選択するように修正(QEMUでも使えるようにした)
(2008/10/19)sethelloの値が不正だったのを修正(必要がなければこの行は消してもOK)
(2008/10/24)brctlの場所を変数に入れる形に修正・もし「[ルータのホスト名]: 不明なホスト」のように出てしまう場合は/etc/hostsにルータのホスト名とIPアドレスを記述する
ファイル名: ~/bin/vbox-init-user
#! /bin/sh ## 自分の所有するTUN/TAPインターフェースを新規作成 sudo /opt/VirtualBox/VBoxTunctl -u $(whoami) -t $2 ## インターフェースを有効にする sudo /sbin/ifconfig $2 0.0.0.0 up ## ブリッジに追加 sudo /sbin/brctl addif br0 $2
(2007/10/19)VirtualBoxに付属のVBoxTunctlを使用するよう修正
ファイル名: ~/bin/vbox-init-user-stop (実は不要?)
#! /bin/sh ## TUN/TAPインターフェースをブリッジから削除 sudo /sbin/brctl delif br0 $2 ## インターフェースを削除(所有者=ユーザの権限で実行させる) /usr/bin/tunctl -d $2
「$2」には、指定した「tun0」などのインターフェース名が入る。仮想マシンを追加する場合、「tun1」など、別の名前にするとよい。
使い方は、
$ vbox-init-system start
で一連の準備を行った後でVirtualBoxを起動し、終了後に
$ vbox-init-system stop
で元に戻す。後始末はしなくても、ネットワークは使用できるが、dmesgを見ると、
device eth0 entered promiscuous mode
のままで、後始末を終えるまではずっと、NICがネットワーク上の他のマシン宛てのデータを捨てずに拾うモード(プロミスキャス/無差別モード)で動作するため、これが気になるのであれば、stopの処理は行うのがよい。
device eth0 left promiscuous mode
という行が出て、通常のモードに戻る。なお、VMware製品の場合、ブリッジ機能を使用すると、仮想マシンの起動/終了時に自動的にこのモードを切り替えているようだ。
下は、VirtualBoxの仮想マシンのネットワーク設定例。
(2007/8/1)終了アプリケーションの設定が無くても後始末ができることが分かったため、設定を修正
Gentoo Linuxで起動時にTUN/TAPとブリッジを設定するための設定
起動時にTUN/TAPデバイスを作成し、NICの接続とブリッジさせるようにするには、既存のeth0などの設定を消した上で
ファイル名: /etc/conf.d/net
config_eth0=( "null" ) tuntap_tun0="tap" config_tun0=( "null" ) tunctl_tun0="-u [ユーザ名]" # config_br0=( "dhcp" ) config_br0=( "[ブリッジのIPアドレス] netmask [ネットマスク]" ) routes_br0=( "default via [デフォルトゲートウェイ]" ) bridge_br0="eth0 tun0" depend_br0 () { need net.eth0 net.tun0 } brctl_br0=( "setfd 0" "sethello 0" )
のようにする。更に、OS起動時のスクリプト自動起動の設定として
$ cd /etc/init.d/ $ for i in br0 tun0; do sudo ln -s net.lo net.$i; done $ sudo rc-update del net.eth0 default $ sudo rc-update add net.br0 default
を行う。ただ、この方法を使うよりは、仮想マシン起動時にスクリプトを実行したほうがよい気がする。なお、この場合、VirtualBoxの設定で、スクリプトの欄は空欄にしておく。
TUN/TAPの初期化に失敗する問題(CAP_NET_ADMIN絡み)
準備をして仮想マシンを起動させようとしても、「VERR_HOSTIF_INIT_FAILED」というエラーが出て、TUN/TAPデバイスが使えない。
これだけではよく分からなかったのでQEMUでも試したところ
warning: could not configure /dev/net/tun: no virtual network emulation
となり、このメッセージをWeb検索してみた。
http://www.mail-archive.com/qemu-devel@nongnu.org/msg07032.html
で始まるスレッドなどによると、Linux 2.6.18以降、QEMUでTUN/TAPを使用する場合にも、rootでなければうまくいかない*1という状態が続いているという。手間のかからない回避策としては、カーネルのTUN/TAPドライバのソースを修正するか、アプリケーション側をsuidで動かすかのどちらか。
QEMUの場合はsetuidで動くのだが、VirtualBoxでは
$ sudo chmod u+s /opt/VirtualBox/VirtualBox $ virtualbox /opt/VirtualBox/VirtualBox: error while loading shared libraries: VBoxRT.so: cannot open shared object file: No such file or directory
セキュリティ上の理由か、共有ライブラリが読めず、失敗。
結局、カーネルのdrivers/net/tun.cを修正することに。
要領としては、
!capable(CAP_NET_ADMIN)
の条件を全て外していけばOK。今後、数が増えたとしても、このやり方で対処できる。
--- linux-2.6.21-suspend2-r6/drivers/net/tun.c.orig +++ linux-2.6.21-suspend2-r6/drivers/net/tun.c @@ -461,7 +461,7 @@ /* Check permissions */ if (tun->owner != -1 && - current->euid != tun->owner && !capable(CAP_NET_ADMIN)) + current->euid != tun->owner) return -EPERM; } else if (__dev_get_by_name(ifr->ifr_name)) @@ -472,9 +472,6 @@ err = -EINVAL; - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - /* Set dev type */ if (ifr->ifr_flags & IFF_TUN) { /* TUN device */
(2007/10/13)Linux 2.6.23の時点でも修正は必要だが、以下の修正のみで動作している。VirtualBoxのバージョンは1.5.0。
--- linux-2.6.23-gentoo/drivers/net/tun.c.orig +++ linux-2.6.23-gentoo/drivers/net/tun.c @@ -483,9 +483,6 @@ err = -EINVAL; - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - /* Set dev type */ if (ifr->ifr_flags & IFF_TUN) { /* TUN device */
ここで関係する権限「ケーパビリティ」については
http://www.atmarkit.co.jp/fsecurity/rensai/lids03/lids01.html
が参考になるかも知れない。
本来は、実行ファイルに対してCAP_NET_ADMINの権限を与えることが望ましいのだが、これを実現するためには、カーネルとlibcapに別のパッチを当てたりしないといけないようだ(非常に面倒だが、QEMUでの動作報告もあり、不可能ではない)。
iptablesのFORWARDチェインのポリシーがDROPの場合
(2007/7/24)iptablesでパケットフィルタリングをしていて、FORWARDチェイン*2のポリシーをDROP(破棄)にしていて、かつ、ゲストOSを固定IPアドレスにしている場合、
for i in [仮想マシンのIPアドレスリスト(スペース区切り)] do iptables -A FORWARD -s $i -j ACCEPT iptables -A FORWARD -d $i -j ACCEPT done
とすることで、仮想マシンから外部と通信ができるようになる。上の設定では、ポート番号ごとの設定などはしていないが、これはゲストOS側のファイアウォールで設定したほうがよい気がする。
(2007/8/1)もし、ホストOSのネットワーク上でDHCPが使用したい場合、送信元/宛先のIPアドレスが不定なので
iptables -A FORWARD -i br0 -j ACCEPT
として、インターフェースbr0を使用する「経由」パケットを通す形で設定する。あるいは
iptables -A FORWARD -i br0 -p udp --sport 67:68 --dport 67:68 -j ACCEPT
でDHCPだけ通すような設定を書いておいて、DHCPサーバ側で、仮想マシンのMACアドレスと、貸し出すIPアドレスとを関連付ける(固定させる)設定をすることで、DHCPを使用しながらIPアドレスは固定させる、ということができるため、br0に対する経由の全許可をせずに、仮想マシンのIPアドレスに対する許可設定ができる。
(2007/10/29)OSインストール中に(デフォルトで)DHCPを使用するDebianインストーラでは、DHCPサーバに接続するところでキャンセルするか、ホスト名設定で「戻る」をした後で手動設定を選択すると、IPアドレスを固定にする設定ができるので、DHCPサーバ側でMACアドレスとIPアドレスを関連付けできない場合は、このようにするとよい。
使用したバージョン:
- virtualbox-bin 1.4.0-r1
- bridge-utils 1.2
- usermode-utilities 20040406-r1
- Linux(suspend2-sources) 2.6.21-r6