本連載では、エンタープライズシステムでコンテナ/Kubernetesを活用した業務システムを開発・運用するエンジニアに向けて、知っておくべきKubernetesセキュリティの基礎知識、Microsoftが提供するパブリッククラウド「Azure」を使ったクラウドでのKubernetesセキュリティ対策のポイント、気を付けておきたい注意点などの実践的なノウハウを紹介します。

今回より、コンテナセキュリティの基礎となる、以下のコンテナ技術について3回に分けて説明します。

  • カーネルの分離 namespace
  • リソースの分離 cgroup
  • namespaceとcgroupで下から見るKubernetes

初回となる今回は、カーネルの分離を実現するために利用されているnamespaceを取り上げます。

コンテナはOSを論理的に分割し、隔離された空間でアプリケーションを動作させる技術です。したがって、よく言われるように仮想マシンの場合はハイパーバイザーが仮想的なハードウェアを提供していて、その上で仮想マシンのOSがさらにその上でアプリケーションが動きますが、コンテナの場合はカーネルの機能によってプロセスを隔離しているだけという違いがあります。

コンテナ内でアプリケーションのプロセスだけを動かすことができ、ホストのOSからはプロセスを通常どおり起動するのと違わないため、仮想マシンに比べると圧倒的に速く起動します。

namespaceとは

「OSを論理的に分割し隔離された空間でアプリケーションを動作させる」際に中核となっているのがnamespaceです。ここではUbuntu 20.04でnamespaceの挙動を確認していくことにします。

namespaceには以下の種類があります。

  • PID プロセス群
  • Mount マウントポイント
  • Network ネットワーク関連
  • UTS ホスト名など
  • IPC プロセス間通信
  • User ユーザー/グループ、権限
  • PID namespaceの例

では、namespaceがどのような動きをするのかを確認していきましょう。主に使うのは以下の4つのコマンドです。

  • unshareコマンド: Namespaceを作成し指定したコマンドを実行
  • nsenterコマンド: 既存のNamespaceに関連付けて指定したコマンドを実行
  • ip netnsコマンド: Network Namespaceの操作
  • lsnsコマンド: namespaceの一覧を表示

まずは、DockerやKubernetesを動かしていない素の状態を確認します。使うのはlsnsコマンドです。

# lsns
        NS TYPE   NPROCS   PID USER             COMMAND
4026531835 cgroup     97     1 root             /sbin/init
4026531836 pid        97     1 root             /sbin/init
4026531837 user       97     1 root             /sbin/init
4026531838 uts        94     1 root             /sbin/init
4026531839 ipc        97     1 root             /sbin/init
4026531840 mnt        91     1 root             /sbin/init
4026531860 mnt         1    15 root             kdevtmpfs
4026531992 net        97     1 root             /sbin/init
4026532193 mnt         1   367 root             /lib/systemd/systemd-udevd
4026532194 uts         1   367 root             /lib/systemd/systemd-udevd
4026532195 mnt         1   494 systemd-timesync /lib/systemd/systemd-timesyncd
4026532196 uts         1   494 systemd-timesync /lib/systemd/systemd-timesyncd
4026532197 mnt         1   641 systemd-network  /lib/systemd/systemd-networkd
4026532198 mnt         1   643 systemd-resolve  /lib/systemd/systemd-resolved
4026532254 mnt         1   675 root             /lib/systemd/systemd-logind
4026532255 uts         1   675 root             /lib/systemd/systemd-logind

initやsystemdでnamespaceが使われているのが分かります。initで使われているnamespaceを調べる場合は、以下のように/proc/initのPID以下を見ます。

# ll /proc/1/ns
total 0
dr-x--x--x 2 root root 0 Nov  7 01:05 ./
dr-xr-xr-x 9 root root 0 Nov  7 01:05 ../
lrwxrwxrwx 1 root root 0 Nov  7 01:10 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 Nov  7 01:10 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 root root 0 Nov  7 01:05 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 root root 0 Nov  7 01:10 net -> 'net:[4026531992]'
lrwxrwxrwx 1 root root 0 Nov  7 01:10 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Nov  7 01:11 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Nov  7 01:10 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Nov  7 01:10 uts -> 'uts:[4026531838]'

nsenterを使うと、特定のnamespaceの中に入ることができます。以下のような感じで使います。1は対象にするプロセスのPIDを指定していて、ここでは1なのでinitです。--allは/proc/[pid]/ns/*以下のすべてという意味です。種類を指定したい場合は「--net」や「--pid」を指定します。

nsenter --target 1 --all bash

中に入って何かを確認していくのは少し後回しにして、それぞれのNamespaceを一つずつ確認していきましょう。