卜部昌平のあまりreblogしないtumblr RSS

Archive

Mar
23rd
Tue
permalink

lxcについてくるlxc-execute(1)がお手軽すぎてヤバい

lxcは普段は仮想マシンみたいなのを作る用途で使うわけだけども、そんな大規模の必要ないよプロセスが一個ぽつんと隔離されて起動してくれればいいよ、というありがちなケースをカバーしてくれるのがlxc-execute(1)で、つまりこれはsudo(1)とかchroot(1)とかfreebsdのjail(1)みたいな使い方をするわけだ。特徴としては、

  • まずpid/uidの名前空間が他と分離されるので、仮にrootが奪取されても他のプロセスをkillしたりできない
  • ネットワークも他と分離されるので、仮にrootが奪取されてもパケットスニファとかからは何も見えない
  • もちろんファイルシステムも分離されてるので、仮にroot奪取されてもファイル弄られたりしない
  • デバイスへのアクセスも禁止なので、仮にroot奪取されても/dev/sda1をmknodして適当なマウントポイントにマウントとか不可、/dev/kmemも見えない
  • で、さらに、設定さえ書けばそれらをあえて他と共有させる事も可能

うぬ完璧だ。まあKernelの穴を突かれるとNGってのはあるので、油断は禁物けどな。あとなあ、デフォルトがちょっといけてないなあ。lxc(7)のmanに

lxc-execute -n foo -f lxc-macvlan.conf /bin/bash

って書いてあるけど、これだとほとんど全部のリソースがbashから見えてる。いけてない。デフォルトは安全な方に倒すべきだろ。無引数で起動すると何もかもが見えない状態からスタートすべき。そんでlibc.soがリンクできなくて起動失敗する所から始まるべき。設定緩めるのは設定書けばいいんだから。

まあ、ないもんはしゃあないから自分で作ってみようか。プロセスが起動するのに最低限何が必要かを考えると、

  • そのバイナリ
  • そいつがリンクしてるライブラリ — ldd(1)すりゃ分かる
  • 特権放棄のためsudo
  • sudoがリンクしてるライブラリ
  • sudoが要求する/etc/sudoersと/etc/passwd
  • libcが要求する/lib/libnss_files.soとか系
  • lxc-executeが要求する/usr/lib/lxc/lxc-init
  • lxc-initが要求する/procと/dev/shm (マウントポイントが必要)

こんなもんかな。とあるバイナリを受けたら、いろんなものを殺した設定と上記ファイル群を適当なディレクトリにreadonly bind mountで展開してから lxc-execute -- sudo -u bar foo とかを実行するスクリプトがありゃいいわけだよ。そんなに難しくないべ(下記)? lxc-sandbox.rbとか名前つけて保存すると、

zsh % sudo ruby lxc-sandmox.rb /bin/bash
sudo: unable to resolve host lxc-testhead
sudo: unable to initialize PAM: No such file or directory
bash-4.1$ ls /
bash: ls: command not found
bash-4.1$ ^D
zsh % sudo ruby lxc-sandmox.rb /bin/mknod /dev/kmem c 1 2
sudo: unable to resolve host lxc-testhead
sudo: unable to initialize PAM: No such file or directory
/bin/mknod: `/dev/kmem': Operation not permitted

なんも見えない感じだね。よさげに動いている模様。resolv.confがないとかpamがないとか怒られてるけど、まあそのへんは適当に修正してもらってもいいし、なくても動くからこのままでもいいんじゃねという気はするね。

#! /path/to/ruby
require "pathname"
require "tmpdir"
require "fileutils"

def mount_em_all bin
   files = [bin]
   ret = []
   `ldd #{bin}`.each_line do |i|
      m = %r"/\S+".match i
      if m
         j = Pathname.new m.to_s
         files.push j
      end
   end
   while i = files.pop do
      if i.lstat.ftype == "link"
         j = i.readlink
         j = i.dirname + j if j.relative?
         files.push j
      end
      ret.push i
   end
   return ret
end

passwd = <<END
root:*:0:0:root:/root:/bin/sh
nobody:*:65534:65534:nobody:/nonexistent:/bin/sh
END
bin = Pathname.new ARGV.first
sudo = Pathname.new `/bin/which sudo`.chomp
lxcinit = Pathname.new "/usr/lib/lxc/lxc-init"
root = Pathname.new "/"
Dir.mktmpdir bin.basename.to_s do |d|
   dir = Pathname.new d
   rootfs = dir + "rootfs"
   fstab = dir + "fstab"
   lxcconf = dir + "lxc.conf"
   entries = []
   rootfs.mkpath
   Dir.chdir rootfs do
      files = mount_em_all bin
      files += mount_em_all sudo
      files += mount_em_all lxcinit
      Pathname.glob "/lib/libnss_*" do |e|
         files += mount_em_all e
      end
      files.each do |i|
         r = i.relative_path_from root
         r.dirname.mkpath
         FileUtils.touch r
         entries.push r.to_s
      end
      FileUtils.mkdir_p "proc"
      FileUtils.mkdir_p "dev/shm"
      FileUtils.mkdir_p "etc"
      Dir.chdir "etc" do
         open "passwd", "w" do |f| f.puts passwd end
         open "sudoers", "w" do |f|
            # only to throw away root privilege
            f.puts "root ALL=(nobody) #{bin}"
         end
         File.chmod 0440, "sudoers"
         File.chmod 0644, "passwd"
      end
      File.chmod 0551, "etc"
   end
   entries.uniq!
   open fstab, "w" do |f|
      entries.each do |e|        
         f.puts "/#{e} #{d}/rootfs/#{e} none ro,bind 0 0"
      end
   end
   open lxcconf, "w" do |f|
      f.puts <<END
# empty network is currently buggy.
lxc.network.type = veth
lxc.rootfs = #{rootfs}
lxc.mount = #{fstab}
lxc.cgroup.devices.deny = a
END
   end
   system 'lxc-execute',
      '-n', dir.basename, '-f', lxcconf,
      '--', sudo, '-u', 'nobody', *ARGV
end
  1. kens-notepad reblogged this from shyouhei and added:
    chrootとかjailは面倒だけど同じような事をしたい時に便利ですね
  2. tyru-tech reblogged this from mrkn20
  3. mrkn20 reblogged this from shyouhei
  4. eban reblogged this from shyouhei
  5. highfrontier reblogged this from atm09td
  6. atm09td reblogged this from shyouhei
  7. mirupon reblogged this from shyouhei
  8. send reblogged this from shyouhei
  9. reretlet reblogged this from shyouhei
  10. shyouhei posted this