mirror of
https://github.com/elastic/logstash.git
synced 2025-04-24 06:37:19 -04:00
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:
commit
1a011db2fc
3 changed files with 561 additions and 0 deletions
441
lib/logstash/codecs/collectd.rb
Normal file
441
lib/logstash/codecs/collectd.rb
Normal 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
|
|
@ -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
119
spec/codecs/collectd.rb
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue