Skip to content

Commit

Permalink
Merge pull request #2752 from newrelic/k2
Browse files Browse the repository at this point in the history
Integration of Newrelic Ruby Security agent
  • Loading branch information
kaylareopelle authored Jul 22, 2024
2 parents 2a6ca35 + 7c0b475 commit 06653ce
Show file tree
Hide file tree
Showing 11 changed files with 353 additions and 7 deletions.
15 changes: 10 additions & 5 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2023-05-18 21:20:20 UTC using RuboCop version 1.51.0.
# on 2023-10-26 22:54:31 UTC using RuboCop version 1.54.0.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

# Offense count: 30
# Offense count: 31
# Configuration parameters: EnforcedStyle, AllowedGems, Include.
# SupportedStyles: Gemfile, gems.rb, gemspec
# Include: **/*.gemspec, **/Gemfile, **/gems.rb
Expand All @@ -15,15 +15,20 @@ Gemspec/DevelopmentDependencies:
- 'infinite_tracing/newrelic-infinite_tracing.gemspec'
- 'newrelic_rpm.gemspec'

# Offense count: 416
# Offense count: 443
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
Metrics/AbcSize:
Max: 40
Exclude:
- 'lib/new_relic/agent/configuration/default_source.rb'
- infinite_tracing/test/**/*
- lib/new_relic/cli/commands/deployments.rb
- test/**/*

Metrics/CollectionLiteralLength:
Exclude:
- 'lib/new_relic/agent/configuration/default_source.rb'

# Offense count: 7
Minitest/AssertRaisesCompoundBody:
Exclude:
Expand All @@ -37,15 +42,15 @@ Minitest/DuplicateTestRun:
- 'test/multiverse/suites/rails/error_tracing_test.rb'
- 'test/multiverse/suites/sinatra/ignoring_test.rb'

# Offense count: 276
# Offense count: 284
Minitest/MultipleAssertions:
Max: 28

# Offense count: 19
Minitest/TestFileName:
Enabled: false

# Offense count: 22
# Offense count: 20
# This cop supports safe autocorrection (--autocorrect).
Minitest/TestMethodName:
Enabled: false
Expand Down
1 change: 1 addition & 0 deletions lib/new_relic/agent/agent_logger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

require 'thread'
require 'logger'
require 'singleton'
require 'new_relic/agent/hostname'
require 'new_relic/agent/log_once'
require 'new_relic/agent/instrumentation/logger/instrumentation'
Expand Down
80 changes: 79 additions & 1 deletion lib/new_relic/agent/configuration/default_source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def self.transform_for(key)
value_from_defaults(key, :transform)
end

def self.config_search_paths # rubocop:disable Metrics/AbcSize
def self.config_search_paths
proc {
yaml = 'newrelic.yml'
config_yaml = File.join('config', yaml)
Expand Down Expand Up @@ -2570,6 +2570,84 @@ def self.enforce_fallback(allowed_values: nil, fallback: nil)
:type => Integer,
:allowed_from_server => false,
:description => 'This value represents the total amount of memory available to the host (not the process), in mebibytes (1024 squared or 1,048,576 bytes).'
},
# security agent
:'security.agent.enabled' => {
:default => false,
:external => true,
:public => true,
:type => Boolean,
:allowed_from_server => false,
:description => "If `true`, the security agent is loaded (a Ruby 'require' is performed)"
},
:'security.enabled' => {
:default => false,
:external => true,
:public => true,
:type => Boolean,
:allowed_from_server => false,
:description => 'If `true`, the security agent is started (the agent runs in its event loop)'
},
:'security.mode' => {
:default => 'IAST',
:external => true,
:public => true,
:type => String,
:allowed_from_server => true,
:allowlist => %w[IAST RASP],
:description => 'Defines the mode for the security agent to operate in. Currently only `IAST` is supported',
:dynamic_name => true
},
:'security.validator_service_url' => {
:default => 'wss://csec.nr-data.net',
:external => true,
:public => true,
:type => String,
:allowed_from_server => true,
:description => 'Defines the endpoint URL for posting security-related data',
:dynamic_name => true
},
:'security.detection.rci.enabled' => {
:default => true,
:external => true,
:public => true,
:type => Boolean,
:allowed_from_server => false,
:description => 'If `true`, enables RCI (remote code injection) detection'
},
:'security.detection.rxss.enabled' => {
:default => true,
:external => true,
:public => true,
:type => Boolean,
:allowed_from_server => false,
:description => 'If `true`, enables RXSS (reflected cross-site scripting) detection'
},
:'security.detection.deserialization.enabled' => {
:default => true,
:external => true,
:public => true,
:type => Boolean,
:allowed_from_server => false,
:description => 'If `true`, enables deserialization detection'
},
:'security.application_info.port' => {
:default => nil,
:allow_nil => true,
:public => true,
:type => Integer,
:external => true,
:allowed_from_server => false,
:description => 'The port the application is listening on. This setting is mandatory for Passenger servers. Other servers should be detected by default.'
},
:'security.request.body_limit' => {
:default => 300,
:allow_nil => true,
:public => true,
:type => Integer,
:external => true,
:allowed_from_server => false,
:description => 'Defines the request body limit to process in security events (in KB). The default value is 300, for 300KB.'
}
}.freeze
# rubocop:enable Metrics/CollectionLiteralLength
Expand Down
1 change: 1 addition & 0 deletions lib/new_relic/agent/database/obfuscator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

require 'singleton'
require 'new_relic/agent/database/obfuscation_helpers'

