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.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -2
  3. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +1 -4
  4. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +7 -0
  5. data/ext/datadog_profiling_native_extension/extconf.rb +3 -0
  6. data/ext/datadog_profiling_native_extension/heap_recorder.c +8 -1
  7. data/ext/libdatadog_api/crashtracker.c +1 -9
  8. data/ext/libdatadog_api/crashtracker.h +5 -0
  9. data/ext/libdatadog_api/datadog_ruby_common.c +1 -4
  10. data/ext/libdatadog_api/datadog_ruby_common.h +7 -0
  11. data/ext/libdatadog_api/init.c +15 -0
  12. data/ext/libdatadog_api/library_config.c +122 -0
  13. data/ext/libdatadog_api/library_config.h +19 -0
  14. data/ext/libdatadog_api/process_discovery.c +117 -0
  15. data/ext/libdatadog_api/process_discovery.h +5 -0
  16. data/lib/datadog/appsec/actions_handler.rb +3 -2
  17. data/lib/datadog/appsec/assets/waf_rules/recommended.json +1344 -0
  18. data/lib/datadog/appsec/assets/waf_rules/strict.json +1344 -0
  19. data/lib/datadog/appsec/autoload.rb +1 -1
  20. data/lib/datadog/appsec/component.rb +11 -4
  21. data/lib/datadog/appsec/configuration/settings.rb +31 -18
  22. data/lib/datadog/appsec/context.rb +1 -1
  23. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +10 -12
  24. data/lib/datadog/appsec/contrib/active_record/integration.rb +1 -1
  25. data/lib/datadog/appsec/contrib/active_record/patcher.rb +22 -22
  26. data/lib/datadog/appsec/contrib/devise/data_extractor.rb +2 -3
  27. data/lib/datadog/appsec/contrib/devise/ext.rb +1 -0
  28. data/lib/datadog/appsec/contrib/devise/integration.rb +1 -1
  29. data/lib/datadog/appsec/contrib/devise/patcher.rb +3 -5
  30. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +17 -4
  31. data/lib/datadog/appsec/contrib/excon/integration.rb +1 -1
  32. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +9 -10
  33. data/lib/datadog/appsec/contrib/faraday/integration.rb +1 -1
  34. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +8 -9
  35. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +8 -9
  36. data/lib/datadog/appsec/contrib/graphql/integration.rb +1 -1
  37. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +22 -32
  38. data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
  39. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +16 -16
  40. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +11 -13
  41. data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
  42. data/lib/datadog/appsec/contrib/rails/patcher.rb +21 -21
  43. data/lib/datadog/appsec/contrib/rest_client/integration.rb +1 -1
  44. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +10 -11
  45. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +17 -23
  46. data/lib/datadog/appsec/contrib/sinatra/integration.rb +1 -1
  47. data/lib/datadog/appsec/event.rb +85 -95
  48. data/lib/datadog/appsec/instrumentation/gateway/argument.rb +5 -2
  49. data/lib/datadog/appsec/metrics/telemetry.rb +1 -1
  50. data/lib/datadog/appsec/monitor/gateway/watcher.rb +42 -12
  51. data/lib/datadog/appsec/processor/rule_loader.rb +26 -28
  52. data/lib/datadog/appsec/processor/rule_merger.rb +5 -5
  53. data/lib/datadog/appsec/processor.rb +1 -1
  54. data/lib/datadog/appsec/remote.rb +14 -13
  55. data/lib/datadog/appsec/response.rb +6 -6
  56. data/lib/datadog/appsec/security_engine/runner.rb +1 -1
  57. data/lib/datadog/appsec/security_event.rb +39 -0
  58. data/lib/datadog/appsec.rb +1 -1
  59. data/lib/datadog/core/configuration/agentless_settings_resolver.rb +176 -0
  60. data/lib/datadog/core/configuration/components.rb +19 -10
  61. data/lib/datadog/core/configuration/option.rb +61 -25
  62. data/lib/datadog/core/configuration/settings.rb +10 -0
  63. data/lib/datadog/core/configuration/stable_config.rb +23 -0
  64. data/lib/datadog/core/configuration.rb +24 -0
  65. data/lib/datadog/core/crashtracking/component.rb +1 -9
  66. data/lib/datadog/core/environment/git.rb +1 -0
  67. data/lib/datadog/core/environment/variable_helpers.rb +1 -1
  68. data/lib/datadog/core/metrics/client.rb +8 -7
  69. data/lib/datadog/core/process_discovery.rb +32 -0
  70. data/lib/datadog/core/remote/client.rb +7 -0
  71. data/lib/datadog/core/runtime/metrics.rb +1 -1
  72. data/lib/datadog/core/telemetry/component.rb +60 -50
  73. data/lib/datadog/core/telemetry/emitter.rb +17 -11
  74. data/lib/datadog/core/telemetry/event.rb +7 -4
  75. data/lib/datadog/core/telemetry/http/adapters/net.rb +12 -97
  76. data/lib/datadog/core/telemetry/request.rb +3 -3
  77. data/lib/datadog/core/telemetry/transport/http/api.rb +43 -0
  78. data/lib/datadog/core/telemetry/transport/http/client.rb +49 -0
  79. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +92 -0
  80. data/lib/datadog/core/telemetry/transport/http.rb +63 -0
  81. data/lib/datadog/core/telemetry/transport/telemetry.rb +52 -0
  82. data/lib/datadog/core/telemetry/worker.rb +45 -0
  83. data/lib/datadog/core/utils/time.rb +12 -0
  84. data/lib/datadog/core/workers/async.rb +20 -2
  85. data/lib/datadog/core/workers/interval_loop.rb +12 -1
  86. data/lib/datadog/core/workers/runtime_metrics.rb +2 -2
  87. data/lib/datadog/core.rb +8 -0
  88. data/lib/datadog/di/boot.rb +34 -0
  89. data/lib/datadog/di/remote.rb +2 -0
  90. data/lib/datadog/di.rb +5 -32
  91. data/lib/datadog/error_tracking/collector.rb +87 -0
  92. data/lib/datadog/error_tracking/component.rb +167 -0
  93. data/lib/datadog/error_tracking/configuration/settings.rb +63 -0
  94. data/lib/datadog/error_tracking/configuration.rb +11 -0
  95. data/lib/datadog/error_tracking/ext.rb +18 -0
  96. data/lib/datadog/error_tracking/extensions.rb +16 -0
  97. data/lib/datadog/error_tracking/filters.rb +77 -0
  98. data/lib/datadog/error_tracking.rb +18 -0
  99. data/lib/datadog/kit/identity.rb +1 -1
  100. data/lib/datadog/profiling/exporter.rb +1 -1
  101. data/lib/datadog/tracing/analytics.rb +1 -1
  102. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +2 -0
  103. data/lib/datadog/tracing/contrib/karafka/monitor.rb +1 -1
  104. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +8 -0
  105. data/lib/datadog/tracing/contrib/mongodb/ext.rb +1 -0
  106. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +18 -1
  107. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  108. data/lib/datadog/tracing/distributed/b3_single.rb +1 -1
  109. data/lib/datadog/tracing/distributed/datadog.rb +2 -2
  110. data/lib/datadog/tracing/sampling/rate_sampler.rb +2 -1
  111. data/lib/datadog/tracing/span_operation.rb +38 -14
  112. data/lib/datadog/tracing/trace_operation.rb +15 -7
  113. data/lib/datadog/tracing/tracer.rb +7 -3
  114. data/lib/datadog/tracing/utils.rb +1 -1
  115. data/lib/datadog/version.rb +1 -1
  116. data/lib/datadog.rb +2 -3
  117. metadata +34 -8
  118. data/lib/datadog/core/telemetry/http/env.rb +0 -20
  119. data/lib/datadog/core/telemetry/http/ext.rb +0 -28
  120. data/lib/datadog/core/telemetry/http/response.rb +0 -70
  121. 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 = Datadog::AppSec.active_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
