すごく分かるWarden

岩嵜 雄大
NTT Software Innovation Center
2012-07-26


                 NTT Software Innovation Center
Warden(ウォードン)とは




                    https://github.com/cloudfoundry/warden




             DEA上でアプリケーションを隔離する仕組み



2012-07-26          NTT Software Innovation Center           2
Outline




              なぜWardenが必要なのか



              Wardenの動作原理




2012-07-26            NTT Software Innovation Center   3
なぜWardenが必要なのか




2012-07-26      NTT Software Innovation Center   4
なぜWardenが必要なのか

       ユーザ権限で
         動作
                   App               App                  App




                         DEAホスト(OS)


              Appはユーザが自由に開発する
              – 悪意のあるAppの可能性もある


              現在はUNIX UserによりAppを隔離

2012-07-26               NTT Software Innovation Center         5
Unix Userの問題点


             App         App       App
                                          脆弱性に弱い
                                               – 特権昇格
                   DEAホスト(OS)




                   App
                               A
                               p
                                     A
                                     p
                                          他のAppの影響を受ける
                                               – CPU、メモリ、IO、etc.
                               p     p


                   DEAホスト(OS)




             App         App       App
                                          情報が他Appに伝わる
                                               – PID、CPU、メモリ、etc.
                                               – 気持ち悪い
                   DEAホスト(OS)




2012-07-26                               NTT Software Innovation Center   6
求められるもの




                  App               App                  App    制限



                               DEAホスト

                                                               一方通行
              App環境の隔離
              リソース制限の導入
              操作用API

2012-07-26              NTT Software Innovation Center                7
FAQ
  AppごとにVM建てればいいのでは?
      – パフォーマンス的に難しい


  chroot jailではダメ?
      – マウントポイントしか隠ぺいできない


  LXCではダメ?
      – 初期のWardenはLXCを利用していたが…
      – Linuxでしか動かない
      – 機能過多




2012-07-26       NTT Software Innovation Center   8
Wardenの動作原理




2012-07-26    NTT Software Innovation Center   9
基本方針

              コンテナ
              – 環境の隔離:Namespaces
              – リソース制限:Cgroups
              – /sbin/init起動によるシステムコンテナ


              操作用API
              – Wardenサーバがコンテナを管理
              – Warden Protocl経由でサーバにアクセス




2012-07-26              NTT Software Innovation Center   10
Cgroups

              Linux Kernel 2.6.24から

              プロセスをグループに分離
              – メモリ使用量、CPU・IOの優先度




                                                           書いてあるよ!




2012-07-26                NTT Software Innovation Center             11
Namepaces

              プロセスの名前空間を分離
              – PID、Network, Mount、UTS、IPC、User
              – unshare(1), clone(2)


              マウント状況も分離される
              – 高級版chroot jail


              ネットワークも分離される
              – 仮想NIC(veth)を使用




2012-07-26                NTT Software Innovation Center   12
Wardenの概要
        Warden
       クライアント


                                                                      ②コンテナの起動
                 EMサーバ                        シェル
                  (Ruby)                    スクリプト群
                  Linux
                  クラス                                       clone.c     DEA

             コンテナ
                                                            ①コンテナの生成
             ジョブ
                          sshd
                                       ③ジョブの起動
                   OS


2012-07-26                 NTT Software Innovation Center                        13
サーバ本体

        Warden
       クライアント


                                                                      ②コンテナの起動
                 EMサーバ                        シェル
                  (Ruby)                    スクリプト群
                  Linux
                  クラス                                       clone.c     DEA

             コンテナ
                                                            ①コンテナの生成
             ジョブ
                          sshd
                                       ③ジョブの起動
                   OS


2012-07-26                 NTT Software Innovation Center                        14
サーバ本体

              /lib/warden

              命令を受け取ってコンテナの操作を行う
              – EMがListen
              – OS・環境ごとのクラスに委譲


              Linuxクラス
              – シェルスクリプトに処理を委託




