Bundler v2.6: lockfile checksums are finally there

by David Rodríguez on

We’re happy to announce Bundler 2.6, featuring gem checksum verification, right
in the Gemfile.lock file.

This feature has actually been implemented for more than a year. However, it was
merged very close to the Bundler 2.5 release and we did not yet have a good plan for
enabling the feature in a graceful manner, so we’ve kept it hidden until now.

Bundler 2.6 finally officially allows to opt-in into this beta feature.

What’s this feature for?

A lockfile is an easy way to ensure all environments will use a consistent
version of every dependency. However, Bundler lockfiles did not protect from
potential tampering of the sources of that specific version. This is what the
this feature does. When enabled, Bundler will keep track of the checksum of
every version in the lockfile, in the lockfile itself. Then, before installing
that lockfile on any machine, it will verify that the checksum of the .gem
file it’s about to install matches the checksum previously recorded in the
lockfile. If they don’t match, Bundler will refuse to install that package and
consider it compromised.

How to enable lockfile checksums?

Bundler 2.6 provides two ways of enabling checksums.

  • For a single lockfile, you can run bundle lock --add-checksums. This will add
    a new CHECKSUMS section to the lockfile that Bundler will keep up to date.

  • To configure Bundler to always include checksums in new lockfiles, run bundle config lockfile_checksums true.

How does it work, once the feature is enabled?

Hopefully nothing will ever change for you after enabling the feature. However,
if bundle install ever downloads a package (or tries to install it from cache)
the checksum of which does not match what’s recorded in the lockfile for that package,
Bundler will print an error like the following and abort installation:

$ bundle install
Fetching gem metadata from https://rubygems.org/........
Bundler found mismatched checksums. This is a potential security risk.
  rake (13.2.1) sha256=46cb38dae65d7d74b6020a4ac9d48afed8eb8149c040eccf0523bec91907059d
    from the lockfile CHECKSUMS at Gemfile.lock:1241:17
    and the API at https://rubygems.org/
  rake (13.2.1) sha256=17e8b7c1b247f3349d4a7160c3f587b6c7fd67cf7be3b3710e118b8416f94ddb
    from the gem at /my/repo/vendor/cache/rake-13.2.1.gem

If you trust the lockfile CHECKSUMS at Gemfile.lock:1241:17, to resolve this issue you can:
  1. remove the gem at /my/repo/vendor/cache/rake-13.2.1.gem
  2. run `bundle install`

To ignore checksum security warnings, disable checksum validation with
  `bundle config set --local disable_checksum_validation true`

Compatibility

Bundler will keep the CHECKSUMS section in the lockfile up to date if it’s
already there, and verify that .gem packages checksums match what’s recorded
in the lockfile before installing.

On the other hand, Bundler will work like before if it doesn’t find a
CHECKSUMS section in the lockfile.

All lockfiles including a CHECKUMS section should have Bundler >= 2.6 in the
BUNDLED WITH section. Since Bundler internally has a mechanism to make sure it
switches to the version of itself included in the BUNDLED WITH lockfile
section, a lockfile with a CHECKSUMS section should always be run by
Bundler >= 2.6.

Some platforms, like Heroku or Dependabot, don’t respect this auto-switching
mechanism. However, these platforms are already using Bundler >= 2.5 and
since this feature is luckily already present (but hidden) since Bundler 2.5,
they should deal just fine with new lockfiles including the new section.

Should I take any other steps before enabling this feature?

If your lockfile only includes ruby in the PLATFORMS section, that means
that Bundler is most likely not storing platform-specific variants of your gems
in the Gemfile.lock file. For example, your lockfile may include only
nokogiri-1.18.0, while Bundler will actually install the most appropriate
1.18.0 variant for your platform, say nokogiri-1.18.0-x86_64-linux. However,
that makes the lockfile checksums feature not work fine for nokigiri because
Bundler will only check if the generic variant (what’s in the lockfile) is
changed, but not the variant that’s actually installed.

Because of this, Bundler 2.6 will print a warning when it ends up installing a
different variant than the one included in the lockfile, and tell you to
“normalize” the lockfile and add platform specific variants through bundle lock --normalize-platforms.

Other notable changes in Bundler 2.6

This is a non-exhaustive list of other notable improvements in Bundler 2.6:

  • Better support for switching between different versions of Ruby.

Sometimes when switching between different Ruby versions, even if the resolution
recorded in the Gemfile.lock is stable, the most optimal platform-specific
version for a gem may change. For example, nokogiri-1.18.0.rc1-x86_64-darwin
does not support Ruby 3.5, but nokogiri-1.18.0.rc1 does. Bundler 2.6 should be
able to keep a consistent lockfile with all platform-specific variants, and use
the one that works best for each Ruby version.

  • Better handling of git dependencies in application caches.

Bundler can keep .gem packages for your dependencies in vendor/cache so
that, for example, they can be installed offline on a different machine. This
did not work fine for git gems, though. Bundler 2.6 should now allow properly
caching git gems in vendor/cache. You can enable this with bundle config cache_all true.

  • More careful redaction of gem server credentials in logs and bundle config output.

Bundler already did this but we found a few cases where it was not properly
redacting sensitive info and fixed those.

  • Better bundle exec behaviour on Windows.

We have introduced several improvements so that bundle exec works the usual
way, also on Windows.

  • Better documentation of commands and CLI flags.

We have introduced changes to make sure that this site, and the bundle CLI,
properly documents all commands and CLI flags, and made sure it will stay like
that going forward.

And like always, many other bug fixes and improvements were implemented. Check
the full Bundler 2.6 changelog for
details.

Happy bundling!