loggregator_emitter 1.0.1 → 2.0.0.pre

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.
@@ -5,12 +5,13 @@ module LoggregatorEmitter
5
5
  MAX_MESSAGE_BYTE_SIZE = (9 * 1024) - 512
6
6
  TRUNCATED_STRING = "TRUNCATED"
7
7
 
8
- def initialize(loggregator_server, source_type, source_id = nil)
8
+ def initialize(loggregator_server, source_type, source_id = nil, secret=nil)
9
9
  raise ArgumentError, "Must provide valid source type" unless valid_source_type?(source_type)
10
10
 
11
11
  @host, @port = loggregator_server.split(/:([^:]*$)/)
12
12
  raise ArgumentError, "Must provide valid loggregator server: #{loggregator_server}" if !valid_hostname || !valid_port
13
13
 
14
+ @secret = secret
14
15
  @source_type = source_type
15
16
  @source_id = source_id && source_id.to_s
16
17
  end
@@ -36,12 +37,17 @@ module LoggregatorEmitter
36
37
  def emit_message(app_id, message, type)
37
38
  return unless app_id && message && message.strip.length > 0
38
39
 
39
- if message.bytesize > MAX_MESSAGE_BYTE_SIZE
40
- message = message.byteslice(0, MAX_MESSAGE_BYTE_SIZE-TRUNCATED_STRING.bytesize) + TRUNCATED_STRING
41
- end
40
+ message.split(/\r\n|\n\r|\n|\r/).each do |m|
41
+ if m.bytesize > MAX_MESSAGE_BYTE_SIZE
42
+ m = m.byteslice(0, MAX_MESSAGE_BYTE_SIZE-TRUNCATED_STRING.bytesize) + TRUNCATED_STRING
43
+ end
42
44
 
43
- lm = create_log_message(app_id, message, type)
44
- send_message(lm)
45
+ if @secret.nil? || @secret.empty?
46
+ send_protobuffer(create_log_message(app_id, m, type))
47
+ else
48
+ send_protobuffer(create_log_envelope(app_id, m, type))
49
+ end
50
+ end
45
51
  end
46
52
 
47
53
  def create_log_message(app_id, message, type)
@@ -55,7 +61,17 @@ module LoggregatorEmitter
55
61
  lm
56
62
  end
57
63
 
58
- def send_message(lm)
64
+ def create_log_envelope(app_id, message, type)
65
+ crypter = Encryption::Symmetric.new
66
+ le = LogEnvelope.new()
67
+ le.routing_key = app_id
68
+ le.log_message = create_log_message(app_id, message, type)
69
+ digest = crypter.digest(le.log_message.message)
70
+ le.signature = crypter.encrypt(@secret, digest)
71
+ le
72
+ end
73
+
74
+ def send_protobuffer(lm)
59
75
  result = lm.encode.buf
60
76
  result.unpack("C*")
61
77
 
@@ -1,3 +1,4 @@
1
1
  require "loggregator_emitter/emit"
2
2
  require "loggregator_messages/log_message.pb"
3
3
  require "loggregator_messages/log_message_extender"
4
+ require "symmetric/encryption"
@@ -1,4 +1,4 @@
1
- ## Generated from log_message.proto for logMessage
1
+ ## Generated from log_message.proto for logmessage
2
2
  require "beefcake"
3
3
 
4
4
 
@@ -21,7 +21,19 @@ class LogMessage
21
21
  required :message, :bytes, 1
22
22
  required :message_type, LogMessage::MessageType, 2
23
23
  required :timestamp, :sint64, 3
24
- optional :app_id, :string, 4
24
+ required :app_id, :string, 4
25
25
  required :source_type, LogMessage::SourceType, 5
26
26
  optional :source_id, :string, 6
27
+ repeated :drain_urls, :string, 7
28
+
29
+ end
30
+
31
+ class LogEnvelope
32
+ include Beefcake::Message
33
+
34
+
35
+ required :routing_key, :string, 1
36
+ required :signature, :bytes, 2
37
+ required :log_message, LogMessage, 3
38
+
27
39
  end
@@ -1,4 +1,4 @@
1
- package logMessage;
1
+ package logmessage;
2
2
 
