Skip to content
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

add support for elasticsearch database #58

Open
wants to merge 11 commits into
base: dev
Choose a base branch
from
1 change: 1 addition & 0 deletions Gemfile_test
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ gem 'httpx'
gem 'typhoeus'
gem 'async-http'
gem 'ethon'
gem 'elasticsearch'
if RUBY_ENGINE == 'jruby'
gem 'activerecord-jdbc-adapter'
gem 'jdbc-sqlite3'
Expand Down
1 change: 1 addition & 0 deletions lib/newrelic_security/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module NewRelic::Security
SQLITE = 'SQLITE'
MYSQL = 'MYSQL'
POSTGRES = 'POSTGRES'
ES = 'ES'
ISO_8859_1 = 'ISO-8859-1'
UTF_8 = 'UTF-8'
RAILS = 'rails'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module NewRelic::Security
module Instrumentation
module Elasticsearch
module Chain

def self.instrument!
elastic_module_name = RUBY_VERSION >= '2.5' ? ::Elastic::Transport::Client : ::Elasticsearch::Transport::Client
elastic_module_name.class_eval do
include NewRelic::Security::Instrumentation::Elasticsearch

alias_method :perform_request_without_security, :perform_request

def perform_request(*args)
retval = nil
event = perform_request_on_enter(*args) { retval = perform_request_without_security(*args) }
perform_request_on_exit(event) { return retval }
end
end
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require_relative 'prepend'
require_relative 'chain'

module NewRelic::Security
module Instrumentation
module Elasticsearch

def perform_request_on_enter(*args)
event = nil
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
hash = {}
hash[:method] = args[0]
hash[:path] = args[1]
hash[:params] = args[2]
hash[:body] = args[3].to_json
hash[:headers] = args[4]
event = NewRelic::Security::Agent::Control::Collector.collect(NOSQL_DB_COMMAND, [hash], ES) unless NewRelic::Security::Instrumentation::InstrumentationUtils.sql_filter_events?(hash[:sql])
rescue => exception
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
ensure
yield
return event
end

def perform_request_on_exit(event)
NewRelic::Security::Agent.logger.debug "OnExit : #{self.class}.#{__method__}"
NewRelic::Security::Agent::Utils.create_exit_event(event)
rescue => exception
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
ensure
yield
end

end
end
end

elastic_module_name = RUBY_VERSION >= '2.5' ? ::Elastic::Transport::Client : ::Elasticsearch::Transport::Client
NewRelic::Security::Instrumentation::InstrumentationLoader.install_instrumentation(:elasticsearch, elastic_module_name, ::NewRelic::Security::Instrumentation::Elasticsearch)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module NewRelic::Security
module Instrumentation
module Elasticsearch
module Prepend
include NewRelic::Security::Instrumentation::Elasticsearch

def perform_request(*args)
retval = nil
event = perform_request_on_enter(*args) { retval = super }
perform_request_on_exit(event) { return retval }
end

end
end
end
end
34 changes: 34 additions & 0 deletions test/helpers/database_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ module Test
POSTGRESQL_USER = 'postgres'
POSTGRESQL_DATABASE = 'postgres'

ELASTICSEARCH_HOST = 'localhost'
ELASTICSEARCH_PORT = '9200'

module DatabaseHelper
extend self

Expand Down Expand Up @@ -110,6 +113,37 @@ def remove_postgresql_container
end
end

ELASTICSEARCH_CONFIG = {
'Image' => 'elasticsearch:8.14.1',
'name' => 'es_test',
'Env' => ['discovery.type=single-node',
'xpack.security.enabled=false'],
'HostConfig' => {
'PortBindings' => {
'9200/tcp' => [{ 'HostPort' => ELASTICSEARCH_PORT }]
}
}
}

def create_elasticsearch_container
image = Docker::Image.create('fromImage' => 'elasticsearch:8.14.1')
image.refresh!
begin
Docker::Container.get('es_test').remove(force: true)
rescue
end
container = Docker::Container.create(ELASTICSEARCH_CONFIG)
container.start
sleep 20
end

def remove_elasticsearch_container
begin
Docker::Container.get('es_test').remove(force: true)
rescue
end
end

