newrelic_rpm 5.1.0.344 → 5.2.0.345

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 542ac1ce1a083c278c83f047f78ab8b3c8810a0916c87f83894a1f923d0a3fd2
4
- data.tar.gz: 4e1eb213e1a4dac5877552f15e971bdf1b5005d2fb208a5364a8e1e59b9d25a0
3
+ metadata.gz: d6f27f55d62839f2426dcc139af19e97f815c70783944d4c67363a7d54a5e208
4
+ data.tar.gz: 6543b4fa585ab70291df98c295818ccb74eb4d2ab00a9ac3f5093c7b89656d4d
5
5
  SHA512:
6
- metadata.gz: eaf4583a69257f2e22aaae8a6148a5b38d130f83e9585fd69b70ddaf6696e18d7da0a814e7d0046fa6fc6d1afd23f591f961194b56c3da4d86c97cc9d975da4e
7
- data.tar.gz: 49de5e3b35b731ef61835a2f3fed0a82fcf7ff9af97db695c829cd3936b19bd7ecf1087ff69c5a755a743f203a1efec8c070d4587737e0419e364116bd827121
6
+ metadata.gz: 658044904957344ad87805e072168e9f366453733f0b9e42051230c2999be7170bfe5cac64c402e0ced3483e1f3d5ce091622c01d2ecb685e4354b41e6a11a38
7
+ data.tar.gz: ec9e153d940103faddd85fca6b00191e9214770aa5c7b5d1318fff41e3ff6d69f9720d2300847571ed1ba92b7d9c468fce5afc8dc6cfa711807405384d6cc94e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # New Relic Ruby Agent Release Notes #
2
2
 
3
+ ## v5.2.0 ##
4
+
5
+ * Use priority sampling for errors and custom events
6
+
7
+ Priority sampling replaces the older reservoir event sampling method.
8
+ With this change, the agent will maintain randomness across a given
9
+ time period while improving coordination among transactions, errors,
10
+ and custom events.
11
+
12
+ * Bugfix for wrapping datastore operations
13
+
14
+ The agent will now complete the process of wrapping datastore
15
+ operations even if an error occurs during execution of a callback.
16
+
3
17
  ## v5.1.0 ##
4
18
 
5
19
  * Rails 5.2 support
@@ -8,7 +8,6 @@ require 'net/http'
8
8
  require 'logger'
9
9
  require 'zlib'
10
10
  require 'stringio'
11
- require 'new_relic/agent/sampled_buffer'
12
11
  require 'new_relic/agent/autostart'
13
12
  require 'new_relic/agent/harvester'
14
13
  require 'new_relic/agent/hostname'
@@ -21,7 +20,6 @@ require 'new_relic/agent/event_listener'
21
20
  require 'new_relic/agent/cross_app_monitor'
22
21
  require 'new_relic/agent/distributed_trace_monitor'
23
22
  require 'new_relic/agent/synthetics_monitor'
24
- require 'new_relic/agent/synthetics_event_buffer'
25
23
  require 'new_relic/agent/transaction_event_recorder'
26
24
  require 'new_relic/agent/custom_event_aggregator'
27
25
  require 'new_relic/agent/sampler_collection'
@@ -183,7 +181,8 @@ module NewRelic
183
181
  return if !needs_restart ||
184
182
  !Agent.config[:agent_enabled] ||
185
183
  !Agent.config[:monitor_mode] ||
186
- disconnected?
184
+ disconnected? ||
185
+ !control.security_settings_valid?
187
186
 
188
187
  ::NewRelic::Agent.logger.debug "Starting the worker thread in #{Process.pid} (parent #{Process.ppid}) after forking."
189
188
 
@@ -844,6 +843,20 @@ module NewRelic
844
843
 
845
844
  @service.agent_id = config_data['agent_run_id']
846
845
 
846
+ security_policies = config_data.delete('security_policies')
847
+
848
+ add_server_side_config(config_data)
849
+ add_security_policy_config(security_policies) if security_policies
850
+
851
+ log_connection!(config_data)
852
+ @transaction_rules = RulesEngine.create_transaction_rules(config_data)
853
+ @stats_engine.metric_rules = RulesEngine.create_metric_rules(config_data)
854
+
855
+ # If you're adding something else here to respond to the server-side config,
856
+ # use Agent.instance.events.subscribe(:finished_configuring) callback instead!
857
+ end
858
+
859
+ def add_server_side_config(config_data)
847
860
  if config_data['agent_config']
848
861
  ::NewRelic::Agent.logger.debug "Using config from server"
849
862
  end
@@ -851,13 +864,14 @@ module NewRelic
851
864
  ::NewRelic::Agent.logger.debug "Server provided config: #{config_data.inspect}"
852
865
  server_config = NewRelic::Agent::Configuration::ServerSource.new(config_data, Agent.config)
853
866
  Agent.config.replace_or_add_config(server_config)
854
- log_connection!(config_data)
855
-
856
- @transaction_rules = RulesEngine.create_transaction_rules(config_data)
857
- @stats_engine.metric_rules = RulesEngine.create_metric_rules(config_data)
867
+ end
858
868
 
859
- # If you're adding something else here to respond to the server-side config,
860
- # use Agent.instance.events.subscribe(:finished_configuring) callback instead!
869
+ def add_security_policy_config(security_policies)
870
+ ::NewRelic::Agent.logger.info 'Installing security policies'
871
+ security_policy_source = NewRelic::Agent::Configuration::SecurityPolicySource.new(security_policies)
872
+ Agent.config.replace_or_add_config(security_policy_source)
873
+ # drop data collected before applying security policies
874
+ drop_buffered_data
861
875
  end
862
876
 
863
877
  class WaitOnConnectTimeout < StandardError
@@ -313,6 +313,13 @@ module NewRelic
313
313
  :allowed_from_server => false,
314
314
  :description => 'If <code>true</code>, enables <a href="https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security">high security mode</a>. Ensure you understand the implications of high security mode before enabling this setting.'
315
315
  },
316
+ :security_policies_token => {
317
+ :default => '',
318
+ :public => true,
319
+ :type => String,
320
+ :allowed_from_server => false,
321
+ :description => 'Applies Language Agent Security Policy settings.'
322
+ },
316
323
  :proxy_host => {
317
324
  :default => nil,
318
325
  :allow_nil => true,
@@ -9,6 +9,7 @@ require 'new_relic/agent/configuration/default_source'
9
9
  require 'new_relic/agent/configuration/server_source'
10
10
  require 'new_relic/agent/configuration/environment_source'
11
11
  require 'new_relic/agent/configuration/high_security_source'
12
+ require 'new_relic/agent/configuration/security_policy_source'
12
13
 
13
14
  module NewRelic
14
15
  module Agent
@@ -44,12 +45,13 @@ module NewRelic
44
45
 
45
46
  def remove_config_type(sym)
46
47
  source = case sym
47
- when :high_security then @high_security_source
48
- when :environment then @environment_source
49
- when :server then @server_source
50
- when :manual then @manual_source
51
- when :yaml then @yaml_source
52
- when :default then @default_source
48
+ when :security_policy then @security_policy_source
49
+ when :high_security then @high_security_source
50
+ when :environment then @environment_source
51
+ when :server then @server_source
52
+ when :manual then @manual_source
53
+ when :yaml then @yaml_source
54
+ when :default then @default_source
53
55
  end
54
56
 
55
57
  remove_config(source)
@@ -57,12 +59,13 @@ module NewRelic
57
59
 
58
60
  def remove_config(source)
59
61
  case source
60
- when HighSecuritySource then @high_security_source = nil
61
- when EnvironmentSource then @environment_source = nil
62
- when ServerSource then @server_source = nil
63
- when ManualSource then @manual_source = nil
64
- when YamlSource then @yaml_source = nil
65
- when DefaultSource then @default_source = nil
62
+ when SecurityPolicySource then @security_policy_source = nil
63
+ when HighSecuritySource then @high_security_source = nil
64
+ when EnvironmentSource then @environment_source = nil
65
+ when ServerSource then @server_source = nil
66
+ when ManualSource then @manual_source = nil
67
+ when YamlSource then @yaml_source = nil
68
+ when DefaultSource then @default_source = nil
66
69
  else
67
70
  @configs_for_testing.delete_if {|src,lvl| src == source}
68
71
  end
@@ -78,12 +81,13 @@ module NewRelic
78
81
 
79
82
  invoke_callbacks(:add, source)
80
83
  case source
81
- when HighSecuritySource then @high_security_source = source
82
- when EnvironmentSource then @environment_source = source
83
- when ServerSource then @server_source = source
84
- when ManualSource then @manual_source = source
85
- when YamlSource then @yaml_source = source
86
- when DefaultSource then @default_source = source
84
+ when SecurityPolicySource then @security_policy_source = source
85
+ when HighSecuritySource then @high_security_source = source
86
+ when EnvironmentSource then @environment_source = source
87
+ when ServerSource then @server_source = source
88
+ when ManualSource then @manual_source = source
89
+ when YamlSource then @yaml_source = source
90
+ when DefaultSource then @default_source = source
87
91
  else
88
92
  NewRelic::Agent.logger.warn("Invalid config format; config will be ignored: #{source}")
89
93
  end
@@ -323,14 +327,15 @@ module NewRelic
323
327
 
324
328
  # Generally only useful during initial construction and tests
325
329
  def reset_to_defaults
326
- @high_security_source = nil
327
- @environment_source = EnvironmentSource.new
328
- @server_source = nil
329
- @manual_source = nil
330
- @yaml_source = nil
331
- @default_source = DefaultSource.new
330
+ @security_policy_source = nil
331
+ @high_security_source = nil
332
+ @environment_source = EnvironmentSource.new
333
+ @server_source = nil
334
+ @manual_source = nil
335
+ @yaml_source = nil
336
+ @default_source = DefaultSource.new
332
337
 
333
- @configs_for_testing = []
338
+ @configs_for_testing = []
334
339
 
335
340
  reset_cache
336
341
  end
@@ -350,13 +355,14 @@ module NewRelic
350
355
  end
351
356
 
352
357
  def delete_all_configs_for_testing
353
- @high_security_source = nil
354
- @environment_source = nil
355
- @server_source = nil
356
- @manual_source = nil
357
- @yaml_source = nil
358
- @default_source = nil
359
- @configs_for_testing = []
358
+ @security_policy_source = nil
359
+ @high_security_source = nil
360
+ @environment_source = nil
361
+ @server_source = nil
362
+ @manual_source = nil
363
+ @yaml_source = nil
364
+ @default_source = nil
365
+ @configs_for_testing = []
360
366
  end
361
367
 
362
368
  def num_configs_for_testing
@@ -370,7 +376,8 @@ module NewRelic
370
376
  private
371
377
 
372
378
  def config_stack
373
- stack = [@high_security_source,
379
+ stack = [@security_policy_source,
380
+ @high_security_source,
374
381
  @environment_source,
375
382
  @server_source,
376
383
  @manual_source,
@@ -0,0 +1,224 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
+
5
+ require 'new_relic/agent/configuration/dotted_hash'
6
+
7
+ module NewRelic
8
+ module Agent
9
+ module Configuration
10
+ class SecurityPolicySource < DottedHash
11
+ class << self
12
+ def enabled?(option)
13
+ Agent.config[option]
14
+ end
15
+
16
+ def record_sql_enabled?(option)
17
+ Agent.config[option] == 'obfuscated' ||
18
+ Agent.config[option] == 'raw' ||
19
+ false
20
+ end
21
+
22
+ def not_empty?(option)
23
+ !Agent.config[option].empty?
24
+ end
25
+
26
+ def change_setting(policies, option, new_value)
27
+ current_value = Agent.config[option]
28
+ unless current_value == new_value
29
+ NewRelic::Agent.logger.info \
30
+ "Setting changed: {#{option}: from #{current_value} " \
31
+ "to #{new_value}}. Source: SecurityPolicySource"
32
+ end
33
+ policies[option] = new_value
34
+ end
35
+ end
36
+
37
+ # The keys of the security settings map are the names of security
38
+ # policies received from the server. They map to multiple configuration
39
+ # options in the local config. There is a hash of metadata that
40
+ # corresponds to each configuration option with the following keys:
41
+ #
42
+ # option: the configuration option name
43
+ # supported: true if the agent has one or more corresponding
44
+ # configuration options
45
+ # enabled_fn: a callable that takes the configuration option and returns
46
+ # true if the option is enabled, false otherwise
47
+ # disabled_value: the value of the configuration option when it is
48
+ # disabled
49
+ # permitted_fn: a callable, that will be executed if an option is
50
+ # permitted by the security policy and is also enabled by the config
51
+
52
+ SECURITY_SETTINGS_MAP = {
53
+ "record_sql" => [
54
+ {
55
+ option: :'transaction_tracer.record_sql',
56
+ supported: true,
57
+ enabled_fn: method(:record_sql_enabled?),
58
+ disabled_value: 'off',
59
+ permitted_fn: proc { |policies|
60
+ change_setting(policies, :'transaction_tracer.record_sql', 'obfuscated')
61
+ }
62
+ },
63
+ {
64
+ option: :'slow_sql.record_sql',
65
+ supported: true,
66
+ enabled_fn: method(:record_sql_enabled?),
67
+ disabled_value: 'off',
68
+ permitted_fn: proc { |policies|
69
+ change_setting(policies, :'slow_sql.record_sql', 'obfuscated')
70
+ }
71
+ },
72
+ {
73
+ option: :'mongo.capture_queries',
74
+ supported: true,
75
+ enabled_fn: method(:enabled?),
76
+ disabled_value: false,
77
+ permitted_fn: proc{ |policies|
78
+ change_setting(policies, :'mongo.obfuscate_queries', true)
79
+ }
80
+ },
81
+ {
82
+ option: :'transaction_tracer.record_redis_arguments',
83
+ supported: true,
84
+ enabled_fn: method(:enabled?),
85
+ disabled_value: false,
86
+ permitted_fn: nil
87
+ }
88
+ ],
89
+ "attributes_include" => [
90
+ {
91
+ option: :'attributes.include',
92
+ supported: true,
93
+ enabled_fn: method(:not_empty?),
94
+ disabled_value: [],
95
+ permitted_fn: nil
96
+ },
97
+ {
98
+ option: :'transaction_tracer.attributes.include',
99
+ supported: true,
100
+ enabled_fn: method(:not_empty?),
101
+ disabled_value: [],
102
+ permitted_fn: nil
103
+ },
104
+ {
105
+ option: :'transaction_events.attributes.include',
106
+ supported: true,
107
+ enabled_fn: method(:not_empty?),
108
+ disabled_value: [],
109
+ permitted_fn: nil
110
+ },
111
+ {
112
+ option: :'error_collector.attributes.include',
113
+ supported: true,
114
+ enabled_fn: method(:not_empty?),
115
+ disabled_value: [],
116
+ permitted_fn: nil
117
+ },
118
+ {
119
+ option: :'browser_monitoring.attributes.include',
120
+ supported: true,
121
+ enabled_fn: method(:not_empty?),
122
+ disabled_value: [],
123
+ permitted_fn: nil
124
+ }
125
+ ],
126
+ "allow_raw_exception_messages" => [
127
+ {
128
+ option: :'strip_exception_messages.enabled',
129
+ supported: true,
130
+ enabled_fn: method(:enabled?),
131
+ disabled_value: false,
132
+ permitted_fn: nil
133
+ }
134
+ ],
135
+ "custom_events" => [
136
+ {
137
+ option: :'custom_insights_events.enabled',
138
+ supported: true,
139
+ enabled_fn: method(:enabled?),
140
+ disabled_value: false,
141
+ permitted_fn: nil
142
+ }
143
+ ],
144
+ "custom_parameters" => [
145
+ {
146
+ option: :'custom_attributes.enabled',
147
+ supported: true,
148
+ enabled_fn: method(:enabled?),
149
+ disabled_value: false,
150
+ permitted_fn: nil
151
+ }
152
+ ],
153
+ "custom_instrumentation_editor" => [
154
+ {
155
+ option: nil,
156
+ supported: false,
157
+ enabled_fn: nil,
158
+ disabled_value: nil,
159
+ permitted_fn: nil
160
+ }
161
+ ],
162
+ "message_parameters" => [
163
+ {
164
+ option: :'message_tracer.segment_parameters.enabled',
165
+ supported: true,
166
+ enabled_fn: method(:enabled?),
167
+ disabled_value: false,
168
+ permitted_fn: nil
169
+ }
170
+ ],
171
+ "job_arguments" => [
172
+ {
173
+ option: :'resque.capture_params',
174
+ supported: true,
175
+ enabled_fn: method(:enabled?),
176
+ disabled_value: false,
177
+ permitted_fn: nil
178
+ },
179
+ {
180
+ option: :'sidekiq.capture_params',
181
+ supported: true,
182
+ enabled_fn: method(:enabled?),
183
+ disabled_value: false,
184
+ permitted_fn: nil
185
+ }
186
+ ]
187
+ }
188
+
189
+ def initialize(security_policies)
190
+ super(build_overrides(security_policies))
191
+ end
192
+
193
+ ENABLED = "enabled".freeze
194
+ COLON_COLON = "::".freeze
195
+
196
+ def build_overrides(security_policies)
197
+ security_policies.inject({}) do |settings, (policy_name, policy_settings)|
198
+ SECURITY_SETTINGS_MAP[policy_name].each do |policy|
199
+ next unless policy[:supported]
200
+ if policy_settings[ENABLED]
201
+ if policy[:enabled_fn].call(policy[:option])
202
+ if permitted_fn = policy[:permitted_fn]
203
+ permitted_fn.call(settings)
204
+ end
205
+ else
206
+ config_source = Agent.config.source(policy[:option]).class.name.split(COLON_COLON).last
207
+ NewRelic::Agent.logger.info \
208
+ "Setting applied: {#{policy[:option]}: #{policy[:disabled_value]}}. " \
209
+ "Source: #{config_source}"
210
+ end
211
+ else
212
+ settings[policy[:option]] = policy[:disabled_value]
213
+ NewRelic::Agent.logger.info \
214
+ "Setting applied: {#{policy[:option]}: #{policy[:disabled_value]}}. " \
215
+ "Source: SecurityPolicySource"
216
+ end
217
+ end
218
+ settings
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
@@ -12,6 +12,7 @@ module NewRelic
12
12
 
13
13
  TYPE = 'type'.freeze
14
14
  TIMESTAMP = 'timestamp'.freeze
15
+ PRIORITY = 'priority'.freeze
15
16
  EVENT_TYPE_REGEX = /^[a-zA-Z0-9:_ ]+$/.freeze
16
17
 
17
18
  named :CustomEventAggregator
@@ -23,25 +24,35 @@ module NewRelic
23
24
  raise ArgumentError, "Expected Hash but got #{attributes.class}"
24
25
  end
25
26
 
27
+ return unless enabled?
28
+
26
29
  type = @type_strings[type]
27
30
  unless type =~ EVENT_TYPE_REGEX
28
31
  note_dropped_event(type)
29
32
  return false
30
33
  end
31
34
 
32
- event = [
33
- { TYPE => type, TIMESTAMP => Time.now.to_i },
34
- AttributeProcessing.flatten_and_coerce(attributes)
35
- ]
35
+ priority = attributes[:priority] || rand
36
36
 
37
37
  stored = @lock.synchronize do
38
- @buffer.append(event)
38
+ @buffer.append(priority: priority) do
39
+ create_event(type, priority, attributes)
40
+ end
39
41
  end
40
42
  stored
41
43
  end
42
44
 
43
45
  private
44
46
 
47
+ def create_event(type, priority, attributes)
48
+ [
49
+ { TYPE => type, TIMESTAMP => Time.now.to_i,
50
+ PRIORITY => priority
51
+ },
52
+ AttributeProcessing.flatten_and_coerce(attributes)
53
+ ]
54
+ end
55
+
45
56
  def after_initialize
46
57
  @type_strings = Hash.new { |hash, key| hash[key] = key.to_s.freeze }
47
58
  end
@@ -121,11 +121,14 @@ module NewRelic
121
121
  begin
122
122
  result = yield
123
123
  ensure
124
- if callback
125
- elapsed_time = (Time.now - segment.start_time).to_f
126
- callback.call(result, segment.name, elapsed_time)
124
+ begin
125
+ if callback
126
+ elapsed_time = (Time.now - segment.start_time).to_f
127
+ callback.call(result, segment.name, elapsed_time)
128
+ end
129
+ ensure
130
+ segment.finish if segment
127
131
  end
128
- segment.finish if segment
129
132
  end
130
133
  end
131
134
 
@@ -209,7 +209,7 @@ module NewRelic
209
209
  noticed_error = create_noticed_error(exception, options)
210
210
  error_trace_aggregator.add_to_error_queue(noticed_error)
211
211
  payload = state.current_transaction ? state.current_transaction.payload : nil
212
- error_event_aggregator.append_event(noticed_error, payload)
212
+ error_event_aggregator.record(noticed_error, payload)
213
213
  exception
214
214
  rescue => e
215
215
  ::NewRelic::Agent.logger.warn("Failure when capturing error '#{exception}':", e)
@@ -5,6 +5,7 @@
5
5
 
6
6
  require 'new_relic/agent/event_aggregator'
7
7
  require 'new_relic/agent/transaction_error_primitive'
8
+ require 'new_relic/agent/priority_sampled_buffer'
8
9
 
9
10
  module NewRelic
10
11
  module Agent
@@ -13,12 +14,15 @@ module NewRelic
13
14
  named :ErrorEventAggregator
14
15
  capacity_key :'error_collector.max_event_samples_stored'
15
16
  enabled_key :'error_collector.capture_events'
17
+ buffer_class PrioritySampledBuffer
16
18
 
17
- def append_event noticed_error, transaction_payload = nil
19
+ def record noticed_error, transaction_payload = nil
18
20
  return unless enabled?
19
21
 
22
+ priority = (transaction_payload && transaction_payload[:priority]) || rand
23
+
20
24
  @lock.synchronize do
21
- @buffer.append do
25
+ @buffer.append(priority: priority) do
22
26
  create_event(noticed_error, transaction_payload)
23
27
  end
24
28
  notify_if_full
@@ -2,7 +2,7 @@
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
4
 
5
- require 'new_relic/agent/sampled_buffer'
5
+ require 'new_relic/agent/priority_sampled_buffer'
6
6
 
7
7
  module NewRelic
8
8
  module Agent
@@ -24,7 +24,7 @@ module NewRelic
24
24
  if klass
25
25
  @buffer_class = klass
26
26
  else
27
- @buffer_class ||= SampledBuffer
27
+ @buffer_class ||= PrioritySampledBuffer
28
28
  end
29
29
  end
30
30
  end
@@ -79,7 +79,7 @@ module NewRelic
79
79
  @buffer.decrement_lifetime_counts_by samples.count
80
80
  end
81
81
 
82
- samples.each { |s| @buffer.append s }
82
+ samples.each { |s| @buffer.append event: s }
83
83
  end
84
84
  end
85
85
 
@@ -8,6 +8,7 @@ require 'new_relic/agent/audit_logger'
8
8
  require 'new_relic/agent/new_relic_service/encoders'
9
9
  require 'new_relic/agent/new_relic_service/marshaller'
10
10
  require 'new_relic/agent/new_relic_service/json_marshaller'
11
+ require 'new_relic/agent/new_relic_service/security_policy_settings'
11
12
 
12
13
  module NewRelic
13
14
  module Agent
@@ -15,7 +16,7 @@ module NewRelic
15
16
  # Specifies the version of the agent's communication protocol with
16
17
  # the NewRelic hosted site.
17
18
 
18
- PROTOCOL_VERSION = 15
19
+ PROTOCOL_VERSION = 16
19
20
 
20
21
  # 1f147a42: v10 (tag 3.5.3.17)
21
22
  # cf0d1ff1: v9 (tag 3.5.0)
@@ -68,16 +69,35 @@ module NewRelic
68
69
  end
69
70
 
70
71
  def connect(settings={})
71
- if host = preconnect
72
- @collector = NewRelic::Control.instance.server_from_host(host)
72
+ security_policies = nil
73
+ if response = preconnect
74
+ if host = response['redirect_host']
75
+ @collector = NewRelic::Control.instance.server_from_host(host)
76
+ end
77
+ if policies = response['security_policies']
78
+ security_policies = SecurityPolicySettings.preliminary_settings(policies)
79
+ settings.merge!(security_policies)
80
+ end
73
81
  end
74
82
  response = invoke_remote(:connect, [settings])
75
83
  self.agent_id = response['agent_run_id']
84
+ response.merge!(security_policies) if security_policies
76
85
  response
77
86
  end
78
87
 
79
88
  def preconnect
80
- invoke_remote(:preconnect)
89
+ token = Agent.config[:security_policies_token]
90
+
91
+ if token && !token.empty?
92
+ response = invoke_remote(:preconnect, [{'security_policies_token' => token}])
93
+
94
+ validator = SecurityPolicySettings::Validator.new(response)
95
+ validator.validate_matching_agent_config!
96
+
97
+ response
98
+ else
99
+ invoke_remote(:preconnect, [])
100
+ end
81
101
  end
82
102
 
83
103
  def shutdown(time)
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
+
5
+ module NewRelic
6
+ module Agent
7
+ class NewRelicService
8
+ module SecurityPolicySettings
9
+ EXPECTED_SECURITY_POLICIES = %w(
10
+ record_sql
11
+ attributes_include
12
+ allow_raw_exception_messages
13
+ custom_events
14
+ custom_parameters
15
+ custom_instrumentation_editor
16
+ message_parameters
17
+ job_arguments).map(&:freeze)
18
+
19
+ def self.preliminary_settings(security_policies)
20
+ enabled_key = 'enabled'.freeze
21
+ settings = EXPECTED_SECURITY_POLICIES.inject({}) do |memo, policy_name|
22
+ memo[policy_name] = {enabled_key => security_policies[policy_name][enabled_key]}
23
+ memo
24
+ end
25
+ {'security_policies' => settings}
26
+ end
27
+
28
+ class Validator
29
+ def initialize(preconnect_response)
30
+ @preconnect_policies = preconnect_response['security_policies'] || {}
31
+ end
32
+
33
+ def validate_matching_agent_config!
34
+ agent_keys = EXPECTED_SECURITY_POLICIES
35
+ all_server_keys = @preconnect_policies.keys
36
+ required = 'required'
37
+ required_server_keys = @preconnect_policies.keys.select do |key|
38
+ key if @preconnect_policies[key][required]
39
+ end
40
+
41
+ missing_from_agent = required_server_keys - agent_keys
42
+ unless missing_from_agent.empty?
43
+ message = "The agent received one or more required security policies \
44
+ that it does not recognize and will shut down: #{missing_from_agent.join(',')}. \
45
+ Please check if a newer agent version supports these policies or contact support."
46
+ raise NewRelic::Agent::UnrecoverableAgentException.new(message)
47
+ end
48
+
49
+ missing_from_server = agent_keys - all_server_keys
50
+ unless missing_from_server.empty?
51
+ message = "The agent did not receive one or more security policies \
52
+ that it expected and will shut down: #{missing_from_server.join(',')}. Please \
53
+ contact support."
54
+ raise NewRelic::Agent::UnrecoverableAgentException.new(message)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -3,10 +3,11 @@
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
4
 
5
5
  require 'new_relic/agent/heap'
6
+ require 'new_relic/agent/event_buffer'
6
7
 
7
8
  module NewRelic
8
9
  module Agent
9
- class PrioritySampledBuffer < SampledBuffer
10
+ class PrioritySampledBuffer < EventBuffer
10
11
  PRIORITY_KEY = "priority".freeze
11
12
 
12
13
  attr_reader :seen_lifetime, :captured_lifetime
@@ -28,12 +29,17 @@ module NewRelic
28
29
  if full?
29
30
  priority ||= priority_for(event)
30
31
  if priority_for(@items[0]) < priority
31
- @items[0] = event || blk.call
32
+ incoming = event || blk.call
33
+ @items[0] = incoming
32
34
  @items.fix(0)
35
+ incoming
36
+ else
37
+ nil
33
38
  end
34
39
  else
35
40
  @items << (event || blk.call)
36
41
  @captured_lifetime += 1
42
+ @items[-1]
37
43
  end
38
44
  end
39
45
 
@@ -52,6 +58,22 @@ module NewRelic
52
58
  @items.to_a.dup
53
59
  end
54
60
 
61
+ def decrement_lifetime_counts_by n
62
+ @captured_lifetime -= n
63
+ @seen_lifetime -= n
64
+ end
65
+
66
+ def sample_rate_lifetime
67
+ @captured_lifetime > 0 ? (@captured_lifetime.to_f / @seen_lifetime) : 0.0
68
+ end
69
+
70
+ def metadata
71
+ super.merge!(
72
+ :captured_lifetime => @captured_lifetime,
73
+ :seen_lifetime => @seen_lifetime
74
+ )
75
+ end
76
+
55
77
  private
56
78
 
57
79
  def increment_seen
@@ -4,30 +4,23 @@
4
4
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
5
5
 
6
6
  require 'new_relic/agent/event_aggregator'
7
- require 'new_relic/agent/synthetics_event_buffer'
7
+ require 'new_relic/agent/timestamp_sampled_buffer'
8
8
 
9
9
  module NewRelic
10
10
  module Agent
11
11
  class SyntheticsEventAggregator < EventAggregator
12
+ TIMESTAMP = 'timestamp'.freeze
12
13
 
13
14
  named :SyntheticsEventAggregator
14
15
  capacity_key :'synthetics.events_limit'
15
16
  enabled_key :'analytics_events.enabled'
16
- buffer_class SyntheticsEventBuffer
17
+ buffer_class TimestampSampledBuffer
17
18
 
18
- def append_or_reject event
19
+ def record event
19
20
  return unless enabled?
20
21
 
21
22
  @lock.synchronize do
22
- @buffer.append_with_reject event
23
- end
24
- end
25
-
26
- # slightly different semantics than the EventAggregator for merge
27
- def merge! payload
28
- _, events = payload
29
- @lock.synchronize do
30
- events.each { |e| @buffer.append_with_reject e}
23
+ @buffer.append event: event, priority: -event[0][TIMESTAMP]
31
24
  end
32
25
  end
33
26
 
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
+
5
+ require 'new_relic/agent/heap'
6
+
7
+ module NewRelic
8
+ module Agent
9
+ class TimestampSampledBuffer < PrioritySampledBuffer
10
+ TIMESTAMP_KEY = "timestamp".freeze
11
+
12
+ private
13
+
14
+ def priority_for(event)
15
+ -event[0][TIMESTAMP_KEY]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -30,6 +30,7 @@ module NewRelic
30
30
  SYNTHETICS_RESOURCE_ID_KEY = "nr.syntheticsResourceId".freeze
31
31
  SYNTHETICS_JOB_ID_KEY = "nr.syntheticsJobId".freeze
32
32
  SYNTHETICS_MONITOR_ID_KEY = "nr.syntheticsMonitorId".freeze
33
+ PRIORITY_KEY = "priority".freeze
33
34
 
34
35
  def create noticed_error, payload
35
36
  [
@@ -54,6 +55,7 @@ module NewRelic
54
55
  attrs[NAME_KEY] = payload[:name]
55
56
  attrs[DURATION_KEY] = payload[:duration]
56
57
  attrs[SAMPLED_KEY] = payload[:'sampled'] if Agent.config[:'distributed_tracing.enabled']
58
+ attrs[PRIORITY_KEY] = payload[:'priority']
57
59
  append_synthetics payload, attrs
58
60
  append_cat payload, attrs
59
61
  append_distributed_trace_intrinsics payload, attrs
@@ -17,7 +17,7 @@ module NewRelic
17
17
  enabled_key :'analytics_events.enabled'
18
18
  buffer_class PrioritySampledBuffer
19
19
 
20
- def append priority: nil, event:nil, &blk
20
+ def record priority: nil, event:nil, &blk
21
21
  unless(event || priority && blk)
22
22
  raise ArgumentError, "Expected priority and block, or event"
23
23
  end
@@ -30,18 +30,6 @@ module NewRelic
30
30
  end
31
31
  end
32
32
 
33
- def merge! payload, adjust_count = true
34
- @lock.synchronize do
35
- _, samples = payload
36
-
37
- if adjust_count
38
- @buffer.decrement_lifetime_counts_by samples.count
39
- end
40
-
41
- samples.each { |s| @buffer.append event: s }
42
- end
43
- end
44
-
45
33
  private
46
34
 
47
35
  def after_harvest metadata
@@ -24,10 +24,10 @@ module NewRelic
24
24
 
25
25
  if synthetics_event? payload
26
26
  event = create_event payload
27
- _, rejected = synthetics_event_aggregator.append_or_reject event
28
- transaction_event_aggregator.append event: event if rejected
27
+ result = synthetics_event_aggregator.record event
28
+ transaction_event_aggregator.record event: event if result.nil?
29
29
  else
30
- transaction_event_aggregator.append(priority: payload[:priority]) { create_event(payload) }
30
+ transaction_event_aggregator.record(priority: payload[:priority]) { create_event(payload) }
31
31
  end
32
32
  end
33
33
 
@@ -68,7 +68,10 @@ module NewRelic
68
68
  Module.send :include, NewRelic::Agent::MethodTracer
69
69
  init_config(options)
70
70
  NewRelic::Agent.agent = NewRelic::Agent::Agent.instance
71
- if Agent.config[:agent_enabled] && !NewRelic::Agent.instance.started?
71
+
72
+ if !security_settings_valid?
73
+ handle_invalid_security_settings
74
+ elsif Agent.config[:agent_enabled] && !NewRelic::Agent.instance.started?
72
75
  start_agent
73
76
  install_instrumentation
74
77
  elsif !Agent.config[:agent_enabled]
@@ -100,12 +103,26 @@ module NewRelic
100
103
  config_file_path = @config_file_override || Agent.config[:config_path]
101
104
  Agent.config.replace_or_add_config(Agent::Configuration::YamlSource.new(config_file_path, env))
102
105
 
103
- if Agent.config[:high_security]
106
+ if security_settings_valid? && Agent.config[:high_security]
104
107
  Agent.logger.info("Installing high security configuration based on local configuration")
105
108
  Agent.config.replace_or_add_config(Agent::Configuration::HighSecuritySource.new(Agent.config))
106
109
  end
107
110
  end
108
111
 
112
+ def security_settings_valid?
113
+ !Agent.config[:high_security] ||
114
+ Agent.config[:security_policies_token].empty?
115
+ end
116
+
117
+ def handle_invalid_security_settings
118
+ NewRelic::Agent.logger.error "Security Policies and High Security " \
119
+ "Mode cannot both be present in the agent configuration. If " \
120
+ "Security Policies have been set for your account, please " \
121
+ "ensure the security_policies_token is set but high_security is " \
122
+ "disabled (default)."
123
+ install_shim
124
+ end
125
+
109
126
  # Install the real agent into the Agent module, and issue the start command.
110
127
  def start_agent
111
128
  @started_in_env = self.env
@@ -11,7 +11,7 @@ module NewRelic
11
11
  end
12
12
 
13
13
  MAJOR = 5
14
- MINOR = 1
14
+ MINOR = 2
15
15
  TINY = 0
16
16
 
17
17
  begin
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: newrelic_rpm
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0.344
4
+ version: 5.2.0.345
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Wear
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2018-04-25 00:00:00.000000000 Z
14
+ date: 2018-05-23 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rake
@@ -217,6 +217,7 @@ files:
217
217
  - lib/new_relic/agent/configuration/manager.rb
218
218
  - lib/new_relic/agent/configuration/manual_source.rb
219
219
  - lib/new_relic/agent/configuration/mask_defaults.rb
220
+ - lib/new_relic/agent/configuration/security_policy_source.rb
220
221
  - lib/new_relic/agent/configuration/server_source.rb
221
222
  - lib/new_relic/agent/configuration/yaml_source.rb
222
223
  - lib/new_relic/agent/cross_app_monitor.rb
@@ -331,6 +332,7 @@ files:
331
332
  - lib/new_relic/agent/new_relic_service/encoders.rb
332
333
  - lib/new_relic/agent/new_relic_service/json_marshaller.rb
333
334
  - lib/new_relic/agent/new_relic_service/marshaller.rb
335
+ - lib/new_relic/agent/new_relic_service/security_policy_settings.rb
334
336
  - lib/new_relic/agent/null_logger.rb
335
337
  - lib/new_relic/agent/obfuscator.rb
336
338
  - lib/new_relic/agent/parameter_filtering.rb
@@ -343,7 +345,6 @@ files:
343
345
  - lib/new_relic/agent/rules_engine.rb
344
346
  - lib/new_relic/agent/rules_engine/replacement_rule.rb
345
347
  - lib/new_relic/agent/rules_engine/segment_terms_rule.rb
346
- - lib/new_relic/agent/sampled_buffer.rb
347
348
  - lib/new_relic/agent/sampler.rb
348
349
  - lib/new_relic/agent/sampler_collection.rb
349
350
  - lib/new_relic/agent/samplers/cpu_sampler.rb
@@ -351,7 +352,6 @@ files:
351
352
  - lib/new_relic/agent/samplers/memory_sampler.rb
352
353
  - lib/new_relic/agent/samplers/object_sampler.rb
353
354
  - lib/new_relic/agent/samplers/vm_sampler.rb
354
- - lib/new_relic/agent/sized_buffer.rb
355
355
  - lib/new_relic/agent/sql_sampler.rb
356
356
  - lib/new_relic/agent/stats.rb
357
357
  - lib/new_relic/agent/stats_engine.rb
@@ -359,13 +359,13 @@ files:
359
359
  - lib/new_relic/agent/stats_engine/stats_hash.rb
360
360
  - lib/new_relic/agent/supported_versions.rb
361
361
  - lib/new_relic/agent/synthetics_event_aggregator.rb
362
- - lib/new_relic/agent/synthetics_event_buffer.rb
363
362
  - lib/new_relic/agent/synthetics_monitor.rb
364
363
  - lib/new_relic/agent/system_info.rb
365
364
  - lib/new_relic/agent/threading/agent_thread.rb
366
365
  - lib/new_relic/agent/threading/backtrace_node.rb
367
366
  - lib/new_relic/agent/threading/backtrace_service.rb
368
367
  - lib/new_relic/agent/threading/thread_profile.rb
368
+ - lib/new_relic/agent/timestamp_sampled_buffer.rb
369
369
  - lib/new_relic/agent/transaction.rb
370
370
  - lib/new_relic/agent/transaction/abstract_segment.rb
371
371
  - lib/new_relic/agent/transaction/attributes.rb
@@ -484,7 +484,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
484
484
  version: 1.3.1
485
485
  requirements: []
486
486
  rubyforge_project:
487
- rubygems_version: 2.7.6
487
+ rubygems_version: 2.7.7
488
488
  signing_key:
489
489
  specification_version: 4
490
490
  summary: New Relic Ruby Agent
@@ -1,68 +0,0 @@
1
- # encoding: utf-8
2
- # This file is distributed under New Relic's license terms.
3
- # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
-
5
- # This class acts like an Array with a fixed capacity that randomly samples
6
- # from a stream of items such that the probability of each item being included
7
- # in the Array is equal. It uses reservoir sampling in order to achieve this:
8
- # http://xlinux.nist.gov/dads/HTML/reservoirSampling.html
9
-
10
- require 'new_relic/agent/event_buffer'
11
-
12
- module NewRelic
13
- module Agent
14
- class SampledBuffer < EventBuffer
15
- attr_reader :seen_lifetime, :captured_lifetime
16
-
17
- def initialize(capacity)
18
- super
19
- @captured_lifetime = 0
20
- @seen_lifetime = 0
21
- end
22
-
23
- def append(x = nil, &blk)
24
- @seen += 1
25
- @seen_lifetime += 1
26
- append_event(x, &blk)
27
- end
28
-
29
- def append_event(x = nil, &blk)
30
- raise ArgumentError, "Expected argument or block, but received both" if x && blk
31
-
32
- if @items.size < @capacity
33
- x = blk.call if block_given?
34
- @items << x
35
- @captured_lifetime += 1
36
- return x
37
- else
38
- m = rand(@seen) # [0, @seen)
39
- if m < @capacity
40
- x = blk.call if block_given?
41
- @items[m] = x
42
- return x
43
- else
44
- # discard current sample
45
- return nil
46
- end
47
- end
48
- end
49
-
50
- def decrement_lifetime_counts_by n
51
- @captured_lifetime -= n
52
- @seen_lifetime -= n
53
- end
54
-
55
- def sample_rate_lifetime
56
- @captured_lifetime > 0 ? (@captured_lifetime.to_f / @seen_lifetime) : 0.0
57
- end
58
-
59
- def metadata
60
- super.merge!(
61
- :captured_lifetime => @captured_lifetime,
62
- :seen_lifetime => @seen_lifetime
63
- )
64
- end
65
- end
66
- end
67
- end
68
-
@@ -1,23 +0,0 @@
1
- # encoding: utf-8
2
- # This file is distributed under New Relic's license terms.
3
- # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
-
5
- require 'new_relic/agent/event_buffer'
6
-
7
- module NewRelic
8
- module Agent
9
- class SizedBuffer < EventBuffer
10
-
11
- def append_event(x)
12
- if @items.size < @capacity
13
- @items << x
14
- return x
15
- else
16
- return nil
17
- end
18
- end
19
-
20
- end
21
- end
22
- end
23
-
@@ -1,40 +0,0 @@
1
- # encoding: utf-8
2
- # This file is distributed under New Relic's license terms.
3
- # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
-
5
- require 'new_relic/agent/sized_buffer'
6
-
7
- module NewRelic
8
- module Agent
9
- class SyntheticsEventBuffer < SizedBuffer
10
-
11
- def append_with_reject(x)
12
- @seen += 1
13
- if full?
14
- timestamp = timestamp_for(x)
15
- latest_event = @items.max_by do |item|
16
- timestamp_for(item)
17
- end
18
-
19
- if timestamp < timestamp_for(latest_event)
20
- # Make room!
21
- @items.delete(latest_event)
22
- return [append_event(x), latest_event]
23
- else
24
- return [nil, x]
25
- end
26
- else
27
- return [append_event(x), nil]
28
- end
29
- end
30
-
31
- TIMESTAMP = "timestamp".freeze
32
-
33
- def timestamp_for(event)
34
- main_event, _ = event
35
- main_event[TIMESTAMP]
36
- end
37
- end
38
- end
39
- end
40
-