#!/usr/local/bin/ruby # # Copyright (c) 2008, Thomas Hurst # # Use of this file is unrestricted provided this notice is retained. # If you use it, it'd be nice if you dropped me a note. Also beer. # # # mkjail -- trace an executable, track dependent files, and # attempt to produce a minimal functional tree capable # of running it in a chroot/jail. # # Example usage: # # mkjail /jail/ruby /usr/local/bin/ruby -e 'p "Hello"' # chroot /jail/ruby /usr/local/bin/ruby -e 'p "Hello"' # # It's been tested successfully with Rails applications, PHP, Apache, # and various other tools. require 'set' require 'fileutils' Basefiles = %w{ /libexec/ld-elf.so.1 /etc/passwd /tmp } def tracebin(bin) STDERR.puts "Test run of process, if it won't die by itself, please kill it shortly." tracefile = `mktemp -t mkjail-#{File.basename(bin.first)}`.chomp + ".ktrace" cmdargs = ['ktrace', '-t', 'n', '-if', tracefile, '--', *bin] STDERR.puts cmdargs.inspect system(*cmdargs) STDERR.puts "Trace saved tp #{tracefile}" tracefile end def detectfiles(bin) trace = tracebin(bin) files = Set.new ofiles = [] File.popen("kdump -sf #{trace}") do |tr| tr.each_line do |line| line =~ /(\d+)\s+(\S+)\s+(\S+)\s+(.*)/ pid, cmd, type, what = $1, $2, $3, $4 what =~ /"(.*)"/ file = $1 if type == 'NAMI' and file != '.' and File.exists?(file) unless files.include?(file) files << file ofiles << file end end end end ofiles end def copytree(files, dest) files.each do |source| target = File.join(dest, source) if File.size(source) > 67108864 puts "Skipping #{source}: > 64MB" next end # puts "#{source} => #{target}" if File.file?(source) dir = File.dirname(target) puts "Making directory #{dir}" FileUtils.makedirs(File.dirname(target)) begin FileUtils.cp(source, target, :verbose => true) rescue => e p e end elsif File.directory?(source) mode = File.stat(source).mode puts "Making directory #{target} mode #{mode.to_s(8)}" FileUtils.makedirs(target) FileUtils.chmod(mode, target) else puts "Skipping #{source}, not a regular file" end end end dest = ARGV.shift unless File.directory?(dest) puts "Usage: #{$0} /path/to/chroot [command [args]]" puts "Ensure the chroot directory exists. Don't forget to add devfs as required." exit 1 end cmd = ARGV STDERR.puts "Tracing #{cmd.inspect}" files = detectfiles(cmd) #puts files.join("\n") files += Basefiles copytree(files, dest) File.open(File.join(dest, "mkjail-manifest"), "w") do |f| f.puts("mkjail using command: #{cmd.inspect}") f.puts files.join("\n") end