- Datadog::AppSec::Event.tag_and_keep!(context, result)
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
- context.events << {
37
- waf_result: result,
38
- trace: context.trace,
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
- Datadog::AppSec::ActionsHandler.handle(result.actions)
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
- stack.call(user)
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
- begin
14
- case ruleset
15
- when :recommended, :strict
16
- JSON.parse(Datadog::AppSec::Assets.waf_rules(ruleset))
17
- when :risky
18
- Datadog.logger.warn(
19
- 'The :risky Application Security Management ruleset has been deprecated and no longer available.'\
20
- 'The `:recommended` ruleset will be used instead.'\
21
- 'Please remove the `appsec.ruleset = :risky` setting from your Datadog.configure block.'
22
- )
23
- JSON.parse(Datadog::AppSec::Assets.waf_rules(:recommended))
24
- when String
25
- JSON.parse(File.read(File.expand_path(ruleset)))
26
- when File, StringIO
27
- JSON.parse(ruleset.read || '').tap { ruleset.rewind }
28
- when Hash
29
- ruleset
30
- else
31
- raise ArgumentError, "unsupported value for ruleset setting: #{ruleset.inspect}"
32
- end
33
- rescue StandardError => e
34
- Datadog.logger.error do
35
- "libddwaf ruleset failed to load, ruleset: #{ruleset.inspect} error: #{e.inspect}"
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
- telemetry.report(e, description: 'libddwaf ruleset failed to load')
37
+ telemetry.report(e, description: 'libddwaf ruleset failed to load')
39
38
 
40
- nil
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| { 'value' => v.to_s, 'expiration' => 2**63 } }
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 StandardError => e
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 StandardError => e
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 = { 'value' => entry[0] }
149
+ value = {'value' => entry[0]}
150
150
  value['expiration'] = entry[1] if entry[1]
