BSD界隈四方山話

第3回複数バージョンのFreeBSDをJailで共有させる方法

現実問題としてのバージョン固定化

管理の簡単さやセキュリティの面などいくつかの側面から、どのホストも軒並み最新版へアップグレードし続けることが望まれるわけですが、実際にはそう言う状況にはなりません。一度動き出したシステムはできるだけアップグレードを避けたがる傾向があり、複数のバージョンが混在しているというのはよく見られる光景です。

しかし、ハードウェアの耐用年数には限界がありますので、いずれは環境を新しいハードウェアへ移行させるなり、仮想環境へ移行させる必要があります。ここで問題になるのは古いカーネルのデバイスドライバです。Intelが2年おきに新しいハードウェアをリリースしていますので、さまざまなチップが2年おきに新しくなっています。

つまり、2年以上経過すると、ドライバをアップデートするなりバックポートするなりしないと、動作しないケースが増えます。たとえば10.1-RELEASEが出ている今の状況で6.1の環境を現在のハードウェアに持ってきても、動かないケースが多いと思います。仮想環境であればレガシーモードで古いハードウェアの設定に切り替えて使うところですが、直接ハードウェアとなると難しいものがあります。

複数のバージョンのユーザランドを混在させる

こうした場合にはいくつか対処方法がありますが、そうしたものの1つにJailを使うという方法があります。カーネルは後方互換性を保持していることが多いので、最新のカーネルを使いつつ、ユーザランドは古いものを持ってきて使う、と言った内容です。

アプリケーションによっては特定のバージョンでないといろいろ都合が悪いとか、アップデートするつもりがそもそもないといった場合には、この方法が使えます。Jailの環境内だけを別のバージョンにしてカーネルはホストの提供する最新版を使います。

この方法はXenやKVM、bhyve、VMware、VirtualBoxなどの仮想化技術を使う場合と比べて、消費するリソースがとても少ないという特徴があります。異なるカーネルや異なるオペレーティングシステムを混在させるにはハイパーバイザ系の仮想化技術を採用する必要がありますが、単一のカーネルで良い場合にはJailが最適でしょう。

Jailってなに

Jailは最初にFreeBSDに実装された区画化技術の1つで、基本的にはファイルシステムとプロセス空間をホストから切り離して独立しているかのように見せかける技術です。もともと一般ユーザにroot権限を付与する技術として考案されました。