2012-07-26                   NTT Software Innovation Center   15
サーバ本体
  /lib/warden/container/linux.rb

   def do_create
     sh "#{env_command} #{root_path}/create.sh #{handle}", :timeout => nil
     debug "container created"

      write_bind_mount_commands                                 コンテナの生成
      debug "wrote bind mount commands"

     sh "#{container_path}/start.sh", :timeout => nil
     debug "container started"
   end
                                                     コンテナの起動
                                                   (コンテナは常時起動)




2012-07-26                     NTT Software Innovation Center                16
シェルスクリプト部分

        Warden
       クライアント


                                                                      ②コンテナの起動
                 EMサーバ                        シェル
                  (Ruby)                    スクリプト群
                  Linux
                  クラス                                       clone.c     DEA

             コンテナ
                                                            ①コンテナの生成
             ジョブ
                          sshd
                                       ③ジョブの起動
                   OS


2012-07-26                 NTT Software Innovation Center                        17
シェルスクリプト部分

              /root/linux

              コンテナの生成・削除

              Skeleton
               – ファイルがコンテナごとにコピーされる




2012-07-26                   NTT Software Innovation Center   18
シェルスクリプト部分(コンテナ生成)
  /root/linux/create.sh
   # インスタンスごとのディレクトリ
   target="instances/${1}"
   mkdir -p instances
   …
   cp -r skeleton "${target}“
   unshare -m "${target}"/setup.sh

      – スケルトンをコピー
      – マウント名前空間をunshareしてsetup.shを実行


  /root/linux/skeleton/setup.sh
      – 設定を/etc内に書きだす
      – /etc/ssh, /etc/init.d, /etc/hosts, etc.

2012-07-26                NTT Software Innovation Center   19
シェルスクリプト部分(コンテナ起動)
  /root/linux/skeleton/start.sh

   env -i unshare -n ../../../../src/clone/clone


      – network名前空間をunshareしてclone.cを実行




2012-07-26          NTT Software Innovation Center   20
シェルスクリプト部分

        Warden
       クライアント


                                                                      ②コンテナの起動
                 EMサーバ                        シェル
                  (Ruby)                    スクリプト群
                  Linux
                  クラス                                       clone.c     DEA

             コンテナ
                                                            ①コンテナの生成
             ジョブ
                          sshd
                                       ③ジョブの起動
                   OS


2012-07-26                 NTT Software Innovation Center                        21
clone.c

              /src/clone/clone.c

              システムコールによるコンテナ生成
               – clone(2)




2012-07-26                  NTT Software Innovation Center   22
clone.c
  /src/clone/clone.c
   rv = unshare(CLONE_NEWNS);
   …
   # clone前のフック
   # コンテナ用のファイルシステムをマウントする
   rv = run("./hook-parent-before-clone.sh");
   …
   # コンテナ内のinitプロセスを起動する
   rv = parent_clone_child(h);
   …
   # clone後のフック
   rv = run("./hook-parent-after-clone.sh");



2012-07-26          NTT Software Innovation Center   23
clone.c
  /src/clone/clone.c
   rv = unshare(CLONE_NEWNS);
   …
   # clone前のフック
   # コンテナ用のファイルシステムをマウントする
   rv = run("./hook-parent-before-clone.sh");
   …
   # コンテナ内のinitプロセスを起動する
   rv = parent_clone_child(h);
   …
   # clone後のフック
   rv = run("./hook-parent-after-clone.sh");



2012-07-26          NTT Software Innovation Center   24
skeletonに寄り道
  /root/linux/skeleton/hook-parent-before-clone.sh
   setup_fs

  /root/linux/skeleton/common.sh:setup_fs()
   if [ ! -f fs ]; then
       dd if=/dev/null of=fs bs=1M seek=${disk_size_mb}
       …
   fi
     mkdir -p rootfs ${target}
     mount -n -o loop fs rootfs
   …
       mount -n -t overlayfs -o
   rw,upperdir=rootfs,lowerdir=../../base/rootfs none ${target}


      – コンテナごとのファイルをループバックマウント
      – OSが入っているベースにオーバーレイ
         • ベースは書き込み禁止状態で共有