3
3
  message LogMessage {
4
4
  enum MessageType {
@@ -18,7 +18,14 @@ message LogMessage {
18
18
  required bytes message = 1;
19
19
  required MessageType message_type = 2;
20
20
  required sint64 timestamp = 3;
21
- optional string app_id = 4;
21
+ required string app_id = 4;
22
22
  required SourceType source_type = 5;
23
23
  optional string source_id = 6;
24
+ repeated string drain_urls = 7;
25
+ }
26
+
27
+ message LogEnvelope {
28
+ required string routing_key = 1;
29
+ required bytes signature = 2;
30
+ required LogMessage log_message = 3;
24
31
  }
@@ -0,0 +1,51 @@
1
+ require 'digest/sha1'
2
+ require 'openssl'
3
+
4
+ module Encryption
5
+ class Symmetric
6
+ AES_BLOCKSIZE = 16
7
+
8
+ def encrypt(key, message)
9
+ cipher = OpenSSL::Cipher::AES128.new(:CBC)
10
+ cipher.encrypt
11
+ cipher.key = get_encryption_key(key)
12
+ cipher.padding = 0
13
+ iv = cipher.random_iv
14
+
15
+ iv + cipher.update(pad_buffer(message)) + cipher.final
16
+ end
17
+
18
+ def decrypt(key, encrypted)
19
+ cipher = OpenSSL::Cipher::AES128.new(:CBC)
20
+ cipher.padding = 0
21
+ cipher.decrypt
22
+ cipher.key = get_encryption_key(key)
23
+ cipher.iv = encrypted[0..AES_BLOCKSIZE-1]
24
+
25
+ unpad_buffer(cipher.update(encrypted[AES_BLOCKSIZE..encrypted.length]) + cipher.final)
26
+ end
27
+
28
+ def digest(value)
29
+ Digest::SHA256.digest(value)
30
+ end
31
+
32
+ private
33
+
34
+ def get_encryption_key(key)
35
+ digest(key)[0..AES_BLOCKSIZE-1]
36
+ end
37
+
38
+ def pad_buffer(message)
39
+ bytes_to_pad = AES_BLOCKSIZE - message.length % AES_BLOCKSIZE
40
+
41
+ message + "\x80" + "\x00" * (bytes_to_pad - 1)
42
+ end
43
+
44
+ def unpad_buffer(message)
45
+ raise OpenSSL::Cipher::CipherError unless message.match(/\x80\x00*$/)
46
+
47
+ message.gsub(/\x80\x00*$/, '')
48
+ end
49
+ end
50
+ end
51
+
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "loggregator_emitter"
5
- spec.version = '1.0.1'
5
+ spec.version = '2.0.0.pre'
6
6
  spec.authors = ["Pivotal"]
7
7
  spec.email = ["cf-eng@pivotallabs.com"]
8
8
  spec.description = "Library to emit data to Loggregator"
@@ -42,6 +42,60 @@ describe LoggregatorEmitter do
42
42
  end
43
43
  end
44
44
 
45
+
46
+ describe "emit_log_envelope" do
47
+ def make_emitter(host)
48
+ LoggregatorEmitter::Emitter.new("#{host}:#{free_port}", LogMessage::SourceType::CLOUD_CONTROLLER, 42, "secret")
49
+ end
50
+
51
+ before do
52
+ @server = FakeLoggregatorServer.new(free_port)
53
+ @server.start
54
+ end
55
+
56
+ after do
57
+ @server.stop
58
+ end
59
+
60
+ it "successfully writes envelope protobuffers" do
61
+ emitter = make_emitter("0.0.0.0")
62
+ emitter.emit("my_app_id", "Hello there!")
63
+
64
+ @server.wait_for_messages(1)
65
+
66
+ messages = @server.messages
67
+
68
+ expect(messages.length).to eq 1
69
+ message = messages[0]
70
+ expect(message.routing_key).to eq "my_app_id"
71
+
72
+ actual_digest = Encryption::Symmetric.new.decrypt("secret", message.signature)
73
+ expected_digest = Encryption::Symmetric.new.digest(message.log_message.message)
74
+ expect(actual_digest).to eq expected_digest
75
+
76
+ expect(message.log_message.message).to eq "Hello there!"
77
+ expect(message.log_message.app_id).to eq "my_app_id"
78
+ expect(message.log_message.source_type).to eq LogMessage::SourceType::CLOUD_CONTROLLER
79
+ expect(message.log_message.source_id).to eq "42"
80
+ expect(message.log_message.message_type).to eq LogMessage::MessageType::OUT
81
+ end
82
+
83
+ it "makes the right protobuffer" do
84
+ emitter = make_emitter("0.0.0.0")
85
+
86
+ message = nil
87
+ emitter.stub(:send_protobuffer) do |arg|
88
+ result = arg.encode.buf
89
+ message = result.unpack("C*")
90
+ end
91
+ emitter.emit("my_app_id", "Hello there!")
92
+
93
+ #This test is here to create arrays of bytes to be used in the golang emitter to verify that they are compatible.
94
+ #One of the results we saw:
95
+ #[10, 9, 109, 121, 95, 97, 112, 112, 95, 105, 100, 18, 96, 163, 227, 248, 110, 81, 17, 141, 224, 211, 132, 74, 230, 43, 169, 76, 169, 244, 119, 169, 212, 160, 121, 128, 89, 13, 149, 218, 136, 72, 217, 89, 226, 41, 57, 80, 77, 24, 152, 98, 120, 145, 125, 29, 239, 34, 26, 20, 162, 137, 215, 170, 121, 185, 167, 221, 161, 139, 87, 139, 102, 152, 137, 11, 232, 137, 227, 74, 252, 166, 44, 176, 208, 6, 131, 15, 250, 43, 193, 233, 254, 189, 26, 194, 237, 43, 35, 97, 123, 156, 215, 47, 201, 228, 136, 210, 245, 26, 43, 10, 12, 72, 101, 108, 108, 111, 32, 116, 104, 101, 114, 101, 33, 16, 1, 24, 224, 175, 235, 159, 154, 239, 210, 177, 38, 34, 9, 109, 121, 95, 97, 112, 112, 95, 105, 100, 40, 1, 50, 2, 52, 50]
96
+ end
97
+ end
98
+
45
99
  {"emit" => LogMessage::MessageType::OUT, "emit_error" => LogMessage::MessageType::ERR}.each do |emit_method, message_type|
46
100
  describe "##{emit_method}" do
47
101
  def make_emitter(host)
@@ -117,14 +171,24 @@ describe LoggregatorEmitter do
117
171
  emitter = make_emitter("localhost")
118
172
  message = (124*1024).times.collect { "a" }.join("")
119
173
  emitter.send(emit_method, "my_app_id", message)
120
- messages = @server.messages
121
174
 
122
175
  sleep 0.5
123
176
 
177
+ messages = @server.messages
124
178
  expect(messages.length).to eq 1
125
179
  expect(messages[0].message.bytesize <= LoggregatorEmitter::Emitter::MAX_MESSAGE_BYTE_SIZE).to be_true
126
180
  expect(messages[0].message.slice(-9..-1)).to eq("TRUNCATED")
127
181
  end
182
+
183
+ it "splits messages by newlines" do
184
+ emitter = make_emitter("localhost")
185
+ message = "hi\n\rworld\nhow are you\r\ndoing\r"
186
+ emitter.send(emit_method, "my_app_id", message)
187
+
188
+ sleep 0.5
189
+ messages = @server.messages
190
+ expect(messages.length).to eq 4
191
+ end
128
192
  end
129
193
  end
130
194
 
@@ -139,7 +203,7 @@ describe LoggregatorEmitter do
139
203
  end
140
204
 
141
205
  let(:emit_message) do
142
- @emitter.emit_error("my_app_id", "Hello there!")
206
+ @emitter.emit_error("my_app_id", "Hello there!")
143
207
 
144
208
  @server.wait_for_messages(2)
145
209
 
@@ -0,0 +1,71 @@
1
+ # encoding: utf-8
2
+
3
+ require "loggregator_emitter"
4
+
5
+ describe Encryption do
6
+ subject(:crypter) { Encryption::Symmetric.new }
7
+ it "pads" do
8
+ key = "aaaaaaaaaaaaaaaa"
9
+ message = "1234567890123456"
10
+ encrypted = crypter.encrypt(key, message)
11
+ val = encrypted.unpack("C*")
12
+ end
13
+
14
+ it "encrypts" do
15
+ key = "aaaaaaaaaaaaaaaa"
16
+ message = "Super secret message that no one should read"
17
+ encrypted = crypter.encrypt(key, message)
18
+ decrypted = crypter.decrypt(key, encrypted)
19
+
20
+ expect(message).to eq decrypted
21
+ expect(message).not_to eq encrypted
22
+ end
23
+
24
+ it "encrypts non-deterministicly" do
25
+ key = "aaaaaaaaaaaaaaaa"
26
+ message = "Super secret message that no one should read"
27
+ encrypted1 = crypter.encrypt(key, message)
28
+ encrypted2 = crypter.encrypt(key, message)
29
+
30
+ expect(encrypted1).not_to eq encrypted2
31
+ end
32
+
33
+ it "encrypts with a short key" do
34
+ key = "short key"
35
+ message = "Super secret message that no one should read"
36
+ encrypted = crypter.encrypt(key, message)
37
+ decrypted = crypter.decrypt(key, encrypted)
38
+
39
+ expect(message).to eq decrypted
40
+ expect(message).not_to eq encrypted
41
+ end
42
+
43
+ it "does not decryption with wrong key" do
44
+ key = "aaaaaaaaaaaaaaaa"
45
+ message = "Super secret message that no one should read"
46
+ encrypted = crypter.encrypt(key, message)
47
+
48
+ expect {
49
+ crypter.decrypt(key + "something went wrong", encrypted)
50
+ }.to raise_exception(OpenSSL::Cipher::CipherError)
51
+ end
52
+
53
+ describe "compatibility with the encryption done in the Go library github.com/cloudfoundry/loggregatorlib/symmetric" do
54
+ it "get_encryption_key generates the same key as the go version" do
55
+ key = "12345"
56
+ new_key = crypter.send(:get_encryption_key, key)
57
+
58
+ expected_hex = [0x59, 0x94, 0x47, 0x1a, 0xbb, 0x1, 0x11, 0x2a, 0xfc, 0xc1, 0x81, 0x59, 0xf6, 0xcc, 0x74, 0xb4]
59
+
60
+ expect(new_key.unpack("C*")).to eq expected_hex
61
+ end
62
+
63
+ it "computes digests the same way as the go version" do
64
+ value = "some-key"
65
+
66
+ expected_hex = [0x68, 0x2f, 0x66, 0x97, 0xfa, 0x93, 0xec, 0xa6, 0xc8, 0x1, 0xa2, 0x32, 0x51, 0x9a, 0x9, 0xe3, 0xfe, 0xc, 0x5c, 0x33, 0x94, 0x65, 0xee, 0x53, 0xc3, 0xf9, 0xed, 0xf9, 0x2f, 0xd0, 0x1f, 0x35]
67
+
68
+ expect(crypter.digest(value).unpack("C*")).to eq expected_hex
69
+ end
70
+ end
71
+ end
@@ -29,7 +29,7 @@ class FakeLoggregatorServer
29
29
  end
30
30
 
31
31
  def stop
32
- @sockets.each { |socket| socket.close}
32
+ @sockets.each { |socket| socket.close }
33
33
  @threads.each { |thread| Thread.kill(thread) }
34
34
  end
35
35
 
@@ -42,10 +42,16 @@ class FakeLoggregatorServer
42
42
  while true
43
43
  begin
44
44
  stuff = socket.recv(65536)
45
- messages << LogMessage.decode(stuff)
45
+ decoded_data = LogMessage.decode(stuff.dup)
46
+ messages << decoded_data
46
47
  rescue Beefcake::Message::WrongTypeError, Beefcake::Message::RequiredFieldNotSetError, Beefcake::Message::InvalidValueError => e
47
- puts "ERROR"
48
- puts e
48
+ begin
49
+ decoded_data = LogEnvelope.decode(stuff.dup)
50
+ messages << decoded_data
51
+ rescue Beefcake::Message::WrongTypeError, Beefcake::Message::RequiredFieldNotSetError, Beefcake::Message::InvalidValueError => e
52
+ puts "ERROR: neither envelope nor message extraction worked"
53
+ puts e
54
+ end
49
55
  end
50
56
  end
51
57
  end
metadata CHANGED
@@ -1,18 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loggregator_emitter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.0.pre
5
+ prerelease: 6
5
6
  platform: ruby
6
7
  authors:
7
8
  - Pivotal
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-11-04 00:00:00.000000000 Z
12
+ date: 2013-10-29 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: beefcake
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
19
  - - ~>
18
20
  - !ruby/object:Gem::Version
@@ -20,6 +22,7 @@ dependencies:
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
27
  - - ~>
25
28
  - !ruby/object:Gem::Version
@@ -27,6 +30,7 @@ dependencies:
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: bundler
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
35
  - - ~>
32
36
  - !ruby/object:Gem::Version
@@ -34,6 +38,7 @@ dependencies:
34
38
  type: :development
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
43
  - - ~>
39
44
  - !ruby/object:Gem::Version
@@ -41,6 +46,7 @@ dependencies:
41
46
  - !ruby/object:Gem::Dependency
42
47
  name: rake
43
48
  requirement: !ruby/object:Gem::Requirement
49
+ none: false
44
50
  requirements:
45
51
  - - ~>
46
52
  - !ruby/object:Gem::Version
@@ -48,6 +54,7 @@ dependencies:
48
54
  type: :development
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
51
58
  requirements:
52
59
  - - ~>
53
60
  - !ruby/object:Gem::Version
@@ -55,6 +62,7 @@ dependencies:
55
62
  - !ruby/object:Gem::Dependency
56
63
  name: rspec
57
64
  requirement: !ruby/object:Gem::Requirement
65
+ none: false
58
66
  requirements:
59
67
  - - ~>
60
68
  - !ruby/object:Gem::Version
@@ -62,6 +70,7 @@ dependencies:
62
70
  type: :development
63
71
  prerelease: false
64
72
  version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
65
74
  requirements:
66
75
  - - ~>
67
76
  - !ruby/object:Gem::Version
@@ -84,35 +93,39 @@ files:
84
93
  - lib/loggregator_messages/log_message.pb.rb
85
94
  - lib/loggregator_messages/log_message.proto
86
95
  - lib/loggregator_messages/log_message_extender.rb
96
+ - lib/symmetric/encryption.rb
87
97
  - loggregator_emitter.gemspec
88
98
  - spec/loggregator_emitter/emit_spec.rb
99
+ - spec/loggregator_emitter/encryption_spec.rb
89
100
  - spec/loggregator_emitter/log_message_extender_spec.rb
90
101
  - spec/support/fake_loggregator_server.rb
91
102
  homepage: https://www.github.com/cloudfoundry/loggregator_emitter
92
103
  licenses:
93
104
  - Apache 2.0
94
- metadata: {}
95
105
  post_install_message:
96
106
  rdoc_options: []
97
107
  require_paths:
98
108
  - lib
99
109
  required_ruby_version: !ruby/object:Gem::Requirement
110
+ none: false
100
111
  requirements:
101
112
  - - ! '>='
102
113
  - !ruby/object:Gem::Version
103
114
  version: 1.9.3
104
115
  required_rubygems_version: !ruby/object:Gem::Requirement
116
+ none: false
105
117
  requirements:
106
- - - ! '>='
118
+ - - ! '>'
107
119
  - !ruby/object:Gem::Version
108
- version: '0'
120
+ version: 1.3.1
109
121
  requirements: []
110
122
  rubyforge_project:
111
- rubygems_version: 2.0.5
123
+ rubygems_version: 1.8.25
112
124
  signing_key:
113
- specification_version: 4
125
+ specification_version: 3
114
126
  summary: Library to emit data to Loggregator
115
127
  test_files:
116
128
  - spec/loggregator_emitter/emit_spec.rb
129
+ - spec/loggregator_emitter/encryption_spec.rb
117
130
  - spec/loggregator_emitter/log_message_extender_spec.rb
118
131
  - spec/support/fake_loggregator_server.rb
checksums.yaml DELETED
@@ -1,15 +0,0 @@
1
- ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- MzEyMzMwNDA5NTlkZWE5YTM0ZmJkOTQwNTMzYmQyYjljMmM1YzM2NQ==
5
- data.tar.gz: !binary |-
6
- YmVkNmIwOGM3MDVmZWFhN2Q4OThkYzFmMTRlZjQ1ZTgzY2ViOWQzZQ==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- M2RlMmQyMDVhYjY0NTJjYWI4OGU3ZGNiMTZmNGZhZWE2ZjI5ZmU1ODRjN2Nj
10
- MzQ4ZDVhOWRjYTQwMGE1ODBmZjg0YmQzYmYwYjQ3NDY1ZGMxODBhNDg1MDcx
11
- MThjMmQ1ZDE1MGNmMjc3Mjg0MzhjYTljN2EzYWUzOWMyOWU4ZmQ=
12
- data.tar.gz: !binary |-
13
- OTkxNjgwNjk5ZWYyNTg5M2IxMTI0MmNiZGRjZWZhYzBkNmRhYjUxMmExOTAx
14
- YjZmNzU5ODQ2NDE4YmFmODNhYzQ3YjJkY2NjMjJlNGY5M2VjZGMwZGFhZTgw
15
- NGQxYTk5ZTRlZDNhMjIxOWIwYTlhZTM1MTliZjNlNzUxNzAyMGY=