UNIX系のオペレーティングシステムは基本的にスーパーユーザ(root)とそれ以外のユーザと言う区別しかもっておらず、rootの権限の一部を一般ユーザに付与するという機能を基本的に提供していません(setuid、setgid、sticky bitみたいな仕組みはあります⁠⁠。

Jailはこれを可能にする技術で、Jail内部では一般ユーザにrootのアカウントを付与できます。一般ユーザはJail空間の中ではrootとして振る舞うことができるのですが、このrootはできることが限られていて、ホストやほかのJailに影響が出ないようになっています。

この機能を利用すれば、たとえば10.1-RELEASEの中に9.3-RELEASEのユーザランドを構築して利用すると言ったことができます。Jailが登場した当時、この機能を使うのは少々面倒でしたが、10系に入ってからは管理機能が本体に導入されたので、簡単に利用できるようになりました。

9.3-RELEASEのJailを作ってみる

9.3-RELEASEのJail環境を作るには、次のように成果物をダウンロードしてきて展開すれば終了です。スーパーユーザで展開してください。

図1 Jail環境を用意する
# mkdir /path/to/j9
# cd /path/to/j9/
# fetch ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/amd64/9.3-RELEASE/base.txz
# tar zxpf base.txz
# rm base.tgz

base.txzを展開するとルートディレクトリに展開されるいつものファイルやディレクトリがあることを確認できます。

図2 配布物を展開すればそのまま利用できる
# tree -L 1
.
├── COPYRIGHT
├── bin
├── boot
├── dev
├── etc
├── lib
├── libexec
├── media
├── mnt
├── proc
├── rescue
├── root
├── sbin
├── sys -> usr/src/sys
├── tmp
├── usr
└── var

15 directories, 2 files
# 

ここでは/Users/daichi/j9というディレクトリにファイルを展開したので、/etc/jail.confファイルを作成して次のような設定を追加してあげます。

リスト1 /etc/jail.confに追加する設定
j9 {
        jid = 9;
        name = j9;
        path =/Users/daichi/j9;
        ip4.addr = 192.168.1.252;
        host.hostname = jail9.ongs.co.jp;
        allow.raw_sockets;
        exec.start = "/bin/sh /etc/rc";
        exec.stop = "/bin/sh /etc/rc.shutdown";
        interface = bge0;
        mount.devfs;
}

意味は書いてあるとおりですが、簡単に説明すると次のようになります。

  • jid:JailのID番号
  • name:Jailの名前
  • ip4.addr:Jailに割り当てるIPv4アドレス
  • host.hostname:Jailに割り当てるホスト名
  • allow.raw_sockets:ping(8)が通るようにする
  • exec.start:起動時の処理
  • exec.stop:終了時の処理
  • interface:割り当てるホスト側のNIC
  • mount.dev.fs:/dev/を生やす

いろいろな設定ができます。このあたりの設定はjail.conf(5)のオンラインマニュアルにまとまっていますので、使用する際には読んでみてください。

Jailを作成したら次のようにコマンドを実行して環境を起動します。

図3 作成したJailの起動
# service jail start j9
Starting jails: j9.
# 

Jail環境はjls(8)コマンドで表示させることができます。ここでは今作成したj9というJail以外にもj1という環境が動作していることがわかります。

図4 作成したJail環境を一覧表示
# jls
   JID  IP Address      Hostname                      Path
     1  192.168.1.251   jail1.ongs.co.jp              /Users/daichi/j1
     9  192.168.1.252   jail9.ongs.co.jp              /Users/daichi/j9
# jls -v
   JID  Hostname                      Path
        Name                          State
        CPUSetID
        IP Address(es)
     1  jail1.ongs.co.jp              /Users/daichi/j1
        j1                            ACTIVE
        2
        192.168.1.251
     9  jail9.ongs.co.jp              /Users/daichi/j9
        j9                            ACTIVE
        3
        192.168.1.252
# 

Jailの中へダイブ

起動したJail環境へダイブするにはjexec(8)コマンドを使う方法が簡単です。次のようにJail名を指定して、そのJail環境のどのコマンドを実行するかを指定すると、指定したJailの中でそのコマンドが実行されます。

図5 作成したJail環境へダイブ
# jexec j9 /bin/sh      ← ホストで実行
# ifconfig              ← ここはもうJail9の中
bge0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=c019b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,TSO4,VLAN_HWTSO,LINKSTATE>
        ether fc:aa:14:a2:25:4a  すでにIPも設定されている
        inet 192.168.1.252 netmask 0xffffffff broadcast 192.168.1.252
        media: Ethernet autoselect (1000baseT <full-duplex>)
        status: active
bge1: flags=8802<BROADCAST,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=c019b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,TSO4,VLAN_HWTSO,LINKSTATE>
        ether fc:aa:14:a2:25:4b
        media: Ethernet autoselect
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
        options=600003<RXCSUM,TXCSUM,RXCSUM_IPV6,TXCSUM_IPV6>
#

uname(1)を実行してみましょう。9.3-RELEASEの環境なのですが、カーネルはホスト側の10.1なのでそちらの情報が出力されます。freebsd-version(1)を使うとカーネルとユーザランドのバージョンを個別にチェックできます(が、9.3-RELEASEにはまだfreebsd-version(1)が登場していませんので、その方法は使えません⁠⁠。

図6 カーネルは10.1なのでuname(1)の出力はバージョン10.1になる
# uname -a
FreeBSD jail9.ongs.co.jp 10.1-RELEASE-p9 FreeBSD 10.1-RELEASE-p9 #0: Tue Apr  7 01:09:46 UTC 2015     root@amd64-builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC  amd64
#

たとえばユーザランドのファイルを見てみると、ファイルが9.3-RELEASEのものであることを確認できます。

図7 ユーザランドは9.3なので、配布物の内容は9.3になっている
# head -30 /COPYRIGHT
# $FreeBSD: releng/9.3/COPYRIGHT 267655 2014-06-20 00:13:56Z gjb $
#       @(#)COPYRIGHT   8.2 (Berkeley) 3/21/94

The compilation of software known as FreeBSD is distributed under the
following terms:

Copyright (c) 1992-2014 The FreeBSD Project. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS \`\`AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

The 4.4BSD and 4.4BSD-Lite software is distributed under the following
#

Jail環境を微調整

この環境は配布物を展開しただけなので、少々設定が足りていません。いつもはインストーラで行っている設定をしてあげましょう。

まず、次のようにタイムゾーンを日本標準時に設定します。なにも設定しないとUTCのままなので、時刻がホストとずれて表示されているからです。

図8 タイムゾーンをJSTへ設定
# date
Mon May 11 10:48:54 UTC 2015
# tzsetup Asia/Tokyo    ← タイムゾーンをJSTへ設定
# date
Mon May 11 19:49:21 JST 2015
#

FreeBSDがcron(8)経由で定期的に実施している処理は、ゲストとしてJailで動作する場合には不要ですので(なにせホストがすでにその処理をやっていますので⁠⁠、次のように/etc/crontabファイルの設定をコメントアウトして、cron(8)を再起動しておきます。

リスト2 /etc/crontabで不要な処理をコメントアウト
# /etc/crontab - root's crontab for FreeBSD
#
# $FreeBSD: releng/9.3/etc/crontab 194170 2009-06-14 06:37:19Z brian $
#
SHELL=/bin/sh
PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin
#
#minute hour    mday    month   wday    who     command
#
#*/5    *       *       *       *       root    /usr/libexec/atrun
#
# Save some entropy so that /dev/random can re-seed on boot.
#*/11   *       *       *       *       operator /usr/libexec/save-entropy
#
# Rotate log files every hour, if necessary.
#0      *       *       *       *       root    newsyslog
#
# Perform daily/weekly/monthly maintenance.
#1      3       *       *       *       root    periodic daily
#15     4       *       *       6       root    periodic weekly
#30     5       1       *       *       root    periodic monthly
#
# Adjust the time zone if the CMOS clock keeps local time, as opposed to
# UTC time.  See adjkerntz(8) for details.
#1,31   0-5     *       *       *       root    adjkerntz -a
図9 cron(8)を再起動
# service cron restart
Stopping cron.
Waiting for PIDS: 4966.
Starting cron.
#

最終的にはssh(1)でログインして利用する方が便利ですので、adduser(8)コマンドでユーザを追加します。

図10 ユーザを追加
# adduser
Username: daichi
Full name: Daichi GOTO
Uid (Leave empty for default):
Login group [daichi]:
Login group is daichi. Invite daichi into other groups? []:
Login class [default]:
Shell (sh csh tcsh nologin) [sh]:
Home directory [/home/daichi]:
Home directory permissions (Leave empty for default):
Use password-based authentication? [yes]:
Use an empty password? (yes/no) [no]:
Use a random password? (yes/no) [no]:
Enter password:
Enter password again:
Lock out the account after creation? [no]:
Username   : daichi
Password   : *****
Full Name  : Daichi GOTO
Uid        : 1001
Class      :
Groups     : daichi
Home       : /home/daichi
Home Mode  :
Shell      : /bin/sh
Locked     : no
OK? (yes/no): yes
adduser: INFO: Successfully added (daichi) to the user database.
Add another user? (yes/no): no
Goodbye!
#

DNSも使えるようにする必要があるので、/etc/resolv.confにDNSサーバの設定を加えます。9.3-RELEASEだとdig(1)コマンドで設定が動作しているか確認できます。10系以降はdrill(1)コマンドで動作を確認します。

リスト3 /etc/resolv.confにDNSサーバのIPを設定
nameserver 8.8.8.8
図11 DNSの動作を確認
# dig gihyo.jp

; <<>> DiG 9.9.5 <<>> gihyo.jp
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 31468
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;gihyo.jp.                      IN      A

;; ANSWER SECTION:
gihyo.jp.               9628    IN      A       49.212.34.191

;; Query time: 9 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Mon May 11 19:58:30 JST 2015
;; MSG SIZE  rcvd: 53

#

sshdを有効にする設定を/etc/rc.confに追加して、次のようにservice(8)コマンドを実行してsshd(8)デーモンを起動します。

リスト4 /etc/rc.confにsshd(8)を起動するための設定を追加
sshd_enable="YES"
図12 sshd(8)デーモンを起動
# service sshd start
Generating RSA1 host key.
2048 81:c1:8e:3e:e7:c3:67:d9:48:1b:18:32:75:7e:68:4d  root@jail9.ongs.co.jp (RSA1)
Generating RSA host key.
2048 da:fe:8a:54:29:6e:f4:bd:14:6a:1e:49:35:35:56:0a  root@jail9.ongs.co.jp (RSA)
Generating DSA host key.
1024 71:37:48:5b:09:ee:37:1c:d6:38:11:b6:f2:63:d9:35  root@jail9.ongs.co.jp (DSA)
Generating ECDSA host key.
256 dd:e3:3c:17:00:33:25:0f:41:ca:7b:86:fc:67:20:1d  root@jail9.ongs.co.jp (ECDSA)
Generating ED25519 host key.
256 d6:1d:c8:53:7e:98:84:08:60:df:ef:b6:4f:52:6b:67  root@jail9.ongs.co.jp (ED25519)
Performing sanity check on sshd configuration.
Starting sshd.
#

Jail環境はホストとも別のJailともプロセス空間が隔離されているので、ps(1)でプロセスを表示させるとJail環境内で動作している以外のプロセスが表示されません。カーネルスレッドなども見えませんし、ホストで動作しているプロセスも見えません。

図13 Jail 9.3環境で動作しているプロセス
# ps auxwwd
USER  PID %CPU %MEM   VSZ  RSS TT  STAT STARTED    TIME COMMAND
root 4907  0.0  0.0 12080 1828 ??  SsJ   7:32PM 0:00.02 /usr/sbin/syslogd -s
root 4961  0.0  0.1 20336 4464 ??  SsJ   7:32PM 0:00.06 sendmail: accepting connections (sendmail)
root 6640  0.0  0.0 14188 1816 ??  IsJ   7:52PM 0:00.00 /usr/sbin/cron -s
root 7461  0.0  0.1 49276 5664 ??  IsJ   8:01PM 0:00.00 /usr/sbin/sshd
root 5890  0.0  0.0 14544 2336  2  SJ    7:43PM 0:00.15 /bin/sh
root 7564  0.0  0.0 16300 1816  2  R+J   8:03PM 0:00.00 - ps auxwwd
#

ホストやほかのホストからsshでログイン

ちなみにホストではbge0に対して複数のIPアドレスが割り当てられているように見えます。Jail環境はホストと同じように、ほかのホストからネットワーク経由でログインできます。

図14 ホストからはこのようにNICが設定されている
% ifconfig bge0
bge0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=c019b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,TSO4,VLAN_HWTSO,LINKSTATE>
        ether fc:aa:14:a2:25:4a
        inet 192.168.1.101 netmask 0xffffff00 broadcast 192.168.1.255
        inet 192.168.1.251 netmask 0xffffffff broadcast 192.168.1.251
        inet 192.168.1.252 netmask 0xffffffff broadcast 192.168.1.252
        nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
        media: Ethernet autoselect (1000baseT <full-duplex>)
        status: active
% 

ログインすると次のようになります。

図15 ssh(1)でJail 9.3ゲストにログインして作業
% ssh daichi@192.168.1.252
The authenticity of host '192.168.1.252 (192.168.1.252)' can't be established.
ECDSA key fingerprint is dd:e3:3c:17:00:33:25:0f:41:ca:7b:86:fc:67:20:1d.
No matching host key fingerprint found in DNS.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.1.252' (ECDSA) to the list of known hosts.
Password for daichi@jail9.ongs.co.jp:
Last login: Mon May 11 20:17:29 2015 from 192.168.1.252
FreeBSD ?.?.?  (UNKNOWN)

Welcome to FreeBSD!

Before seeking technical support, please use the following resources:

o  Security advisories and updated errata information for all releases are
   at http://www.FreeBSD.org/releases/ - always consult the ERRATA section
   for your release first as it's updated frequently.

o  The Handbook and FAQ documents are at http://www.FreeBSD.org/ and,
   along with the mailing lists, can be searched by going to
   http://www.FreeBSD.org/search/.  If the doc package has been installed
   (or fetched via pkg_add -r lang-freebsd-doc, where lang is the
   2-letter language code, e.g. en), they are also available formatted
   in /usr/local/share/doc/freebsd.

If you still have a question or problem, please take the output of
\`uname -a', along with any relevant error messages, and email it
as a question to the questions@FreeBSD.org mailing list.  If you are
unfamiliar with FreeBSD's directory layout, please refer to the hier(7)
manual page.  If you are not familiar with manual pages, type \`man man'.

Edit /etc/motd to change this login announcement.

$

バージョン番号が不確定なのが嫌ということであれば、一度だけservice motd startとコマンドを実行してみてください。これでログイン時に10.1-RELEASEと表示されるようになります。実際は9.3-RELEASEのユーザランドなので、気持ちが悪いのであれば/etc/motdファイルを直接編集して書き換えてください。

とっても簡単、Jailを活用しよう

慣れてくると、たとえば上記をすべて手動でやったとしても作業にかかる時間は5分から10分と言ったところです。こうした環境は別バージョンの動作を確認したい場合とか、クリーンな環境で動作を確認したいと言った場合にも使用できます(もちろんカーネルが違うバージョンを使うことになるので、互換性のない部分は動きませんよ⁠⁠。

とくに10系からはJailの扱いが簡単になっていますので、だいぶ前にJailを使った以来使ったことがないとか、そもそも仮想化ソフトウェアは使ったことがあるけれど、Jail系の技術は使ったことがないという場合には、一度試して欲しいと思います。慣れてくるとアンダー・ザ・コントロールな機能でとても良い感じです。

おすすめ記事

記事・ニュース一覧