lib/logstash/outputs/syslog.rb in logstash-output-syslog-2.1.0 vs lib/logstash/outputs/syslog.rb in logstash-output-syslog-2.1.1
- old
+ new
@@ -1,9 +1,10 @@
# encoding: utf-8
require "logstash/outputs/base"
require "logstash/namespace"
require "date"
+require "logstash/codecs/plain"
# Send events to a syslog server.
#
# You can send messages compliant with RFC3164 or RFC5424
@@ -26,16 +27,14 @@
"syslogd",
"line printer",
"network news",
"uucp",
"clock",
- "security/authorization",
"ftp",
"ntp",
"log audit",
"log alert",
- "clock",
"local0",
"local1",
"local2",
"local3",
"local4",
@@ -62,69 +61,131 @@
config :port, :validate => :number, :required => true
# when connection fails, retry interval in sec.
config :reconnect_interval, :validate => :number, :default => 1
- # syslog server protocol. you can choose between udp and tcp
- config :protocol, :validate => ["tcp", "udp"], :default => "udp"
+ # syslog server protocol. you can choose between udp, tcp and ssl/tls over tcp
+ config :protocol, :validate => ["tcp", "udp", "ssl-tcp"], :default => "udp"
+ # Verify the identity of the other end of the SSL connection against the CA.
+ config :ssl_verify, :validate => :boolean, :default => false
+
+ # The SSL CA certificate, chainfile or CA path. The system CA path is automatically included.
+ config :ssl_cacert, :validate => :path
+
+ # SSL certificate path
+ config :ssl_cert, :validate => :path
+
+ # SSL key path
+ config :ssl_key, :validate => :path
+
+ # SSL key passphrase
+ config :ssl_key_passphrase, :validate => :password, :default => nil
+
+ # use label parsing for severity and facility levels
+ # use priority field if set to false
+ config :use_labels, :validate => :boolean, :default => true
+
+ # syslog priority
+ # The new value can include `%{foo}` strings
+ # to help you build a new value from other parts of the event.
+ config :priority, :validate => :string, :default => "%{syslog_pri}"
+
# facility label for syslog message
- config :facility, :validate => FACILITY_LABELS, :required => true
+ # default fallback to user-level as in rfc3164
+ # The new value can include `%{foo}` strings
+ # to help you build a new value from other parts of the event.
+ config :facility, :validate => :string, :default => "user-level"
# severity label for syslog message
- config :severity, :validate => SEVERITY_LABELS, :required => true
+ # default fallback to notice as in rfc3164
+ # The new value can include `%{foo}` strings
+ # to help you build a new value from other parts of the event.
+ config :severity, :validate => :string, :default => "notice"
- # source host for syslog message
+ # source host for syslog message. The new value can include `%{foo}` strings
+ # to help you build a new value from other parts of the event.
config :sourcehost, :validate => :string, :default => "%{host}"
# timestamp for syslog message
config :timestamp, :validate => :string, :default => "%{@timestamp}", :deprecated => "This setting is no longer necessary. The RFC setting will determine what time format is used."
- # application name for syslog message
+ # application name for syslog message. The new value can include `%{foo}` strings
+ # to help you build a new value from other parts of the event.
config :appname, :validate => :string, :default => "LOGSTASH"
- # process id for syslog message
+ # process id for syslog message. The new value can include `%{foo}` strings
+ # to help you build a new value from other parts of the event.
config :procid, :validate => :string, :default => "-"
- # message text to log
+ # message text to log. The new value can include `%{foo}` strings
+ # to help you build a new value from other parts of the event.
config :message, :validate => :string, :default => "%{message}"
- # message id for syslog message
+ # message id for syslog message. The new value can include `%{foo}` strings
+ # to help you build a new value from other parts of the event.
config :msgid, :validate => :string, :default => "-"
# syslog message format: you can choose between rfc3164 or rfc5424
config :rfc, :validate => ["rfc3164", "rfc5424"], :default => "rfc3164"
def register
@client_socket = nil
- facility_code = FACILITY_LABELS.index(@facility)
- severity_code = SEVERITY_LABELS.index(@severity)
- @priority = (facility_code * 8) + severity_code
+ if ssl?
+ @ssl_context = setup_ssl
+ end
+
+ if @codec.instance_of? LogStash::Codecs::Plain
+ if @codec.config["format"].nil?
+ @codec = LogStash::Codecs::Plain.new({"format" => @message})
+ end
+ end
+ @codec.on_event(&method(:publish))
# use instance variable to avoid string comparison for each event
@is_rfc3164 = (@rfc == "rfc3164")
end
def receive(event)
+ @codec.encode(event)
+ end
+
+ def publish(event, payload)
appname = event.sprintf(@appname)
procid = event.sprintf(@procid)
sourcehost = event.sprintf(@sourcehost)
+ message = payload.to_s.rstrip.gsub(/[\r][\n]/, "\n").gsub(/[\n]/, '\n')
+
+ # fallback to pri 13 (facility 1, severity 5)
+ if @use_labels
+ facility_code = (FACILITY_LABELS.index(event.sprintf(@facility)) || 1)
+ severity_code = (SEVERITY_LABELS.index(event.sprintf(@severity)) || 5)
+ priority = (facility_code * 8) + severity_code
+ else
+ priority = Integer(event.sprintf(@priority)) rescue 13
+ priority = 13 if (priority < 0 || priority > 191)
+ end
+
if @is_rfc3164
timestamp = event.sprintf("%{+MMM dd HH:mm:ss}")
- syslog_msg = "<#{@priority.to_s}>#{timestamp} #{sourcehost} #{appname}[#{procid}]: #{event.sprintf(@message)}"
+ syslog_msg = "<#{priority.to_s}>#{timestamp} #{sourcehost} #{appname}[#{procid}]: #{message}"
else
msgid = event.sprintf(@msgid)
timestamp = event.sprintf("%{+YYYY-MM-dd'T'HH:mm:ss.SSSZZ}")
- syslog_msg = "<#{@priority.to_s}>1 #{timestamp} #{sourcehost} #{appname} #{procid} #{msgid} - #{event.sprintf(@message)}"
+ syslog_msg = "<#{priority.to_s}>1 #{timestamp} #{sourcehost} #{appname} #{procid} #{msgid} - #{message}"
end
begin
@client_socket ||= connect
@client_socket.write(syslog_msg + "\n")
rescue => e
+ # We don't expect udp connections to fail because they are stateless, but ...
+ # udp connections may fail/raise an exception if used with localhost/127.0.0.1
+ return if udp?
+
@logger.warn("syslog " + @protocol + " output exception: closing, reconnecting and resending event", :host => @host, :port => @port, :exception => e, :backtrace => e.backtrace, :event => event)
@client_socket.close rescue nil
@client_socket = nil
sleep(@reconnect_interval)
@@ -136,16 +197,52 @@
def udp?
@protocol == "udp"
end
+ def ssl?
+ @protocol == "ssl-tcp"
+ end
+
def connect
socket = nil
if udp?
socket = UDPSocket.new
socket.connect(@host, @port)
else
socket = TCPSocket.new(@host, @port)
+ if ssl?
+ socket = OpenSSL::SSL::SSLSocket.new(socket, @ssl_context)
+ begin
+ socket.connect
+ rescue OpenSSL::SSL::SSLError => ssle
+ @logger.error("SSL Error", :exception => ssle,
+ :backtrace => ssle.backtrace)
+ # NOTE(mrichar1): Hack to prevent hammering peer
+ sleep(5)
+ raise
+ end
+ end
end
socket
+ end
+
+ def setup_ssl
+ require "openssl"
+ ssl_context = OpenSSL::SSL::SSLContext.new
+ ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(@ssl_cert))
+ ssl_context.key = OpenSSL::PKey::RSA.new(File.read(@ssl_key),@ssl_key_passphrase)
+ if @ssl_verify
+ cert_store = OpenSSL::X509::Store.new
+ # Load the system default certificate path to the store
+ cert_store.set_default_paths
+ if File.directory?(@ssl_cacert)
+ cert_store.add_path(@ssl_cacert)
+ else
+ cert_store.add_file(@ssl_cacert)
+ end
+ ssl_context.cert_store = cert_store
+ ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
+ end
+ ssl_context
end
end