diff --git a/lib/vagrant/action/builtin/box_add.rb b/lib/vagrant/action/builtin/box_add.rb index 8f3897dfa..330b8f633 100644 --- a/lib/vagrant/action/builtin/box_add.rb +++ b/lib/vagrant/action/builtin/box_add.rb @@ -2,6 +2,7 @@ require "digest/sha1" require "log4r" require "vagrant/util/downloader" +require "vagrant/util/file_checksum" require "vagrant/util/platform" module Vagrant @@ -18,6 +19,22 @@ module Vagrant def call(env) @download_interrupted = false + # Determine the checksum type to use + checksum = (env[:box_checksum] || "").to_s + checksum_klass = case env[:box_checksum_type].to_sym + when nil + nil + when :md5 + Digest::MD5 + when :sha1 + Digest::SHA1 + when :sha256 + Digest::SHA2 + else + raise Errors::BoxChecksumInvalidType, + type: env[:box_checksum_type].to_s + end + # Go through each URL and attempt to download it download_error = nil download_url = nil @@ -45,6 +62,20 @@ module Vagrant # If all the URLs failed, then raise an exception raise download_error if download_error + if checksum_klass + @logger.info("Validating checksum with #{checksum_klass}") + @logger.info("Expected checksum: #{checksum}") + + env[:ui].info(I18n.t("vagrant.actions.box.add.checksumming", + name: env[:box_name])) + actual = FileChecksum.new(@temp_path, checksum_klass).checksum + if actual != checksum + raise Errors::BoxChecksumMismatch, + actual: actual, + expected: checksum + end + end + box_formats = env[:box_provider] if box_formats # Determine the formats a box can support and allow the box to diff --git a/lib/vagrant/action/builtin/handle_box_url.rb b/lib/vagrant/action/builtin/handle_box_url.rb index eed8f307d..6a71be6a5 100644 --- a/lib/vagrant/action/builtin/handle_box_url.rb +++ b/lib/vagrant/action/builtin/handle_box_url.rb @@ -41,6 +41,8 @@ module Vagrant box_name = env[:machine].config.vm.box box_url = env[:machine].config.vm.box_url box_download_ca_cert = env[:machine].config.vm.box_download_ca_cert + box_download_checksum = env[:machine].config.vm.box_download_checksum + box_download_checksum_type = env[:machine].config.vm.box_download_checksum_type box_download_client_cert = env[:machine].config.vm.box_download_client_cert box_download_insecure = env[:machine].config.vm.box_download_insecure @@ -69,8 +71,10 @@ module Vagrant begin env[:action_runner].run(Vagrant::Action.action_box_add, { - :box_download_ca_cert => box_download_ca_cert, + :box_checksum => box_download_checksum, + :box_checksum_type => box_download_checksum_type, :box_client_cert => box_download_client_cert, + :box_download_ca_cert => box_download_ca_cert, :box_download_insecure => box_download_insecure, :box_name => box_name, :box_provider => box_formats, diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb index ac3ff70f5..66dc3e6a7 100644 --- a/lib/vagrant/errors.rb +++ b/lib/vagrant/errors.rb @@ -120,6 +120,14 @@ module Vagrant error_key(:already_exists, "vagrant.actions.box.unpackage") end + class BoxChecksumInvalidType < VagrantError + error_key(:box_checksum_invalid_type) + end + + class BoxChecksumMismatch < VagrantError + error_key(:box_checksum_mismatch) + end + class BoxConfigChangingBox < VagrantError error_key(:box_config_changing_box) end diff --git a/lib/vagrant/util/file_checksum.rb b/lib/vagrant/util/file_checksum.rb index b1c00e600..28a21ccd0 100644 --- a/lib/vagrant/util/file_checksum.rb +++ b/lib/vagrant/util/file_checksum.rb @@ -8,7 +8,7 @@ class DigestClass end class FileChecksum - BUFFER_SIZE = 1024 + BUFFER_SIZE = 16328 # Initializes an object to calculate the checksum of a file. The given # ``digest_klass`` should implement the ``DigestClass`` interface. Note @@ -16,7 +16,7 @@ class FileChecksum # Digest::MD5, Digest::SHA1, etc. def initialize(path, digest_klass) @digest_klass = digest_klass - @path = path + @path = path end # This calculates the checksum of the file and returns it as a @@ -24,7 +24,7 @@ class FileChecksum # # @return [String] def checksum - digest= @digest_klass.new + digest = @digest_klass.new File.open(@path, "r") do |f| while !f.eof diff --git a/plugins/commands/box/command/add.rb b/plugins/commands/box/command/add.rb index 3099c6c37..f53283704 100644 --- a/plugins/commands/box/command/add.rb +++ b/plugins/commands/box/command/add.rb @@ -11,6 +11,14 @@ module VagrantPlugins o.banner = "Usage: vagrant box add [--provider provider] [-h]" o.separator "" + o.on("--checksum VALUE", String, "Checksum") do |c| + options[:checksum] = c + end + + o.on("--checksum-type VALUE", String, "Checksum type") do |c| + options[:checksum_type] = c.to_sym + end + o.on("-c", "--clean", "Remove old temporary download if it exists.") do |c| options[:clean] = c end @@ -51,6 +59,8 @@ module VagrantPlugins :box_name => argv[0], :box_provider => provider, :box_url => argv[1], + :box_checksum_type => options[:checksum_type], + :box_checksum => options[:checksum], :box_clean => options[:clean], :box_force => options[:force], :box_download_ca_cert => options[:ca_cert], diff --git a/plugins/kernel_v2/config/vm.rb b/plugins/kernel_v2/config/vm.rb index 191e7a3d1..f1ee63dde 100644 --- a/plugins/kernel_v2/config/vm.rb +++ b/plugins/kernel_v2/config/vm.rb @@ -19,6 +19,8 @@ module VagrantPlugins attr_accessor :box attr_accessor :box_url attr_accessor :box_download_ca_cert + attr_accessor :box_download_checksum + attr_accessor :box_download_checksum_type attr_accessor :box_download_client_cert attr_accessor :box_download_insecure attr_accessor :graceful_halt_timeout @@ -30,6 +32,8 @@ module VagrantPlugins def initialize @boot_timeout = UNSET_VALUE @box_download_ca_cert = UNSET_VALUE + @box_download_checksum = UNSET_VALUE + @box_download_checksum_type = UNSET_VALUE @box_download_client_cert = UNSET_VALUE @box_download_insecure = UNSET_VALUE @box_url = UNSET_VALUE @@ -256,6 +260,8 @@ module VagrantPlugins # Defaults @boot_timeout = 300 if @boot_timeout == UNSET_VALUE @box_download_ca_cert = nil if @box_download_ca_cert == UNSET_VALUE + @box_download_checksum = nil if @box_download_checksum == UNSET_VALUE + @box_download_checksum_type = nil if @box_download_checksum_type == UNSET_VALUE @box_download_client_cert = nil if @box_download_client_cert == UNSET_VALUE @box_download_insecure = false if @box_download_insecure == UNSET_VALUE @box_url = nil if @box_url == UNSET_VALUE @@ -264,6 +270,10 @@ module VagrantPlugins @hostname = nil if @hostname == UNSET_VALUE @hostname = @hostname.to_s if @hostname + if @box_download_checksum_type + @box_download_checksum_type = @box_download_checksum_type.to_sym + end + # Make sure the box URL is an array if it is set if @box_url && !@box_url.is_a?(Array) @box_url = [@box_url] @@ -385,6 +395,16 @@ module VagrantPlugins end end + if box_download_checksum_type + if box_download_checksum == "" + errors << I18n.t("vagrant.config.vm.box_download_checksum_blank") + end + else + if box_download_checksum != "" + errors << I18n.t("vagrant.config.vm.box_download_checksum_notblank") + end + end + has_nfs = false used_guest_paths = Set.new @__synced_folders.each do |id, options| diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 1c5562094..cfbdffdec 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -177,6 +177,18 @@ en: If the box appears to be booting properly, you may want to increase the timeout ("config.vm.boot_timeout") value. + box_checksum_invalid_type: |- + The specified checksum type is not supported by Vagrant: %{type}. + Vagrant supports the following checksum types: + + md5, sha1, sha256 + box_checksum_mismatch: |- + The checksum of the dowloaded box did not match the expected + value. Please verify that you have the proper URL setup and that + you're downloading the proper file. + + Expected: %{expected} + Received: %{actual} box_config_changing_box: |- While loading the Vagrantfile, the provider override specified a new box. This box, in turn, specified a different box. This isn't @@ -722,6 +734,10 @@ en: base_mac_invalid: "Base MAC address for eth0/NAT must be set. Contact box maintainer for more information." box_download_ca_cert_not_found: |- "box_download_ca_cert" file not found: %{path} + box_download_checksum_blank: |- + Checksum type specified but "box_download_checksum" is blank + box_download_checksum_notblank: |- + Checksum specified but must also specify "box_download_checksum_type" box_missing: "A box must be specified." box_not_found: "The box '%{name}' could not be found." hostname_invalid_characters: |- @@ -1121,6 +1137,8 @@ en: Extracting box... added: |- Successfully added box '%{name}' with provider '%{provider}'! + checksumming: |- + Calculating and comparing box checksum... destroy: destroying: "Deleting box '%{name}'..." download: diff --git a/website/docs/source/v2/cli/box.html.md b/website/docs/source/v2/cli/box.html.md index 178054136..dddfef532 100644 --- a/website/docs/source/v2/cli/box.html.md +++ b/website/docs/source/v2/cli/box.html.md @@ -44,6 +44,15 @@ after the initial download. * `--cert CERTFILE` - A client certificate to use when downloading the box, if necessary. +* `--checksum VALUE` - A checksum for the box that is downloaded. If specified, + Vagrant will compare this checksum to what is actually downloaded and will + error if the checksums do not match. This is highly recommended since + box files are so large. If this is specified, `--checksum-type` must + also be specified. + +* `--checksum-type TYPE` - The type of checksum that `--checksum` is if it + is specified. Supported values are currently "md5", "sha1", and "sha256". + * `--clean` - If given, Vagrant will remove any old temporary files from prior downloads of the same URL. This is useful if you don't want Vagrant to resume a download from a previous point, perhaps because the contents diff --git a/website/docs/source/v2/vagrantfile/machine_settings.html.md b/website/docs/source/v2/vagrantfile/machine_settings.html.md index 45e253f3c..2f101252c 100644 --- a/website/docs/source/v2/vagrantfile/machine_settings.html.md +++ b/website/docs/source/v2/vagrantfile/machine_settings.html.md @@ -23,6 +23,23 @@ the installed boxes on the system.
+`config.vm.box_download_checksum` - The checksum of the box specified by +`config.vm.box_url`. If not specified, no checksum comparison will be done. +If specified, Vagrant will compare the checksum of the downloaded box to +this value and error if they do not match. Checksum checking is only done +when Vagrant must download the box. + +If this is specified, then `config.vm.box_download_checksum_type` must +also be specified. + +
+ +`config.vm.box_download_checksum_type` - The type of checksum specified +by `config.vm.box_download_checksum` (if any). Supported values are +currently "md5", "sha1", and "sha256". + +
+ `config.vm.box_download_client_cert` - Path to a client certificate to use when downloading the box, if it is necessary. By default, no client certificate is used to download the box.