LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術

第12回LXCの設定 [2]

今回は前回紹介できなかったセキュリティ、ネットワーク系の設定を説明していきたいと思います。

セキュリティ機能

ケーパビリティ

コンテナがrootで実行されている場合でも、コンテナ内では一部の特権を認めたくない場合はあるでしょう。このような場合、Linuxカーネルの持つケーパビリティの機能を使ってコンテナの特権を制御できます。

ケーパビリティはcapabilities(7)にあるようにCAP_SYS_MODULEというようなCAP_で始まる大文字で表されます。LXCではこの冒頭のCAP_を除いた文字列を小文字で指定します。たとえばCAP_SYS_MODULEの場合はsys_moduleと指定します。

ケーパビリティの指定は以下のどちらかで行います。各設定ともに、スペース区切りで一行に複数の値を指定できます。

lxc.cap.drop
コンテナ内で削除したいケーパビリティを指定します。
lxc.cap.keep
コンテナ内で保持したいケーパビリティを指定します。指定したケーパビリティ以外は全て削除されます。

lxc-createでコンテナを作成する際にincludeされる各ディストリビューションの標準設定ファイルでは、lxc.cap.dropでコンテナに与えると問題になることの多いケーパビリティを削除しています。たとえばUbuntuコンテナ用の設定ファイルでは以下のように設定されています。

lxc.cap.drop = sys_module mac_admin mac_override sys_time

セキュリティ系の機能とはいえ、コンテナを実用的に使えるようにしながら、ケーパビリティの指定でセキュリティを確保するのは難しいため、どちらかというとコンテナを運用する際に起こる問題を解消するためにケーパビリティを指定する方が多いでしょう。

AppArmor

ホストOSでAppArmorが利用できる環境では、コンテナで使うAppArmorプロファイルが指定できます。設定は使用したいプロファイルをlxc.aa_profileに指定するだけです。

lxc.aa_profile = lxc-container-default-with-mounting

ホストOS環境がUbuntuの場合、LXCパッケージをインストールするとAppArmor関連のファイルも同時にインストールされ、以下のプロファイルが使用できるようになります。

lxc-container-default
lxc.aa_profileでプロファイルを指定しない場合はこのプロファイルが使われます
lxc-container-default-with-nesting
lxc-container-defaultに、コンテナ内でコンテナを動作させる場合に必要な権限が追加されます
lxc-container-default-with-mounting
lxc-container-defaultに、ファイルシステムをマウントするための権限が追加されます
unconfined
デフォルトで指定されるlxc-container-defaultを無効にしたい場合に指定します

もちろん自分で作成した独自のプロファイルも使えます。

SELinux

ホストOSでSELinuxが利用できる環境では、AppArmorと同様にコンテナが使うSELinuxのコンテキストが指定できます。

lxc.se_context = system_u:system_r:lxc_t:s0:c62,c86,c150,c228

今のところ、多くのディストリビューションでSELinuxを有効にしてLXCのパッケージを作成しているようです。しかしコンテナに対してSELinuxを使うように設定しているディストリビューションは今のところないようです。

Seccomp

SeccompはLinuxカーネルに実装されている、実行できるシステムコールを設定できるフィルタリング機能です。

LXCではlxc.seccompにフィルタリングを記述したファイルを指定して利用します。1.0.5からlxc-createでコンテナを作成する際にincludeされる、各ディストリビューションの標準設定ファイルでLXCが標準で準備するフィルタリングが適用されるようになりました。

lxc.seccomp = /usr/share/lxc/config/common.seccomp

このcommon.seccompファイルは以下のような内容です。

2
blacklist
[all]
kexec_load errno 1
open_by_handle_at errno 1
init_module errno 1
finit_module errno 1
delete_module errno 1

このファイルの1行目はバージョン番号を表しており、現在は1か2が指定できます。

2行目はポリシーのタイプを指定します。

バージョン1の場合はSeccompのポリシーはホワイトリストのみとなるので、whitelistのみが指定できます。そして3行目以降に実行可能なシステムコールの番号を書きます。

たとえば以下のように書くと、コンテナ内では103番のシステムコール(syslog)のみが実行可能になります。

1whitelist
103

バージョン2の場合は先にあげたcommon.seccompのようにblacklistを指定して、ポリシーとしてブラックリストの指定ができます。

そしてアーキテクチャをブラケット"[]"で指定し、その後の行の一行にシステムコール名をテキストで書くとともに、そのシステムコールが呼ばれた際の処理を記述します。

