Verified Commit 00f19a4b authored by Stan Hu's avatar Stan Hu
Browse files

Add tools for checking and enabling FIPS mode

The code needed for checking whether FIPS is enabled and to use
OpenSSL's implementation of hash algorithms instead of Ruby's will be
needed by any Ruby GitLab project. This code was extracted from the
main GitLab Rails app.

Relates to https://gitlab.com/gitlab-org/gitlab/-/issues/360174
parent 9b90d31f
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -60,3 +60,6 @@ Lint/RedundantSafeNavigation: # (new in 0.93)
  Enabled: true
Style/ClassEqualityComparison: # (new in 0.93)
  Enabled: true

CodeReuse/ActiveRecord:
  Enabled: false
+2 −1
Original line number Diff line number Diff line
@@ -16,10 +16,11 @@ The changelog is available via [**tagged release notes**](https://gitlab.com/git

## Functionality

LabKit-Ruby provides functionality in three areas:
LabKit-Ruby provides functionality in a number of areas:

1. `Labkit::Context` used for providing context information to log messages.
1. `Labkit::Correlation` For accessing the correlation id. (Generated and propagated by `Labkit::Context`)
1. `Labkit::FIPS` for checking for FIPS mode and using FIPS-compliant algorithms.
1. `Labkit::Logging` for sanitizing log messages.
1. `Labkit::Tracing` for handling and propagating distributed traces.

+1 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ module Labkit

  autoload :Correlation, "labkit/correlation"
  autoload :Context, "labkit/context"
  autoload :FIPS, "labkit/fips"
  autoload :Tracing, "labkit/tracing"
  autoload :Logging, "labkit/logging"
  autoload :Middleware, "labkit/middleware"

lib/labkit/fips.rb

0 → 100644
+47 −0
Original line number Diff line number Diff line
# frozen_string_literal: true

module Labkit
  module Digest
    module SHA2
      def new(*args, &block)
        bitlen = args.first || 256
        ::OpenSSL::Digest.const_get("SHA#{bitlen}").new
      end
    end
  end

  class FIPS
    OPENSSL_DIGESTS = %i[SHA1 SHA256 SHA384 SHA512].freeze

    class << self
      # Returns whether we should be running in FIPS mode or not
      #
      # @return [Boolean]
      def enabled?
        # Attempt to auto-detect FIPS mode from OpenSSL
        return true if OpenSSL.fips_mode

        # Otherwise allow it to be set manually via the env vars
        return true if %w[1 true yes].include?(ENV["FIPS_MODE"])

        false
      end

      # Swap Ruby's Digest::SHAx implementations for OpenSSL::Digest::SHAx.
      def enable_fips_mode!
        require "digest"
        require "digest/sha2"

        ::Digest::SHA2.singleton_class.prepend(Labkit::Digest::SHA2)
        OPENSSL_DIGESTS.each { |alg| use_openssl_digest(alg, alg) }
      end

      private

      def use_openssl_digest(ruby_algorithm, openssl_algorithm)
        ::Digest.send(:remove_const, ruby_algorithm) # rubocop:disable GitlabSecurity/PublicSend
        ::Digest.const_set(ruby_algorithm, OpenSSL::Digest.const_get(openssl_algorithm, false))
      end
    end
  end
end
+38 −0
Original line number Diff line number Diff line
# frozen_string_literal: true

# Inspired by https://github.com/ljkbennett/stub_env/blob/master/lib/stub_env/helpers.rb
module StubENV
  def stub_env(key_or_hash, value = nil)
    init_stub unless env_stubbed?

    if key_or_hash.is_a? Hash
      key_or_hash.each { |k, v| add_stubbed_value(k, v) }
    else
      add_stubbed_value key_or_hash, value
    end
  end

  private

  STUBBED_KEY = "__STUBBED__"

  def add_stubbed_value(key, value)
    allow(ENV).to receive(:[]).with(key).and_return(value)
    allow(ENV).to receive(:key?).with(key).and_return(true)
    allow(ENV).to receive(:fetch).with(key).and_return(value)
    allow(ENV).to receive(:fetch).with(key, anything) do |_, default_val|
      value || default_val
    end
  end

  def env_stubbed?
    ENV[STUBBED_KEY]
  end

  def init_stub
    allow(ENV).to receive(:[]).and_call_original
    allow(ENV).to receive(:key?).and_call_original
    allow(ENV).to receive(:fetch).and_call_original
    add_stubbed_value(STUBBED_KEY, true)
  end
end
Loading