module NewRelic
Expand Down
3 changes: 3 additions & 0 deletions lib/new_relic/agent/instrumentation/rack/instrumentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class << builder_class
attr_accessor :_nr_deferred_detection_ran
end
builder_class._nr_deferred_detection_ran = false
NewRelic::Control::SecurityInterface.instance.wait = true
end

def deferred_dependency_check
Expand All @@ -21,6 +22,8 @@ def deferred_dependency_check
NewRelic::Agent.logger.info('Doing deferred dependency-detection before Rack startup')
DependencyDetection.detect!
self.class._nr_deferred_detection_ran = true
NewRelic::Control::SecurityInterface.instance.wait = false
NewRelic::Control::SecurityInterface.instance.init_agent
end

def check_for_late_instrumentation(app)
Expand Down
2 changes: 1 addition & 1 deletion lib/new_relic/control.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
require 'new_relic/language_support'
require 'new_relic/helper'

require 'singleton'
require 'erb'
require 'socket'
require 'net/https'
Expand All @@ -18,6 +17,7 @@
require 'new_relic/control/instrumentation'
require 'new_relic/control/class_methods'
require 'new_relic/control/instance_methods'
require 'new_relic/control/security_interface'

require 'new_relic/agent'
require 'new_relic/delayed_job_injection'
Expand Down
1 change: 1 addition & 0 deletions lib/new_relic/control/instance_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def init_plugin(options = {})
init_config(options)
NewRelic::Agent.agent = NewRelic::Agent::Agent.instance
init_instrumentation
init_security_agent
end

def determine_env(options)
Expand Down
4 changes: 4 additions & 0 deletions lib/new_relic/control/private_instance_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ def init_instrumentation
DependencyDetection.detect!
end
end

def init_security_agent
SecurityInterface.instance.init_agent
end
end
end
end
57 changes: 57 additions & 0 deletions lib/new_relic/control/security_interface.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

require 'singleton'

module NewRelic
class Control
class SecurityInterface
include Singleton

attr_accessor :wait

SUPPORTABILITY_PREFIX_SECURITY = 'Supportability/Ruby/SecurityAgent/Enabled/'
SUPPORTABILITY_PREFIX_SECURITY_AGENT = 'Supportability/Ruby/SecurityAgent/Agent/Enabled/'
ENABLED = 'enabled'
DISABLED = 'disabled'

def agent_started?
(@agent_started ||= false) == true
end

def waiting?
(@wait ||= false) == true
end

def init_agent
return if agent_started? || waiting?

record_supportability_metrics

if Agent.config[:'security.agent.enabled'] && !Agent.config[:high_security]
Agent.logger.info('Invoking New Relic security module')
require 'newrelic_security'

@agent_started = true
else
Agent.logger.info('New Relic Security is completely disabled by one of the user-provided configurations: `security.agent.enabled` or `high_security`. Not loading security capabilities.')
Agent.logger.info("high_security = #{Agent.config[:high_security]}")
Agent.logger.info("security.agent.enabled = #{Agent.config[:'security.agent.enabled']}")
end
rescue LoadError
Agent.logger.info('New Relic security agent not found - skipping')
rescue StandardError => exception
Agent.logger.error("Exception in New Relic security module loading: #{exception} #{exception.backtrace}")
end

def record_supportability_metrics
Agent.config[:'security.agent.enabled'] ? security_agent_metric(ENABLED) : security_agent_metric(DISABLED)
end

def security_agent_metric(setting)
NewRelic::Agent.record_metric_once(SUPPORTABILITY_PREFIX_SECURITY_AGENT + setting)
end
end
end
end
49 changes: 49 additions & 0 deletions newrelic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,55 @@ common: &default_settings
# Foundry environment.
# utilization.detect_pcf: true

#
# BEGIN security agent
#
# NOTE: At this time, the security agent is intended for use only within
# a dedicated security testing environment with data that can tolerate
# modification or deletion. The security agent is available as a
# separate Ruby gem, newrelic_security. It is recommended that this
# separate gem only be introduced to a security testing environment
# by leveraging Bundler grouping like so:
#
# # Gemfile
# gem 'newrelic_rpm' # New Relic APM observability agent
# gem 'newrelic-infinite_tracing' # New Relic Infinite Tracing
#
# group :security do
# gem 'newrelic_security' # New Relic security agent
# end
#
# NOTE: All "security.*" configuration parameters are related only to the
# security agent, and all other configuration parameters that may
# have "security" in the name some where are related to the APM agent.
#

# If true, the security agent is loaded (a Ruby 'require' is performed)
# security.agent.enabled: false

# If true, the security agent is started (the agent runs in its event loop)
# security.enabled: false

# Defines the mode for the security agent to operate in. Currently only 'IAST' is supported
# security.mode: IAST

# Defines the endpoint URL for posting security related data
# security.validator_service_url: wss://csec.nr-data.net

# If `true`, enables RCI(Remote Code Injection) detection
# security.detection.rci.enabled: true

# If `true`, enables RXSS(Reflected Cross-site Scripting) detection
# security.detection.rxss.enabled: true

# If `true`, enables deserialization detection
# security.detection.deserialization.enabled: true

# The port the application is listening on. This setting is mandatory for Passenger servers. Other servers should be detected by default.
# security.application_info.port: nil

# END security agent

# Environment-specific settings are in this section.
# RAILS_ENV or RACK_ENV (as appropriate) is used to determine the environment.
# If your application has other named environments, configure them here.
Expand Down
Loading

0 comments on commit 06653ce

Please sign in to comment.