先にあげたcommon.seccompの場合は、"[all]"で全てのアーキテクチャを指定し、システムコールのkexec_load, open_by_handle_at, init_module, finit_module, delete_moduleが呼ばれた場合は全てエラー番号1を返しています。

LXCのマニュアルにはこれ以上の情報がなく、アーキテクチャや処理として何が書けるかがわかりません。

LXCのソースコードを見る限り、以下が指定できるようです。

  • アーキテクチャ
    • all
    • x86
    • x86_64
    • arm
  • 処理
    • kill
    • errno
    • allow
    • trap

処理はlibseccompのマニュアルであるseccomp_rule_add(3)の説明で挙げられている有効なアクションに対応しているようです。カーネル付属文書のprctl/seccomp_filter.txtにも解説があります。

ネットワーク

LXCのネットワーク設定の基本的な設定項目を説明した後、いろいろな設定を行った場合の実例を紹介します。

インターフェースの指定 ~lxc.network.link

コンテナにはホストの物理的なインターフェースを割り当てることもできますし、vethやmacvlanのような仮想的なインターフェースを割り当てることもできま す。

このときにコンテナが使うホスト上のインターフェースを指定します。

lxc.network.link = eth0

このように設定すると、コンテナはホストのeth0経由で通信を行います。

UbuntuのLXCパッケージの/etc/lxc/default.confでは以下のように設定されており、コンテナはLXC用に作られたブリッジlxcbr0に接続されます。

lxc.network.link = lxcbr0

コンテナ起動時のインターフェースの動作 ~lxc.network.flags

lxc.network.flagsupと指定すると、コンテナ起動時にコンテナのネットワークインターフェースを起動させます。

lxc.network.flags = up

コンテナでネットワークを使う場合は通常行う設定ですね。

仮想インターフェースのMACアドレス ~lxc.network.hwaddr

コンテナに仮想インターフェースを割り当てる際の、仮想インターフェースのMACアドレスを指定したい場合に行う設定です。設定しない場合は自動的にランダムなMACアドレスが設定されます。

lxc.network.hwaddr = 00:16:3e:d0:e3:9d

このように設定するとそのままの値が設定されますし、xという文字を使うとその部分はランダムな値となります。

lxc.network.hwaddr = 00:16:3e:xx:xx:xx

UbuntuのLXCパッケージの/etc/lxc/default.confには以上のようなxを使った値が設定されています。これを使うとテンプレートにMACアドレスを指定して、コンテナ作成時に固定値が割り当てできます。

ネットワークタイプの設定 ~lxc.network.type

コンテナに対して設定するネットワークインターフェースのタイプをlxc.network.typeで設定します。この設定には以下の6つの値が設定できます。

none

この値を設定するとコンテナ用に新しいネットワーク名前空間を作成しません。つまりホストのネットワークをそのまま使います。

ただしシステムコンテナの場合で、ホストでもコンテナ内でもUpstartのようなUNIXドメインソケット経由で命令を受け取るようなプログラムを使っている場合、ホストと名前空間が分離していないと正常に動作しません。

アプリケーションコンテナでホストのネットワークインターフェースを使いたいような場合に使えるでしょう。

empty

コンテナ内にループバックインターフェースのみを作成します。

lxc.network.type = empty
lxc.network.flags = up

以上のように設定して起動したコンテナ内のネットワークインターフェースは以下のようにloのみとなります。

# lxc-attach -n ct01 -- ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

veth

第6回で紹介したvethを使ってペアのインターフェースを作り、片方をホストに、もう片方をコンテナに割り当てます。

vethペアのホスト側のインターフェース名は自動的に付与されます。lxc.network.veth.pairを使うと、このホスト側のインターフェース名を指定できます。

UbuntuでLXCパッケージを入れてデフォルトのまま使用するとコンテナは以下のように設定されます。

lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = lxcbr0
lxc.network.hwaddr = 00:16:3e:d0:e3:9d

lxc.network.hwaddr/etc/lxc/default.conf

lxc.network.hwaddr = 00:16:3e:xx:xx:xx

と設定されており、lxc-create時に"x"が置き換えられます。

このように設定してコンテナを起動すると、コンテナ内のネットワークインターフェースは以下のようになります。

# lxc-attach -n ct01 -- ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
8: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 00:16:3e:d0:e3:9d brd ff:ff:ff:ff:ff:ff

このとき、ホスト側のvethインターフェースは以下のような名前になっています。

# ip link show
  :(略)
9: vethA50BP0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master lxcbr0 state UP mode DEFAULT group default qlen 1000
    link/ether fe:08:b1:76:81:63 brd ff:ff:ff:ff:ff:ff

ここでlxc.network.veth.pairを使って名前を指定してみます。

lxc.network.veth.pair = veth_gihyo
# ip link show
  :(略)
13: veth_gihyo: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master lxcbr0 state UP mode DEFAULT group default qlen 1000
    link/ether fe:79:92:23:dc:35 brd ff:ff:ff:ff:ff:ff

以上のように指定した名前でホスト側のvethインターフェースが作成されています。

macvlan

第6回で紹介したmacvlanインターフェースをlxc.network.linkに指定したネットワークインターフェースに作成し、コンテナに割り当てます。

第6回で説明したようにmacvlanにはモードが存在しますので、どのモードを使用するかをlxc.network.macvlan.modeで指定します。

macvlanのモードを使った実例はあとで紹介します。

vlan

Linuxカーネルの持つVLANを扱う機能を使い、lxc.network.linkで指定されたネットワークインターフェース上にVLANインターフェースを作成し、コンテナに割り当てます。

タグVLANのIDはlxc.network.vlan.idで指定します。

phys

lxc.network.linkで指定したネットワークインターフェースを直接コンテナに割り当てます。指定したインターフェースはコンテナのネットワーク名前空間に割り当てられますので、当然ホスト上では見えなくなります。

ホスト上や他のコンテナで使っていないネットワークインターフェースの場合に指定できます。

コンテナのIPアドレスの指定

システムコンテナを起動する場合は、LXCの設定ファイルでは最低限の設定だけ行い、UbuntuやDebianの/etc/network/interfacesや、CentOSやFedoraの/etc/sysconfig/network-scripts以下の設定ファイルなどで、各ディストリビューションのネットワークの設定方法に従ってコンテナ内で設定を行えば、コンテナにIPアドレスやデフォルトゲートウェイを設定できます。

しかし、アプリケーションコンテナの場合は直接アプリケーションが起動しますので、システムコンテナの起動時のようにネットワークの設定を行うところがありません。

このような場合はコンテナの設定ファイルにアドレスの指定を行います。

たとえば、Apacheを起動するアプリケーションコンテナで以下のような設定を行います。

lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = lxcbr0
lxc.network.hwaddr = 00:16:3e:fb:c0:12

lxc.network.ipv4 = 10.0.3.200/24 10.0.3.255
lxc.network.ipv4.gateway = 10.0.3.1

以上のようにlxc.network.ipv4で"アドレス/ネットマスク"とブロードキャストアドレスをスペース区切りで指定し、lxc.network.ipv4.gatewayでゲートウェイのアドレスを指定します。

lxc.network.ipv4.gatewayには"auto"という値も設定できます。このときは、lxc.network.linkで指定したインターフェースの最初のアドレスを使います。

この設定を行って、アプリケーションコンテナを起動してみます。

# lxc-execute -n apache01 -- /usr/sbin/apache2ctl start &
# lxc-ls --fancy
NAME      STATE    IPV4        IPV6  AUTOSTART  
----------------------------------------------
apache01  RUNNING  10.0.3.200  -     NO         
# curl -I http://10.0.3.200/
HTTP/1.1 200 OK
Date: Tue, 14 Oct 2014 08:16:18 GMT
Server: Apache/2.4.7 (Ubuntu)
Last-Modified: Tue, 02 Sep 2014 11:11:28 GMT
ETag: "2cf6-502132e500350"
Accept-Ranges: bytes
Content-Length: 11510
Vary: Accept-Encoding
Content-Type: text/html

以上のようにlxc.network.ipv4で設定した10.0.3.200でapacheが起動し、正常にアクセスできるのが確認できました。

ここで紹介したIPv4の例と同様にlxc.network.ipv6, lxc.network.ipv6.gatewayでIPv6のアドレスも設定できます。

macvlanを使ったコンテナ

第6回でmacvlanの各モードについて説明をしました。そのときはそれぞれのモードを実際に設定したときにどのような動きになるかの紹介はしませんでしたので、今回コンテナの設定とあわせて紹介します。

第6回で説明した通り、macvlanはホストOSのネットワークと同じネットワークに属するアドレスをコンテナで使用するときに使います。

macvlanをコンテナで使用するには先に説明した通り、lxc.network.typemacvlanを指定します。

では、macvlanインターフェースをホストOS上のeth0に作成し、コンテナを起動してみましょう。

ここの例では、ホストOSのeth0は以下のように192.168.122.0/24に属しており、このネットワークではDHCPサーバが動いています。

$ ip -4 addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 192.168.122.26/24 brd 192.168.122.255 scope global eth0
       valid_lft forever preferred_lft forever

コンテナの設定は以下のようになります。

