-
-
Notifications
You must be signed in to change notification settings - Fork 2k
FIPS compatibility #4989
Comments
What is FIPS mode? Bundler intentionally uses MD5 as a digest because that is what the server sends as the ETAG for compact index files -- it is in no way used as a cryptographic hash |
Hi @segiddins, "FIPS mode" is a feature of the Linux kernel (and Windows) that only permits the use of cryptographic algorithms validated under the NIST standard FIPS-140-2. When FIPS mode is enabled, OpenSSL will recognize this and deny the use of non-validated algorithms. MD5 is notably not a validated algorithm―so no matter what MD5 is being used for, OpenSSL will barf if an application tries to use it when FIPS mode is enabled. |
I don't see us being able to make this change, unfortunately, as it would be backwards-incompatible, requiring changing the hash used as the etag on the server |
In addition to that ^, I get the feeling that switching to using a different security hashing function for etags would be a temporary fix. That is, some years from now there'll be a FIPS2 that requires |
@segiddins and @lynnco I certainly get that this is a rolling issue (and yes FIPS 140-3 is in the works) but kernel level enforcement of this feature means that anything using Bundler will actively terminate without warning (and a healthy OpenSSL garbage error to boot). At least being able to set this somewhere (or detect it via the underlying SSL lib) and use a known good hash, would be very helpful. BUNDLER_HASH=sha1 or some such thing. At present, even doing a "require 'md5'" will destroy things in a horrible fashion and we just need a way to work around this hopefully without forking bundler for a 4 line change. |
We cant change it though, since the hash has to be what the server is using |
Maybe I'm missing something. What exactly is this hitting? rubygems.org seems to be defaulting to sha256 across the board. Probably in relation to this (which was probably related to md5) http://blog.rubygems.org/2016/04/06/gem-replacement-vulnerability-and-mitigation.html |
No, this is the etag, which is simply a header used for checking whether the server can return a 304 NOT MODIFIED |
FYI, I just switched everything over to |
Thanks for patiently pointing out etags several times, @segiddins―we got there in the end. 😛 I wasn't familiar with how etags were implemented, but after reading through RFC 7616 it looks like the root of our problem is that the digest algorithm for the etag should be negotiated with the server, with a preference for SHA-512/256 over MD5:
If bundler deferred its MD5 operations (including |
I don't even know how to respond to this, honestly. First: Bundler is very serious about backwards compatibility. We're not going to change anything that means current users will no longer be able to continue doing the same thing. Second, the RFC that you just linked to has nothing to do with Bundler whatsoever. We aren't using hashing for HTTP Digest Authentication. Finally, we use MD5 to generate content checksums sent between Bundler and gem servers. That means migrating off of MD5 would mean changing both the client and the server, and breaking every single existing client. Last time I checked, there are roughly 120 million downloads of the existing client. As a result of those two things, the answer is no. No, we can't delay requiring MD5, and no, we can't switch to another algorithm and fall back on MD5 if SHA256 isn't available. If you choose to run Bundler on machines where MD5 is not available, you're welcome to patch Bundler to use a different algorithm. Please keep in mind that if you choose to do that, the rubygems.org server will still be using MD5, and so the checksums that Bundler uses to know that it was able to successfully download gem information will no longer function. |
I'm curious if anyone has created a fork of Bundler for this purpose. We're currently unable to deploy our Ruby applications in fed space due to FIPS 140-2 regulations. |
I think an acceptable solution we could merge would be to skip the compact index fetcher if it fails to load due to that require |
@openface If you go through the code, there are two places where MD5 is used and they're both in upstream projects. You can 100% replace both of these calls with SHA256+ and nothing bad happens. We've been running it that way for a while now and neither of those operations has anything to do with the reason that it couldn't be replaced that was mentioned in this thread. We got some of the upstream projects to fix their materials so it'll show up here eventually. |
No you can't replace it with SHA256, it'll cause bundler to redownload every single compact index file on every install, as we pointed out upthread |
Something like this should probably be sufficient diff --git a/lib/bundler/fetcher/compact_index.rb b/lib/bundler/fetcher/compact_index.rb
index 5d703a3..a498ef6 100644
--- a/lib/bundler/fetcher/compact_index.rb
+++ b/lib/bundler/fetcher/compact_index.rb
@@ -5,8 +5,6 @@ require "bundler/worker"
module Bundler
class Fetcher
class CompactIndex < Base
- require "bundler/compact_index_client"
-
def self.compact_index_request(method_name)
method = instance_method(method_name)
undef_method(method_name)
@@ -61,6 +59,11 @@ module Bundler
compact_index_request :fetch_spec
def available?
+ begin
+ require "bundler/compact_index_client"
+ rescue # whatever exception having fips mode enable raises
+ return false
+ end
user_home = Bundler.user_home
return nil unless user_home.directory? && user_home.writable?
# Read info file checksums out of /versions, so we can know if gems are up to date |
@segiddins As a note for the future, providing a link to the following would have saved me a LOT of confusion http://andre.arko.net/2014/03/28/the-new-rubygems-index-format/. That said, where do we go to ask the RubyGems.org people to update that standard to allow the specification of different checksums? They're really easy to document and you could implement multiples to allow systems that can only handle certain checksums to work properly. |
Given that André and I implemented the format, you'll have to believe us when we say that supporting multiple checksums is non-trivial and not a project we're interested in tackling at this time |
@trevor-vaughan Please be considerate and appreciative of the already given expertise help rather then blaming them for not linking you a blog post. They're here on their own time and are kindly helping you the best they can. |
@colby-swandale This is the moment where I regret that text-based formats don't reflect facial expressions well. @segiddins First, I very much appreciate the time and effort that you put into this software! It makes life for me, and many others much easier. While I understood the statements that were written, I didn't understand that the crux of the issue was the protocol that was linked in the post that I found today. I just didn't understand that the fundamental issue is (I think) the load on the servers that would be caused by maintaining this fork! Technologically, yes, it will work just fine but, if everyone in the world stops using MD5, we're going to bring the upstream servers to their knees. After finding that post, I now think that I understand this and understand where the Hash is located and can potentially start looking at working on PRs that can help fix the issue for the community at large. This is Open Source, and that is awesome! I apologize that I didn't actually understand the issue, even after looking at the code but, in trying to help find a solution, specifications of the underlying API are very useful. My frustration came in getting an explanation of something that I did not realize was an internally generated API as opposed to the trails that were covered based on terms that had different meanings based on various RFC definitions, etc... Finally, @openface if I read that article correctly, the code change will work for your purposes, but you're going to be putting a great deal of load on the upstream Gem server (Bundler metadata server?) every time you do a bundle update. Therefore, I would only suggest doing this if you have a Rubygems.org mirror to work from so that you don't swamp the upstream servers. I would like to try to help solve this issue, in code, if possible. Pretty much all of my users want to use Bundler and, likewise, they need it to run in FIPS mode.If it can't do that, I think that my only recourse is to set up Gem mirrors so that we don't add too much load upstream. Is this correct? |
@trevor-vaughan if you report exactly what exception is caused by FIPS mode, we can patch Bundler to not use the compact index at all. That will cause Bundler to fall back on the full index, which slows down |
@indirect Apologies that it took this long to get here, but this is what I think needs to be done. Also, thank you for your patience. The issue with handling an exception, is that FIPS mode failing doesn't throw an exception, instead it fails in the heart of OpenSSL and just destroys your running process outright. Example: begin require 'digest/md5' Digest::MD5.hexdigest('foo') rescue Exception => e puts e.class puts e.to_s puts e.backtrace end Output: md5_dgst.c(80): OpenSSL internal error, assertion failed: Digest MD5 forbidden in FIPS mode! Aborted Given this, I think that the appropriate course of action is to check OpenSSL itself and then perform your action based on that. This appears to work successfully in my quick test: if OpenSSL::OPENSSL_FIPS puts 'Do the long version' else # The 'require' needs to be inside the else require 'digest/md5' Digest::MD5.hexdigest('foo') end You may want to add a |
I am having the same issue when running bundle on a server in fips mode. Can we get that patch/fix submitted and a new version hopefully released soon with it? Thanks. |
@segiddins Awesome, I am getting anxious when your patch will be merged with master AND when a new bundler release will be made. |
[CompactIndex] Disable when openssl is in fips mode Should close #4989
The fix in PR #5222 incorrectly disables the compact-index for environments where OpenSSL is compiled with FIPS support even though it is not currently enabled (see issue #5433). The only way I've been able to reliably detect whether FIPS mode is actually active is to fork the process and detect whether invoking the MD5 module causes the child process to abort (see PR #5440). I don't think this is a good solution, though, I would prefer to replace #5222 with an alternative solution to this issue. Here are two alternative solutions I can think of:
|
Looks like there's a alternative interface ( Note that disabling the compact index format whenever MD5 is unavailable will prevent the old rubygems index format from being deprecated in the future. Unless it's an explicit goal to never deprecate the legacy format, I think that migrating the compact-index format's checksum algorithm away from MD5 (towards either SHA256 or CRC32, or a configurable algorithm) would still be a better future-proof solution. |
@wjordan I added |
bundler will fail when run in FIPS mode because it uses MD5:
Two changes are needed to fix this (mostly in the vendor code):
Digest::MD5.hexdigest
using a cipher that is permitted by FIPS-140-2 (e.g.,Digest::SHA256.hexdigest
).lib/bundler/vendor/compact_index_client/lib/compact_index_client/cache.rb
lib/bundler/vendor/compact_index_client/lib/compact_index_client/updater.rb
lib/bundler/vendor/thor/lib/thor/runner.rb
lib/bundler/source/rubygems/remote.rb
require "digest/md5"'
should not be required by default, since that will also fail on a system with FIPS mode enabled.lib/bundler/vendor/compact_index_client/lib/compact_index_client/cache.rb
lib/bundler/vendor/thor/lib/thor/runner.rb
The text was updated successfully, but these errors were encountered: