syslog-shipper 1.0.20120102102042 → 1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/HISTORY +8 -0
- data/LICENSE +2 -0
- data/README +18 -6
- data/bin/syslog-shipper +57 -59
- data/lib/syslog_shipper/client.rb +25 -16
- data/lib/syslog_shipper/tls_wrapper.rb +50 -6
- data/spec/client_spec.rb +13 -2
- data/spec/tls_wrapper_spec.rb +59 -0
- metadata +49 -53
data/HISTORY
ADDED
data/LICENSE
ADDED
data/README
CHANGED
@@ -1,9 +1,21 @@
|
|
1
1
|
Ship logs to a syslog server
|
2
2
|
----------------------------
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
data/bin/syslog-shipper
CHANGED
@@ -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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
36
|
-
"What syslog server to ship to (uses TCP)") do |arg|
|
37
|
-
hostarg = arg
|
38
|
-
end
|
32
|
+
files = ARGV || []
|
39
33
|
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
45
|
-
|
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
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
65
|
-
|
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
|
-
|
78
|
-
exclude =
|
79
|
-
|
80
|
-
connection
|
81
|
-
raw
|
82
|
-
|
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
|
-
|
6
|
-
|
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
|
-
@
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
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
|
data/spec/client_spec.rb
CHANGED
@@ -6,8 +6,19 @@ describe SyslogShipper::Client do
|
|
6
6
|
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
|
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
|
-
|
5
|
-
|
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
|
-
-
|
7
|
+
authors:
|
8
|
+
- Neil Matatall
|
13
9
|
autorequire:
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
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
|
-
|
29
|
-
- 0
|
30
|
-
version: "0"
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
31
33
|
type: :runtime
|
32
|
-
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70190739729940
|
33
36
|
description: Ship logs from files to a remote syslog server over TCP
|
34
|
-
email:
|
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
|
-
-
|
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
|
-
|
50
|
-
|
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
|
-
|
64
|
-
|
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
|
-
|
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.
|
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
|
76
|
+
summary: syslog-shipper - a tool for streaming logs from files to a remote syslog
|
77
|
+
server
|
81
78
|
test_files: []
|
82
|
-
|