lxc.network.type = macvlan
lxc.network.flags = up
lxc.network.link = eth0

ではコンテナを起動してみましょう。

$ sudo lxc-start -n ct01 -d
$ sudo lxc-ls --fancy
NAME  STATE    IPV4             IPV6  AUTOSTART  
-----------------------------------------------
ct01  RUNNING  192.168.122.224  -     NO       
  

ご覧のように、DHCPサーバからアドレスをもらってホストOSと同じネットワークに接続されたコンテナが起動しています。ではコンテナのインターフェースを確認してみましょう。

$ sudo lxc-attach -n ct01 -- ip -4 addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
5: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default 
    inet 192.168.122.224/24 brd 192.168.122.255 scope global eth0
       valid_lft forever preferred_lft forever
$ sudo lxc-attach -n ct01 -- ip route (ルーティングテーブルの確認)
default via 192.168.122.1 dev eth0 
192.168.122.0/24 dev eth0  proto kernel  scope link  src 192.168.122.224 

確かに192.168.122.224が割り当たっています。

$ sudo lxc-attach -n ct01 -- ping -c 1 192.168.122.1 (デフォルトゲートウェイへの疎通確認)
PING 192.168.122.1 (192.168.122.1) 56(84) bytes of data.
64 bytes from 192.168.122.1: icmp_seq=1 ttl=64 time=0.207 ms
  : (略)
$ sudo lxc-attach -n ct01 -- curl -I http://gihyo.jp/ (外部のWebサーバにアクセス)
HTTP/1.1 200 OK
Server: nginx
Date: Wed, 15 Oct 2014 11:26:08 GMT
  : (略)

コンテナから外部への通信も問題なく行えています。

第6回のmacvlanの説明では、macvlanに設定できるいくつかのモードを紹介しました。そのうち、privateモードとbridgeモードを紹介しましょう。

privateモード

先ほどの例の環境でもう1つコンテナを起動してみましょう。ネットワークの設定を、lxc.network.hwaddr以外は同じにしたct02という名前のコンテナを起動してみます。

$ sudo lxc-ls --fancy
NAME  STATE    IPV4             IPV6  AUTOSTART  
-----------------------------------------------
ct01  RUNNING  192.168.122.224  -     NO         
ct02  RUNNING  192.168.122.9    -     NO         

お互いに向けてpingを実行します。

$ sudo lxc-attach -n ct01 -- ping 192.168.122.9 (ct01からct02に向けてping)
PING 192.168.122.9 (192.168.122.9) 56(84) bytes of data.
From 192.168.122.224 icmp_seq=1 Destination Host Unreachable
  :(略)
$ sudo lxc-attach -n ct02 -- ping 192.168.122.224 (ct02からct01に向けてping)
PING 192.168.122.224 (192.168.122.224) 56(84) bytes of data.
From 192.168.122.9 icmp_seq=1 Destination Host Unreachable
  :(略)

お互いに通信ができません。そうです、第6回で説明したように、macvlanインターフェースがprivateモードに設定されていると同じインターフェースに属するmacvlanインターフェース同士の通信ができません。

ここで起動した2つのコンテナct01ct02は両方ともprivateモードに設定されているのです。lxc.network.typemacvlanを指定した場合のデフォルトのモードはprivateとなります。

もちろん、以下のように明示的にも指定できます。

lxc.network.macvlan.mode = private

第6回で説明したように、macvlanインターフェースとmacvlanインターフェースを割り当てたインターフェースの間は通信できないことも確認しておきましょう。

$ sudo lxc-attach -n ct01 -- ping 192.168.122.26 (ct01からホストに向けてping)
PING 192.168.122.26 (192.168.122.26) 56(84) bytes of data.
From 192.168.122.9 icmp_seq=1 Destination Host Unreachable
  :(略)
$ sudo lxc-attach -n ct02 -- ping 192.168.122.26 (ct02からホストに向けてping)
PING 192.168.122.26 (192.168.122.26) 56(84) bytes of data.
From 192.168.122.224 icmp_seq=1 Destination Host Unreachable
  :(略)

bridgeモード

ホストOS上の同じインターフェースに属するmacvlanインターフェースを持つコンテナ同士の通信を行いたい場合はbridgeモードに設定します。

lxc.network.macvlan.mode = bridge

上記の設定を行ってコンテナを起動します。

$ sudo lxc-ls --fancy
NAME  STATE    IPV4             IPV6  AUTOSTART  
-----------------------------------------------
ct01  RUNNING  192.168.122.224  -     NO         
ct02  RUNNING  192.168.122.9    -     NO         

pingを実行してみましょう。

