syslog-shipper 1.0.20120102102042 → 1.1

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY ADDED
@@ -0,0 +1,8 @@
1
+ 1.1
2
+
3
+ Added TLS support
4
+ Can supply custom CA cert, bypass peer checking, or accept an unknown certificate
5
+ Ping mode
6
+ When used with verbose, helps debug TLS issues
7
+ Config file
8
+ Use -f to supply a YAML config file that maps to the command line flags
data/LICENSE ADDED
@@ -0,0 +1,2 @@
1
+ This software is licensed under the BSD license as here:
2
+ http://www.opensource.org/licenses/bsd-license.php
data/README CHANGED
@@ -1,9 +1,21 @@
1
1
  Ship logs to a syslog server
2
2
  ----------------------------
3
3
 
4
- Usage: bin/syslog-shipper [options] -s HOST:PORT <path_or_glob> [path_or_glob2] [...]
5
- If a path begins with '+' each line is sent unmodified to the syslog server.
6
- Otherwise, this tool will prefix each line read from the file with a syslog
7
- header.
8
-
9
- For example: syslog-shipper -s somehost:514 +/var/log/messages /var/log/apache2/access.log
4
+ [syslog-shipper (master *+$)]$ bin/syslog-shipper -h
5
+ Usage: bin/syslog-shipper [options] -s HOST:PORT <path_or_glob> [path_or_glob2] [...]
6
+ If a path begins with '+' each line is sent unmodified to the syslog server.
7
+ Otherwise, this tool will prefix each line read from the file with a syslog
8
+ header.
9
+
10
+ For example: bin/syslog-shipper -s somehost:514 +/var/log/messages /var/log/apache2/access.syslog
11
+ --check-interval, -i <i>: How frequently, in seconds, to check the glob patternsfor new files (default: 5)
12
+ --exclude, -x <s+>: A pattern to ignore. Wildcard/globs accepted. Can be specified multiple times
13
+ --server, -s <s>: What syslog server to ship to (uses TCP)
14
+ --verbose, -v: Verbose mode
15
+ --ping, -p: Try to connect and quit immediately after
16
+ --ca-cert, -c <s>: Custom certificate used to verify TLS certificates (implies --tls)
17
+ --skip-peer-check, -k: When connecting with TLS, do not prompt the user to verify the peer (not recommended, implies --tls)
18
+ --tls, -t: Connect via tls
19
+ --config-file, -f <s>: YAML config file (command line options override values in this file)
20
+ --version, -e: Print version and exit
21
+ --help, -h: Show this message
@@ -1,57 +1,53 @@
1
- #!/usr/bin/env ruby
1
+ #!/usr/bin/env ruby -rubygems
2
2
 
3
- require "rubygems"
4
- require "optparse"
5
3
  require File.expand_path('../../lib/syslog_shipper', __FILE__)
4
+ require 'trollop'
5
+ require 'yaml'
6
6
 
7
7
  def main(args)
8
- globcheck_interval = 5
9
- exclude_patterns = []
10
- hostarg = nil
11
- verbose = false
12
-
13
- opts = OptionParser.new do |opts|
14
- opts.banner = [
15
- "Usage: #{$0} [options] -s HOST:PORT <path_or_glob> [path_or_glob2] [...]",
16
- " If a path begins with '+' each line is sent unmodified to the syslog server.",
17
- " Otherwise, this tool will prefix each line read from the file with a syslog",
18
- " header. ",
19
- "",
20
- " For example: #{$0} -s somehost:514 +/var/log/messages /var/log/apache2/access.log",
21
- ].join("\n")
22
-
23
- opts.on("-i SECONDS", "--check-interval SECONDS",
24
- "How frequently, in seconds, to check the glob patterns" \
25
- "for new files") do |x|
26
- globcheck_interval = x.to_f
27
- end # -i SECONDS
28
-
29
- opts.on("-x EXCLUDE", "--exclude EXCLUDE",
30
- "A pattern to ignore. Wildcard/globs accepted." \
31
- " Can be specified multiple times") do |pattern|
32
- exclude_patterns << pattern_to_regexp(pattern)
33
- end
8
+ options = Trollop::options do
9
+ version "syslog-shipper 1.0"
10
+ banner <<-EOS
11
+ Usage: #{$0} [options] -s HOST:PORT <path_or_glob> [path_or_glob2] [...]
12
+ If a path begins with '+' each line is sent unmodified to the syslog server.
13
+ Otherwise, this tool will prefix each line read from the file with a syslog
14
+ header.
15
+
16
+ For example: #{$0} -s somehost:514 +/var/log/messages /var/log/apache2/access.syslog
17
+ EOS
18
+
19
+ opt :check_interval, "How frequently, in seconds, to check the glob patterns" \
20
+ "for new files", :default => 5, :short => :i
21
+ opt :exclude, "A pattern to ignore. Wildcard/globs accepted." \
22
+ " Can be specified multiple times", :short => :x, :type => :strings, :multi => true
23
+ opt :server, "What syslog server to ship to (uses TCP)", :type => :string
24
+ opt :verbose, "Verbose mode"
25
+ opt :ping, "Try to connect and quit immediately after"
26
+ opt :ca_cert, "Custom certificate used to verify TLS certificates (implies --tls)", :type => :string
27
+ opt :skip_peer_check, "When connecting with TLS, do not prompt the user to verify the peer (not recommended, implies --tls)"
28
+ opt :tls, "Connect via tls"
29
+ opt :config_file, "YAML config file (command line options override values in this file)", :type => :string, :short => :f
30
+ end
34
31
 
35
- opts.on("-s HOST:PORT", "--server HOST:PORT",
36
- "What syslog server to ship to (uses TCP)") do |arg|
37
- hostarg = arg
38
- end
32
+ files = ARGV || []
39
33
 
40
- opts.on("-v", "--verbose", "verbose (outputs each log line as seen)") do |arg|
41
- verbose = true
42
- end
34
+ if options[:config_file]
35
+ config = Hash[YAML::load(open(options[:config_file])).map { |k, v| [k.to_sym, v] }]
36
+ # prefer command line values, but take them from the config file
37
+ options.merge!(config) {|k, right_v, left_v| right_v.nil? ? left_v : right_v }
38
+ files += options[:files]
39
+ end
43
40
 
44
- opts.on("-c CERT_PATH", "--ca-cert CERT_PATH", "Certificate authority PEM file to use") do |arg|
45
- SyslogShipper::Client.ca_cert = arg
46
- end
47
- end # OptionParser
41
+ Trollop.die "You must supply files to watch" if files.empty?
42
+ Trollop.die "You must supply the host:port to connect to" if options.server.nil?
48
43
 
49
- opts.parse!(args)
44
+ host, port = options[:server].split(":")
45
+ port = 514 if port == nil
46
+ exluded_patterns = options[:exclude] ? options[:exclude].map{|glob| pattern_to_regexp(glob)} : []
50
47
 
51
- if args.length == 0 or hostarg == nil
52
- puts opts.banner
53
- return 1
54
- end
48
+ options[:tls] = true if (options[:tls] || options[:skip_peer_check] || options[:ca_cert])
49
+
50
+ puts options if options[:verbose]
55
51
 
56
52
  EventMachine.run do
57
53
  Signal.trap("INT") do
@@ -61,26 +57,28 @@ def main(args)
61
57
  end
62
58
  end
63
59
 
64
- host, port = hostarg.split(":")
65
- port = 514 if port == nil
66
-
67
- connection = EventMachine::connect(host, port, SyslogShipper::TlsWrapper)
68
-
69
- args.each do |path|
70
- if path.start_with?("+")
60
+ files.uniq.each do |path|
61
+ if path =~ /\A\+/
71
62
  raw = true
72
63
  path = path[1..-1]
73
64
  else
74
65
  raw = false
75
66
  end
67
+
68
+ connection = if options[:tls]
69
+ EventMachine::connect(host, port, SyslogShipper::TlsWrapper, options[:ca_cert], options[:tls], options[:skip_peer_check], options[:verbose])
70
+ else
71
+ EventMachine::connect(host, port)
72
+ end
73
+
76
74
  EventMachine::FileGlobWatchTail.new(path, SyslogShipper::Client,
77
- interval = globcheck_interval,
78
- exclude = exclude_patterns,
79
- start_pos = -1,
80
- connection = connection,
81
- raw = raw,
82
- verbose = verbose
83
- )
75
+ options[:check_interval],
76
+ exclude = exluded_patterns,
77
+ {:startpos => -1,
78
+ :connection => connection,
79
+ :raw => raw,
80
+ :ping => options[:ping],
81
+ :verbose => options[:verbose]})
84
82
  end # args.each
85
83
  end # EventMachine.run
86
84
  end # def main
@@ -2,29 +2,27 @@ require "socket"
2
2
 
3
3
  module SyslogShipper
4
4
  class Client < EventMachine::FileTail
5
- class << self
6
- attr_accessor :ca_cert
7
- end
8
-
9
- def initialize(path, startpos=-1, connection=nil, raw=false, verbose=false)
10
- super(path, startpos)
5
+ def initialize path, options = {:startpos => -1}
6
+ super path, options[:startpos]
11
7
  @buffer = BufferedTokenizer.new
12
8
  @hostname = Socket.gethostname
13
- @connection = connection
14
- @raw = raw
15
- @verbose = verbose
9
+ @connection = options[:connection]
10
+ @raw = options[:raw]
11
+ @ping = options[:ping]
12
+ @verbose = options[:verbose]
16
13
  end
17
14
 
18
15
  def receive_data(data)
19
16
  @buffer.extract(data).each do |line|
20
- line = if @raw
21
- "#{line}\n"
22
- else
23
- "#{Time.now.strftime("%b %d %H:%M:%S")} #{@hostname} #{path}: #{line}\n"
17
+ if @ping
18
+ puts 'connection successful'
19
+ exit
20
+ end
21
+
22
+ if message = build_message(line)
23
+ puts message if @verbose
24
+ send_data message
24
25
  end
25
-
26
- print line if @verbose
27
- send_data(line)
28
26
  end
29
27
  end
30
28
 
@@ -33,5 +31,16 @@ module SyslogShipper
33
31
  def send_data line
34
32
  @connection.send_data line
35
33
  end
34
+
35
+ def build_message line
36
+ # don't send anything if there is no data
37
+ return if line && line.gsub(/\s/, '').empty?
38
+
39
+ if @raw
40
+ "#{line}\n"
41
+ else
42
+ "#{Time.now.strftime("%b %d %H:%M:%S")} #{@hostname} #{path}: #{line}\n"
43
+ end
44
+ end
36
45
  end
37
46
  end
@@ -1,25 +1,69 @@
1
1
  require 'openssl'
2
+ require 'thread'
2
3
 
3
4
  module SyslogShipper::TlsWrapper
5
+ class << self
6
+ attr_accessor :verified
7
+ end
8
+
9
+ def initialize(ca_cert = nil, with_tls = false, bypass_peer_check = false, verbose = false)
10
+ @ca_cert = ca_cert
11
+ @with_tls = true
12
+ @bypass_peer_check = bypass_peer_check
13
+ @verbose = verbose
14
+ end
15
+
4
16
  def post_init
5
- start_tls(:verify_peer => true)
17
+ puts 'post init' if @verbose
18
+
19
+ start_tls :verify_peer => @with_tls
6
20
  end
7
21
 
8
22
  def connection_completed
9
-
23
+ puts 'connection completed' if @verbose
10
24
  end
11
25
 
12
26
  def ssl_verify_peer cert
13
- ca_cert = OpenSSL::X509::Certificate.new File.read(SyslogShipper::Client.ca_cert)
14
- server_cert = OpenSSL::X509::Certificate.new cert
15
- server_cert.verify ca_cert.public_key
27
+ puts 'verifying peer' if @verbose
28
+ unless defined?(@@verified)
29
+ return true if @bypass_peer_check
30
+
31
+ server_cert = OpenSSL::X509::Certificate.new cert
32
+ verified = false
33
+
34
+ if @ca_cert
35
+ ca_cert = read_ca_cert
36
+ verified = server_cert.verify(ca_cert.public_key)
37
+ end
38
+
39
+ unless verified
40
+ puts server_cert.inspect
41
+ print "The server certificate is not recognized, would you still like to connect? (Y/N) "
42
+ answer = STDIN.gets.chomp
43
+ unless ['y', 'yes'].include?(answer.downcase)
44
+ raise OpenSSL::X509::CertificateError.new("Couldn't verify peer")
45
+ end
46
+ end
47
+
48
+ @@verified = verified
49
+
50
+ puts 'verified peer' if @verbose
51
+ end
52
+
53
+ true
16
54
  end
17
55
 
18
56
  def ssl_handshake_completed
19
- $server_handshake_completed = true
57
+ puts 'ssl handshake completed' if @verbose
20
58
  end
21
59
 
22
60
  def unbind
61
+ puts 'connection unbound!' if @verbose
62
+ end
63
+
64
+ private
23
65
 
66
+ def read_ca_cert
67
+ OpenSSL::X509::Certificate.new(File.read(@ca_cert))
24
68
  end
25
69
  end
@@ -6,8 +6,19 @@ describe SyslogShipper::Client do
6
6
 
7
7
  end
8
8
 
9
- context "sending data" do
10
- it "sends over TLS"
9
+ it 'builds a message'
10
+ it 'prints the message if in verbose mode'
11
+ it 'sends data'
12
+ end
13
+
14
+ describe '#build_message' do
15
+ context 'raw mode' do
16
+ it 'does not modify the message'
17
+ end
18
+
19
+ context 'non-raw mode' do
20
+ it 'prepends the timestamp and host'
11
21
  end
22
+
12
23
  end
13
24
  end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ class MyTlsModule
4
+ include SyslogShipper::TlsWrapper
5
+ end
6
+
7
+ describe SyslogShipper::TlsWrapper do
8
+
9
+ let(:subject) {MyTlsModule.new}
10
+ let(:server_cert) {double}
11
+ let(:ca_cert) {double}
12
+
13
+
14
+ before(:each) do
15
+ MyTlsModule.class_variable_set(:@@verified, false)
16
+ subject.stub(:read_ca_cert).and_return(ca_cert)
17
+ ca_cert.stub(:public_key)
18
+ server_cert.stub(:public_key)
19
+ OpenSSL::X509::Certificate.stub(:new).and_return(server_cert)
20
+ end
21
+
22
+ describe "#ssl_verify_peer" do
23
+ context 'using a valid CA certificate' do
24
+ before(:each) do
25
+ subject.instance_variable_set(:@ca_cert, double)
26
+ server_cert.stub(:verify).and_return true
27
+ end
28
+
29
+ it 'verifies the peer' do
30
+ subject.ssl_verify_peer("").should be_true
31
+ end
32
+ end
33
+
34
+ context 'connecting to an unverified peer' do
35
+ before(:each) do
36
+ server_cert.stub(:verify).and_return false
37
+ end
38
+
39
+ it 'should connect if the bypass peer checking flag is set' do
40
+ subject.instance_variable_set(:@bypass_peer_check, true)
41
+ subject.ssl_verify_peer("").should be_true
42
+ end
43
+
44
+ it 'should connect if the user answers yes to the command prompt' do
45
+ pending("previous tests keeping state...")
46
+ STDIN.should_receive(:gets).and_return("yes")
47
+ subject.ssl_verify_peer("").should be_true
48
+ end
49
+
50
+ it 'should not connect to an untrusted peer' do
51
+ pending("previous tests keeping state...")
52
+ STDIN.stub(:gets).and_return("no")
53
+ lambda {
54
+ subject.ssl_verify_peer("")
55
+ }.should raise_error(OpenSSL::X509::CertificateError)
56
+ end
57
+ end
58
+ end
59
+ end
metadata CHANGED
@@ -1,82 +1,78 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: syslog-shipper
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 1
7
- - 0
8
- - 20120102102042
9
- version: 1.0.20120102102042
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.1'
5
+ prerelease:
10
6
  platform: ruby
11
- authors:
12
- - Jordan Sissel
7
+ authors:
8
+ - Neil Matatall
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
-
17
- date: 2012-01-02 00:00:00 -08:00
18
- default_executable:
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2012-01-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: eventmachine-tail
16
+ requirement: &70190739730420 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
22
23
  prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
+ version_requirements: *70190739730420
25
+ - !ruby/object:Gem::Dependency
26
+ name: trollop
27
+ requirement: &70190739729940 !ruby/object:Gem::Requirement
24
28
  none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- segments:
29
- - 0
30
- version: "0"
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
31
33
  type: :runtime
32
- version_requirements: *id001
34
+ prerelease: false
35
+ version_requirements: *70190739729940
33
36
  description: Ship logs from files to a remote syslog server over TCP
34
- email: jls@semicomplete.com
35
- executables:
37
+ email: neil@matatall.com
38
+ executables:
36
39
  - syslog-shipper
37
40
  extensions: []
38
-
39
41
  extra_rdoc_files: []
40
-
41
- files:
42
+ files:
42
43
  - README
43
- - lib/syslog_shipper/tls_wrapper.rb
44
+ - LICENSE
45
+ - HISTORY
44
46
  - lib/syslog_shipper/client.rb
47
+ - lib/syslog_shipper/tls_wrapper.rb
45
48
  - lib/syslog_shipper.rb
46
49
  - bin/syslog-shipper
47
- - spec/spec_helper.rb
48
50
  - spec/client_spec.rb
49
- has_rdoc: true
50
- homepage: https://github.com/jordansissel/syslog-shipper
51
+ - spec/spec_helper.rb
52
+ - spec/tls_wrapper_spec.rb
53
+ homepage: https://github.com/oreoshake/syslog-shipper
51
54
  licenses: []
52
-
53
55
  post_install_message:
54
56
  rdoc_options: []
55
-
56
- require_paths:
57
+ require_paths:
57
58
  - lib
58
- required_ruby_version: !ruby/object:Gem::Requirement
59
+ required_ruby_version: !ruby/object:Gem::Requirement
59
60
  none: false
60
- requirements:
61
- - - ">="
62
- - !ruby/object:Gem::Version
63
- segments:
64
- - 0
65
- version: "0"
66
- required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
66
  none: false
68
- requirements:
69
- - - ">="
70
- - !ruby/object:Gem::Version
71
- segments:
72
- - 0
73
- version: "0"
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
74
71
  requirements: []
75
-
76
72
  rubyforge_project:
77
- rubygems_version: 1.3.7
73
+ rubygems_version: 1.8.10
78
74
  signing_key:
79
75
  specification_version: 3
80
- summary: syslog-shipper - a tool for streaming logs from files to a remote syslog server
76
+ summary: syslog-shipper - a tool for streaming logs from files to a remote syslog
77
+ server
81
78
  test_files: []
82
-