Skip to content

x-pack: add fips validation plugin from x-pack #16940

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/bootstrap/rubygems.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ def unpack(file, path)
package = ::Gem::Package.new(file)
package.extract_files(target_path)

# write gemspec stub, which is needed for:
# bundler: dependency resolution
# logstash: plugin metadata is required for extension hooks, and listing in the plugin manager
::File::write(File.join(target_path, "#{package.spec.name}.gemspec"), package.spec.to_ruby)

return [package, target_path]
end
end
Expand Down
4 changes: 4 additions & 0 deletions logstash-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ idea {
}

dependencies {
runtimeOnly("org.bouncycastle:bc-fips:2.0.0")
runtimeOnly("org.bouncycastle:bcpkix-fips:2.0.7")
runtimeOnly("org.bouncycastle:bctls-fips:2.0.19")
runtimeOnly("org.bouncycastle:bcutil-fips:2.0.3")
api(files("../vendor/jruby/lib/jruby.jar") { // jruby-core.jar
builtBy ':downloadAndInstallJRuby'
}) { because "DEPENDENCY: org.jruby:jruby-core:${jrubyVersion}" } // meta-data for generateLicenseReport
Expand Down
1 change: 1 addition & 0 deletions rakelib/artifacts.rake
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ namespace "artifact" do
bootstrap
plugin:install-default
plugin:trim-for-observabilitySRE
plugin:install-fips-validation-plugin
artifact:clean-bundle-config
).each {|task| Rake::Task[task].invoke }
end
Expand Down
25 changes: 25 additions & 0 deletions rakelib/plugin.rake
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,22 @@ namespace "plugin" do
task.reenable # Allow this task to be run again
end

task "install-fips-validation-plugin" do |task, _|
puts("[plugin:install-fips-validation-plugin] installing fips_validation plugin")

with_merged_env("GEM_BUILD_VERSION" => get_versions.fetch("logstash")) do
name = "logstash-integration-fips_validation"
path = Pathname.new(__dir__).parent / "x-pack" / "distributions" / "internal" / "observabilitySRE" / "plugin" / name

# ensure fresh GEM_BUILD_VERSION comes from the env
path.glob("GEM_BUILD_VERSION").each(&:unlink)

Rake::Task["plugin:install-local-core-gem"].invoke(name, path.to_s)
end

task.reenable # Allow this task to be run again
end

task "clean-local-core-gem", [:name, :path] do |task, args|
name = args[:name]
path = args[:path]
Expand Down Expand Up @@ -167,4 +183,13 @@ namespace "plugin" do

task.reenable # Allow this task to be run again
end

# @param env [Hash]
def with_merged_env(env)
backup = ENV.to_hash
ENV.replace(backup.merge(env).compact)
yield
ensure
ENV.replace(backup)
end
end # namespace "plugin"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.gem
VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@

require "logstash/environment"

require "logstash/plugins/registry"

module LogStash
class FipsValidation < LogStash::UniversalPlugin

include LogStash::Util::Loggable

require 'java'
java_import org.jruby.util.SafePropertyAccessor

def register_hooks(hooks)
logger.debug("registering hooks")
require 'logstash/runner'
hooks.register_hooks(LogStash::Runner, self)
end

def before_bootstrap_checks(runner)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be any of the hooks that are fired by the LogStash::Runner's LogStash::EventDispatcher, but before_bootstrap_checks is the first one it fires.

logger.debug("running before_bootstrap_checks")
accumulator = Accumulator.new(self)

# naive security provider check: specific three in specific order before any others
observed_security_providers = ::Java::java.security.Security.getProviders.map(&:name)
expected_security_providers = %w(BCFIPS BCJSSE SUN)
if observed_security_providers.first(3) == expected_security_providers
accumulator.success "Java security providers are properly configured (observed `#{observed_security_providers}`)"
else
accumulator.failure "Java security providers are misconfigured (expected `#{expected_security_providers}` to be first 3, observed `#{observed_security_providers}`)"
end

