Merge pull request #1101 from untergeek/collectd-untergeek

New collectd codec…
Better performance and raft of other fixes along the way!
This commit is contained in:
Aaron Mildenstein 2014-05-02 14:21:48 -05:00
commit 1a011db2fc
3 changed files with 561 additions and 0 deletions

View file

@ -0,0 +1,441 @@
# encoding utf-8
require "date"
require "logstash/codecs/base"
require "logstash/namespace"
require "tempfile"
require "time"
# Read events from the connectd binary protocol over the network via udp.
# See https://collectd.org/wiki/index.php/Binary_protocol
#
# Configuration in your Logstash configuration file can be as simple as:
# input {
# udp {
# port => 28526
# buffer_size => 1452
# codec => collectd { }
# }
# }
#
# A sample collectd.conf to send to Logstash might be:
#
# Hostname "host.example.com"
# LoadPlugin interface
# LoadPlugin load
# LoadPlugin memory
# LoadPlugin network
# <Plugin interface>
# Interface "eth0"
# IgnoreSelected false
# </Plugin>
# <Plugin network>
# <Server "10.0.0.1" "25826">
# </Server>
# </Plugin>
#
# Be sure to replace "10.0.0.1" with the IP of your Logstash instance.
#
class ProtocolError < LogStash::Error; end
class HeaderError < LogStash::Error; end
class EncryptionError < LogStash::Error; end
class LogStash::Codecs::Collectd < LogStash::Codecs::Base
config_name "collectd"
milestone 1
AUTHFILEREGEX = /([^:]+): (.+)/
PLUGIN_TYPE = 2
COLLECTD_TYPE = 4
SIGNATURE_TYPE = 512
ENCRYPTION_TYPE = 528
TYPEMAP = {
0 => "host",
1 => "@timestamp",
PLUGIN_TYPE => "plugin",
3 => "plugin_instance",
COLLECTD_TYPE => "collectd_type",
5 => "type_instance",
6 => "values",
7 => "interval",
8 => "@timestamp",
9 => "interval",
256 => "message",
257 => "severity",
SIGNATURE_TYPE => "signature",
ENCRYPTION_TYPE => "encryption"
}
PLUGIN_TYPE_FIELDS = {
'host' => true,
'@timestamp' => true,
}
COLLECTD_TYPE_FIELDS = {
'host' => true,
'@timestamp' => true,
'plugin' => true,
'plugin_instance' => true,
}
INTERVAL_VALUES_FIELDS = {
"interval" => true,
"values" => true,
}
INTERVAL_BASE_FIELDS = {
'host' => true,
'collectd_type' => true,
'plugin' => true,
'plugin_instance' => true,
'@timestamp' => true,
'type_instance' => true,
}
INTERVAL_TYPES = {
7 => true,
9 => true,
}
SECURITY_NONE = "None"
SECURITY_SIGN = "Sign"
SECURITY_ENCR = "Encrypt"
# File path(s) to collectd types.db to use.
# The last matching pattern wins if you have identical pattern names in multiple files.
# If no types.db is provided the included types.db will be used (currently 5.4.0).
config :typesdb, :validate => :array
# Prune interval records. Defaults to true.
config :prune_intervals, :validate => :boolean, :default => true
# Security Level. Default is "None". This setting mirrors the setting from the
# collectd [Network plugin](https://collectd.org/wiki/index.php/Plugin:Network)
config :security_level, :validate => [SECURITY_NONE, SECURITY_SIGN, SECURITY_ENCR],
:default => "None"
# Path to the authentication file. This file should have the same format as
# the [AuthFile](http://collectd.org/documentation/manpages/collectd.conf.5.shtml#authfile_filename)
# in collectd. You only need to set this option if the security_level is set to
# "Sign" or "Encrypt"
config :authfile, :validate => :string
public
def register
@logger.info("Starting Collectd codec...")
if @typesdb.nil?
@typesdb = LogStash::Environment.vendor_path("collectd/types.db")
if !File.exists?(@typesdb)
raise "You must specify 'typesdb => ...' in your collectd input (I looked for '#{@typesdb}')"
end
@logger.info("Using types.db", :typesdb => @typesdb.to_s)
end
@types = get_types(@typesdb)
if ([SECURITY_SIGN, SECURITY_ENCR].include?(@security_level))
if @authfile.nil?
raise "Security level is set to #{@security_level}, but no authfile was configured"
else
# Load OpenSSL and instantiate Digest and Crypto functions
require 'openssl'
@sha256 = OpenSSL::Digest::Digest.new('sha256')
@sha1 = OpenSSL::Digest::Digest.new('sha1')
@cipher = OpenSSL::Cipher.new('AES-256-OFB')
@auth = {}
parse_authfile
end
end
end # def register
public
def get_types(paths)
types = {}
# Get the typesdb
paths = Array(paths) # Make sure a single path is still forced into an array type
paths.each do |path|
@logger.info("Getting Collectd typesdb info", :typesdb => path.to_s)
File.open(path, 'r').each_line do |line|
typename, *line = line.strip.split
@logger.debug("typename", :typename => typename.to_s)
next if typename.nil? || typename[0,1] == '#'
types[typename] = line.collect { |l| l.strip.split(":")[0] }
end
end
@logger.debug("Collectd Types", :types => types.to_s)
return types
end # def get_types
# Lambdas for hash + closure methodology
# This replaces when statements for fixed values and is much faster
string_decoder = lambda { |body| body.pack("C*")[0..-2] }
numeric_decoder = lambda { |body| body.slice!(0..7).pack("C*").unpack("E")[0] }
counter_decoder = lambda { |body| body.slice!(0..7).pack("C*").unpack("Q>")[0] }
gauge_decoder = lambda { |body| body.slice!(0..7).pack("C*").unpack("E")[0] }
derive_decoder = lambda { |body| body.slice!(0..7).pack("C*").unpack("q>")[0] }
# For Low-Resolution time
time_decoder = lambda do |body|
byte1, byte2 = body.pack("C*").unpack("NN")
Time.at(( ((byte1 << 32) + byte2))).utc
end
# Hi-Resolution time
hirestime_decoder = lambda do |body|
byte1, byte2 = body.pack("C*").unpack("NN")
Time.at(( ((byte1 << 32) + byte2) * (2**-30) )).utc
end
# Hi resolution intervals
hiresinterval_decoder = lambda do |body|
byte1, byte2 = body.pack("C*").unpack("NN")
Time.at(( ((byte1 << 32) + byte2) * (2**-30) )).to_i
end
# Values decoder
values_decoder = lambda do |body|
body.slice!(0..1) # Prune the header
if body.length % 9 == 0 # Should be 9 fields
count = 0
retval = []
# Iterate through and take a slice each time
types = body.slice!(0..((body.length/9)-1))
while body.length > 0
# Use another hash + closure here...
retval << VALUES_DECODER[types[count]].call(body)
count += 1
end
else
@logger.error("Incorrect number of data fields for collectd record", :body => body.to_s)
end
return retval
end
# Signature
signature_decoder = lambda do |body|
if body.length < 32
@logger.warning("SHA256 signature too small (got #{body.length} bytes instead of 32)")
elsif body.length < 33
@logger.warning("Received signature without username")
else
retval = []
# Byte 32 till the end contains the username as chars (=unsigned ints)
retval << body[32..-1].pack('C*')
# Byte 0 till 31 contain the signature
retval << body[0..31].pack('C*')
end
return retval
end
# Encryption
encryption_decoder = lambda do |body|
retval = []
user_length = (body.slice!(0) << 8) + body.slice!(0)
retval << body.slice!(0..user_length-1).pack('C*') # Username
retval << body.slice!(0..15).pack('C*') # IV
retval << body.pack('C*')
return retval
end
# Lambda Hashes
ID_DECODER = {
0 => string_decoder,
1 => time_decoder,
2 => string_decoder,
3 => string_decoder,
4 => string_decoder,
5 => string_decoder,
6 => values_decoder,
7 => numeric_decoder,
8 => hirestime_decoder,
9 => hiresinterval_decoder,
256 => string_decoder,
257 => numeric_decoder,
512 => signature_decoder,
528 => encryption_decoder
}
# TYPE VALUES:
# 0: COUNTER
# 1: GAUGE
# 2: DERIVE
# 3: ABSOLUTE
VALUES_DECODER = {
0 => counter_decoder,
1 => gauge_decoder,
2 => derive_decoder,
3 => counter_decoder
}
public
def get_values(id, body)
# Use hash + closure/lambda to speed operations
ID_DECODER[id].call(body)
end
private
def parse_authfile
# We keep the authfile parsed in memory so we don't have to open the file
# for every event.
@logger.debug("Parsing authfile #{@authfile}")
if !File.exist?(@authfile)
raise LogStash::ConfigurationError, "The file #{@authfile} was not found"
end
@auth.clear
@authmtime = File.stat(@authfile).mtime
File.readlines(@authfile).each do |line|
#line.chomp!
k,v = line.scan(AUTHFILEREGEX).flatten
if k && v
@logger.debug("Added authfile entry '#{k}' with key '#{v}'")
@auth[k] = v
else
@logger.info("Ignoring malformed authfile line '#{line.chomp}'")
end
end
end # def parse_authfile
private
def get_key(user)
return if @authmtime.nil? or @authfile.nil?
# Validate that our auth data is still up-to-date
parse_authfile if @authmtime < File.stat(@authfile).mtime
key = @auth[user]
@logger.warn("User #{user} is not found in the authfile #{@authfile}") if key.nil?
return key
end # def get_key
private
def verify_signature(user, signature, payload)
# The user doesn't care about the security
return true if @security_level == SECURITY_NONE
# We probably got and array of ints, pack it!
payload = payload.pack('C*') if payload.is_a?(Array)
key = get_key(user)
return false if key.nil?
return OpenSSL::HMAC.digest(@sha256, key, user+payload) == signature
end # def verify_signature
private
def decrypt_packet(user, iv, content)
# Content has to have at least a SHA1 hash (20 bytes), a header (4 bytes) and
# one byte of data
return [] if content.length < 26
content = content.pack('C*') if content.is_a?(Array)
key = get_key(user)
if key.nil?
@logger.debug("Key was nil")
return []
end
# Set the correct state of the cipher instance
@cipher.decrypt
@cipher.padding = 0
@cipher.iv = iv
@cipher.key = @sha256.digest(key);
# Decrypt the content
plaintext = @cipher.update(content) + @cipher.final
# Reset the state, as adding a new key to an already instantiated state
# results in an exception
@cipher.reset
# The plaintext contains a SHA1 hash as checksum in the first 160 bits
# (20 octets) of the rest of the data
hash = plaintext.slice!(0..19)
if @sha1.digest(plaintext) != hash
@logger.warn("Unable to decrypt packet, checksum mismatch")
return []
end
return plaintext.unpack('C*')
end # def decrypt_packet
public
def decode(payload)
payload = payload.bytes.to_a
collectd = {}
was_encrypted = false
while payload.length > 0 do
typenum = (payload.slice!(0) << 8) + payload.slice!(0)
# Get the length of the data in this part, but take into account that
# the header is 4 bytes
length = ((payload.slice!(0) << 8) + payload.slice!(0)) - 4
# Validate that the part length is correct
raise(HeaderError) if length > payload.length
body = payload.slice!(0..length-1)
field = TYPEMAP[typenum]
if field.nil?
@logger.warn("Unknown typenumber: #{typenum}")
next
end
values = get_values(typenum, body)
case typenum
when SIGNATURE_TYPE
raise(EncryptionError) unless verify_signature(values[0], values[1], payload)
next
when ENCRYPTION_TYPE
payload = decrypt_packet(values[0], values[1], values[2])
raise(EncryptionError) if payload.empty?
was_encrypted = true
next
when PLUGIN_TYPE
# We've reached a new plugin, delete everything except for the the host
# field, because there's only one per packet and the timestamp field,
# because that one goes in front of the plugin
collectd.each_key do |k|
collectd.delete(k) unless PLUGIN_TYPE_FIELDS.has_key?(k)
end
when COLLECTD_TYPE
# We've reached a new type within the plugin section, delete all fields
# that could have something to do with the previous type (if any)
collectd.each_key do |k|
collectd.delete(k) unless COLLECTD_TYPE_FIELDS.has_key?(k)
end
end
raise(EncryptionError) if !was_encrypted and @security_level == SECURITY_ENCR
# Fill in the fields.
if values.is_a?(Array)
if values.length > 1 # Only do this iteration on multi-value arrays
values.each_with_index do |value, x|
begin
type = collectd['collectd_type']
key = @types[type]
key_x = key[x]
# assign
collectd[key_x] = value
rescue
@logger.error("Invalid value for type=#{type.inspect}, key=#{@types[type].inspect}, index=#{x}")
end
end
else # Otherwise it's a single value
collectd['value'] = values[0] # So name it 'value' accordingly
end
elsif field != nil # Not an array, make sure it's non-empty
collectd[field] = values # Append values to collectd under key field
end
if INTERVAL_VALUES_FIELDS.has_key?(field)
if ((@prune_intervals && !INTERVAL_TYPES.has_key?(typenum)) || !@prune_intervals)
# Prune these *specific* keys if they exist and are empty.
# This is better than looping over all keys every time.
collectd.delete('type_instance') if collectd['type_instance'] == ""
collectd.delete('plugin_instance') if collectd['plugin_instance'] == ""
# This ugly little shallow-copy hack keeps the new event from getting munged by the cleanup
# With pass-by-reference we get hosed (if we pass collectd, then clean it up rapidly, values can disappear)
yield LogStash::Event.new(collectd.dup)
end
# Clean up the event
collectd.each_key do |k|
collectd.delete(k) if !INTERVAL_BASE_FIELDS.has_key?(k)
end
end
end # while payload.length > 0 do
rescue EncryptionError, ProtocolError, HeaderError
# basically do nothing, we just want out
end # def decode
end # class LogStash::Codecs::Collectd

View file

@ -157,6 +157,7 @@ class LogStash::Inputs::Collectd < LogStash::Inputs::Base
public
def get_types(paths)
# Get the typesdb
paths = Array(paths) # Make sure a single path is still forced into an array type
paths.each do |path|
@logger.info("Getting Collectd typesdb info", :typesdb => path.to_s)
File.open(path, 'r').each_line do |line|

119
spec/codecs/collectd.rb Normal file
View file

@ -0,0 +1,119 @@
require "logstash/codecs/collectd"
require "logstash/event"
require "insist"
require "tempfile"
describe LogStash::Codecs::Collectd do
context "None" do
subject do
next LogStash::Codecs::Collectd.new({})
end
it "should parse a normal packet" do
payload = ["000000236c6965746572732d6b6c6170746f702e70726f742e706c657869732e6575000008000c14b0a645f3eb73c30009000c00000002800000000002000e696e74657266616365000003000a776c616e30000004000e69665f6572726f7273000006001800020202000000000000000000000000000000000008000c14b0a645f3eb525e000300076c6f000004000f69665f7061636b6574730000060018000202020000000000001cd80000000000001cd80008000c14b0a645f3ebf8c10002000c656e74726f70790000030005000004000c656e74726f7079000006000f0001010000000000a063400008000c14b0a645f3eb6c700002000e696e74657266616365000003000a776c616e30000004000f69665f7061636b657473000006001800020202000000000002d233000000000001c3b10008000c14b0a645f3eb59b1000300076c6f000004000e69665f6572726f7273000006001800020202000000000000000000000000000000000008000c14b0a645f425380b00020009737761700000030005000004000973776170000005000975736564000006000f00010100000000000000000008000c14b0a645f4254c8d0005000966726565000006000f00010100000000fcffdf410008000c14b0a645f4255ae70005000b636163686564000006000f00010100000000000000000008000c14b0a645f426f09f0004000c737761705f696f0000050007696e000006000f00010200000000000000000008000c14b0a645f42701e7000500086f7574000006000f00010200000000000000000008000c14b0a645f42a0edf0002000a7573657273000004000a75736572730000050005000006000f00010100000000000022400008000c14b0a645f5967c8b0002000e70726f636573736573000004000d70735f7374617465000005000c72756e6e696e67000006000f00010100000000000000000008000c14b0a645f624706c0005000d736c656570696e67000006000f0001010000000000c067400008000c14b0a645f624861a0005000c7a6f6d62696573000006000f00010100000000000000000008000c14b0a645f62494740005000c73746f70706564000006000f00010100000000000010400008000c14b0a645f6254aa90005000b706167696e67000006000f00010100000000000000000008000c14b0a645f6255b110005000c626c6f636b6564000006000f00010100000000000000000008000c14b0a645f62763060004000e666f726b5f726174650000050005000006000f00010200000000000025390008000c14b0a64873bf8f47000200086370750000030006300000040008637075000005000975736572000006000f0001020000000000023caa0008000c14b0a64873bfc9dd000500096e696365000006000f00010200000000000000030008000c14b0a64873bfe9350005000b73797374656d000006000f00010200000000000078bc0008000c14b0a64873c004290005000969646c65000006000f00010200000000000941fe0008000c14b0a64873c020920005000977616974000006000f00010200000000000002050008000c14b0a64873c03e280005000e696e74657272757074000006000f00010200000000000000140008000c14b0a64873c04ba20005000c736f6674697271000006000f00010200000000000001890008000c14b0a64873c058860005000a737465616c000006000f00010200000000000000000008000c14b0a64873c071b80003000631000005000975736572000006000f000102000000000002440e0008000c14b0a64873c07f31000500096e696365000006000f0001020000000000000007"].pack('H*')
counter = 0
subject.decode(payload) do |event|
case counter
when 0
insist { event['host'] } == "lieters-klaptop.prot.plexis.eu"
insist { event['plugin'] } == "interface"
insist { event['plugin_instance'] } == "wlan0"
insist { event['collectd_type'] } == "if_errors"
insist { event['rx'] } == 0
insist { event['tx'] } == 0
when 2
insist { event['host'] } == "lieters-klaptop.prot.plexis.eu"
insist { event['plugin'] } == "entropy"
insist { event['collectd_type'] } == "entropy"
insist { event['value'] } == 157.0
end
counter += 1
end
insist { counter } == 28
end # it "should parse a normal packet"
it "should drop a part with an header length" do
payload = ["000000236c6965746572732d6b6c6170746f702e70726f742e706c657869732e6575000008000c14b0a645f3eb73c30009000c00000002800000000002000e696e74657266616365000003000a776c616e30000004000e69665f6572726f7273000006001800020202000000000000000000000000000000000008000c14b0a645f3eb525e000300076c6f000004000f69665f7061636b6574730000060018000202020000000000001cd80000000000001cd80008000c14b0a645f3ebf8c10002000c656e74726f70790000030005000004000c656e74726f7079000006000f0001010000000000a063400008000c14b0a645f3eb6c700002000e696e74657266616365000003000a776c616e30000004000f69665f7061636b657473000006001800020202000000000002d233000000000001c3b10008000c14b0a645f3eb59b1000300076c6f000004000e69665f6572726f7273000006001800020202000000000000000000000000000000000008000c14b0a645f425380b00020009737761700000030005000004000973776170000005000975736564000006000f00010100000000000000000008000c14b0a645f4254c8d0005000966726565000006000f00010100000000fcffdf410008000c14b0a645f4255ae70005000b636163686564000006000f00010100000000000000000008000c14b0a645f426f09f0004000c737761705f696f0000050007696e000006000f00010200000000000000000008000c14b0a645f42701e7000500086f7574000006000f00010200000000000000000008000c14b0a645f42a0edf0002000a7573657273000004000a75736572730000050005000006000f00010100000000000022400008000c14b0a645f5967c8b0002000e70726f636573736573000004000d70735f7374617465000005000c72756e6e696e67000006000f00010100000000000000000008000c14b0a645f624706c0005000d736c656570696e67000006000f0001010000000000c067400008000c14b0a645f624861a0005000c7a6f6d62696573000006000f00010100000000000000000008000c14b0a645f62494740005000c73746f70706564000006000f00010100000000000010400008000c14b0a645f6254aa90005000b706167696e67000006000f00010100000000000000000008000c14b0a645f6255b110005000c626c6f636b6564000006000f00010100000000000000000008000c14b0a645f62763060004000e666f726b5f726174650000050005000006000f00010200000000000025390008000c14b0a64873bf8f47000200086370750000030006300000040008637075000005000975736572000006000f0001020000000000023caa0008000c14b0a64873bfc9dd000500096e696365000006000f00010200000000000000030008000c14b0a64873bfe9350005000b73797374656d000006000f00010200000000000078bc0008000c14b0a64873c004290005000969646c65000006000f00010200000000000941fe0008000c14b0a64873c020920005000977616974000006000f00010200000000000002050008000c14b0a64873c03e280005000e696e74657272757074000006000f00010200000000000000140008000c14b0a64873c04ba20005000c736f6674697271000006000f00010200000000000001890008000c14b0a64873c058860005000a737465616c000006000f00010200000000000000000008000c14b0a64873c071b80003000631000005000975736572000006000f000102000000000002440e0008000c14b0a64873c07f31000500316e696365000006000f0001020000000000000007"].pack('H*')
counter = 0
subject.decode(payload) do |event|
case counter
when 0
insist { event['host'] } == "lieters-klaptop.prot.plexis.eu"
insist { event['plugin'] } == "interface"
insist { event['plugin_instance'] } == "wlan0"
insist { event['collectd_type'] } == "if_errors"
insist { event['rx'] } == 0
insist { event['tx'] } == 0
when 2
insist { event['host'] } == "lieters-klaptop.prot.plexis.eu"
insist { event['plugin'] } == "entropy"
insist { event['collectd_type'] } == "entropy"
insist { event['value'] } == 157.0
end
counter += 1
end
# One of these will fail because I altered the payload from the normal packet
insist { counter } == 27
end # it "should drop a part with an header length"
end # context "None"
# Create an authfile for the next tests
authfile = Tempfile.new('logstash-collectd-authfile')
File.open(authfile.path, "a") do |fd|
fd.puts("pieter: aapje1234")
end
context "Sign" do
subject do
next LogStash::Codecs::Collectd.new({"authfile" => authfile.path,
"security_level" => "Sign"})
end
it "should parse a correctly signed packet" do
payload = ["0200002a815d5d7e1e72250eee4d37251bf688fbc06ec87e3cbaf289390ef47ad7c413ce706965746572000000236c6965746572732d6b6c6170746f702e70726f742e706c657869732e6575000008000c14b0aa39ef05b3a80009000c000000028000000000020008697271000004000869727100000500084d4953000006000f00010200000000000000000008000c14b0aa39ef06c381000200096c6f616400000400096c6f616400000500050000060021000301010148e17a14ae47e13f85eb51b81e85db3f52b81e85eb51e03f0008000c14b0aa39ef0a7a150002000b6d656d6f7279000004000b6d656d6f7279000005000975736564000006000f000101000000006ce8dc410008000c14b0aa39ef0a87440005000d6275666665726564000006000f00010100000000c0eaa9410008000c14b0aa39ef0a91850005000b636163686564000006000f000101000000002887c8410008000c14b0aa39ef0a9b2f0005000966726565000006000f00010100000000580ed1410008000c14b0aa39ef1b3b8f0002000e696e74657266616365000003000974756e30000004000e69665f6f63746574730000050005000006001800020202000000000000df5f00000000000060c10008000c14b0aa39ef1b49ea0004000f69665f7061636b6574730000060018000202020000000000000177000000000000017a0008000c14b0aa39ef1b55570004000e69665f6572726f7273000006001800020202000000000000000000000000000000000008000c14b0aa39ef1b7a400003000965746830000004000e69665f6f6374657473000006001800020202000000000000000000000000000000000008000c14b0aa39ef1b85160004000f69665f7061636b657473000006001800020202000000000000000000000000000000000008000c14b0aa39ef1b93bc0004000e69665f6572726f7273000006001800020202000000000000000000000000000000000008000c14b0aa39ef1bb0bc000300076c6f000004000e69665f6f63746574730000060018000202020000000000a92d840000000000a92d840008000c14b0aa39ef1bbbdd0004000f69665f7061636b6574730000060018000202020000000000002c1e0000000000002c1e0008000c14b0aa39ef1bc8760004000e69665f6572726f7273000006001800020202000000000000000000000000000000000008000c14b0aa39ef1be36a0003000a776c616e30000004000e69665f6f6374657473000006001800020202000000001043329b0000000001432a5d0008000c14b0aa39ef1bef6c0004000f69665f7061636b6574730000060018000202020000000000043884000000000002931e0008000c14b0aa39ef1bfa8d0004000e69665f6572726f7273000006001800020202000000000000000000000000000000000008000c14b0aa39ef6e4ff5000200096469736b000003000873646100000400106469736b5f6f637465747300000600180002020200000000357c5000000000010dfb10000008000c14b0aa39ef6e8e5a0004000d6469736b5f6f7073000006001800020202000000000000a6fe0000000000049ee00008000c14b0aa39ef6eae480004000e6469736b5f74696d65000006001800020202000000000000000400000000000000120008000c14b0aa39ef6ecc2a000400106469736b5f6d6572676564000006001800020202000000000000446500000000000002460008000c14b0aa39ef6ef9dc000300097364613100000400106469736b5f6f637465747300000600180002020200000000000bf00000000000000000000008000c14b0aa39ef6f05490004000d6469736b5f6f707300000600180002020200000000000000bf0000000000000000"].pack('H*')
counter = 0
subject.decode(payload) do |event|
counter += 1
end
insist { counter } == 24
end # it "should parse a correctly signed packet"
it "should not parse an incorrectly signed packet" do
payload = ["0200002a815d5d7f1e72250eee4d37251bf688fbc06ec87e3cbaf289390ef47ad7c413ce706965746572000000236c6965746572732d6b6c6170746f702e70726f742e706c657869732e6575000008000c14b0aa39ef05b3a80009000c000000028000000000020008697271000004000869727100000500084d4953000006000f00010200000000000000000008000c14b0aa39ef06c381000200096c6f616400000400096c6f616400000500050000060021000301010148e17a14ae47e13f85eb51b81e85db3f52b81e85eb51e03f0008000c14b0aa39ef0a7a150002000b6d656d6f7279000004000b6d656d6f7279000005000975736564000006000f000101000000006ce8dc410008000c14b0aa39ef0a87440005000d6275666665726564000006000f00010100000000c0eaa9410008000c14b0aa39ef0a91850005000b636163686564000006000f000101000000002887c8410008000c14b0aa39ef0a9b2f0005000966726565000006000f00010100000000580ed1410008000c14b0aa39ef1b3b8f0002000e696e74657266616365000003000974756e30000004000e69665f6f63746574730000050005000006001800020202000000000000df5f00000000000060c10008000c14b0aa39ef1b49ea0004000f69665f7061636b6574730000060018000202020000000000000177000000000000017a0008000c14b0aa39ef1b55570004000e69665f6572726f7273000006001800020202000000000000000000000000000000000008000c14b0aa39ef1b7a400003000965746830000004000e69665f6f6374657473000006001800020202000000000000000000000000000000000008000c14b0aa39ef1b85160004000f69665f7061636b657473000006001800020202000000000000000000000000000000000008000c14b0aa39ef1b93bc0004000e69665f6572726f7273000006001800020202000000000000000000000000000000000008000c14b0aa39ef1bb0bc000300076c6f000004000e69665f6f63746574730000060018000202020000000000a92d840000000000a92d840008000c14b0aa39ef1bbbdd0004000f69665f7061636b6574730000060018000202020000000000002c1e0000000000002c1e0008000c14b0aa39ef1bc8760004000e69665f6572726f7273000006001800020202000000000000000000000000000000000008000c14b0aa39ef1be36a0003000a776c616e30000004000e69665f6f6374657473000006001800020202000000001043329b0000000001432a5d0008000c14b0aa39ef1bef6c0004000f69665f7061636b6574730000060018000202020000000000043884000000000002931e0008000c14b0aa39ef1bfa8d0004000e69665f6572726f7273000006001800020202000000000000000000000000000000000008000c14b0aa39ef6e4ff5000200096469736b000003000873646100000400106469736b5f6f637465747300000600180002020200000000357c5000000000010dfb10000008000c14b0aa39ef6e8e5a0004000d6469736b5f6f7073000006001800020202000000000000a6fe0000000000049ee00008000c14b0aa39ef6eae480004000e6469736b5f74696d65000006001800020202000000000000000400000000000000120008000c14b0aa39ef6ecc2a000400106469736b5f6d6572676564000006001800020202000000000000446500000000000002460008000c14b0aa39ef6ef9dc000300097364613100000400106469736b5f6f637465747300000600180002020200000000000bf00000000000000000000008000c14b0aa39ef6f05490004000d6469736b5f6f707300000600180002020200000000000000bf0000000000000000"].pack('H*')
counter = 0
subject.decode(payload) do |event|
counter += 1
end
insist { counter } == 0
end # it "should not parse and incorrectly signed packet"
end # context "Sign"
context "Encrypt" do
subject do
next LogStash::Codecs::Collectd.new({"authfile" => authfile.path,
"security_level" => "Encrypt"})
end
it "should parse an encrypted packet" do
payload = ["0210055b0006706965746572a8e1874742655f163fa5b1ae4c7c37cd4c271e4f6e2dc53f0a2dfb6391c11f9200645abd545de9042bc7f36c3119e5d301115acfd44ff298d2565cf20799fa322bbe2e72268ef1b5f24b8003e512b0f8f52ce5d3fb0a5aafbff83ac7a49047e2fbf908a3f8c043154feeb594953e5dbd93eafdc75866b336d25e135d2fea6efcebaf9041c86081dda8b999d816e23106a3615efee7191610d9f2eab626cccf00879d76e82a3e60f60cf594435c723ac302c605f9a3ddc6c994acb75d461fa82e57f8b9823081a80a07386b8cdeca387792a52a58f1c367cacec8ecc292b06c5101b5fdcc0320bfd473fb751bef559e51031ef4207404702fa4899b152bf264c4b0f11cf6ab37fc4c7fb996fa6d2dce9051373c5adf06bbb588d38a1251258f2fd690c55a9d2c87b916ca159b261b3fce068b91fd94ca31f90c237df7ac6fcd7c9e73d77c49b3fb93be59cdcf51ea3dcdfd00cdeff379f979cc7341369c47b741651fe5b8de82498cebf35d8c9bad1ef02384e8418d57765aeede95bbd70078516136351b39e4f1e668786ce3885ac8f0f0246337ed6842f5789536474d3c1390b846aaf859b5af6efad027439dc0e444d3a9ab289a4deab4aeecbd9514e1fabadcd7b4565b6d96f12007b600dd0cc135b0c6a521f8c9c17b109d4ba5a42d32f00757c4da50bc0e5ff2bd1114df97f3edfc25102fdc43faa2c2087a5ee9cc0137438eac807bf19f883023adb1293623e15bf94ce7bb2fb6af68978c12642b1dd04badcbf74ee9d08ed5629904376a084348fc51ea382a9d83cd41d021be24f3fea3f079de815c0a89e0c3684501eb6ead89b515cca706218702fb56fe4c8ca0b3d7969dbee7a5a12a17843f990e408974c65aaa3d719f8774098eee7d5be5adb025de24e719434073e59ee91d38192007c5df97d79174de8218ecf89d7778282814ec8ad92f9622d2b875881666d59949b9487f2b231203b570418dd69218e2e86205af2618b74f1a83bdab0465f44d0647548598018ba0180e6d9a8496854c8fbb85698c4ec56d9f524ebf37953601a0c470c360f2d8fa83215c761cbb4d8ae475bbb3dec60e6a5c7af7aab1b8bb56b8fa18619a0c240e5ccf2d02326fc08db42f74b99b9be5263061b36a1b750e061f3cad72db6480e8194a6fe78bc3403551473d03b5067a3d72457563777f398f3df4ae24c09fc66c2c0b06331fdabb33e7ef22a7e7f4a5d8e92cdaaabc7aabd2ab15cf6204e2a531ef4fdc98ed4895e71ea9e406b759d6d547b0b97c2715551c73efd415e55f0c0d73d7134b63c0636728bab0a59bff59de8a31f40f4f1f77a3e1e52d2035f69ab453dfd14889c5dfa7fcc27180cb35f92a3282dfc520716968bec6f22e99351889d53628e57f48f5ad70899881b81699454d8d5aff6791672cbf258d1130dabf27ddee7f6e105752c3773257a2a5616350551965e7c60603c8b0465169af66b52ff900be147ead7a8bfb9bf1419709b539a8f003da13abe286855850530135a1eba0231a9995736abf55b6f50aa85e42afc7b4e7574cc53b8919d0b05c4630af1e5fa98a1bd6a2b7e4fbda02c68c73d07bf0f117d63d1ed51d613464146dba12460a0769c79517a928e66417ef4ee19248a7abd1a734eb53443ff44a742d6bf96782de8593ec8561ea974b61f0f2d5ab1671c4eb323c0a07bf6d042564161c5688a722cf8de4c39346082b7a3d635bcf5e24c7ab421ed206f3a93c17d26f0b28a99e25bc3387f3f5fcd99b6560c51f055ac1887f3d84fb8ad0eb03304663bad111fcf531e4efe918143062ca1724857edd138ca9eca0476a5205c3fe1db899d4b26a8d3398df52e8548ecdfb94044e8c095df60139d00c3bc01c205d44fd81fc30ec02b20f281da57c106b86e567585e0b561555ea491eda05"].pack('H*')
counter = 0
subject.decode(payload) do |event|
counter += 1
end
insist { counter } == 24
end # it "should parse an encrypted packet"
it "should not parse unencrypted packets when encrypt is configured" do
payload = ["000000236c6965746572732d6b6c6170746f702e70726f742e706c657869732e6575000008000c14b0a645f3eb73c30009000c00000002800000000002000e696e74657266616365000003000a776c616e30000004000e69665f6572726f7273000006001800020202000000000000000000000000000000000008000c14b0a645f3eb525e000300076c6f000004000f69665f7061636b6574730000060018000202020000000000001cd80000000000001cd80008000c14b0a645f3ebf8c10002000c656e74726f70790000030005000004000c656e74726f7079000006000f0001010000000000a063400008000c14b0a645f3eb6c700002000e696e74657266616365000003000a776c616e30000004000f69665f7061636b657473000006001800020202000000000002d233000000000001c3b10008000c14b0a645f3eb59b1000300076c6f000004000e69665f6572726f7273000006001800020202000000000000000000000000000000000008000c14b0a645f425380b00020009737761700000030005000004000973776170000005000975736564000006000f00010100000000000000000008000c14b0a645f4254c8d0005000966726565000006000f00010100000000fcffdf410008000c14b0a645f4255ae70005000b636163686564000006000f00010100000000000000000008000c14b0a645f426f09f0004000c737761705f696f0000050007696e000006000f00010200000000000000000008000c14b0a645f42701e7000500086f7574000006000f00010200000000000000000008000c14b0a645f42a0edf0002000a7573657273000004000a75736572730000050005000006000f00010100000000000022400008000c14b0a645f5967c8b0002000e70726f636573736573000004000d70735f7374617465000005000c72756e6e696e67000006000f00010100000000000000000008000c14b0a645f624706c0005000d736c656570696e67000006000f0001010000000000c067400008000c14b0a645f624861a0005000c7a6f6d62696573000006000f00010100000000000000000008000c14b0a645f62494740005000c73746f70706564000006000f00010100000000000010400008000c14b0a645f6254aa90005000b706167696e67000006000f00010100000000000000000008000c14b0a645f6255b110005000c626c6f636b6564000006000f00010100000000000000000008000c14b0a645f62763060004000e666f726b5f726174650000050005000006000f00010200000000000025390008000c14b0a64873bf8f47000200086370750000030006300000040008637075000005000975736572000006000f0001020000000000023caa0008000c14b0a64873bfc9dd000500096e696365000006000f00010200000000000000030008000c14b0a64873bfe9350005000b73797374656d000006000f00010200000000000078bc0008000c14b0a64873c004290005000969646c65000006000f00010200000000000941fe0008000c14b0a64873c020920005000977616974000006000f00010200000000000002050008000c14b0a64873c03e280005000e696e74657272757074000006000f00010200000000000000140008000c14b0a64873c04ba20005000c736f6674697271000006000f00010200000000000001890008000c14b0a64873c058860005000a737465616c000006000f00010200000000000000000008000c14b0a64873c071b80003000631000005000975736572000006000f000102000000000002440e0008000c14b0a64873c07f31000500096e696365000006000f0001020000000000000007"].pack('H*')
counter = 0
subject.decode(payload) do |event|
counter += 1
end
insist { counter } == 0
end # it "should not parse unencrypted packets when encrypt is configured"
end # context "Encrypt"
end # describe LogStash::Codecs::Collectd