logstash-logger 0.22.1 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,8 @@
1
+ ## 0.23.0
2
+
3
+ - Adds support for SSL host verification to the TCP device. [#114](https://github.com/dwbutler/logstash-logger/pull/114)
4
+ - Fixes `NoMethodError` when `nil` tags are used in tagged logging. [#123](https://github.com/dwbutler/logstash-logger/issues/123)
5
+
1
6
  ## 0.22.1
2
7
 
3
8
  - Fixes compatibility with ActiveJob. [#112](https://github.com/dwbutler/logstash-logger/issues/112)
data/README.md CHANGED
@@ -1,9 +1,10 @@
1
1
  # LogStashLogger
2
2
  [![Build Status](https://travis-ci.org/dwbutler/logstash-logger.svg?branch=master)](https://travis-ci.org/dwbutler/logstash-logger) [![Code Climate](https://codeclimate.com/github/dwbutler/logstash-logger/badges/gpa.svg)](https://codeclimate.com/github/dwbutler/logstash-logger) [![codecov.io](http://codecov.io/github/dwbutler/logstash-logger/coverage.svg?branch=master)](http://codecov.io/github/dwbutler/logstash-logger?branch=master)
3
3
 
4
- LogStashLogger extends Ruby's `Logger` class to log directly to [logstash](http://logstash.net).
4
+ LogStashLogger extends Ruby's `Logger` class to log directly to
5
+ [Logstash](https://www.elastic.co/products/logstash).
5
6
  It supports writing to various outputs in logstash JSON format. This is an improvement over
6
- writing to a file or syslog since logstash can receive the structured data directly.
7
+ writing to a file or syslog since Logstash can receive the structured data directly.
7
8
 
8
9
  ## Features
9
10
 
@@ -185,6 +186,15 @@ You can also enable SSL without a certificate:
185
186
  LogStashLogger.new(type: :tcp, port: 5228, ssl_enable: true)
186
187
  ```
187
188
 
189
+ Specify an SSL context to have more control over the behavior. For example,
190
+ set the verify mode:
191
+
192
+ ```ruby
193
+ ctx = OpenSSL::SSL::SSLContext.new
194
+ ctx.set_params(verify_mode: OpenSSL::SSL::VERIFY_NONE)
195
+ LogStashLogger.new(type: :tcp, port: 5228, ssl_context: ctx)
196
+ ```
197
+
188
198
  The following Logstash configuration is required for SSL:
189
199
 
190
200
  ```ruby
@@ -200,6 +210,53 @@ input {
200
210
  }
201
211
  ```
202
212
 
213
+ ### Hostname Verification
214
+
215
+ Hostname verification is enabled by default. Without further configuration,
216
+ the hostname supplied to `:host` will be used to verify the server's certificate
217
+ identity.
218
+
219
+ If you don't pass an `:ssl_context` or pass a falsey value to the
220
+ `:verify_hostname` option, hostname verification will not occur.
221
+
222
+ #### Examples
223
+
224
+ **Verify the hostname from the `:host` option**
225
+
226
+ ```ruby
227
+ ctx = OpenSSL::SSL::SSLContext.new
228
+ ctx.cert = '/path/to/cert.pem'
229
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
230
+
231
+ LogStashLogger.new \
232
+ type: :tcp,
233
+ host: 'logstash.example.com'
234
+ port: 5228,
235
+ ssl_context: ctx
236
+ ```
237
+
238
+ **Verify a hostname different from the `:host` option**
239
+
240
+ ```ruby
241
+ LogStashLogger.new \
242
+ type: :tcp,
243
+ host: '1.2.3.4'
244
+ port: 5228,
245
+ ssl_context: ctx,
246
+ verify_hostname: 'server.example.com'
247
+ ```
248
+
249
+ **Explicitly disable hostname verification**
250
+
251
+ ```ruby
252
+ LogStashLogger.new \
253
+ type: :tcp,
254
+ host: '1.2.3.4'
255
+ port: 5228,
256
+ ssl_context: ctx,
257
+ verify_hostname: false
258
+ ```
259
+
203
260
  ## Custom Log Fields
204
261
 
205
262
  `LogStashLogger` by default will log a JSON object with the format below.
@@ -719,6 +776,9 @@ logger = LogStashLogger.new('localhost', 5228, :tcp)
719
776
  * [Courtland Caldwell](https://github.com/caldwecr)
720
777
  * [Bibek Shrestha](https://github.com/bibstha)
721
778
  * [Alex Ianus](https://github.com/aianus)
779
+ * [Craig Read](https://github.com/Catharz)
780
+ * [glaszig](https://github.com/glaszig)
781
+ * [Bin Lan](https://github.com/lanxx019)
722
782
 
723
783
  ## Contributing
724
784
 
@@ -35,12 +35,6 @@ module LogStashLogger
35
35
  end
36
36
  end
37
37
 
38
- def write_batch(messages, group = nil)
39
- messages.each do |message|
40
- @io.write(message)
41
- end
42
- end
43
-
44
38
  def flush
45
39
  @io && @io.flush
46
40
  end
@@ -9,36 +9,75 @@ module LogStashLogger
9
9
  super
10
10
 
11
11
  @ssl_certificate = opts[:ssl_certificate]
12
- @use_ssl = !!(@ssl_certificate || opts[:use_ssl] || opts[:ssl_enable])
12
+ @ssl_context = opts[:ssl_context]
13
+ @use_ssl = !!(@ssl_certificate || opts[:ssl_context])
14
+ @use_ssl = opts[:ssl_enable] if opts.has_key? :ssl_enable
15
+ if opts.has_key?(:use_ssl)
16
+ @use_ssl = opts[:use_ssl]
17
+ warn "[LogStashLogger] The use_ssl option is deprecated. Use ssl_enable instead."
18
+ end
19
+ @verify_hostname = opts.fetch(:verify_hostname, true)
20
+ end
21
+
22
+ def ssl_context
23
+ return unless use_ssl?
24
+ @ssl_context || certificate_context
13
25
  end
14
26
 
15
27
  def use_ssl?
16
- @use_ssl || !@ssl_certificate.nil?
28
+ @use_ssl
17
29
  end
18
30
 
19
31
  def connect
20
32
  if use_ssl?
21
- ssl_connect
22
- else
23
- non_ssl_connect
33
+ io.connect
34
+ verify_hostname!
24
35
  end
36
+ io
37
+ end
25
38
 
26
- @io
39
+ def io
40
+ @io ||= if use_ssl?
41
+ ssl_io
42
+ else
43
+ tcp_io
44
+ end
27
45
  end
28
46
 
29
47
  protected
30
48
 
31
- def non_ssl_connect
32
- @io = TCPSocket.new(@host, @port).tap do |socket|
49
+ def tcp_io
50
+ TCPSocket.new(@host, @port).tap do |socket|
33
51
  socket.sync = sync unless sync.nil?
34
52
  end
35
53
  end
36
54
 
37
- def ssl_connect
38
- non_ssl_connect
39
- #openssl_cert = OpenSSL::X509::Certificate.new(::File.read(@ssl_certificate))
40
- @io = OpenSSL::SSL::SSLSocket.new(@io)
41
- @io.connect
55
+ def ssl_io
56
+ OpenSSL::SSL::SSLSocket.new(tcp_io, ssl_context)
57
+ end
58
+
59
+ def certificate_context
60
+ return unless @ssl_certificate
61
+ @certificate_context ||= OpenSSL::SSL::SSLContext.new.tap do |ctx|
62
+ ctx.set_params(cert: @ssl_certificate)
63
+ end
64
+ end
65
+
66
+ def verify_hostname?
67
+ return false unless ssl_context
68
+ !! @verify_hostname
69
+ end
70
+
71
+ def verify_hostname!
72
+ @io.post_connection_check(hostname) if verify_hostname?
73
+ end
74
+
75
+ def hostname
76
+ if String === @verify_hostname
77
+ @verify_hostname
78
+ else
79
+ @host
80
+ end
42
81
  end
43
82
  end
44
83
  end
@@ -19,7 +19,7 @@ module LogStashLogger
19
19
  end
20
20
 
21
21
  def push_tags(*tags)
22
- tags.flatten.reject(&:empty?).tap do |new_tags|
22
+ tags.flatten.reject{ |t| t.nil? || t.empty? }.tap do |new_tags|
23
23
  current_tags.concat new_tags
24
24
  end
25
25
  end
@@ -1,3 +1,3 @@
1
1
  module LogStashLogger
2
- VERSION = "0.22.1"
2
+ VERSION = "0.23.0"
3
3
  end
@@ -12,6 +12,8 @@ describe LogStashLogger::Device::TCP do
12
12
 
13
13
  allow(OpenSSL::SSL::SSLSocket).to receive(:new) { ssl_socket }
14
14
  allow(ssl_socket).to receive(:connect)
15
+ allow(ssl_socket).to receive(:post_connection_check)
16
+ allow(ssl_tcp_device).to receive(:warn)
15
17
  end
16
18
 
17
19
  context "when not using SSL" do
@@ -23,6 +25,10 @@ describe LogStashLogger::Device::TCP do
23
25
  it "returns false for #use_ssl?" do
24
26
  expect(tcp_device.use_ssl?).to be_falsey
25
27
  end
28
+
29
+ it "exposes the TCP socket via #io" do
30
+ expect(tcp_device.io).to eq tcp_socket
31
+ end
26
32
  end
27
33
 
28
34
  context "when using SSL" do
@@ -31,8 +37,84 @@ describe LogStashLogger::Device::TCP do
31
37
  ssl_tcp_device.write('test')
32
38
  end
33
39
 
34
- it "returns false for #use_ssl?" do
40
+ it "returns true for #use_ssl?" do
35
41
  expect(ssl_tcp_device.use_ssl?).to be_truthy
36
42
  end
43
+
44
+ it "exposes the SSL socket via #io" do
45
+ expect(ssl_tcp_device.io).to eq ssl_socket
46
+ end
47
+
48
+ context 'hostname validation' do
49
+ let(:ssl_context) { double('test_ssl_context', verify_mode: OpenSSL::SSL::VERIFY_PEER) }
50
+ let(:ssl_tcp_options) { { type: :tcp, port: port, sync: true, ssl_context: ssl_context } }
51
+
52
+ context 'is enabled by default' do
53
+ let(:ssl_tcp_device) { LogStashLogger::Device.new(ssl_tcp_options) }
54
+
55
+ it 'validates' do
56
+ expect(ssl_tcp_device.send(:verify_hostname?)).to be_truthy
57
+ expect(ssl_socket).to receive(:post_connection_check).with HOST
58
+ ssl_tcp_device.connect
59
+ end
60
+ end
61
+
62
+ context 'is disabled explicitly' do
63
+ let(:ssl_tcp_device) { LogStashLogger::Device.new(ssl_tcp_options.merge(verify_hostname: false)) }
64
+
65
+ it 'does not validate' do
66
+ expect(ssl_tcp_device.send(:verify_hostname?)).to be_falsey
67
+ expect(ssl_socket).not_to receive(:post_connection_check)
68
+ ssl_tcp_device.connect
69
+ end
70
+ end
71
+
72
+ context 'is implicitly enabled by providing a hostname' do
73
+ let(:hostname) { 'www.example.com' }
74
+ let(:ssl_tcp_device) { LogStashLogger::Device.new(ssl_tcp_options.merge(verify_hostname: hostname)) }
75
+
76
+ it 'validates with supplied hostname' do
77
+ expect(ssl_socket).to receive(:post_connection_check).with hostname
78
+ ssl_tcp_device.connect
79
+ end
80
+ end
81
+ end
82
+
83
+ context 'with a provided SSL context' do
84
+ let(:ssl_context) { double('test_ssl_context', verify_mode: OpenSSL::SSL::VERIFY_PEER) }
85
+ let(:ssl_tcp_device) { LogStashLogger::Device.new(type: :tcp, port: port, sync: true, ssl_context: ssl_context) }
86
+
87
+ it 'creates the socket using that context' do
88
+ expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, ssl_context)
89
+ ssl_tcp_device.connect
90
+ end
91
+
92
+ it 'implicitly sets @use_ssl to true' do
93
+ expect(ssl_tcp_device.use_ssl?).to be_truthy
94
+ end
95
+
96
+ context 'and :ssl_enable explicitly set to false' do
97
+ let(:ssl_tcp_device) { LogStashLogger::Device.new(type: :tcp, port: port, sync: true, ssl_enable: false, ssl_context: ssl_context) }
98
+
99
+ it 'explicitly sets @use_ssl to false' do
100
+ expect(ssl_tcp_device.use_ssl?).to be_falsey
101
+ end
102
+ end
103
+ end
104
+
105
+ context 'without a provided SSL context' do
106
+ it 'ssl_context returns nil' do
107
+ expect(ssl_tcp_device.ssl_context).to be_nil
108
+ end
109
+ end
110
+
111
+ context 'only providing a certificate file' do
112
+ let(:ssl_tcp_device) { LogStashLogger::Device.new(type: :tcp, port: port, ssl_enable: true, sync: true, ssl_certificate: '/path/to/cert.pem') }
113
+
114
+ it 'implicitly uses a context with the configured certificate' do
115
+ expect(ssl_tcp_device.ssl_context.cert).to eq('/path/to/cert.pem')
116
+ end
117
+ end
37
118
  end
119
+
38
120
  end
@@ -29,4 +29,4 @@ describe LogStashLogger do
29
29
  logger.info(message)
30
30
  end
31
31
  end
32
- end
32
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.22.1
4
+ version: 0.23.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Butler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-25 00:00:00.000000000 Z
11
+ date: 2017-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -171,8 +171,10 @@ executables: []
171
171
  extensions: []
172
172
  extra_rdoc_files: []
173
173
  files:
174
+ - ".codeclimate.yml"
174
175
  - ".gitignore"
175
176
  - ".rspec"
177
+ - ".rubocop.yml"
176
178
  - ".travis.yml"
177
179
  - Appraisals
178
180
  - CHANGELOG.md