loggregator_emitter 1.0.1 → 2.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -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=