diff --git a/README.md b/README.md index a08b299..e782ca0 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,24 @@ Copy any additional build inputs into a directory named _inputs_. Then execute the build using a YAML description file (can be run as non-root): - bin/gbuild -desc.yml + bin/gbuild .yml or if you need to specify a commit for one of the git remotes: - bin/gbuild --commit = -desc.yml + bin/gbuild --commit = .yml The resulting report will appear in result/\-res.yml +To sign the result, perform: + + bin/gsign --signer --release .yml + +Where is your signing PGP key ID and is the name for the current release. This will put the result and signature in the sigs//. The sigs/ directory can be managed through git to coordinate multiple signers. + +After you've merged everybody's signatures, verify them: + + bin/gverify --release .yml + ## Poking around * Log files are captured to the _var_ directory @@ -38,7 +48,7 @@ The resulting report will appear in result/\-res.yml * To start the target VM run `start-target 32 lucid-i386` or `start-target 64 lucid-amd64` * To ssh into the target run `on-target` or `on-target -u root` * On the target, the _build_ directory contains the code as it is compiled and _install_ contains intermediate libraries -* By convention, the script in \-desc.yml starts with any environment setup you would need to manually compile things on the target +* By convention, the script in \.yml starts with any environment setup you would need to manually compile things on the target TODO: - disable sudo in target, just in case of a hypervisor exploit diff --git a/bin/gverify b/bin/gverify new file mode 100755 index 0000000..a0ae613 --- /dev/null +++ b/bin/gverify @@ -0,0 +1,92 @@ +#!/usr/bin/ruby + +require 'optparse' +require 'yaml' +require 'fileutils' +require 'pathname' + +@options = {} + +def system!(cmd) + system(cmd) or raise "failed to run #{cmd}" +end + +def sanitize(str, where) + raise "unsanitary string in #{where}" if (str =~ /[^\w.-]/) + str +end + +def sanitize_path(str, where) + raise "unsanitary string in #{where}" if (str =~ /[^@\w\/.-]/) + str +end + +def info(str) + puts str unless @options[:quiet] +end + +################################ + +OptionParser.new do |opts| + opts.banner = "Usage: build [options] .yml" + + opts.on("-q", "--quiet", "be quiet") do |v| + @options[:quiet] = v + end + opts.on("-r REL", "--release REL", "release name") do |v| + @options[:release] = v + end + + opts.on("-d DEST", "--destination DEST", "directory to place signature in") do |v| + @options[:destination] = v + end +end.parse! + +base_dir = Pathname.new(__FILE__).expand_path.dirname.parent + +build_desc_file = ARGV.shift or raise "must supply YAML build description file" + +build_desc = YAML.load_file(build_desc_file) + +in_sums = [] + +result_dir = 'result' + +package_name = build_desc["name"] or raise "must supply name" +package_name = sanitize(package_name, "package name") + +destination = @options[:destination] || File.join(base_dir, "sigs", package_name) +release = @options[:release] || "current" +release = sanitize(release, "release") + +release_path = File.join(destination, release) + +File.exists?(release_path) or raise "#{release_path} does not exist" + +result_file = "#{package_name}-res.yml" + +#system!("gpg --detach-sign -u #{signer} -o #{release_path}/signature.pgp #{result_path}") + +current_manifest = nil + +did_fail = false + +Dir.foreach(release_path) do |signer_dir| + next if signer_dir == "." or signer_dir == ".." + signer_path = sanitize_path(File.join(release_path, signer_dir), "signer path") + next if !File.directory?(signer_path) + result_path = sanitize_path(File.join(signer_path, result_file), "result path") + result = YAML.load_file(result_path) + if !system("gpg --quiet --batch --verify #{File.join(signer_path, 'signature.pgp')} #{result_path}") + puts "#{signer_dir}: BAD SIGNATURE" + did_fail = true + elsif current_manifest and result['out_manifest'] != current_manifest + puts "#{signer_dir}: MISMATCH" + did_fail = true + else + puts "#{signer_dir}: OK" + end + current_manifest = result['out_manifest'] +end + +exit 1 if did_fail