$ sudo lxc-attach -n ct01 -- ping 192.168.122.9   (ct01からct02に向けてping)
PING 192.168.122.9 (192.168.122.9) 56(84) bytes of data.
64 bytes from 192.168.122.9: icmp_seq=1 ttl=64 time=0.102 ms
  :(略)
$ sudo lxc-attach -n ct02 -- ping 192.168.122.224 (ct02からct01に向けてping)
PING 192.168.122.224 (192.168.122.224) 56(84) bytes of data.
64 bytes from 192.168.122.224: icmp_seq=1 ttl=64 time=0.054 ms
  :(略)
$ sudo lxc-attach -n ct01 -- ping 192.168.122.26  (ct01からホストに向けてping)
PING 192.168.122.26 (192.168.122.26) 56(84) bytes of data.
From 192.168.122.224 icmp_seq=1 Destination Host Unreachable
  :(略)
$ sudo lxc-attach -n ct02 -- ping 192.168.122.26 (ct02からホストに向けてping)
PING 192.168.122.26 (192.168.122.26) 56(84) bytes of data.
From 192.168.122.9 icmp_seq=1 Destination Host Unreachable
  :(略)

以上のようにct01ct02の間で通信ができます。各コンテナとホスト間の通信はprivateモードと同じくできません。

複数のネットワークインターフェースの割り当て

コンテナには複数のネットワークインターフェースを割り当てることができます。

ホストOSにlxcbr0(10.0.3.1)というブリッジとeth0(192.168.122.26)というインターフェースがある場合に以下のような設定を行ってみます。

lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = lxcbr0
lxc.network.hwaddr = 00:16:3e:25:09:xx

lxc.network.type = macvlan
lxc.network.flags = up
lxc.network.link = eth0
lxc.network.hwaddr = 00:16:3e:25:09:xx

複数のインターフェースを定義する場合は、上記のように同じインターフェースに対する定義を固めて書かないと予期しない結果になる可能性がありますので注意しましょう。

コンテナ内にも複数のインターフェースが起動するような設定を行います。ここではct01はUbuntuコンテナでしたので、/var/lib/lxc/ct01/rootfs/etc/network/interfaces(コンテナ内では/etc/network/interfacesに以下のような設定を追加しました。

auto eth1
iface eth1 inet dhcp

そしてコンテナを起動すると、以下のように2つアドレスが割り当たっています。

$ sudo lxc-start -n ct01 -d
$ sudo lxc-ls --fancy
NAME  STATE    IPV4                         IPV6  AUTOSTART  
-----------------------------------------------------------
ct01  RUNNING  10.0.3.127, 192.168.122.247  -     NO         
ct02  STOPPED  -                            -     NO         

図で示すと図1のようになります。コンテナ用の隔離したネットワークを構成したりするのに使えますね。

図1 複数のインターフェース設定のイメージ
図1 複数のインターフェース設定のイメージ

コンテナ内でインターフェースの情報を見ると以下のようになります。設定ファイルに書いた順にeth0がvethインターフェース、eth1がmacvlanインターフェースになっています。

$ sudo lxc-attach -n ct01 -- ip -4 addr show
  :(略)
18: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 10.0.3.127/24 brd 10.0.3.255 scope global eth0
       valid_lft forever preferred_lft forever
20: eth1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default 
    inet 192.168.122.247/24 brd 192.168.122.255 scope global eth1
       valid_lft forever preferred_lft forever

まとめ

今回はセキュリティとネットワークに関する主な設定を紹介し、ネットワークについては実際に設定したときの動きもいくつか見てみました。

ネットワーク関係の設定で紹介できていない設定項目についてはman lxc.container.confをご参照ください。

前回と今回でLXCでコンテナを起動する際の基本的な設定を紹介しました。もう少し進んだ使いかたをする際の設定については、この連載の後の回で紹介する予定です。

さて、今回まででLXCでコンテナを使う場合の基本的な使い方や設定が紹介できました。LXCを使う場合の代表的なディストリビューションであるUbuntuでの紹介もあわせて行いましたので、次回以降は他のディストリビューション特有のLXCの使い方を紹介していきます。

次回は筆者と同じくPlamo Linuxのメンテナをつとめており、LXCに付属するPlamo Linux用テンプレートのメンテナンスだけでなく、LXCの開発にも参加されている田向さんにPlamo特有の使い方やPlamoならではの進んだ使い方の紹介をしていただく予定です。

その後も田向さんにRPM系の代表的なディストリビューションであるFedoraでのLXCの使いかたの紹介をしていただく予定です。

お楽しみに。

おすすめ記事

記事・ニュース一覧