sqreen 1.19.1-java → 1.21.0.beta3-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|