end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
require 'elasticsearch'
require_relative '../../../test_helper'
require 'newrelic_security/instrumentation-security/elasticsearch/instrumentation'

module NewRelic::Security
module Test
module Instrumentation
class TestElasticsearch < Minitest::Test
@@before_all_flag = false

def setup
$event_list.clear()
NewRelic::Security::Agent::Control::HTTPContext.set_context({})
unless @@before_all_flag
NewRelic::Security::Test::DatabaseHelper.create_elasticsearch_container
@@before_all_flag = true
end
end

def test_index
client = Elasticsearch::Client.new(url: "http://#{ELASTICSEARCH_HOST}:#{ELASTICSEARCH_PORT}", request_timeout: 30)

client.index(index: 'blind', body: {:key => :value})
$event_list.clear()
document = { title: 'Sample Document', content: 'This is a sample document' }
index_response = client.index(index: 'my_index', body: document)
doc_id = index_response['_id']
args = [{:method=>"POST", :path=>"my_index/_doc", :params=>{}, :body=>"{\"title\":\"Sample Document\",\"content\":\"This is a sample document\"}", :headers=>{}}]
expected_event = NewRelic::Security::Agent::Control::Event.new(NOSQL_DB_COMMAND, args, ES)
assert_equal 1, NewRelic::Security::Agent::Control::Collector.get_event_count(NOSQL_DB_COMMAND)
assert_equal expected_event.caseType, $event_list[0].caseType
assert_equal expected_event.parameters, $event_list[0].parameters
assert_equal expected_event.eventCategory, $event_list[0].eventCategory
$event_list.clear()

get_response = client.get(index: 'my_index', id: doc_id)
args = [{:method=>"GET", :path=>"my_index/_doc/#{doc_id}", :params=>{}, :body=>"null", :headers=>{}}]
expected_event = NewRelic::Security::Agent::Control::Event.new(NOSQL_DB_COMMAND, args, ES)
assert_equal document[:title], get_response['_source']["title"]
assert_equal 1, NewRelic::Security::Agent::Control::Collector.get_event_count(NOSQL_DB_COMMAND)
assert_equal expected_event.caseType, $event_list[0].caseType
assert_equal expected_event.parameters, $event_list[0].parameters
assert_equal expected_event.eventCategory, $event_list[0].eventCategory
$event_list.clear()

update_document = { title: 'Updated Document', content: 'This is an updated document' }
update_response = client.update(index:'my_index', id: doc_id, body: { doc: document })
args = [{:method=>"POST", :path=>"my_index/_update/#{doc_id}", :params=>{}, :body=>"{\"doc\":{\"title\":\"Sample Document\",\"content\":\"This is a sample document\"}}", :headers=>{}}]
expected_event = NewRelic::Security::Agent::Control::Event.new(NOSQL_DB_COMMAND, args, ES)
assert_equal doc_id, update_response['_id']
assert_equal 1, NewRelic::Security::Agent::Control::Collector.get_event_count(NOSQL_DB_COMMAND)
assert_equal expected_event.caseType, $event_list[0].caseType
assert_equal expected_event.parameters, $event_list[0].parameters
assert_equal expected_event.eventCategory, $event_list[0].eventCategory
$event_list.clear()

delete_response = client.delete(index: 'my_index', id: doc_id)
args = [{:method=>"DELETE", :path=>"my_index/_doc/#{doc_id}", :params=>{}, :body=>"null", :headers=>{}}]
expected_event = NewRelic::Security::Agent::Control::Event.new(NOSQL_DB_COMMAND, args, ES)
assert_equal doc_id, delete_response['_id']
assert_equal 1, NewRelic::Security::Agent::Control::Collector.get_event_count(NOSQL_DB_COMMAND)
assert_equal expected_event.caseType, $event_list[0].caseType
assert_equal expected_event.parameters, $event_list[0].parameters
assert_equal expected_event.eventCategory, $event_list[0].eventCategory
$event_list.clear()
end

def teardown
$event_list.clear()
NewRelic::Security::Agent::Control::HTTPContext.reset_context
end

Minitest.after_run do
NewRelic::Security::Test::DatabaseHelper.remove_elasticsearch_container
end

end
end
end
end

Loading