151
151
 
152
152
  acc << value
@@ -82,7 +82,7 @@ module Datadog
82
82
  @diagnostics = e.diagnostics if e.diagnostics
83
83
 
84
84
  false
85
- rescue StandardError => e
85
+ rescue => e
86
86
  Datadog.logger.error do
87
87
  "libddwaf failed to initialize, error: #{e.inspect}"
88
88
  end
@@ -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 = 1 << 0 # RESERVED
16
- CAP_ASM_ACTIVATION = 1 << 1 # Remote activation via ASM_FEATURES product
17
- CAP_ASM_IP_BLOCKING = 1 << 2 # accept IP blocking data from ASM_DATA product
18
- CAP_ASM_DD_RULES = 1 << 3 # read ASM rules from ASM_DD product
19
- CAP_ASM_EXCLUSIONS = 1 << 4 # exclusion filters (passlist) via ASM product
20
- CAP_ASM_REQUEST_BLOCKING = 1 << 5 # can block on request info
21
- CAP_ASM_RESPONSE_BLOCKING = 1 << 6 # can block on response info
22
- CAP_ASM_USER_BLOCKING = 1 << 7 # accept user blocking data from ASM_DATA product
23
- CAP_ASM_CUSTOM_RULES = 1 << 8 # accept custom rules
24
- CAP_ASM_CUSTOM_BLOCKING_RESPONSE = 1 << 9 # supports custom http code or redirect sa blocking response
25
- CAP_ASM_TRUSTED_IPS = 1 << 10 # supports trusted ip
26
- CAP_ASM_RASP_SSRF = 1 << 23 # support for server-side request forgery exploit prevention rules
27
- CAP_ASM_RASP_SQLI = 1 << 21 # support for SQL injection exploit prevention rules
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
- when nil, 'auto' then content_type(http_accept_header)
34
- else FORMAT_TO_CONTENT_TYPE.fetch(interrupt_params['type'], DEFAULT_CONTENT_TYPE)
35
- end
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: { 'Content-Type' => content_type },
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: { 'Location' => interrupt_params.fetch('location') },
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
@@ -44,7 +44,7 @@ module Datadog
44
44
  appsec_component.reconfigure_lock(&block)
45
45
  end
46
46
 
47
- def api_security_enabled?
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::Crashtracking::Component::LIBDATADOG_API_FAILURE)
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