forked from ytti/oxidized
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsyslog.rb
executable file
·148 lines (125 loc) · 3.81 KB
/
syslog.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/env ruby
# IOS:
# logging discriminator CFG mnemonics includes CONFIG_I
# logging host SERVER discriminator CFG
# JunOS:
# set system syslog host SERVER interactive-commands notice
# set system syslog host SERVER match "^mgd\[[0-9]+\]: UI_COMMIT: .*"
# Ports < 1024 need extra privileges, use a port higher than this by setting the port option in your oxidized config file.
# To use the default port for syslog (514) you shouldn't pass an argument, but you will need to allow this with:
# sudo setcap 'cap_net_bind_service=+ep' /usr/bin/ruby
# Config options are:
# syslogd
# port (Default = 514)
# file (Default = messages)
# resolve (Default = true)
# To stop the resolution of IP's to PTR you can set resolve to false
# exit if fork ## TODO: proper daemonize
require 'socket'
require 'resolv'
require_relative 'rest_client'
module Oxidized
require 'asetus'
class Config
Root = File.join Dir.home, '.config', 'oxidized'
end
CFGS = Asetus.new name: 'oxidized', load: false, key_to_s: true
CFGS.default.syslogd.port = 514
CFGS.default.syslogd.file = 'messages'
CFGS.default.syslogd.resolve = true
CFGS.default.syslogd.dns_map = {
'(.*)\.strip\.this\.domain\.com' => '\\1',
'(.*)\.also\.this\.net' => '\\1'
}
begin
CFGS.load
rescue StandardError => e
raise InvalidConfig, "Error loading config: #{e.message}"
ensure
CFG = CFGS.cfg # convenienence, instead of Config.cfg.password, CFG.password
end
class SyslogMonitor
MSG = {
ios: /%SYS-(SW[0-9]+-)?5-CONFIG_I:/,
junos: 'UI_COMMIT:',
eos: /%SYS-5-CONFIG_I:/,
nxos: /%VSHD-5-VSHD_SYSLOG_CONFIG_I:/,
aruba: 'Notice-Type=\'Running'
}.freeze
class << self
def udp(port = Oxidized::CFG.syslogd.port, listen = 0)
io = UDPSocket.new
io.bind listen, port
new io, :udp
end
def file(syslog_file = Oxidized::CFG.syslogd.file)
io = File.open syslog_file, 'r'
io.seek 0, IO::SEEK_END
new io, :file
end
end
private
def initialize(io, mode = :udp)
@mode = mode
run io
end
def rest(opt)
Oxidized::RestClient.next opt
end
def ios(log, index, **opts)
# TODO: we need to fetch 'ip/name' in mode == :file here
opts[:user] = log[index + 5]
opts[:from] = log[-1][1..-2]
opts
end
alias nxos ios
alias eos ios
def junos(log, index, **opts)
# TODO: we need to fetch 'ip/name' in mode == :file here
opts[:user] = log[index + 2][1..-2]
opts[:msg] = log[(index + 6)..-1].join(' ')[10..-2]
opts.delete(:msg) if opts[:msg] == 'none'
opts
end
def aruba(log, index, **opts)
opts.merge user: log[index + 2].split('=')[4].split(',')[0][1..-2]
end
def handle_log(log, ipaddr)
log = log.to_s.split
index, vendor = MSG.find do |key, value|
index = log.find_index { |e| e.match value }
break index, key if index
end
rest send(vendor, log, index, ip: ipaddr, name: getname(ipaddr), model: vendor.to_s) if index
end
def run(io)
loop do
log = select [io]
log, ip = log.first.first, nil
if @mode == :udp
log, ip = log.recvfrom_nonblock 2000
ip = ip.last
else
begin
log = log.read_nonblock 2000
rescue EOFError
sleep 1
retry
end
end
handle_log log, ip
end
end
def getname(ipaddr)
if Oxidized::CFG.syslogd.resolve == false
ipaddr
else
name = (Resolv.getname ipaddr.to_s rescue ipaddr)
Oxidized::CFG.syslogd.dns_map.each { |re, sub| name.sub! Regexp.new(re.to_s), sub }
name
end
end
end
end
Oxidized::SyslogMonitor.udp
# Oxidized::SyslogMonitor.file '/var/log/poop'