# naive secure-random provider check:
observed_random_provider = ::Java::java.security.SecureRandom.new.getProvider.getName
expected_random_provider = "BCFIPS"
if observed_random_provider != expected_random_provider
accumulator.failure "Java SecureRandom provider is misconfigured (expected `#{expected_random_provider}`; observed `#{observed_random_provider}`)"
else
accumulator.success "Java SecureRandom provider is properly configured (observed `#{observed_random_provider}`)"
end

# ensure Bouncycastle is configured and ready
begin
if Java::org.bouncycastle.crypto.CryptoServicesRegistrar.isInApprovedOnlyMode
accumulator.success "Bouncycastle Crytpo is in `approved-only` mode"
else
accumulator.failure "Bouncycastle Crypto is not in 'approved-only' mode"
end

if ::Java::org.bouncycastle.crypto.fips.FipsStatus.isReady
accumulator.success "Bouncycastle Crypto is fips-ready"
else
accumulator.failure "Bouncycastle Crypto is not fips-ready"
end
rescue => ex
accumulator.failure "Bouncycastle Crypto unavailable: (#{ex.class}) #{ex.message}"
end

# ensure non-compliant jruby openssl provider isn't registered or eligible for later registration
if org.jruby.ext.openssl.SecurityHelper.isProviderRegistered
accumulator.failure "non-compliant Jruby OpenSSL security helper is registered"
elsif org.jruby.util.SafePropertyAccessor.getBoolean("jruby.openssl.provider.register") != false
accumulator.failure "non-compliant Jruby OpenSSL security helper is eligible to be registered"
else
accumulator.success "non-compliant Jruby OpenSSL security helper is correctly not registered"
end

# hard-exit if there were _any_ failures
if accumulator.failure?
logger.fatal "Logstash is not configured in a FIPS-compliant manner"
exit 1
end

logger.info("FIPS OK")
end

class Accumulator
def initialize(logger_context)
@logger = logger_context.logger
@success = []
@failure = []
end

def success(message)
@success << message
@logger.info(message)
end

def failure(message)
@failure << message
@logger.error(message)
end

def failure?
@failure.any?
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

require_relative "logstash/fips_validation"

LogStash::PLUGIN_REGISTRY.add(:universal, "fips_validation", LogStash::FipsValidation)
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# -*- encoding: utf-8 -*-

gem_version_file = File.expand_path("GEM_BUILD_VERSION", __dir__)
unless File.exist?(gem_version_file)
File.write(gem_version_file, ENV.fetch("GEM_BUILD_VERSION"))
end

Gem::Specification.new do |s|
s.name = File.basename(__FILE__, ".gemspec")
s.version = File.read(gem_version_file).chomp
s.licenses = ['Elastic-2.0']
s.summary = "A logstash plugin that ensures FIPS 140-3 compliance"
s.description = <<~DESC
This plugin is to be included in Logstash distributions that need FedRAMP HIGH
FIPS 140-3 compliance; its hooks run before pipelines are loaded to ensure that
the process is running with the correct settings for cryptography.
DESC
s.authors = ["Elasticsearch"]
s.email = '[email protected]'
s.homepage = "http://www.elasticsearch.org/guide/en/logstash/current/index.html"

s.require_paths = ["lib"]

# Files
s.files = Dir::glob("lib/**/*.rb") |
Dir::glob("*.gemspec") |
Dir.glob("VERSION")

# Special flag to let us know this is actually a logstash plugin
s.metadata = {
"logstash_plugin" => "true",
"logstash_group" => "integration",
"integration_plugins" => "", # empty; no config-accessible plugins
}

# Gem dependencies
s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
end
1 change: 1 addition & 0 deletions x-pack/lib/x-pack/logstash_registry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
xpack_modules.each do |name|
$LOAD_PATH << File.join(LogStash::XPACK_PATH, "modules", name, "lib")
end

require "logstash/plugins/registry"
require "logstash/modules/util"
require "monitoring/monitoring"
Expand Down