2012-07-26                    NTT Software Innovation Center      25
clone.c
  /src/clone/clone.c
   rv = unshare(CLONE_NEWNS);
   …
   # clone前のフック
   # コンテナ用のファイルシステムをマウントする
   rv = run("./hook-parent-before-clone.sh");
   …
   # コンテナ内のinitプロセスを起動する
   rv = parent_clone_child(h);
   …
   # clone後のフック
   rv = run("./hook-parent-after-clone.sh");



2012-07-26          NTT Software Innovation Center   26
clone.c

   int parent_clone_child(clone_helper_t *h) {
     …
     # 名前空間分離の設定
     /* Setup namespaces */
     flags |= CLONE_NEWIPC;
     flags |= CLONE_NEWNET;
     flags |= CLONE_NEWNS;
     flags |= CLONE_NEWPID;
     flags |= CLONE_NEWUTS;

       # start()から子プロセス起動
       pid = clone(start, stack, flags, h);
       …
   }

2012-07-26            NTT Software Innovation Center   27
clone.c



   int start(void *data) {
   …
     rv = run(hook_before_pivot
   …
     rv = run(hook_after_pivot);
   …
     # /sbin/initをexecvpして起動完了
     char * const argv[] = { "/sbin/init", "--debug", NULL };
     execvp(argv[0], argv);
   …
   }




2012-07-26                 NTT Software Innovation Center       28
clone.c
  /src/clone/clone.c
   rv = unshare(CLONE_NEWNS);
   …
   # clone前のフック
   # コンテナ用のファイルシステムをマウントする
   rv = run("./hook-parent-before-clone.sh");
   …
   # コンテナ内のinitプロセスを起動する
   rv = parent_clone_child(h);
   …
   # clone後のフック
   rv = run("./hook-parent-after-clone.sh");



2012-07-26          NTT Software Innovation Center   29
skeletonに寄り道
  /root/linux/skeleton/hook-parent-after-clone.sh


   # 新しいcgroupを作って
   mkdir -p /dev/cgroup/instance-${id}
   pushd /dev/cgroup/instance-${id} > /dev/null

   # 上限などを決め
   cat ../cpuset.cpus > cpuset.cpus
   cat ../cpuset.mems > cpuset.mems

   # 子プロセスに値をコピーするコールバックを呼ぶ設定
   echo 1 > cgroup.clone_children
   # cgroupsに現在のプロセスを登録
   echo ${PID} > tasks




2012-07-26              NTT Software Innovation Center   30
ジョブの起動

        Warden
       クライアント


                                                                      ②コンテナの起動
                 EMサーバ                        シェル
                  (Ruby)                    スクリプト群
                  Linux
                  クラス                                       clone.c     DEA

             コンテナ
                                                            ①コンテナの生成
             ジョブ
                          sshd
                                       ③ジョブの起動
                   OS


2012-07-26                 NTT Software Innovation Center                        31
ファイルの送受信とコマンドの実行

              DEA用の特別な仕掛けは見当たらない
              – ファイルの送受信(rsync)
              – コマンドの実行(ssh)
                 • ジョブとして入出力を管理




2012-07-26            NTT Software Innovation Center   32
ジョブの起動
  /lib/warden/container/linux.rb
   def create_job(request)
     user = request.privileged ? "root" : "vcap"

       # -T: Never request a TTY
       # -F: Use configuration from <container_path>/ssh/ssh_config
       args = ["-T",
               "-F", File.join(container_path, "ssh", "ssh_config"),
               "#{user}@container"]
       args << { :input => request.script }

       child = DeferredChild.new("ssh", *args)
   …




2012-07-26                   NTT Software Innovation Center            33
ファイルの送受信
  /lib/warden/container/linux.rb
   def do_copy_in(request, response)
     src_path = request.src_path
     dst_path = request.dst_path

     perform_rsync(src_path, "vcap@container:#{dst_path}")

     nil
   end

   def do_copy_out(request, response)
     src_path = request.src_path
     dst_path = request.dst_path

     perform_rsync("vcap@container:#{src_path}", dst_path)

     if request.owner
       sh "chown", "-R", request.owner, dst_path
     end

     nil
   end

   private

   def perform_rsync(src_path, dst_path)
     ssh_config_path = File.join(container_path, "ssh", "ssh_config")

     # Build arguments
     args = ["rsync"]
     args += ["-e", "ssh -T -F #{ssh_config_path}"]
     args += ["-r"] # Recursive copy
     args += ["-p"] # Preserve permissions
     args += ["--links"] # Preserve symlinks
     args += [src_path, dst_path]

     # Add option hash
     args << { :timeout => nil }

     sh *args
   end



2012-07-26                                               NTT Software Innovation Center   34
まとめ



              WardenはApp隔離用のコンテナ

              LinuxではCgroupsとNamespacesを使用

              サーバがコンテナの操作を行う

              ファイルを転送してコマンドを実行する




2012-07-26             NTT Software Innovation Center   35

すごく分かるwarden

  • 1.
    すごく分かるWarden 岩嵜 雄大 NTT SoftwareInnovation Center 2012-07-26 NTT Software Innovation Center
  • 2.
    Warden(ウォードン)とは https://github.com/cloudfoundry/warden DEA上でアプリケーションを隔離する仕組み 2012-07-26 NTT Software Innovation Center 2
  • 3.
    Outline  なぜWardenが必要なのか  Wardenの動作原理 2012-07-26 NTT Software Innovation Center 3
  • 4.
    なぜWardenが必要なのか 2012-07-26 NTT Software Innovation Center 4
  • 5.
    なぜWardenが必要なのか ユーザ権限で 動作 App App App DEAホスト(OS)  Appはユーザが自由に開発する – 悪意のあるAppの可能性もある  現在はUNIX UserによりAppを隔離 2012-07-26 NTT Software Innovation Center 5
  • 6.
    Unix Userの問題点 App App App  脆弱性に弱い – 特権昇格 DEAホスト(OS) App A p A p  他のAppの影響を受ける – CPU、メモリ、IO、etc. p p DEAホスト(OS) App App App  情報が他Appに伝わる – PID、CPU、メモリ、etc. – 気持ち悪い DEAホスト(OS) 2012-07-26 NTT Software Innovation Center 6
  • 7.
    求められるもの App App App 制限 DEAホスト 一方通行  App環境の隔離  リソース制限の導入  操作用API 2012-07-26 NTT Software Innovation Center 7
  • 8.
    FAQ  AppごとにVM建てればいいのでは? – パフォーマンス的に難しい  chroot jailではダメ? – マウントポイントしか隠ぺいできない  LXCではダメ? – 初期のWardenはLXCを利用していたが… – Linuxでしか動かない – 機能過多 2012-07-26 NTT Software Innovation Center 8
  • 9.
    Wardenの動作原理 2012-07-26 NTT Software Innovation Center 9
  • 10.
    基本方針  コンテナ – 環境の隔離:Namespaces – リソース制限:Cgroups – /sbin/init起動によるシステムコンテナ  操作用API – Wardenサーバがコンテナを管理 – Warden Protocl経由でサーバにアクセス 2012-07-26 NTT Software Innovation Center 10
  • 11.
    Cgroups  Linux Kernel 2.6.24から  プロセスをグループに分離 – メモリ使用量、CPU・IOの優先度 書いてあるよ! 2012-07-26 NTT Software Innovation Center 11
  • 12.
    Namepaces  プロセスの名前空間を分離 – PID、Network, Mount、UTS、IPC、User – unshare(1), clone(2)  マウント状況も分離される – 高級版chroot jail  ネットワークも分離される – 仮想NIC(veth)を使用 2012-07-26 NTT Software Innovation Center 12
  • 13.
    Wardenの概要 Warden クライアント ②コンテナの起動 EMサーバ シェル (Ruby) スクリプト群 Linux クラス clone.c DEA コンテナ ①コンテナの生成 ジョブ sshd ③ジョブの起動 OS 2012-07-26 NTT Software Innovation Center 13
  • 14.
    サーバ本体 Warden クライアント ②コンテナの起動 EMサーバ シェル (Ruby) スクリプト群 Linux クラス clone.c DEA コンテナ ①コンテナの生成 ジョブ sshd ③ジョブの起動 OS 2012-07-26 NTT Software Innovation Center 14
  • 15.
    サーバ本体  /lib/warden  命令を受け取ってコンテナの操作を行う – EMがListen – OS・環境ごとのクラスに委譲  Linuxクラス – シェルスクリプトに処理を委託 2012-07-26 NTT Software Innovation Center 15
  • 16.
    サーバ本体  /lib/warden/container/linux.rb def do_create sh "#{env_command} #{root_path}/create.sh #{handle}", :timeout => nil debug "container created" write_bind_mount_commands コンテナの生成 debug "wrote bind mount commands" sh "#{container_path}/start.sh", :timeout => nil debug "container started" end コンテナの起動 (コンテナは常時起動) 2012-07-26 NTT Software Innovation Center 16
  • 17.
    シェルスクリプト部分 Warden クライアント ②コンテナの起動 EMサーバ シェル (Ruby) スクリプト群 Linux クラス clone.c DEA コンテナ ①コンテナの生成 ジョブ sshd ③ジョブの起動 OS 2012-07-26 NTT Software Innovation Center 17
  • 18.
    シェルスクリプト部分  /root/linux  コンテナの生成・削除  Skeleton – ファイルがコンテナごとにコピーされる 2012-07-26 NTT Software Innovation Center 18
  • 19.
    シェルスクリプト部分(コンテナ生成)  /root/linux/create.sh # インスタンスごとのディレクトリ target="instances/${1}" mkdir -p instances … cp -r skeleton "${target}“ unshare -m "${target}"/setup.sh – スケルトンをコピー – マウント名前空間をunshareしてsetup.shを実行  /root/linux/skeleton/setup.sh – 設定を/etc内に書きだす – /etc/ssh, /etc/init.d, /etc/hosts, etc. 2012-07-26 NTT Software Innovation Center 19
  • 20.
    シェルスクリプト部分(コンテナ起動)  /root/linux/skeleton/start.sh env -i unshare -n ../../../../src/clone/clone – network名前空間をunshareしてclone.cを実行 2012-07-26 NTT Software Innovation Center 20
  • 21.
    シェルスクリプト部分 Warden クライアント ②コンテナの起動 EMサーバ シェル (Ruby) スクリプト群 Linux クラス clone.c DEA コンテナ ①コンテナの生成 ジョブ sshd ③ジョブの起動 OS 2012-07-26 NTT Software Innovation Center 21
  • 22.
    clone.c  /src/clone/clone.c  システムコールによるコンテナ生成 – clone(2) 2012-07-26 NTT Software Innovation Center 22
  • 23.
    clone.c  /src/clone/clone.c rv = unshare(CLONE_NEWNS); … # clone前のフック # コンテナ用のファイルシステムをマウントする rv = run("./hook-parent-before-clone.sh"); … # コンテナ内のinitプロセスを起動する rv = parent_clone_child(h); … # clone後のフック rv = run("./hook-parent-after-clone.sh"); 2012-07-26 NTT Software Innovation Center 23
  • 24.
    clone.c  /src/clone/clone.c rv = unshare(CLONE_NEWNS); … # clone前のフック # コンテナ用のファイルシステムをマウントする rv = run("./hook-parent-before-clone.sh"); … # コンテナ内のinitプロセスを起動する rv = parent_clone_child(h); … # clone後のフック rv = run("./hook-parent-after-clone.sh"); 2012-07-26 NTT Software Innovation Center 24
  • 25.
    skeletonに寄り道  /root/linux/skeleton/hook-parent-before-clone.sh setup_fs  /root/linux/skeleton/common.sh:setup_fs() if [ ! -f fs ]; then dd if=/dev/null of=fs bs=1M seek=${disk_size_mb} … fi mkdir -p rootfs ${target} mount -n -o loop fs rootfs … mount -n -t overlayfs -o rw,upperdir=rootfs,lowerdir=../../base/rootfs none ${target} – コンテナごとのファイルをループバックマウント – OSが入っているベースにオーバーレイ • ベースは書き込み禁止状態で共有 2012-07-26 NTT Software Innovation Center 25
  • 26.
    clone.c  /src/clone/clone.c rv = unshare(CLONE_NEWNS); … # clone前のフック # コンテナ用のファイルシステムをマウントする rv = run("./hook-parent-before-clone.sh"); … # コンテナ内のinitプロセスを起動する rv = parent_clone_child(h); … # clone後のフック rv = run("./hook-parent-after-clone.sh"); 2012-07-26 NTT Software Innovation Center 26
  • 27.
    clone.c int parent_clone_child(clone_helper_t *h) { … # 名前空間分離の設定 /* Setup namespaces */ flags |= CLONE_NEWIPC; flags |= CLONE_NEWNET; flags |= CLONE_NEWNS; flags |= CLONE_NEWPID; flags |= CLONE_NEWUTS; # start()から子プロセス起動 pid = clone(start, stack, flags, h); … } 2012-07-26 NTT Software Innovation Center 27
  • 28.
    clone.c int start(void *data) { … rv = run(hook_before_pivot … rv = run(hook_after_pivot); … # /sbin/initをexecvpして起動完了 char * const argv[] = { "/sbin/init", "--debug", NULL }; execvp(argv[0], argv); … } 2012-07-26 NTT Software Innovation Center 28
  • 29.
    clone.c  /src/clone/clone.c rv = unshare(CLONE_NEWNS); … # clone前のフック # コンテナ用のファイルシステムをマウントする rv = run("./hook-parent-before-clone.sh"); … # コンテナ内のinitプロセスを起動する rv = parent_clone_child(h); … # clone後のフック rv = run("./hook-parent-after-clone.sh"); 2012-07-26 NTT Software Innovation Center 29
  • 30.
    skeletonに寄り道  /root/linux/skeleton/hook-parent-after-clone.sh # 新しいcgroupを作って mkdir -p /dev/cgroup/instance-${id} pushd /dev/cgroup/instance-${id} > /dev/null # 上限などを決め cat ../cpuset.cpus > cpuset.cpus cat ../cpuset.mems > cpuset.mems # 子プロセスに値をコピーするコールバックを呼ぶ設定 echo 1 > cgroup.clone_children # cgroupsに現在のプロセスを登録 echo ${PID} > tasks 2012-07-26 NTT Software Innovation Center 30
  • 31.
    ジョブの起動 Warden クライアント ②コンテナの起動 EMサーバ シェル (Ruby) スクリプト群 Linux クラス clone.c DEA コンテナ ①コンテナの生成 ジョブ sshd ③ジョブの起動 OS 2012-07-26 NTT Software Innovation Center 31
  • 32.
    ファイルの送受信とコマンドの実行  DEA用の特別な仕掛けは見当たらない – ファイルの送受信(rsync) – コマンドの実行(ssh) • ジョブとして入出力を管理 2012-07-26 NTT Software Innovation Center 32
  • 33.
    ジョブの起動  /lib/warden/container/linux.rb def create_job(request) user = request.privileged ? "root" : "vcap" # -T: Never request a TTY # -F: Use configuration from <container_path>/ssh/ssh_config args = ["-T", "-F", File.join(container_path, "ssh", "ssh_config"), "#{user}@container"] args << { :input => request.script } child = DeferredChild.new("ssh", *args) … 2012-07-26 NTT Software Innovation Center 33
  • 34.
    ファイルの送受信  /lib/warden/container/linux.rb def do_copy_in(request, response) src_path = request.src_path dst_path = request.dst_path perform_rsync(src_path, "vcap@container:#{dst_path}") nil end def do_copy_out(request, response) src_path = request.src_path dst_path = request.dst_path perform_rsync("vcap@container:#{src_path}", dst_path) if request.owner sh "chown", "-R", request.owner, dst_path end nil end private def perform_rsync(src_path, dst_path) ssh_config_path = File.join(container_path, "ssh", "ssh_config") # Build arguments args = ["rsync"] args += ["-e", "ssh -T -F #{ssh_config_path}"] args += ["-r"] # Recursive copy args += ["-p"] # Preserve permissions args += ["--links"] # Preserve symlinks args += [src_path, dst_path] # Add option hash args << { :timeout => nil } sh *args end 2012-07-26 NTT Software Innovation Center 34
  • 35.
    まとめ  WardenはApp隔離用のコンテナ  LinuxではCgroupsとNamespacesを使用  サーバがコンテナの操作を行う  ファイルを転送してコマンドを実行する 2012-07-26 NTT Software Innovation Center 35