sqreen 1.19.1-java → 1.21.0.beta3-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +34 -0
- data/lib/sqreen/actions/block_user.rb +1 -1
- data/lib/sqreen/actions/redirect_ip.rb +1 -1
- data/lib/sqreen/actions/redirect_user.rb +1 -1
- data/lib/sqreen/agent_message.rb +20 -0
- data/lib/sqreen/aggregated_metric.rb +25 -0
- data/lib/sqreen/attack_detected.html +1 -2
- data/lib/sqreen/ca.crt +24 -0
- data/lib/sqreen/condition_evaluator.rb +9 -2
- data/lib/sqreen/conditionable.rb +24 -6
- data/lib/sqreen/configuration.rb +11 -5
- data/lib/sqreen/deferred_logger.rb +50 -14
- data/lib/sqreen/deliveries/batch.rb +12 -2
- data/lib/sqreen/deliveries/simple.rb +4 -0
- data/lib/sqreen/deprecation.rb +38 -0
- data/lib/sqreen/ecosystem.rb +96 -0
- data/lib/sqreen/ecosystem/dispatch_table.rb +43 -0
- data/lib/sqreen/ecosystem/exception_reporting.rb +26 -0
- data/lib/sqreen/ecosystem/http/net_http.rb +50 -0
- data/lib/sqreen/ecosystem/http/rack_request.rb +39 -0
- data/lib/sqreen/ecosystem/loggable.rb +13 -0
- data/lib/sqreen/ecosystem/module_api.rb +30 -0
- data/lib/sqreen/ecosystem/module_api/event_listener.rb +18 -0
- data/lib/sqreen/ecosystem/module_api/instrumentation.rb +23 -0
- data/lib/sqreen/ecosystem/module_api/message_producer.rb +51 -0
- data/lib/sqreen/ecosystem/module_api/signal_producer.rb +24 -0
- data/lib/sqreen/ecosystem/module_api/tracing.rb +45 -0
- data/lib/sqreen/ecosystem/module_api/tracing/client_data.rb +31 -0
- data/lib/sqreen/ecosystem/module_api/tracing/server_data.rb +27 -0
- data/lib/sqreen/ecosystem/module_api/tracing_id_generation.rb +16 -0
- data/lib/sqreen/ecosystem/module_api/transaction_storage.rb +71 -0
- data/lib/sqreen/ecosystem/module_registry.rb +44 -0
- data/lib/sqreen/ecosystem/redis/redis_connection.rb +43 -0
- data/lib/sqreen/ecosystem/tracing/modules/client.rb +31 -0
- data/lib/sqreen/ecosystem/tracing/modules/server.rb +30 -0
- data/lib/sqreen/ecosystem/tracing/sampler.rb +160 -0
- data/lib/sqreen/ecosystem/tracing/sampling_configuration.rb +150 -0
- data/lib/sqreen/ecosystem/tracing/signals/tracing_client.rb +53 -0
- data/lib/sqreen/ecosystem/tracing/signals/tracing_server.rb +53 -0
- data/lib/sqreen/ecosystem/tracing_broker.rb +101 -0
- data/lib/sqreen/ecosystem/tracing_id_setup.rb +34 -0
- data/lib/sqreen/ecosystem/transaction_storage.rb +64 -0
- data/lib/sqreen/ecosystem/util/call_writers_from_init.rb +13 -0
- data/lib/sqreen/ecosystem_integration.rb +87 -0
- data/lib/sqreen/ecosystem_integration/around_callbacks.rb +99 -0
- data/lib/sqreen/ecosystem_integration/instrumentation_service.rb +42 -0
- data/lib/sqreen/ecosystem_integration/request_lifecycle_tracking.rb +58 -0
- data/lib/sqreen/ecosystem_integration/signal_consumption.rb +35 -0
- data/lib/sqreen/endpoint_testing.rb +184 -0
- data/lib/sqreen/event.rb +7 -5
- data/lib/sqreen/events/attack.rb +23 -18
- data/lib/sqreen/events/remote_exception.rb +0 -22
- data/lib/sqreen/events/request_record.rb +15 -71
- data/lib/sqreen/frameworks/generic.rb +24 -1
- data/lib/sqreen/frameworks/rails.rb +0 -7
- data/lib/sqreen/frameworks/request_recorder.rb +15 -2
- data/lib/sqreen/graft/call.rb +106 -19
- data/lib/sqreen/graft/callback.rb +1 -1
- data/lib/sqreen/graft/hook.rb +212 -100
- data/lib/sqreen/graft/hook_point.rb +18 -11
- data/lib/sqreen/kit/signals/specialized/aggregated_metric.rb +72 -0
- data/lib/sqreen/kit/signals/specialized/attack.rb +57 -0
- data/lib/sqreen/kit/signals/specialized/binning_metric.rb +76 -0
- data/lib/sqreen/kit/signals/specialized/http_trace.rb +26 -0
- data/lib/sqreen/kit/signals/specialized/sdk_track_call.rb +50 -0
- data/lib/sqreen/kit/signals/specialized/sqreen_exception.rb +57 -0
- data/lib/sqreen/legacy/instrumentation.rb +22 -10
- data/lib/sqreen/legacy/old_event_submission_strategy.rb +228 -0
- data/lib/sqreen/legacy/waf_redactions.rb +49 -0
- data/lib/sqreen/log.rb +3 -2
- data/lib/sqreen/log/loggable.rb +2 -1
- data/lib/sqreen/logger.rb +24 -0
- data/lib/sqreen/metrics.rb +1 -0
- data/lib/sqreen/metrics/base.rb +3 -0
- data/lib/sqreen/metrics/req_detailed.rb +41 -0
- data/lib/sqreen/metrics_store.rb +33 -12
- data/lib/sqreen/null_logger.rb +22 -0
- data/lib/sqreen/performance_notifications/binned_metrics.rb +8 -2
- data/lib/sqreen/remote_command.rb +4 -0
- data/lib/sqreen/rules.rb +12 -6
- data/lib/sqreen/rules/blacklist_ips_cb.rb +2 -2
- data/lib/sqreen/rules/custom_error_cb.rb +3 -3
- data/lib/sqreen/rules/not_found_cb.rb +2 -0
- data/lib/sqreen/rules/rule_cb.rb +6 -2
- data/lib/sqreen/rules/waf_cb.rb +16 -13
- data/lib/sqreen/runner.rb +138 -16
- data/lib/sqreen/sensitive_data_redactor.rb +19 -31
- data/lib/sqreen/session.rb +53 -43
- data/lib/sqreen/signals/conversions.rb +288 -0
- data/lib/sqreen/signals/http_trace_redaction.rb +111 -0
- data/lib/sqreen/signals/signals_submission_strategy.rb +78 -0
- data/lib/sqreen/version.rb +1 -1
- data/lib/sqreen/weave/budget.rb +35 -0
- data/lib/sqreen/weave/legacy/instrumentation.rb +277 -135
- data/lib/sqreen/worker.rb +6 -2
- metadata +86 -10
- data/lib/sqreen/backport.rb +0 -9
- data/lib/sqreen/backport/clock_gettime.rb +0 -74
- data/lib/sqreen/backport/original_name.rb +0 -88
- data/lib/sqreen/encoding_sanitizer.rb +0 -27
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'sqreen/log/loggable'
|
2
|
+
require 'sqreen/metrics_store'
|
3
|
+
require 'sqreen/ecosystem'
|
4
|
+
require 'sqreen/ecosystem/dispatch_table'
|
5
|
+
require 'sqreen/ecosystem_integration/around_callbacks'
|
6
|
+
require 'sqreen/ecosystem_integration/instrumentation_service'
|
7
|
+
require 'sqreen/ecosystem_integration/request_lifecycle_tracking'
|
8
|
+
require 'sqreen/ecosystem_integration/signal_consumption'
|
9
|
+
|
10
|
+
module Sqreen
|
11
|
+
# This class is the interface through which the agent interacts
|
12
|
+
# with the ecosystem.
|
13
|
+
#
|
14
|
+
# Other classes in the EcosystemIntegration module implement the
|
15
|
+
# functionality that the ecosystem requires in order to deliver
|
16
|
+
# data to the agent and to be informed by the agent of certain
|
17
|
+
# key events (see Sqreen::Ecosystem::DispatchTable).
|
18
|
+
class EcosystemIntegration
|
19
|
+
include Sqreen::Log::Loggable
|
20
|
+
|
21
|
+
# @param [Sqreen::Framework] framework
|
22
|
+
# @param [Proc] create_binning_metric
|
23
|
+
def initialize(framework, queue, create_binning_metric)
|
24
|
+
@framework = framework
|
25
|
+
@queue = queue
|
26
|
+
# XXX: created metrics are insulated from feature upgrades
|
27
|
+
@create_binning_metric = create_binning_metric
|
28
|
+
@request_lifecycle = RequestLifecycleTracking.new
|
29
|
+
@online = false
|
30
|
+
end
|
31
|
+
|
32
|
+
def init
|
33
|
+
raise 'already initialized' if @online
|
34
|
+
|
35
|
+
setup_dispatch_table
|
36
|
+
AroundCallbacks.create_metric = @create_binning_metric
|
37
|
+
Ecosystem.init
|
38
|
+
logger.info 'Ecosystem successfully initialized'
|
39
|
+
@online = true
|
40
|
+
rescue ::Exception => e # rubocop:disable Lint/RescueException
|
41
|
+
logger.warn { "Error initializing Ecosystem: #{e.message}" }
|
42
|
+
logger.debug { e.backtrace.map { |x| " #{x}" }.join("\n") }
|
43
|
+
Sqreen::RemoteException.record(e)
|
44
|
+
end
|
45
|
+
|
46
|
+
def disable
|
47
|
+
raise NotImplementedYet
|
48
|
+
end
|
49
|
+
|
50
|
+
def request_start(rack_request)
|
51
|
+
return unless @online
|
52
|
+
|
53
|
+
Ecosystem.start_transaction
|
54
|
+
@request_lifecycle.notify_request_start(rack_request)
|
55
|
+
end
|
56
|
+
|
57
|
+
def request_end
|
58
|
+
return unless @online
|
59
|
+
|
60
|
+
Ecosystem.end_transaction
|
61
|
+
end
|
62
|
+
|
63
|
+
def handle_tracing_command(trace_id_prefix, scopes_config)
|
64
|
+
return unless @online
|
65
|
+
|
66
|
+
Ecosystem.configure_sampling(trace_id_prefix, scopes_config)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def setup_dispatch_table
|
72
|
+
Ecosystem::DispatchTable.consume_signal =
|
73
|
+
create_signal_consumption.method(:consume_signal)
|
74
|
+
|
75
|
+
Ecosystem::DispatchTable.add_request_start_listener =
|
76
|
+
@request_lifecycle.method(:add_start_observer)
|
77
|
+
|
78
|
+
Ecosystem::DispatchTable.fetch_logger = lambda { logger }
|
79
|
+
|
80
|
+
Ecosystem::DispatchTable.instrument = InstrumentationService.method(:instrument)
|
81
|
+
end
|
82
|
+
|
83
|
+
def create_signal_consumption
|
84
|
+
SignalConsumption.new(@framework, @request_lifecycle, @queue)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'sqreen/metrics_store'
|
2
|
+
require 'sqreen/log/loggable'
|
3
|
+
require 'sqreen/events/remote_exception'
|
4
|
+
require 'sqreen/mono_time'
|
5
|
+
require 'sqreen/graft/call'
|
6
|
+
|
7
|
+
module Sqreen
|
8
|
+
class EcosystemIntegration
|
9
|
+
module AroundCallbacks
|
10
|
+
class << self
|
11
|
+
include Log::Loggable::ClassMethods
|
12
|
+
|
13
|
+
# @return [Proc]
|
14
|
+
attr_accessor :create_metric
|
15
|
+
|
16
|
+
# for instrumentation hooks
|
17
|
+
# instrumentation hooks already handle budgets/metrics
|
18
|
+
def wrap_instrumentation_hook(module_name, action, callable)
|
19
|
+
# make tag similar to that of rules
|
20
|
+
# TODO: move to structured tags to avoid silly string manips
|
21
|
+
action_for_metric = if %w[pre post failing finally].include?(action)
|
22
|
+
action
|
23
|
+
else
|
24
|
+
'pre'
|
25
|
+
end
|
26
|
+
metric_name = "sq.ecosystem_#{module_name}.#{action_for_metric}"
|
27
|
+
create_metric.call(metric_name)
|
28
|
+
|
29
|
+
Proc.new do |*args|
|
30
|
+
begin
|
31
|
+
callable.call(*args)
|
32
|
+
rescue ::Exception => e # rubocop:disable Lint/RescueException
|
33
|
+
# 2) rescue exceptions
|
34
|
+
logger.warn { "Error in #{module_name}:#{action}: #{e.message}" }
|
35
|
+
logger.debug { e.backtrace.map { |x| " #{x}" }.join("\n") }
|
36
|
+
Sqreen::RemoteException.record(e)
|
37
|
+
end # end begin
|
38
|
+
end # end proc
|
39
|
+
end
|
40
|
+
|
41
|
+
# XXX: not used yet
|
42
|
+
def wrap_generic_callback(module_name, action, callable)
|
43
|
+
timer_name = "ecosystem:#{module_name}@#{action}"
|
44
|
+
perf_notif_name = "ecosystem_#{module_name}"
|
45
|
+
|
46
|
+
# XXX: register metric
|
47
|
+
|
48
|
+
Proc.new do |*args|
|
49
|
+
begin
|
50
|
+
req_storage = Thread.current[:sqreen_http_request]
|
51
|
+
|
52
|
+
timer = Graft::Timer.new(timer_name) do |t|
|
53
|
+
# this is an epilogue to measure()
|
54
|
+
req_storage && req_storage[:timed_hooks] << t
|
55
|
+
end
|
56
|
+
|
57
|
+
req_timer = nil
|
58
|
+
timer.measure do
|
59
|
+
# not in a request, no budget; call cb
|
60
|
+
next callable.call(*args) unless req_storage
|
61
|
+
|
62
|
+
# 1) budget enforcement
|
63
|
+
# skip callback if budget already expended
|
64
|
+
next if req_storage[:time_budget_expended]
|
65
|
+
|
66
|
+
budget = req_storage[:time_budget]
|
67
|
+
if budget
|
68
|
+
req_timer = req_storage[:timer]
|
69
|
+
remaining = budget - req_timer.elapsed
|
70
|
+
unless remaining > 0
|
71
|
+
req_storage[:time_budget_expended] = true
|
72
|
+
next # skip callback
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
callable.call(*args)
|
77
|
+
end
|
78
|
+
rescue ::Exception => e # rubocop:disable Lint/RescueException
|
79
|
+
# 2) rescue exceptions
|
80
|
+
logger.warn { "Error in #{module_name}:#{action}: #{e.message}" }
|
81
|
+
logger.debug { e.backtrace.map { |x| " #{x}" }.join("\n") }
|
82
|
+
Sqreen::RemoteException.record(e)
|
83
|
+
ensure
|
84
|
+
# 3) contribute to performance metrics
|
85
|
+
if timer
|
86
|
+
req_timer.include_measurements(timer) if req_timer
|
87
|
+
|
88
|
+
# XXX: PerformanceNotifications is used no more
|
89
|
+
Sqreen::PerformanceNotifications.notify(
|
90
|
+
perf_notif_name, action, *timer.start_and_end
|
91
|
+
)
|
92
|
+
end
|
93
|
+
end # end begin
|
94
|
+
end # end proc
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'sqreen/graft/hook'
|
2
|
+
require 'sqreen/ecosystem_integration/around_callbacks'
|
3
|
+
|
4
|
+
module Sqreen
|
5
|
+
class EcosystemIntegration
|
6
|
+
module InstrumentationService
|
7
|
+
class << self
|
8
|
+
# @param [String] module_name
|
9
|
+
# @param [String] method in form A::B#c or A::B.c
|
10
|
+
# @param [Hash{Symbol=>Proc}] spec
|
11
|
+
def instrument(module_name, method, spec)
|
12
|
+
hook = Sqreen::Graft::Hook[method].add do
|
13
|
+
if spec[:before]
|
14
|
+
cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'pre', spec[:before])
|
15
|
+
tag = "weave,rule=ecosystem_#{module_name}"
|
16
|
+
before(tag, flow: true, &cb)
|
17
|
+
end
|
18
|
+
|
19
|
+
if spec[:after]
|
20
|
+
cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'post', spec[:after])
|
21
|
+
tag = "weave,rule=ecosystem_#{module_name}"
|
22
|
+
after(tag, flow: true, &cb)
|
23
|
+
end
|
24
|
+
|
25
|
+
if spec[:raised]
|
26
|
+
cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'failing', spec[:raised])
|
27
|
+
tag = "weave,rule=ecosystem_#{module_name}"
|
28
|
+
raised(tag, flow: true, &cb)
|
29
|
+
end
|
30
|
+
|
31
|
+
if spec[:ensured]
|
32
|
+
cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'finally', spec[:ensured])
|
33
|
+
tag = "weave,rule=ecosystem_#{module_name}"
|
34
|
+
ensured(tag, flow: true, &cb)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
hook.install
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'sqreen/events/remote_exception'
|
2
|
+
require 'sqreen/log/loggable'
|
3
|
+
|
4
|
+
module Sqreen
|
5
|
+
class EcosystemIntegration
|
6
|
+
# This class gets notified of request start/end and
|
7
|
+
# 1) distributes such events to listeners (typically ecosystem modules;
|
8
|
+
# the method add_start_observer is exposed to ecosystem modules through
|
9
|
+
# +Sqreen::Ecosystem::ModuleApi::EventListener+ and the dispatch table).
|
10
|
+
# 2) keeps track of whether a request is active on this thread. This is
|
11
|
+
# so that users of this class can have this information without needing
|
12
|
+
# to subscribe to request start/events and keeping thread local state
|
13
|
+
# themselves.
|
14
|
+
# XXX: Since the Ecosystem is also notified of request start/end, it could
|
15
|
+
# notify its modules of request start without going through the dispatch
|
16
|
+
# table and call add_start_observer. We need to think if we want to keep
|
17
|
+
# the transaction / request distinction or if they should just be
|
18
|
+
# assumed to be the same, though.
|
19
|
+
class RequestLifecycleTracking
|
20
|
+
include Sqreen::Log::Loggable
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@start_observers = []
|
24
|
+
@tl_key = "#{object_id}_req_in_flight"
|
25
|
+
end
|
26
|
+
|
27
|
+
# API for classes needing to know the request state
|
28
|
+
|
29
|
+
# @param cb A callback taking a Rack::Request
|
30
|
+
def add_start_observer(cb)
|
31
|
+
@start_observers << cb
|
32
|
+
end
|
33
|
+
|
34
|
+
def in_request?
|
35
|
+
Thread.current[@tl_key] ? true : false
|
36
|
+
end
|
37
|
+
|
38
|
+
# API for classes notifying this one of request events
|
39
|
+
|
40
|
+
def notify_request_start(rack_req)
|
41
|
+
Thread.current[@tl_key] = true
|
42
|
+
return if @start_observers.empty?
|
43
|
+
@start_observers.each do |cb|
|
44
|
+
begin
|
45
|
+
cb.call(rack_req)
|
46
|
+
rescue ::Exception => e # rubocop:disable Lint/RescueException
|
47
|
+
logger.warn { "Error calling #{cb} on request start: #{e.message}" }
|
48
|
+
Sqreen::RemoteException.record(e)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def notify_request_end
|
54
|
+
Thread.current[@tl_key] = false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'sqreen/log/loggable'
|
2
|
+
|
3
|
+
module Sqreen
|
4
|
+
class EcosystemIntegration
|
5
|
+
class SignalConsumption
|
6
|
+
include Sqreen::Log::Loggable
|
7
|
+
|
8
|
+
PAYLOAD_CREATOR_SECTIONS = %w[request response params headers].freeze
|
9
|
+
|
10
|
+
# @param [Sqreen::Frameworks::GenericFramework] framework
|
11
|
+
# @param [Sqreen::EcosystemIntegration::RequestLifecycleTracking]
|
12
|
+
# @param [Sqreen::CappedQueue]
|
13
|
+
def initialize(framework, req_lifecycle, queue)
|
14
|
+
@framework = framework
|
15
|
+
@req_lifecycle = req_lifecycle
|
16
|
+
@queue = queue
|
17
|
+
end
|
18
|
+
|
19
|
+
def consume_signal(signal)
|
20
|
+
# transitional
|
21
|
+
unless Sqreen.features.fetch('use_signals', DEFAULT_USE_SIGNALS)
|
22
|
+
logger.debug { "Discarding signal #{signal} (signals disabled)" }
|
23
|
+
return
|
24
|
+
end
|
25
|
+
|
26
|
+
if @req_lifecycle.in_request?
|
27
|
+
# add it to the request record
|
28
|
+
@framework.observe(:signals, signal, PAYLOAD_CREATOR_SECTIONS, true)
|
29
|
+
else
|
30
|
+
@queue.push signal
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'sqreen/agent_message'
|
3
|
+
require 'sqreen/log/loggable'
|
4
|
+
|
5
|
+
module Sqreen
|
6
|
+
class EndpointTesting
|
7
|
+
Endpoint = Struct.new(:url, :ca_store)
|
8
|
+
class ChosenEndpoints
|
9
|
+
def initialize
|
10
|
+
@messages = []
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Sqreen::EndpointTesting::Endpoint]
|
14
|
+
attr_accessor :control
|
15
|
+
|
16
|
+
# @return [Sqreen::EndpointTesting::Endpoint]
|
17
|
+
attr_accessor :ingestion
|
18
|
+
|
19
|
+
# @return [Array<Sqreen::AgentMessage>]
|
20
|
+
attr_reader :messages
|
21
|
+
|
22
|
+
# @param [Sqreen::AgentMessage] message
|
23
|
+
def add_message(message)
|
24
|
+
@messages << message
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
MAIN_CONTROL_HOST = 'back.sqreen.com'.freeze
|
29
|
+
MAIN_INJECTION_HOST = 'ingestion.sqreen.com'.freeze
|
30
|
+
FALLBACK_ENDPOINT_URL = 'https://back.sqreen.io/'.freeze
|
31
|
+
GLOBAL_TIMEOUT = 30
|
32
|
+
|
33
|
+
CONTROL_ERROR_KIND = 'back_sqreen_com_unavailable'.freeze
|
34
|
+
INGESTION_ERROR_KIND = 'ingestion_sqreen_com_unavailable'.freeze
|
35
|
+
|
36
|
+
class << self
|
37
|
+
include Log::Loggable
|
38
|
+
|
39
|
+
# reproduces behaviour before endpoint testing was introduced
|
40
|
+
def no_test_endpoints(config_url, config_ingestion_url)
|
41
|
+
endpoints = ChosenEndpoints.new
|
42
|
+
|
43
|
+
endpoints.control = Endpoint.new(
|
44
|
+
config_url || "https://#{MAIN_CONTROL_HOST}/", cert_store
|
45
|
+
)
|
46
|
+
endpoints.ingestion = Endpoint.new(
|
47
|
+
config_ingestion_url || "https://#{MAIN_INJECTION_HOST}/", nil
|
48
|
+
)
|
49
|
+
|
50
|
+
endpoints
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_endpoints(proxy_url, config_url, config_ingestion_url)
|
54
|
+
proxy_params = create_proxy_params(proxy_url)
|
55
|
+
|
56
|
+
# execute the tests in separate threads and wait for them
|
57
|
+
thread_control = Thread.new do
|
58
|
+
thread_main(config_url, proxy_params, MAIN_CONTROL_HOST)
|
59
|
+
end
|
60
|
+
thread_injection = Thread.new do
|
61
|
+
thread_main(config_ingestion_url, proxy_params, MAIN_INJECTION_HOST)
|
62
|
+
end
|
63
|
+
|
64
|
+
wait_for_threads(thread_control, thread_injection)
|
65
|
+
|
66
|
+
# build and return result
|
67
|
+
fallback = Endpoint.new(FALLBACK_ENDPOINT_URL, cert_store)
|
68
|
+
endpoints = ChosenEndpoints.new
|
69
|
+
endpoints.control = thread_control[:endpoint] || fallback
|
70
|
+
endpoints.ingestion = thread_injection[:endpoint] || fallback
|
71
|
+
|
72
|
+
if thread_control[:endpoint_error]
|
73
|
+
msg = AgentMessage.new(CONTROL_ERROR_KIND, thread_control[:endpoint_error])
|
74
|
+
endpoints.add_message msg
|
75
|
+
end
|
76
|
+
if thread_injection[:endpoint_error]
|
77
|
+
msg = AgentMessage.new(INGESTION_ERROR_KIND, thread_injection[:endpoint_error])
|
78
|
+
endpoints.add_message msg
|
79
|
+
end
|
80
|
+
|
81
|
+
endpoints
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def thread_main(configured_url, proxy_params, host)
|
87
|
+
res = if configured_url
|
88
|
+
Endpoint.new(configured_url, nil)
|
89
|
+
else
|
90
|
+
EndpointTesting.send(:test_with_store_variants, proxy_params, host)
|
91
|
+
end
|
92
|
+
|
93
|
+
Thread.current[:endpoint] = res
|
94
|
+
rescue StandardError => e
|
95
|
+
Thread.current[:endpoint_error] = e.message
|
96
|
+
end
|
97
|
+
|
98
|
+
def create_proxy_params(proxy_url)
|
99
|
+
return [] unless proxy_url
|
100
|
+
|
101
|
+
proxy = URI.parse(proxy_url)
|
102
|
+
|
103
|
+
return [] unless proxy.scheme == 'http'
|
104
|
+
|
105
|
+
[proxy.host, proxy.port, proxy.user, proxy.password]
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_with_store_variants(proxy_params, server_name)
|
109
|
+
# first without custom store
|
110
|
+
do_test(proxy_params, server_name, false)
|
111
|
+
rescue StandardError => _e
|
112
|
+
do_test(proxy_params, server_name, true)
|
113
|
+
end
|
114
|
+
|
115
|
+
# @param [Array] proxy_params
|
116
|
+
# @param [String] server_name
|
117
|
+
# @param [Boolean] custom_store
|
118
|
+
def do_test(proxy_params, server_name, custom_store)
|
119
|
+
http = Net::HTTP.new(server_name, 443, *proxy_params)
|
120
|
+
http.use_ssl = true
|
121
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if ENV['SQREEN_SSL_NO_VERIFY']
|
122
|
+
http.verify_callback = lambda do |preverify_ok, ctx|
|
123
|
+
unless preverify_ok
|
124
|
+
logger.warn do
|
125
|
+
"Certificate validation failure for certificate issued to " \
|
126
|
+
"#{ctx.chain[0].subject}: #{ctx.error_string}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
preverify_ok
|
131
|
+
end
|
132
|
+
|
133
|
+
http.open_timeout = 13
|
134
|
+
http.ssl_timeout = 7
|
135
|
+
http.read_timeout = 7
|
136
|
+
http.close_on_empty_response = true
|
137
|
+
|
138
|
+
http.cert_store = cert_store if custom_store
|
139
|
+
|
140
|
+
resp = http.get('/ping')
|
141
|
+
|
142
|
+
logger.info do
|
143
|
+
"Got response from #{server_name}'s ping endpoint. " \
|
144
|
+
"Status code is #{resp.code} (custom CA store: #{custom_store})"
|
145
|
+
end
|
146
|
+
|
147
|
+
unless resp.code == '200'
|
148
|
+
raise "Response code for /ping is #{resp.code}, not 200"
|
149
|
+
end
|
150
|
+
|
151
|
+
Endpoint.new("https://#{server_name}/", http.cert_store)
|
152
|
+
rescue StandardError => e
|
153
|
+
logger.info do
|
154
|
+
"Error in request to #{server_name} " \
|
155
|
+
"(custom store: #{custom_store}): #{e.message}"
|
156
|
+
end
|
157
|
+
|
158
|
+
raise "Error in request to #{server_name}: #{e.message}"
|
159
|
+
end
|
160
|
+
|
161
|
+
def wait_for_threads(thread_control, thread_injection)
|
162
|
+
deadline = Time.now + GLOBAL_TIMEOUT
|
163
|
+
[thread_control, thread_injection].each do |thread|
|
164
|
+
rem = deadline - Time.now
|
165
|
+
rem = 0.1 if rem < 0.1
|
166
|
+
next if thread.join(rem)
|
167
|
+
logger.debug { "Timeout for thread #{thread}" }
|
168
|
+
thread.kill
|
169
|
+
thread[:endpoint_error] = "Timeout doing endpoint testing"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def cert_store
|
174
|
+
@cert_store ||= begin
|
175
|
+
cert_file = File.join(File.dirname(__FILE__), 'ca.crt')
|
176
|
+
cert_store = OpenSSL::X509::Store.new
|
177
|
+
cert_store.add_file cert_file
|
178
|
+
|
179
|
+
cert_store
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|