FreeBSDでは、jailと呼ばれる仕組みを利用することにより、FreeBSD環境上に仮想のFreeBSD環境を複数構築することが出来ます。
jailはUNIXに古くから存在するchrootを発展させたもので、ホスト環境(以下、便宜上「jailer」と呼びます)上に構築したjail環境(以下、便宜上「prisoner」と呼びます)は、ホスト環境とは別に動作するFreeBSDマシンとして振舞います。
prisonerからはjailerおよび並列する別のprisonerに直接アタッチすることが出来ないため、システム管理者がシステムの一部の権利を他者に委譲しなくてはならない場合等に有効な解決策となります。
このページでは、このjailを利用した仮想FreeBSD環境の構築方法について説明します。
用途によって、jailが有効な場合とそうでない場合があります。
ここでは、jailの特徴と向いている用途について説明します。
jailの主な特徴は以下の通りです。
・エミュレーションではないため、高速に動作する
・prisonerごとにjailerとは別のIPアドレスをひとつ振ることができる
・各prisonerごとのリソース配分を予め設定しておくことができない
・jailerとprisonerで同一のファイルシステムが利用されるため、UID/GIDの割り当てに注意する必要がある
jailの一般的な用途としては、
・脆弱性などの懸念があるソフトウェアを安全に(jailerに影響を与えないように)動作させたいとき
・システム管理者がシステムの一部の権利を委譲したいとき
が挙げられます。
応用例として、「portsからのpackage構築を、jailerにpackageをインストールすることなく実行する」ようなことも可能です。
一方、各prisonerごとにIPアドレスがひとつ振られてしまうため、複数のサーバ名を持つWebサーバ(apache等)をネームベースのvirtualホストで構築するような用途には向きません。
それでは、早速jail環境の構築に入ります。
以下では、jailer内に4つのprisoner(prisoner1〜prisoner4)を構築する手順について説明しています。
自分が構築したいprisonerの数にあわせ、適宜読み替えてください。
まずはじめに、各prisonerのルートディレクトリになるディレクトリを作成します。
$ mkdir /home/jail $ mkdir /home/jail/prisoner1 $ mkdir /home/jail/prisoner2 $ mkdir /home/jail/prisoner3 $ mkdir /home/jail/prisoner4
上記の例では、各prisonerのルートを「/home/jail」下に用意していますが、必ずしもこの場所に作らなくてはいけないわけではありません。
自分のご趣味に合わせ、お好みの場所に作成してください。
まず、各prisonerにベースシステムをインストールします。
ベースシステムのインストールは、配布されているベースシステムを展開する方法と、srcからビルドしてインストールする方法の2つがあります。
今回は、freebsd-updateでのメンテナンスが容易な前者の方法について説明します。
FreeBSDのレポジトリからbase.txzを持ってきて、各prisonerに展開します。
レポジトリからbase.txzを取得する際には、jailerと同じアーキテクチャ(amd64/i386)のファイルを取得するようにしてください。
$ fetch ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/amd64/12.2-RELEASE/base.txz $ tar -zxpf base.txz -C /home/jail/prisoner1 $ tar -zxpf base.txz -C /home/jail/prisoner2 $ tar -zxpf base.txz -C /home/jail/prisoner3 $ tar -zxpf base.txz -C /home/jail/prisoner4 $ rm base.txz
上記の例では、AMD64版のFreeBSD 12.2-RELEASEのベースシステムをダウンロードして各prisonerに展開しています。
次に、各prisonerで名前解決ができるよう「resolv.conf」を用意します。
ほとんどのケースで「resolv.conf」の内容は、jailerと同じ設定で問題ないため、このファイルをjailer側からコピーします。
$ cp -p /etc/resolv.conf /home/jail/prisoner1/etc/ $ cp -p /etc/resolv.conf /home/jail/prisoner2/etc/ $ cp -p /etc/resolv.conf /home/jail/prisoner3/etc/ $ cp -p /etc/resolv.conf /home/jail/prisoner4/etc/
続いて、各prisonerのログに、kernel関係の出力がされないよう、「syslog.conf」を編集して設定します。
$ vi /home/jail/prisoner1/etc/syslog.conf $ vi /home/jail/prisoner2/etc/syslog.conf $ vi /home/jail/prisoner3/etc/syslog.conf $ vi /home/jail/prisoner4/etc/syslog.conf
それぞれ「syslog.conf」の「*.err;kern.warning;auth.notice;mail.crit /dev/console」の行をコメントアウトしてください。
続いて、各prisonerのcronによってkernelのタイムゾーンが設定されないよう、crontabを編集します。
(kernelのタイムゾーン設定はjailer側のcronで定期的に行われるため、prisonerでのcronによる設定は不要です。)
$ vi /home/jail/prisoner1/etc/crontab $ vi /home/jail/prisoner2/etc/crontab $ vi /home/jail/prisoner3/etc/crontab $ vi /home/jail/prisoner4/etc/crontab
それぞれ「crontab」の「1,31 0-5 * * * root adjkerntz -a」の行をコメントアウトしてください。
各prisonerで、自身の名前を解決できるように「hosts」を編集します。
$ vi /home/jail/prisoner1/etc/hosts $ vi /home/jail/prisoner2/etc/hosts $ vi /home/jail/prisoner3/etc/hosts $ vi /home/jail/prisoner4/etc/hosts
それぞれのファイルの一番下に「192.168.0.21 prisoner1.local.kishiro.com」のように、各prisonerに割り当てるIPアドレスとFQDNのペアを追記してください。
jailでは、jailerと各prisonerでひとつのファイルシステムを共有します。
このため、jailer側で利用しているUID/GIDをそのままprisoner側で利用してしまうと、jailer側のユーザがprisoner側のユーザのファイルを自分のものとして扱えてしまうことになります。
これを回避するためには、jailerと各prisonerで重複しないUID/GIDを振る必要があります。
下記のようにして、UID/GIDの帯域をそれぞれ設定してください。
$ vi /etc/pw.conf $ vi /home/jail/prisoner1/etc/pw.conf $ vi /home/jail/prisoner2/etc/pw.conf $ vi /home/jail/prisoner3/etc/pw.conf $ vi /home/jail/prisoner4/etc/pw.conf
ファイルの中には、以下のようにしてUID/GIDの範囲を指定します。
jailerおよび各prisonerごとにminuid/maxuid、およびmingid/maxgidを設定し、jailerおよび各prisoner間で同じUID/GIDが割り当てられないようにしてください。
defaultpasswd no minuid 10000 maxuid 19999 mingid 10000 maxgid 19999
上記の例では、UIDおよびGIDがそれぞれ10000〜19999の範囲に入るように割り当てられます。
ただし、この作業をしても「root」等の予め定義されているユーザ、および「pgsql」や「postfix」等のports経由で追加されるユーザについては、jailerとprisonerで同じUID/GIDが割り当てられます。
これらのユーザのUID/GIDを変更するには、「vipw」を利用して、直接UID/GIDを変更してください。
(「vipw」でUID/GIDを変更しても、これらのユーザが所有者となっているファイルのオーナーのUID/GIDは自動的には更新されません。自分でchown/chgrpしてやる必要があるので、注意してください。)
次に各prisonerでデバイスが利用できるよう、各prisonerのdevをマウントしておきます。
$ mount -t devfs devfs /home/jail/prisoner1/dev $ mount -t devfs devfs /home/jail/prisoner2/dev $ mount -t devfs devfs /home/jail/prisoner3/dev $ mount -t devfs devfs /home/jail/prisoner4/dev
上記までで、jailer側から可能なprisonerの設定は終わりました。
次は、「chroot」を利用して各prisonerのルートディレクトリに遷移し、必要な設定を行います。
「chroot /home/jail/prisoner1」のように、各prisonerごとにルートディレクトリに遷移し、以下の作業を行ってください。
(chrootしたディレクトリからjailer側に戻るときには「exit」を利用してください。)
$ touch /etc/wall_cmos_clock $ ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime $ passwd root
一番最後の処理では、rootのパスワードを変更しています。
そのprisonerにおけるrootのパスワードを設定してください。(このパスワードはjailerのrootのパスワードとは区別されます。)
上記までで、各prisoner内の設定は完了しました。
最後に、jailer側で各prisoner側に割り当てるIPアドレス、FQDN等を設定します。
「/etc/rc.conf」に以下のように追記してください。
ifconfig_re0_alias0="inet 192.168.0.21 netmask 255.255.255.255" ifconfig_re0_alias1="inet 192.168.0.22 netmask 255.255.255.255" ifconfig_re0_alias2="inet 192.168.0.23 netmask 255.255.255.255" ifconfig_re0_alias3="inet 192.168.0.24 netmask 255.255.255.255" jail_enable="YES" jail_list="prisoner1 prisoner2 prisoner3 prisoner4" #jail_sysvipc_allow="YES"
FreeBSD 8.xでは、各prisonerの設定は「/etc/rc.conf」に直接記述していましたが、FreeBSD 9.1より「/etc/jail.conf」に分けて記述するようになりました。
各prisonerの設定を下記の通り、「/etc/jail.conf」に記述します。
prisoner1 { jid=1; path="/home/jail/prisoner1"; ip4.addr=192.168.0.21; host.hostname="prisoner1.local.kishiro.com"; allow.chflags=1; allow.raw_sockets=1; exec.start="/bin/sh /etc/rc"; exec.stop="/bin/sh /etc/rc.shutdown jail"; interface=re0; mount.devfs; devfs_ruleset=4; } prisoner2 { jid=2; path="/home/jail/prisoner2"; ip4.addr=192.168.0.22; host.hostname="prisoner2.local.kishiro.com"; allow.chflags=1; allow.raw_sockets=1; exec.start="/bin/sh /etc/rc"; exec.stop="/bin/sh /etc/rc.shutdown jail"; interface=re0; mount.devfs; devfs_ruleset=4; } prisoner3 { jid=3; path="/home/jail/prisoner3"; ip4.addr=192.168.0.23; host.hostname="prisoner3.local.kishiro.com"; allow.chflags=1; allow.raw_sockets=1; exec.start="/bin/sh /etc/rc"; exec.stop="/bin/sh /etc/rc.shutdown jail"; interface=re0; mount.devfs; devfs_ruleset=4; } prisoner4 { jid=4; path="/home/jail/prisoner4"; ip4.addr=192.168.0.24; host.hostname="prisoner4.local.kishiro.com"; allow.chflags=1; allow.raw_sockets=1; exec.start="/bin/sh /etc/rc"; exec.stop="/bin/sh /etc/rc.shutdown jail"; interface=re0; mount.devfs; devfs_ruleset=4; }
jailでは、jailerで認識しているネットワークデバイス(IPアドレスが割り当て済みのもの)に対し、別のIPアドレスを複数割り当て(alias)、それらのIPアドレスを各prisonerに設定します。
上記の「/etc/rc.conf」の例では、「re0」に対し、jailer側で割り当てられているIPアドレスとは別に、「192.168.0.21〜192.168.0.24」のIPアドレスを割り当てています。
また、aliasされたIPアドレスは「/etc/jail.conf」の中の各prisoner設定内の「ip4.addr=」の部分で各prisonerに割り当てています。
「/etc/rc.conf」内の「jail_sysvipc_allow=」は、prisoner上のプロセスからシェアドメモリにアクセスさせるか否かの設定です。
セキュリティの観点からすると「NO」にしておくのがベストですが、PostgreSQLなどのシェアドメモリを利用するソフトウェアを動かす際には「YES」に設定してください。
また、デフォルトの状態では、prisonerではtracerouteやping、bpfを利用したWIDE-DHCP等のraw socketを利用するプログラムが正常に動作しません。
これらのプログラムを利用するためには、「/etc/jail.conf」内の各prisoner設定内に、「allow.raw_sockets;」の設定を追記してください。
以上で設定は完了です、システムを再起動させて各prisonerを有効にしてください。
上記の通り設定しシステムを再起動しても、コンソールに「cannot start jail」等と表示され、上手く動かない場合があります。
この場合は、jailの起動スクリプトである「/etc/rc.d/jail」の中身を確認し、どこでエラーが出ているのかを確認してください。
私も、FreeBSD 8.4-RELEASEで「cannot start jail」というメッセージが表示され、jailが起動できない現象に見舞われましたが、原因はFreeBSD 8.4-RELEASEで書き換えられた「/etc/rc.d/jail」の下記部分でした。
eval ${_setfib} jail -n ${_jail} ${_flags} -i -c path=${_rootdir} host.hostname=${_hostname} ip4.addr=\"${_addrl}\" ip6.addr=\"${_addr6l}\" ${_parameters} command=${_exec_start} > ${_tmp_jail} 2>&1
最後のリダイレクト部分を取ると、コンソールにエラーメッセージが表示されます。
私の場合は、予めシステムをIPv4のみでビルドしていたため、jailから「ip6.addr=」パラメータのサポートが無くなったことによるエラーでした。(未知のパラメータが指定されている旨のエラーが出ていました。)
上記の「ip6.addr=\"${_addr6l}\"」部分を削除し、無事に起動するようになりました。
prisoner内のportsが古くなったり、設定を誤ったりして、prisoner環境を再度一から構築しなおす場合には、以下のようにしてprisonerを削除し、また上記手順を踏んで環境を再構築してください。
$ /etc/rc.d/jail stop prisoner1 $ chflags -R noschg /home/jail/prisoner1/* $ rm -R /home/jail/prisoner1/*
上記の例では、prisoner1を停止させ、そのルートディレクトリ下のファイルをすべて削除します。
jailerから各prisonerを制御するためのコマンドがいくつか用意されています。
jailer上で「jis」と入力すると、現在稼動中のprisonerの一覧を下記のように表示させることができます。
JID IP Address Hostname Path 1 192.168.0.21 prisoner1.local.kishiro.com /home/jail/prisoner1 2 192.168.0.22 prisoner2.local.kishiro.com /home/jail/prisoner2 3 192.168.0.23 prisoner3.local.kishiro.com /home/jail/prisoner3 4 192.168.0.24 prisoner4.local.kishiro.com /home/jail/prisoner4
一番左側に表示される「JID」はprisonerに割り振られたIDで、後述する「jexec」で遷移するprisonerを特定する際に利用します。
jailer上から「jexec」を利用することによって、任意のprisonerにrootとして遷移することができます。
このコマンドはrootのみ実行できます。
$ jexec 1 /bin/csh
上記の例では、JIDが「1」のprisonerに「/bin/csh」のシェルを利用して遷移します。
prisonerの開始/停止については、「/etc/rc.d/jail」を利用して実現できます。
prisonerすべてを停止させるには、以下のようにします。
$ /etc/rc.d/jail stop
逆に、すべてのprisonerを開始するには、以下のようにします。
$ /etc/rc.d/jail start
すべてのprisonerを開始/停止させるだけでなく、特定のprisonerを指定して開始/停止させることも可能です。
特定のprisonerを指定して開始/停止させるには、以下のようにします。
$ /etc/rc.d/jail start prisoner1
$ /etc/rc.d/jail stop prisoner1
上記手順で作成したprisoner環境では、「sysinstall」が利用できないため、このユーティリティを利用したユーザ/グループの追加ができません。
prisoner環境でユーザ/グループを追加するには、以下のようにしてください。
$ adduser
$ pw groupadd user
上記の例では「user」という名のグループを作成しています。
その他、ユーザを削除する「rmuser」、ホームディレクトリの場所などのユーザの設定情報を変更できる「chpass」などのコマンドが便利です。
prisoner上では無論sshdやapache等を動作させることが可能ですが、その際に1つだけ注意する点があります。
それは、prisoner上やjailer上で動作させるsshd/apacheの設定で、明示的にListenするIPアドレスおよびPort番号を明示しなくてはいけない点です。
たとえば、jailer側でsshdを動作させている場合、設定ファイルの中でListenするIPアドレスを明示的に指定していないことが多いと思います。
ListenするIPアドレスを明示していない場合、sshdはそのマシンに割り当てられているIPアドレスのすべてでListenします。
aliasしたIPアドレスについても「マシンに割り当てられているIPアドアドレス」にあたるため、prisonerに割り当てられているIPアドレスでjailer上のsshdがListenしてしまうことになり、結果的にprisoner上でsshdが起動できなくなってしまいます。
これを回避するために、jailerの「/etc/ssh/sshd_config」に下記の一行を追加してください。
ListenAddress 192.168.16.128
「192.168.16.128」にはjailerのIPアドレスを指定してください。
また、prisoner上でsshdを動作させる際にも、prisonerの「/etc/ssh/sshd_config」内の「ListenAddress」に、そのprisonerのIPアドレスを指定するようにしてください。
上記はsshdの例でしたが、apacheでも同様にjailer上のapacheがListenするIPアドレス、およびprisoner上のapacheがListenするIPアドレスを明示的に指定する必要があります。
apacheではjailer上およびprisoner上の「/usr/local/etc/apache/httpd.conf」に下記の一行を追加してください。
Listen 192.168.16.128:80 Listen 192.168.16.128:443
「192.168.16.128」の部分には、それぞれjailerのIPアドレス、prisonerのIPアドレスを指定してください。
また、80番ポートはHTTP、443ポートはHTTPSになりますので、必要なもののみ適宜してください。
次に、prisoner上でPostgreSQLを動作させる際の注意点ですが、PostgreSQLはOSのシェアドメモリを利用するため、設定が多少煩雑です。
まずはじめに、jailerの「/etc/rc.conf」に以下の記述があるか確認し、なければ追記してください。
jail_sysvipc_allow="YES"
上記の設定は前述した通り、prisoner内のプロセスからjailerのシェアドメモリにアクセスさせるための設定です。
次に、複数のprisoner上およびjailer上で同時にPostgreSQLを動作させるには、このシェアドメモリ内の利用が重複しないよう、各prisoner上およびjailer上のpgsqlユーザのUID/GIDを重複がないように修正してやる必要があります。
PostgreSQLを動作させるprisonerごとに、prisoner内で「vipw」を利用し、「pgsql」ユーザのUID/GIDを重複しないよう変更します。
pgsql:*:10070:10070:PostgreSQL Daemon:/usr/local/pgsql:/bin/sh
続いて「/etc/group」を編集し、pgsqlのグループIDを変更してください。
pgsql:*:10070:
「vipw」でUID/GIDを変更しても、これらのユーザが所有者となっているファイルのオーナーのUID/GIDは自動的には更新されません。
PostgreSQLをportsからインストールした直後の状態では、「pgsql」がオーナーのファイルは「/usr/local/pgsql」フォルダのみですので、下記のようにしてオーナーをpgsqlに変更しなおしてください。
$ cd /usr/local $ chown -R pgsql pgsql $ chgrp -R pgsql pgsql
あとは、「PostgreSQL環境を構築する(PostgreSQL 8.x)」の内容を参考にして、DB等の設定を行ってください。
あと、prisoner上のPHP環境等から「localhost」上のDBに接続を試みても、同じprisoner上で動作するPostgreSQLに接続できない場合があります。
(その際、該当するprisonerの「/var/log/message」に以下のメッセージが表示されます)
FATAL: no pg_hba.conf entry for host "192.168.16.128", user "pgsql", database "xxxx"
この問題を回避するには、「/usr/local/pgsql/data/pg_hba.conf」の最後に以下の一行を追加してください。
host all all 192.168.16.128/32 trust
「192.168.16.128」の部分には、PostgreSQLを動作させているprisonerのIPアドレスを指定してください。
prisonerでデフォルトで利用できるデバイスは、devfsの「devfsrules_jail」というプロフィールで規定された最低限のものに限られています。
このため、標準の状態では、prisonerにインストールしたmplayerでMP3を鳴らしたり、prisonerにインストールしたffmpegにbktrドライバ経由でTV画像をキャップチャさせ、ストリーミング配信したりすることはできません。
これらを実現するためには、特定のprisonerにデバイスへのアクセスを許可する必要があります。
特定のprisonerにデバイスへのアクセスを許可する方法については、「jail環境でデバイスへのフルアクセスを許可する」を参照してください。
portsツリーなど、ファイル・ディレクトリを複数のprisonerで共有したいというニーズは多いのではないかと思います。
複数のprisonerでファイルシステムを共有するには、「nullfs」と呼ばれる特殊なファイルシステムを利用します。
「nullfs」を利用して複数のprisonerでファイルシステムを共有する方法については、「複数のjailクライアント(prisoner)からファイルシステムを共有する (nullfs)」を参照してください。
FreeBSDを安全に使う上で、freebsd-updateによるセキュリティパッチはOSの更新は必須です。
jailerやprisonerをfreebsd-updateで更新するには「jail環境をfreebsd-updateで更新する」を参照してください。
・「/etc/jail.conf」の凡例を設定内容が明示的にわかるように変更。
・FreeBSD 13.2-RELEASEでの動作確認に合わせて修正。
・FreeBSD 13より、jail.confの中に「name=""」でjail名を再定義するとエラーが発生してjailが起動しなくなるため、該当の記述を除去。
(FreeBSD 12でも、name指定をしなくても問題なく動作します。)
・hosts内の記述例を、他の設定方法に合致する形に変更。
・「jailerやprisonerをfreebsd-updateで更新するには」を追加。
・FreeBSD 12.2-RELEASEでの動作確認に合わせて修正。
・「/etc/jail.conf」内の「devfs.ruleset」の誤記を「devfs_ruleset」に訂正。
・FreeBSD 10.xをターゲットとして全面的に書き直し。
(FreeBSD 8.x向け構築方法のバックナンバーはこちら)
・「jailがうまく起動しない場合のトラブルシューティング」を追記。
・prisonerでユーザを削除したり、ユーザの設定情報を変更したりする場合に便利なコマンド「rmuser」「chpass」に関する記述を追記。
・不要ファイルリストの公開停止に伴い、「不要なファイルのリストの取得」の項目を削除。
・prisoner設定用のシェルスクリプトを修正。
・「複数のprisonerでファイルシステムを共有するには」を追加。
・FreeBSD 7.x以降では「mount_devfs」が無くなっている為、「mount -t devfs」を使ってdevfsをマウントする方法を追記。
・「/usr/local/pgsql/data/pg_hba.conf」への追記を促す箇所の凡例のキャプション文言が間違っていたのを訂正。
・頂いたメッセージは管理者のチェックの後、公開されます。
・メッセージの公開を希望されない場合には、「このメッセージを非公開にする」にチェックを入れてください。
・管理者が不適切と判断したメッセージは公開しませんので、予めご了承ください。
まだ評価がありません |
表示できるメッセージはありません。