datadog 2.15.0 → 2.16.0
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 +4 -4
- data/CHANGELOG.md +46 -2
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +1 -4
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +7 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +3 -0
- data/ext/datadog_profiling_native_extension/heap_recorder.c +8 -1
- data/ext/libdatadog_api/crashtracker.c +1 -9
- data/ext/libdatadog_api/crashtracker.h +5 -0
- data/ext/libdatadog_api/datadog_ruby_common.c +1 -4
- data/ext/libdatadog_api/datadog_ruby_common.h +7 -0
- data/ext/libdatadog_api/init.c +15 -0
- data/ext/libdatadog_api/library_config.c +122 -0
- data/ext/libdatadog_api/library_config.h +19 -0
- data/ext/libdatadog_api/process_discovery.c +117 -0
- data/ext/libdatadog_api/process_discovery.h +5 -0
- data/lib/datadog/appsec/actions_handler.rb +3 -2
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +1344 -0
- data/lib/datadog/appsec/assets/waf_rules/strict.json +1344 -0
- data/lib/datadog/appsec/autoload.rb +1 -1
- data/lib/datadog/appsec/component.rb +11 -4
- data/lib/datadog/appsec/configuration/settings.rb +31 -18
- data/lib/datadog/appsec/context.rb +1 -1
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +10 -12
- data/lib/datadog/appsec/contrib/active_record/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/active_record/patcher.rb +22 -22
- data/lib/datadog/appsec/contrib/devise/data_extractor.rb +2 -3
- data/lib/datadog/appsec/contrib/devise/ext.rb +1 -0
- data/lib/datadog/appsec/contrib/devise/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/devise/patcher.rb +3 -5
- data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +17 -4
- data/lib/datadog/appsec/contrib/excon/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +9 -10
- data/lib/datadog/appsec/contrib/faraday/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +8 -9
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +8 -9
- data/lib/datadog/appsec/contrib/graphql/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +22 -32
- data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +16 -16
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +11 -13
- data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/patcher.rb +21 -21
- data/lib/datadog/appsec/contrib/rest_client/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +10 -11
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +17 -23
- data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
- data/lib/datadog/appsec/event.rb +85 -95
- data/lib/datadog/appsec/instrumentation/gateway/argument.rb +5 -2
- data/lib/datadog/appsec/metrics/telemetry.rb +1 -1
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +42 -12
- data/lib/datadog/appsec/processor/rule_loader.rb +26 -28
- data/lib/datadog/appsec/processor/rule_merger.rb +5 -5
- data/lib/datadog/appsec/processor.rb +1 -1
- data/lib/datadog/appsec/remote.rb +14 -13
- data/lib/datadog/appsec/response.rb +6 -6
- data/lib/datadog/appsec/security_engine/runner.rb +1 -1
- data/lib/datadog/appsec/security_event.rb +39 -0
- data/lib/datadog/appsec.rb +1 -1
- data/lib/datadog/core/configuration/agentless_settings_resolver.rb +176 -0
- data/lib/datadog/core/configuration/components.rb +19 -10
- data/lib/datadog/core/configuration/option.rb +61 -25
- data/lib/datadog/core/configuration/settings.rb +10 -0
- data/lib/datadog/core/configuration/stable_config.rb +23 -0
- data/lib/datadog/core/configuration.rb +24 -0
- data/lib/datadog/core/crashtracking/component.rb +1 -9
- data/lib/datadog/core/environment/git.rb +1 -0
- data/lib/datadog/core/environment/variable_helpers.rb +1 -1
- data/lib/datadog/core/metrics/client.rb +8 -7
- data/lib/datadog/core/process_discovery.rb +32 -0
- data/lib/datadog/core/remote/client.rb +7 -0
- data/lib/datadog/core/runtime/metrics.rb +1 -1
- data/lib/datadog/core/telemetry/component.rb +60 -50
- data/lib/datadog/core/telemetry/emitter.rb +17 -11
- data/lib/datadog/core/telemetry/event.rb +7 -4
- data/lib/datadog/core/telemetry/http/adapters/net.rb +12 -97
- data/lib/datadog/core/telemetry/request.rb +3 -3
- data/lib/datadog/core/telemetry/transport/http/api.rb +43 -0
- data/lib/datadog/core/telemetry/transport/http/client.rb +49 -0
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +92 -0
- data/lib/datadog/core/telemetry/transport/http.rb +63 -0
- data/lib/datadog/core/telemetry/transport/telemetry.rb +52 -0
- data/lib/datadog/core/telemetry/worker.rb +45 -0
- data/lib/datadog/core/utils/time.rb +12 -0
- data/lib/datadog/core/workers/async.rb +20 -2
- data/lib/datadog/core/workers/interval_loop.rb +12 -1
- data/lib/datadog/core/workers/runtime_metrics.rb +2 -2
- data/lib/datadog/core.rb +8 -0
- data/lib/datadog/di/boot.rb +34 -0
- data/lib/datadog/di/remote.rb +2 -0
- data/lib/datadog/di.rb +5 -32
- data/lib/datadog/error_tracking/collector.rb +87 -0
- data/lib/datadog/error_tracking/component.rb +167 -0
- data/lib/datadog/error_tracking/configuration/settings.rb +63 -0
- data/lib/datadog/error_tracking/configuration.rb +11 -0
- data/lib/datadog/error_tracking/ext.rb +18 -0
- data/lib/datadog/error_tracking/extensions.rb +16 -0
- data/lib/datadog/error_tracking/filters.rb +77 -0
- data/lib/datadog/error_tracking.rb +18 -0
- data/lib/datadog/kit/identity.rb +1 -1
- data/lib/datadog/profiling/exporter.rb +1 -1
- data/lib/datadog/tracing/analytics.rb +1 -1
- data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/karafka/monitor.rb +1 -1
- data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +8 -0
- data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +18 -1
- data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
- data/lib/datadog/tracing/distributed/b3_single.rb +1 -1
- data/lib/datadog/tracing/distributed/datadog.rb +2 -2
- data/lib/datadog/tracing/sampling/rate_sampler.rb +2 -1
- data/lib/datadog/tracing/span_operation.rb +38 -14
- data/lib/datadog/tracing/trace_operation.rb +15 -7
- data/lib/datadog/tracing/tracer.rb +7 -3
- data/lib/datadog/tracing/utils.rb +1 -1
- data/lib/datadog/version.rb +1 -1
- data/lib/datadog.rb +2 -3
- metadata +34 -8
- data/lib/datadog/core/telemetry/http/env.rb +0 -20
- data/lib/datadog/core/telemetry/http/ext.rb +0 -28
- data/lib/datadog/core/telemetry/http/response.rb +0 -70
- data/lib/datadog/core/telemetry/http/transport.rb +0 -90
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../../event'
|
4
|
+
require_relative '../../security_event'
|
3
5
|
require_relative '../../instrumentation/gateway'
|
4
6
|
|
5
7
|
module Datadog
|
@@ -8,18 +10,24 @@ module Datadog
|
|
8
10
|
module Gateway
|
9
11
|
# Watcher for Apssec internal events
|
10
12
|
module Watcher
|
13
|
+
ARBITRARY_VALUE = 'invalid'
|
14
|
+
EVENT_LOGIN_SUCCESS = 'users.login.success'
|
15
|
+
EVENT_LOGIN_FAILURE = 'users.login.failure'
|
16
|
+
WATCHED_LOGIN_EVENTS = [EVENT_LOGIN_SUCCESS, EVENT_LOGIN_FAILURE].freeze
|
17
|
+
|
11
18
|
class << self
|
12
19
|
def watch
|
13
20
|
gateway = Instrumentation.gateway
|
14
21
|
|
15
22
|
watch_user_id(gateway)
|
23
|
+
watch_user_login(gateway)
|
16
24
|
end
|
17
25
|
|
18
26
|
def watch_user_id(gateway = Instrumentation.gateway)
|
19
27
|
gateway.watch('identity.set_user', :appsec) do |stack, user|
|
20
|
-
context =
|
28
|
+
context = AppSec.active_context
|
21
29
|
|
22
|
-
if user.id.nil? && user.login.nil?
|
30
|
+
if user.id.nil? && user.login.nil? && user.session_id.nil?
|
23
31
|
Datadog.logger.debug { 'AppSec: skipping WAF check because no user information was provided' }
|
24
32
|
next stack.call(user)
|
25
33
|
end
|
@@ -27,24 +35,46 @@ module Datadog
|
|
27
35
|
persistent_data = {}
|
28
36
|
persistent_data['usr.id'] = user.id if user.id
|
29
37
|
persistent_data['usr.login'] = user.login if user.login
|
38
|
+
persistent_data['usr.session_id'] = user.session_id if user.session_id
|
30
39
|
|
31
40
|
result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
|
32
41
|
|
42
|
+
if result.match? || result.derivatives.any?
|
43
|
+
context.events.push(
|
44
|
+
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
33
48
|
if result.match?
|
34
|
-
|
49
|
+
AppSec::Event.tag_and_keep!(context, result)
|
50
|
+
AppSec::ActionsHandler.handle(result.actions)
|
51
|
+
end
|
52
|
+
|
53
|
+
stack.call(user)
|
54
|
+
end
|
55
|
+
end
|
35
56
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
span: context.span,
|
40
|
-
user: user,
|
41
|
-
actions: result.actions
|
42
|
-
}
|
57
|
+
def watch_user_login(gateway = Instrumentation.gateway)
|
58
|
+
gateway.watch('appsec.events.user_lifecycle', :appsec) do |stack, kind|
|
59
|
+
context = AppSec.active_context
|
43
60
|
|
44
|
-
|
61
|
+
next stack.call(kind) unless WATCHED_LOGIN_EVENTS.include?(kind)
|
62
|
+
|
63
|
+
persistent_data = {"server.business_logic.#{kind}" => ARBITRARY_VALUE}
|
64
|
+
result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)
|
65
|
+
|
66
|
+
if result.match? || result.derivatives.any?
|
67
|
+
context.events.push(
|
68
|
+
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
|
69
|
+
)
|
45
70
|
end
|
46
71
|
|
47
|
-
|
72
|
+
if result.match?
|
73
|
+
AppSec::Event.tag_and_keep!(context, result)
|
74
|
+
AppSec::ActionsHandler.handle(result.actions)
|
75
|
+
end
|
76
|
+
|
77
|
+
stack.call(kind)
|
48
78
|
end
|
49
79
|
end
|
50
80
|
end
|
@@ -10,35 +10,33 @@ module Datadog
|
|
10
10
|
module RuleLoader
|
11
11
|
class << self
|
12
12
|
def load_rules(ruleset:, telemetry:)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
13
|
+
case ruleset
|
14
|
+
when :recommended, :strict
|
15
|
+
JSON.parse(Datadog::AppSec::Assets.waf_rules(ruleset))
|
16
|
+
when :risky
|
17
|
+
Datadog.logger.warn(
|
18
|
+
'The :risky Application Security Management ruleset has been deprecated and no longer available.' \
|
19
|
+
'The `:recommended` ruleset will be used instead.' \
|
20
|
+
'Please remove the `appsec.ruleset = :risky` setting from your Datadog.configure block.'
|
21
|
+
)
|
22
|
+
JSON.parse(Datadog::AppSec::Assets.waf_rules(:recommended))
|
23
|
+
when String
|
24
|
+
JSON.parse(File.read(File.expand_path(ruleset)))
|
25
|
+
when File, StringIO
|
26
|
+
JSON.parse(ruleset.read || '').tap { ruleset.rewind }
|
27
|
+
when Hash
|
28
|
+
ruleset
|
29
|
+
else
|
30
|
+
raise ArgumentError, "unsupported value for ruleset setting: #{ruleset.inspect}"
|
31
|
+
end
|
32
|
+
rescue => e
|
33
|
+
Datadog.logger.error do
|
34
|
+
"libddwaf ruleset failed to load, ruleset: #{ruleset.inspect} error: #{e.inspect}"
|
35
|
+
end
|
37
36
|
|
38
|
-
|
37
|
+
telemetry.report(e, description: 'libddwaf ruleset failed to load')
|
39
38
|
|
40
|
-
|
41
|
-
end
|
39
|
+
nil
|
42
40
|
end
|
43
41
|
|
44
42
|
def load_data(ip_denylist: [], user_id_denylist: [])
|
@@ -62,7 +60,7 @@ module Datadog
|
|
62
60
|
{
|
63
61
|
'id' => id,
|
64
62
|
'type' => 'data_with_expiration',
|
65
|
-
'data' => denylist.map { |v| {
|
63
|
+
'data' => denylist.map { |v| {'value' => v.to_s, 'expiration' => 2**63} }
|
66
64
|
}
|
67
65
|
end
|
68
66
|
|
@@ -11,8 +11,8 @@ module Datadog
|
|
11
11
|
# RuleVersionMismatchError
|
12
12
|
class RuleVersionMismatchError < StandardError
|
13
13
|
def initialize(version1, version2)
|
14
|
-
msg = 'Merging rule files with different version could lead to unkown behaviour. '\
|
15
|
-
"We have receieve two rule files with versions: #{version1}, #{version2}. "\
|
14
|
+
msg = 'Merging rule files with different version could lead to unkown behaviour. ' \
|
15
|
+
"We have receieve two rule files with versions: #{version1}, #{version2}. " \
|
16
16
|
'Please validate the configuration is correct and try again.'
|
17
17
|
super(msg)
|
18
18
|
end
|
@@ -27,7 +27,7 @@ module Datadog
|
|
27
27
|
)
|
28
28
|
processors ||= begin
|
29
29
|
default_waf_processors
|
30
|
-
rescue
|
30
|
+
rescue => e
|
31
31
|
Datadog.logger.error("libddwaf rulemerger failed to parse default waf processors. Error: #{e.inspect}")
|
32
32
|
telemetry.report(
|
33
33
|
e,
|
@@ -38,7 +38,7 @@ module Datadog
|
|
38
38
|
|
39
39
|
scanners ||= begin
|
40
40
|
default_waf_scanners
|
41
|
-
rescue
|
41
|
+
rescue => e
|
42
42
|
Datadog.logger.error("libddwaf rulemerger failed to parse default waf scanners. Error: #{e.inspect}")
|
43
43
|
telemetry.report(
|
44
44
|
e,
|
@@ -146,7 +146,7 @@ module Datadog
|
|
146
146
|
end
|
147
147
|
|
148
148
|
result.each_with_object([]) do |entry, acc|
|
149
|
-
value = {
|
149
|
+
value = {'value' => entry[0]}
|
150
150
|
value['expiration'] = entry[1] if entry[1]
|
151
151
|
|
152
152
|
acc << value
|
@@ -9,22 +9,23 @@ module Datadog
|
|
9
9
|
# Remote
|
10
10
|
module Remote
|
11
11
|
class ReadError < StandardError; end
|
12
|
+
|
12
13
|
class NoRulesError < StandardError; end
|
13
14
|
|
14
15
|
class << self
|
15
|
-
CAP_ASM_RESERVED_1
|
16
|
-
CAP_ASM_ACTIVATION
|
17
|
-
CAP_ASM_IP_BLOCKING
|
18
|
-
CAP_ASM_DD_RULES
|
19
|
-
CAP_ASM_EXCLUSIONS
|
20
|
-
CAP_ASM_REQUEST_BLOCKING
|
21
|
-
CAP_ASM_RESPONSE_BLOCKING
|
22
|
-
CAP_ASM_USER_BLOCKING
|
23
|
-
CAP_ASM_CUSTOM_RULES
|
24
|
-
CAP_ASM_CUSTOM_BLOCKING_RESPONSE
|
25
|
-
CAP_ASM_TRUSTED_IPS
|
26
|
-
CAP_ASM_RASP_SSRF
|
27
|
-
CAP_ASM_RASP_SQLI
|
16
|
+
CAP_ASM_RESERVED_1 = 1 << 0 # RESERVED
|
17
|
+
CAP_ASM_ACTIVATION = 1 << 1 # Remote activation via ASM_FEATURES product
|
18
|
+
CAP_ASM_IP_BLOCKING = 1 << 2 # accept IP blocking data from ASM_DATA product
|
19
|
+
CAP_ASM_DD_RULES = 1 << 3 # read ASM rules from ASM_DD product
|
20
|
+
CAP_ASM_EXCLUSIONS = 1 << 4 # exclusion filters (passlist) via ASM product
|
21
|
+
CAP_ASM_REQUEST_BLOCKING = 1 << 5 # can block on request info
|
22
|
+
CAP_ASM_RESPONSE_BLOCKING = 1 << 6 # can block on response info
|
23
|
+
CAP_ASM_USER_BLOCKING = 1 << 7 # accept user blocking data from ASM_DATA product
|
24
|
+
CAP_ASM_CUSTOM_RULES = 1 << 8 # accept custom rules
|
25
|
+
CAP_ASM_CUSTOM_BLOCKING_RESPONSE = 1 << 9 # supports custom http code or redirect sa blocking response
|
26
|
+
CAP_ASM_TRUSTED_IPS = 1 << 10 # supports trusted ip
|
27
|
+
CAP_ASM_RASP_SSRF = 1 << 23 # support for server-side request forgery exploit prevention rules
|
28
|
+
CAP_ASM_RASP_SQLI = 1 << 21 # support for SQL injection exploit prevention rules
|
28
29
|
|
29
30
|
# TODO: we need to dynamically add CAP_ASM_ACTIVATION once we support it
|
30
31
|
ASM_CAPABILITIES = [
|
@@ -30,13 +30,13 @@ module Datadog
|
|
30
30
|
|
31
31
|
def block_response(interrupt_params, http_accept_header)
|
32
32
|
content_type = case interrupt_params['type']
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
when nil, 'auto' then content_type(http_accept_header)
|
34
|
+
else FORMAT_TO_CONTENT_TYPE.fetch(interrupt_params['type'], DEFAULT_CONTENT_TYPE)
|
35
|
+
end
|
36
36
|
|
37
37
|
Response.new(
|
38
38
|
status: interrupt_params['status_code']&.to_i || 403,
|
39
|
-
headers: {
|
39
|
+
headers: {'Content-Type' => content_type},
|
40
40
|
body: [content(content_type)],
|
41
41
|
)
|
42
42
|
end
|
@@ -45,8 +45,8 @@ module Datadog
|
|
45
45
|
status_code = interrupt_params['status_code'].to_i
|
46
46
|
|
47
47
|
Response.new(
|
48
|
-
status: (status_code >= 300 && status_code < 400 ? status_code : 303),
|
49
|
-
headers: {
|
48
|
+
status: ((status_code >= 300 && status_code < 400) ? status_code : 303),
|
49
|
+
headers: {'Location' => interrupt_params.fetch('location')},
|
50
50
|
body: [],
|
51
51
|
)
|
52
52
|
end
|
@@ -42,7 +42,7 @@ module Datadog
|
|
42
42
|
return Result::Error.new(duration_ext_ns: stop_ns - start_ns)
|
43
43
|
end
|
44
44
|
|
45
|
-
klass = result.status == :match ? Result::Match : Result::Ok
|
45
|
+
klass = (result.status == :match) ? Result::Match : Result::Ok
|
46
46
|
klass.new(
|
47
47
|
events: result.events,
|
48
48
|
actions: result.actions,
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module AppSec
|
5
|
+
# A class that represents a security event of any kind. It could be an event
|
6
|
+
# representing an attack or fingerprinting results as derivatives or an API
|
7
|
+
# security check with extracted schema.
|
8
|
+
class SecurityEvent
|
9
|
+
SCHEMA_KEY_PREFIX = '_dd.appsec.s.'
|
10
|
+
FINGERPRINT_KEY_PREFIX = '_dd.appsec.fp.'
|
11
|
+
|
12
|
+
attr_reader :waf_result, :trace, :span
|
13
|
+
|
14
|
+
def initialize(waf_result, trace:, span:)
|
15
|
+
@waf_result = waf_result
|
16
|
+
@trace = trace
|
17
|
+
@span = span
|
18
|
+
end
|
19
|
+
|
20
|
+
def attack?
|
21
|
+
return @is_attack if defined?(@is_attack)
|
22
|
+
|
23
|
+
@is_attack = @waf_result.is_a?(SecurityEngine::Result::Match)
|
24
|
+
end
|
25
|
+
|
26
|
+
def schema?
|
27
|
+
return @has_schema if defined?(@has_schema)
|
28
|
+
|
29
|
+
@has_schema = @waf_result.derivatives.any? { |name, _| name.start_with?(SCHEMA_KEY_PREFIX) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def fingerprint?
|
33
|
+
return @has_fingerprint if defined?(@has_fingerprint)
|
34
|
+
|
35
|
+
@has_fingerprint = @waf_result.derivatives.any? { |name, _| name.start_with?(FINGERPRINT_KEY_PREFIX) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/datadog/appsec.rb
CHANGED
@@ -44,7 +44,7 @@ module Datadog
|
|
44
44
|
appsec_component.reconfigure_lock(&block)
|
45
45
|
end
|
46
46
|
|
47
|
-
def
|
47
|
+
def perform_api_security_check?
|
48
48
|
Datadog.configuration.appsec.api_security.enabled &&
|
49
49
|
Datadog.configuration.appsec.api_security.sample_rate.sample?
|
50
50
|
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Style/*
|
4
|
+
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
require_relative 'agent_settings_resolver'
|
8
|
+
|
9
|
+
module Datadog
|
10
|
+
module Core
|
11
|
+
module Configuration
|
12
|
+
# Agent settings resolver for agentless operations (currently, telemetry
|
13
|
+
# in agentless mode).
|
14
|
+
#
|
15
|
+
# The terminology gets a little confusing here, but transports communicate
|
16
|
+
# with servers which are - for most components in the tracer - the
|
17
|
+
# (local) agent. Hence, "agent settings" to refer to where the server
|
18
|
+
# is located. Telemetry supports sending to the local agent but also
|
19
|
+
# implements agentless mode where it sends directly to Datadog intake
|
20
|
+
# endpoints. The agentless mode is configured using different settings,
|
21
|
+
# and this class produces AgentSettings instances when in agentless mode.
|
22
|
+
#
|
23
|
+
# Agentless settings resolver uses the following configuration sources:
|
24
|
+
#
|
25
|
+
# 1. url_override constructor parameter, if provided
|
26
|
+
# 2. Built-in default host/port/TLS settings for the backend
|
27
|
+
# intake endpoint
|
28
|
+
#
|
29
|
+
# The agentless resolver does NOT use agent settings (since it is
|
30
|
+
# for agentless operation), specifically it ignores:
|
31
|
+
#
|
32
|
+
# - c.agent.host
|
33
|
+
# - DD_AGENT_HOST
|
34
|
+
# - c.agent.port
|
35
|
+
# - DD_AGENT_PORT
|
36
|
+
#
|
37
|
+
# However, agentless resolver does respect the timeout specified via
|
38
|
+
# c.agent.timeout_seconds or DD_TRACE_AGENT_TIMEOUT_SECONDS.
|
39
|
+
class AgentlessSettingsResolver < AgentSettingsResolver
|
40
|
+
# To avoid coupling this class to telemetry, the URL override is
|
41
|
+
# taken here as a parameter instead of being read out of
|
42
|
+
# c.telemetry.agentless_url_override. For the same reason, the
|
43
|
+
# +url_override_source+ parameter should be set to the string
|
44
|
+
# "c.telemetry.agentless_url_override".
|
45
|
+
def self.call(settings, host_prefix:, url_override: nil, url_override_source: nil, logger: Datadog.logger)
|
46
|
+
new(
|
47
|
+
settings,
|
48
|
+
host_prefix: host_prefix,
|
49
|
+
url_override: url_override,
|
50
|
+
url_override_source: url_override_source,
|
51
|
+
logger: logger
|
52
|
+
).send(:call)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
attr_reader \
|
58
|
+
:host_prefix,
|
59
|
+
:url_override,
|
60
|
+
:url_override_source
|
61
|
+
|
62
|
+
def initialize(settings, host_prefix:, url_override: nil, url_override_source: nil, logger: Datadog.logger)
|
63
|
+
if url_override && url_override_source.nil?
|
64
|
+
raise ArgumentError, 'url_override_source must be provided when url_override is provided'
|
65
|
+
end
|
66
|
+
|
67
|
+
super(settings, logger: logger)
|
68
|
+
|
69
|
+
@host_prefix = host_prefix
|
70
|
+
@url_override = url_override
|
71
|
+
@url_override_source = url_override_source
|
72
|
+
end
|
73
|
+
|
74
|
+
def hostname
|
75
|
+
if should_use_uds?
|
76
|
+
nil
|
77
|
+
else
|
78
|
+
configured_hostname || "#{host_prefix}.#{settings.site}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def configured_hostname
|
83
|
+
return @configured_hostname if defined?(@configured_hostname)
|
84
|
+
|
85
|
+
if should_use_uds?
|
86
|
+
nil
|
87
|
+
else
|
88
|
+
@configured_hostname = (parsed_url.hostname if parsed_url)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def configured_port
|
93
|
+
return @configured_port if defined?(@configured_port)
|
94
|
+
|
95
|
+
@configured_port = (parsed_url.port if parsed_url)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Note that this method should always return true or false
|
99
|
+
def ssl?
|
100
|
+
if configured_hostname
|
101
|
+
configured_ssl || false
|
102
|
+
else
|
103
|
+
if should_use_uds?
|
104
|
+
false
|
105
|
+
else
|
106
|
+
# If no hostname is specified, we are communicating with the
|
107
|
+
# default Datadog intake, which uses TLS.
|
108
|
+
true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Note that this method can return nil
|
114
|
+
def configured_ssl
|
115
|
+
return @configured_ssl if defined?(@configured_ssl)
|
116
|
+
|
117
|
+
@configured_ssl = (parsed_url_ssl? if parsed_url)
|
118
|
+
end
|
119
|
+
|
120
|
+
def port
|
121
|
+
if configured_port
|
122
|
+
configured_port
|
123
|
+
else
|
124
|
+
if should_use_uds?
|
125
|
+
nil
|
126
|
+
else
|
127
|
+
# If no hostname is specified, we are communicating with the
|
128
|
+
# default Datadog intake, which exists on port 443.
|
129
|
+
443
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def mixed_http_and_uds
|
135
|
+
false
|
136
|
+
end
|
137
|
+
|
138
|
+
def configured_uds_path
|
139
|
+
return @configured_uds_path if defined?(@configured_uds_path)
|
140
|
+
|
141
|
+
parsed_url_uds_path
|
142
|
+
end
|
143
|
+
|
144
|
+
def can_use_uds?
|
145
|
+
# While in theory agentless transport could communicate via UDS,
|
146
|
+
# in practice "agentless" means we are communicating with Datadog
|
147
|
+
# infrastructure which is always remote.
|
148
|
+
# Permit UDS for proxy usage?
|
149
|
+
!configured_uds_path.nil?
|
150
|
+
end
|
151
|
+
|
152
|
+
def parsed_url
|
153
|
+
return @parsed_url if defined?(@parsed_url)
|
154
|
+
|
155
|
+
@parsed_url =
|
156
|
+
if @url_override
|
157
|
+
parsed = URI.parse(@url_override)
|
158
|
+
|
159
|
+
# Agentless URL should never refer to a UDS?
|
160
|
+
if http_scheme?(parsed) || unix_scheme?(parsed)
|
161
|
+
parsed
|
162
|
+
else
|
163
|
+
log_warning(
|
164
|
+
"Invalid URI scheme '#{parsed.scheme}' for #{url_override_source}. " \
|
165
|
+
"Ignoring the contents of #{url_override_source}."
|
166
|
+
)
|
167
|
+
nil
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# rubocop:enable Style/*
|
@@ -14,9 +14,11 @@ require_relative '../../tracing/component'
|
|
14
14
|
require_relative '../../profiling/component'
|
15
15
|
require_relative '../../appsec/component'
|
16
16
|
require_relative '../../di/component'
|
17
|
+
require_relative '../../error_tracking/component'
|
17
18
|
require_relative '../crashtracking/component'
|
18
19
|
|
19
20
|
require_relative '../environment/agent_info'
|
21
|
+
require_relative '../process_discovery'
|
20
22
|
|
21
23
|
module Datadog
|
22
24
|
module Core
|
@@ -26,12 +28,12 @@ module Datadog
|
|
26
28
|
class << self
|
27
29
|
include Datadog::Tracing::Component
|
28
30
|
|
29
|
-
def build_health_metrics(settings, logger)
|
31
|
+
def build_health_metrics(settings, logger, telemetry)
|
30
32
|
settings = settings.health_metrics
|
31
33
|
options = { enabled: settings.enabled }
|
32
34
|
options[:statsd] = settings.statsd unless settings.statsd.nil?
|
33
35
|
|
34
|
-
Core::Diagnostics::Health::Metrics.new(logger: logger, **options)
|
36
|
+
Core::Diagnostics::Health::Metrics.new(telemetry: telemetry, logger: logger, **options)
|
35
37
|
end
|
36
38
|
|
37
39
|
def build_logger(settings)
|
@@ -41,24 +43,24 @@ module Datadog
|
|
41
43
|
logger
|
42
44
|
end
|
43
45
|
|
44
|
-
def build_runtime_metrics(settings, logger)
|
46
|
+
def build_runtime_metrics(settings, logger, telemetry)
|
45
47
|
options = { enabled: settings.runtime_metrics.enabled }
|
46
48
|
options[:statsd] = settings.runtime_metrics.statsd unless settings.runtime_metrics.statsd.nil?
|
47
49
|
options[:services] = [settings.service] unless settings.service.nil?
|
48
50
|
options[:experimental_runtime_id_enabled] = settings.runtime_metrics.experimental_runtime_id_enabled
|
49
51
|
|
50
|
-
Core::Runtime::Metrics.new(logger: logger, **options)
|
52
|
+
Core::Runtime::Metrics.new(logger: logger, telemetry: telemetry, **options)
|
51
53
|
end
|
52
54
|
|
53
|
-
def build_runtime_metrics_worker(settings, logger)
|
55
|
+
def build_runtime_metrics_worker(settings, logger, telemetry)
|
54
56
|
# NOTE: Should we just ignore building the worker if its not enabled?
|
55
57
|
options = settings.runtime_metrics.opts.merge(
|
56
58
|
enabled: settings.runtime_metrics.enabled,
|
57
|
-
metrics: build_runtime_metrics(settings, logger),
|
59
|
+
metrics: build_runtime_metrics(settings, logger, telemetry),
|
58
60
|
logger: logger,
|
59
61
|
)
|
60
62
|
|
61
|
-
Core::Workers::RuntimeMetrics.new(options)
|
63
|
+
Core::Workers::RuntimeMetrics.new(telemetry: telemetry, **options)
|
62
64
|
end
|
63
65
|
|
64
66
|
def build_telemetry(settings, agent_settings, logger)
|
@@ -68,7 +70,7 @@ module Datadog
|
|
68
70
|
def build_crashtracker(settings, agent_settings, logger:)
|
69
71
|
return unless settings.crashtracking.enabled
|
70
72
|
|
71
|
-
if (libdatadog_api_failure = Datadog::Core::
|
73
|
+
if (libdatadog_api_failure = Datadog::Core::LIBDATADOG_API_FAILURE)
|
72
74
|
logger.debug("Cannot enable crashtracking: #{libdatadog_api_failure}")
|
73
75
|
return
|
74
76
|
end
|
@@ -88,6 +90,7 @@ module Datadog
|
|
88
90
|
:telemetry,
|
89
91
|
:tracer,
|
90
92
|
:crashtracker,
|
93
|
+
:error_tracking,
|
91
94
|
:dynamic_instrumentation,
|
92
95
|
:appsec,
|
93
96
|
:agent_info
|
@@ -118,11 +121,14 @@ module Datadog
|
|
118
121
|
)
|
119
122
|
@environment_logger_extra.merge!(profiler_logger_extra) if profiler_logger_extra
|
120
123
|
|
121
|
-
@runtime_metrics = self.class.build_runtime_metrics_worker(settings, @logger)
|
122
|
-
@health_metrics = self.class.build_health_metrics(settings, @logger)
|
124
|
+
@runtime_metrics = self.class.build_runtime_metrics_worker(settings, @logger, telemetry)
|
125
|
+
@health_metrics = self.class.build_health_metrics(settings, @logger, telemetry)
|
123
126
|
@appsec = Datadog::AppSec::Component.build_appsec_component(settings, telemetry: telemetry)
|
124
127
|
@dynamic_instrumentation = Datadog::DI::Component.build(settings, agent_settings, @logger, telemetry: telemetry)
|
128
|
+
@error_tracking = Datadog::ErrorTracking::Component.build(settings, @tracer, @logger)
|
125
129
|
@environment_logger_extra[:dynamic_instrumentation_enabled] = !!@dynamic_instrumentation
|
130
|
+
# TODO: Re-enable this once we have updated libdatadog to 17.1
|
131
|
+
# @process_discovery_fd = Core::ProcessDiscovery.get_and_store_metadata(settings, @logger)
|
126
132
|
|
127
133
|
self.class.configure_tracing(settings)
|
128
134
|
end
|
@@ -203,6 +209,9 @@ module Datadog
|
|
203
209
|
# enqueue closing event before stopping telemetry so it will be send out on shutdown
|
204
210
|
telemetry.emit_closing! unless replacement
|
205
211
|
telemetry.stop!
|
212
|
+
|
213
|
+
# TODO: Re-enable this once we have updated libdatadog to 17.1
|
214
|
+
# Core::ProcessDiscovery._native_close_tracer_memfd(@process_discovery_fd, @logger) if @process_discovery_fd
|
206
215
|
end
|
207
216
|
end
|
208
217
|
end
|