🍣すしでもどうですか

略してすしどう。趣味のサーバーいじりとかメモ。

とりあえず Jail

看守してみたかった

せっかく FreeBSD なので、用途ごとに環境を Jail で分けてすっきりさせたかった。

まっさらにしたのを機に Jail はじめました。

環境

  • さくらの VPS 1Gプラン(RAM 1GB、HDD 100GB)
  • FreeBSD 12.0-RELEASE
  • ZFS (zroot) です。

どういう構成にするか?

ホストとなる環境では極力デーモンを走らせず、各 Jail 内に収めるようにする。 こちらの記事を大いに参考にさせていただきました。

qiita.com

これと同じように、外からの SSH は pf によるポートフォワードでポートごとに Jail を振り分けています。

再インストール前は pf ではなく ipfw を使っていたのですが、ipfw で NAT しつつ Jail でサービスを公開するという方法がよく分からず頓挫していたんですよね…pf だと楽ちんでした。

HTTP(S) に関しては、玄関口となる Jail を1つ立てて、nginx でホスト名を見て振り分けする予定。

Jail つくるぞ

id:dankogai さんのこちらの記事を参考に。とりあえずやることがさらっと書いてあって分かりやすかった。

blog.livedoor.jp

まずはホスト環境で、Jail 毎に IP アドレスを用意します。 弾さんの記事にもあるように、別にホスト環境の IP アドレスをそのまま使ってもいいようですが、分けたほうがよりスッキリするかなあと思いました。

/etc/rc.conf

(前略)
# lo0 に IP アドレスを振る
ifconfig_lo0="inet 127.0.0.1"
ifconfig_lo0_alias0="inet 10.0.0.254 10.0.0.255 netmask 255.255.255.0"
ifconfig_lo0_alias1="inet 10.0.0.11"
ifconfig_lo0_alias2="inet 10.0.0.12"
:
:

# pf 関係
pf_enable="YES"
pf_rules="/etc/pf.conf"
pf_flags=""
pflog_enable="YES"
pflog_logfile="/var/log/pflog"
pflog_flags=""

# Jail
jail_enable="YES"
(以下略)

こんな感じで、必要なだけ IP アドレスを列挙。 pf と Jail を有効にしておきます。

/etc/pf.conf

rc.conf で出てきた pf.conf を用意します。pf のルールを指定するファイルです。

(最初の記事の例のほとんどそのままです)

(前略)
# external interface
ext_if="vtnet0"

# internal interface
int_if="lo0"

# NW setting for internal NW
table <private> const { 10.0.0.0/24 }

# IP mascarade
nat on $ext_if inet from ($int_if) to ! <private> -> ($ext_if)

## ssh
rdr pass on $ext_if proto tcp from any to ($ext_if) port 2211 -> 10.0.0.11 port 22
rdr pass on $ext_if proto tcp from any to ($ext_if) port 2212 -> 10.0.0.12 port 22
:
:

## httpd
rdr pass on $ext_if proto tcp from any to ($ext_if) port http -> 10.0.0.12 port http
rdr pass on $ext_if proto tcp from any to ($ext_if) port https -> 10.0.0.12 port https

# packet filter
block in log all
pass in proto tcp to any port ssh
pass out all keep state

ここで、10.0.0.12 を HTTP(S) の窓口に割り振るため、SSH 以外に HTTP(S) のポートをそちらにパスさせる設定を追加しています。 また、ホスト環境にも SSH したいのでそれも追加。

/etc/jail.conf

# common
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.clean;
mount.devfs;
interface = lo0;
path = "/jails/$name";
host.hostname = $name;
allow.chflags;
allow.raw_sockets;

# each jail
base {
       ip4.addr = 10.0.0.11;
}

httpd {
        ip4.addr = 10.0.0.12;
}

"# each jail" の下のように書いておくと起動時にいちいち IP アドレスとか指定しなくて良くて楽なんですが、ここに書かれた Jail はホストの起動時に自動的に start されてしまうようです。何か書けば止められるのかな…

rc.conf、pf.conf、jail.conf が整ったら一旦再起動しましょう。

Jail 環境の雛形を作る

FreeBSD の再インストール時に、ルートファイルシステムZFS にしておきました。Jail 毎に ZFS の dataset を割り当てるととても楽そうだからです。

まずは、base という名前で、Jail 環境の雛形を作ってみます。

# zfs create -o mountpoint=/jails zroot/jails
# zfs create zroot/jails/base
# bsdinstall jail /jails/base

見慣れたインストーラーでインストールが始まります。base と lib32 があればとりあえず動きます。

Jail 起動

# service jail start base

として起動します。うまく起動できれば、

# jls
   JID  IP Address      Hostname                      Path
     1  10.0.0.11      base.example.com              /jails/base

みたいな感じでリストに表示されます。

次に、Jail 環境のコンソールに入ってみます。

# jexec base
root@base:/ #

こうなればとりあえず Jail の起動成功です。

できたら、この段階で freebsd-update もやっておきましょう(作業は省略)。

ひと通り眺めたら、Jail のシェルを抜けてから

# service jail stop base

で止めておきます。

雛形環境の複製

ZFS の本領発揮です。

# zfs snapshot zroot/jails/base@12.0p11

これで dataset のスナップショットが取れます。@ の後ろはスナップショットの分かりやすい名前を付けます(ここではリリース番号+パッチ番号)。

そしてこれを複製します。

# zfs clone zroot/jails/base@12.0p11 zroot/jails/httpd

これで、base が httpd にそっくりクローンされました。

名前を変えて繰り返せば、雛形からいくらでも複製できます。

ちなみに、不要になった環境は

# zfs destroy zroot/jails/httpd

とすれば消え去ります。スナップショットも destroy で消せますが、クローン元となっているファイルシステムやスナップショットは、クローンした dataset があるうちは消せません。-R スイッチを付ければクローンもろとも消すことはできます。*1

あとは、# jexec httpd して、普通の FreeBSD 環境と同じように必要なユーザーを adduser したり、パッケージを pkg add したりして環境を作っていきましょう。

*1:

2020/02/12 追記

プロモーション機能というもので依存関係を交換すれば、クローンを残して元の方を削除できるようです。 複数のクローンがある場合の挙動はどうなるんだろう?

ZFS クローン、プロモーション機能 | Oracle やっぱり Sun がスキ! Blog