2 #Copyright (C) 2009-2010 :
3 # Gabes Jean, naparuba@gmail.com
4 # Gerhard Lausser, Gerhard.Lausser@consol.de
5 # Gregory Starck, g.starck@gmail.com
7 #This file is part of Shinken.
9 #Shinken is free software: you can redistribute it and/or modify
10 #it under the terms of the GNU Affero General Public License as published by
11 #the Free Software Foundation, either version 3 of the License, or
12 #(at your option) any later version.
14 #Shinken is distributed in the hope that it will be useful,
15 #but WITHOUT ANY WARRANTY; without even the implied warranty of
16 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 #GNU Affero General Public License for more details.
19 #You should have received a copy of the GNU Affero General Public License
20 #along with Shinken. If not, see <http://www.gnu.org/licenses/>.
22 #sudo nmap 192.168.0.1 -T4 -O --traceroute -oX toto.xml
31 # xml.etree.ElementTree is new in Python 2.5
32 from xml
.etree
.ElementTree
import ElementTree
34 sys
.exit("This script needs the Python ElementTree module. Please install it")
38 parser
= optparse
.OptionParser(
39 "%prog [options] -x nmap.xml -o directory_output",
40 version
="%prog " + VERSION
)
41 parser
.add_option('-x', '--xml-input',
42 dest
="xml_input", help=('Output of nmap'))
43 parser
.add_option('-o', '--dir-output', dest
="output_dir",
44 help="Directory output for results")
45 parser
.add_option('-d', '--cfg-dir-output', dest
="cfg_output_dir",
46 help="Directory output for host/services generated configurations")
47 parser
.add_option('-c', '--criticity', dest
="criticity",
48 help="Criticity level of theses elements in the range (min)[0..5](max).")
50 opts
, args
= parser
.parse_args()
52 if not opts
.xml_input
:
53 parser
.error("Requires one nmap xml output file (option -x/--xml-input")
54 if not opts
.output_dir
:
55 parser
.error("Requires one output directory (option -o/--dir-output")
56 if not opts
.cfg_output_dir
:
57 parser
.error("Requires one configuration output directory (option -d/--cfg-dir-output")
58 #If no criticity is given, use the default one, 3
59 if not opts
.criticity
:
62 criticity
= int(opts
.criticity
)
64 parser
.error("Does not accept any argument.")
67 # Says if a host is up or not
69 status
= h
.find('status')
70 state
= status
.attrib
['state']
74 class ConfigurationManager
:
75 def __init__(self
, h
, path
, criticity
):
77 self
.hosts_path
= os
.path
.join(path
, 'hosts')
78 self
.srvs_path
= os
.path
.join(path
, 'services')
79 self
.templates
= ['generic-host']
81 self
.criticity
= criticity
85 # We search if our potential parent is present in the
86 # other detected hosts. If so, set it as my parent
87 def look_for_parent(self
, all_hosts
):
88 parent
= self
.h
.parent
89 print "Look for my parent", self
.h
.get_name(), "->", parent
90 # Ok, we didn't find any parent
95 print "Is it you?", h
.get_name()
96 if h
.get_name() == parent
:
97 print "Houray, we find our parent", self
.h
.get_name(), "->", h
.get_name()
98 self
.parents
.append(h
.get_name())
102 def fill_system_conf(self
):
105 #Ok, unknown os... not good
109 map = {('Windows', '2000') : 'windows',
110 ('Windows', '2003') : 'windows',
111 ('Windows', '7') : 'windows',
112 ('Windows', 'XP') : 'windows',
113 # ME? you are a stupid moron!
114 ('Windows', 'Me') : 'windows',
115 ('Windows', '2008') : 'windows',
116 # that's a good boy :)
117 ('Linux', '2.6.X') : 'linux',
118 ('Linux', '2.4.X') : 'linux',
119 # HPUX? I think you didn't choose...
120 ('HP-UX', '11.X') : 'hpux',
121 ('HP-UX', '10.X') : 'hpux',
125 print "Unknown OS:", ios
129 self
.templates
.append(t
)
131 # Look for VMWare VM or hosts
132 if self
.h
.is_vmware_vm():
133 self
.templates
.append('vmware-vm')
135 if self
.h
.is_vmware_esx():
136 self
.templates
.append('vmware-host')
139 def get_cfg_for_host(self
):
141 props
['host_name'] = self
.h
.get_name()
142 props
['criticity'] = self
.criticity
144 # Parents if we got some
145 if self
.parents
!= []:
146 props
['parents'] = ','.join(self
.parents
)
149 props
['use'] = ','.join(self
.templates
)
151 print "Want to write", props
152 s
= 'define host {\n'
155 s
+= ' %s %s\n' % (k
, v
)
161 def get_cfg_for_services(self
):
163 print "And now services:"
164 for srv
in self
.services
:
165 desc
= srv
['service_description']
166 s
= 'define service {\n'
169 s
+= ' %s %s\n' % (k
, v
)
177 def fill_ports_services(self
):
178 for p
in self
.h
.open_ports
:
179 print "The port", p
, " is open"
180 f
= getattr(self
, 'gen_srv_'+str(p
), None)
184 def fill_system_services(self
):
185 for t
in self
.templates
:
186 print "Registering services for the template", t
187 # Python functions cannot be -, so we change it by _
188 t
= t
.replace('-','_')
189 print "Search for", 'gen_srv_'+str(t
)
190 f
= getattr(self
, 'gen_srv_'+str(t
), None)
195 def generate_service(self
, desc
, check
):
196 srv
= {'use' : 'generic-service', 'service_description' : desc
, 'check_command' : check
, 'host_name' : self
.h
.get_name()}
197 self
.services
.append(srv
)
200 ######### For network ones
203 def gen_srv_80(self
):
204 self
.generate_service('HTTP', 'check_http')
207 def gen_srv_22(self
):
208 self
.generate_service('SSH', 'check_ssh')
210 # HTTPS + certificate
211 def gen_srv_443(self
):
212 self
.generate_service('HTTPS', 'check_https')
213 self
.generate_service('HTTPS-CERT', 'check_https_certificate')
216 def gen_srv_21(self
):
217 self
.generate_service('FTP', 'check_ftp')
220 def gen_srv_53(self
):
221 self
.generate_service('DNS', 'check_dig!$HOSTADDRESS$')
224 def gen_srv_1521(self
):
225 self
.generate_service('Oracle-Listener', 'check_oracle_listener')
228 def gen_srv_1433(self
):
229 self
.generate_service('MSSQL-Connexion', 'check_mssql_connexion')
230 print "I will need check_mssql_health from http://labs.consol.de/nagios/check_mssql_health/"
233 def gen_srv_25(self
):
234 self
.generate_service('SMTP', 'check_smtp')
237 def gen_srv_465(self
):
238 self
.generate_service('SMTPS', 'check_smtps')
241 def gen_srv_389(self
):
242 self
.generate_service('Ldap', 'check_ldap')
245 def gen_srv_636(self
):
246 self
.generate_service('Ldaps', 'check_ldaps')
249 def gen_srv_3306(self
):
250 self
.generate_service('Mysql', 'check_mysql_connexion')
256 def gen_srv_linux(self
):
257 print "You want a Linux check, but I don't know what to propose, sorry..."
259 def gen_srv_windows(self
):
260 print "You want a Windows check, but I don't know what to propose, sorry..."
262 #For a VM we can add cpu, io, mem and net
263 def gen_srv_vmware_vm(self
):
264 self
.generate_service('VM-Cpu', "check_esx_vm!cpu")
265 self
.generate_service('VM-IO', "check_esx_vm!io")
266 self
.generate_service('VM-Memory', "check_esx_vm!mem")
267 self
.generate_service('VM-Network', "check_esx_vm!net")
268 print "I will need the check_esx3.pl from http://www.op5.org/community/plugin-inventory/op5-projects/op5-plugins"
270 # Quite the same for the host
271 def gen_srv_vmware_host(self
):
272 self
.generate_service('ESX-host-Cpu', "check_esx_host!cpu")
273 self
.generate_service('ESX-host-IO', "check_esx_host!io")
274 self
.generate_service('ESX-host-Memory', "check_esx_host!mem")
275 self
.generate_service('ESX-host-Network', "check_esx_host!net")
276 print "I will need the check_esx3.pl from http://www.op5.org/community/plugin-inventory/op5-projects/op5-plugins"
280 # Write the host cfg file
281 def write_host_configuration(self
):
282 name
= self
.h
.get_name()
283 # If the host is bad, get out
287 # Write the directory with the host config
288 p
= os
.path
.join(self
.hosts_path
, name
)
289 print "I want to create", p
293 # If directory already exists, it's not a problem
294 if not exp
.errno
!= '17':
295 print "Cannot create the directory '%s' : '%s'" % (p
, exp
)
297 cfg_p
= os
.path
.join(p
, name
+'.cfg')
298 print "I want to write", cfg_p
299 s
= self
.get_cfg_for_host()
301 fd
= open(cfg_p
, 'w')
303 print "Cannot create the file '%s' : '%s'" % (cfg_p
, exp
)
309 def write_services_configuration(self
):
310 name
= self
.h
.get_name()
311 # If the host is bad, get out
315 # Write the directory with the host config
316 p
= os
.path
.join(self
.srvs_path
, name
)
317 print "I want to create", p
321 # If directory already exist, it's not a problem
322 if not exp
.errno
!= '17':
323 print "Cannot create the directory '%s' : '%s'" % (p
, exp
)
325 # Ok now we get the services to create
326 r
= c
.get_cfg_for_services()
328 cfg_p
= os
.path
.join(p
, name
+'-'+s
+'.cfg')
329 print "I want to write", cfg_p
332 fd
= open(cfg_p
, 'w')
334 print "Cannot create the file '%s' : '%s'" % (cfg_p
, exp
)
348 self
.os_possibilities
= []
355 # Keep the first name we've got
356 def set_host_name(self
, name
):
357 if self
.host_name
== '':
358 self
.host_name
= name
361 # Get a identifier for this host
363 if self
.host_name
!= '':
364 return self
.host_name
369 # We look for the host VMWare
370 def is_vmware_esx(self
):
371 # If it's not a virtual machine bail out
372 if self
.mac_vendor
!= 'VMware':
374 # If we got all theses ports, we are quite ok for
376 needed_ports
= [22, 80, 443, 902, 903, 5989]
377 for p
in needed_ports
:
378 if p
not in self
.open_ports
:
379 # find one missing port, not a VMWare host
381 # Ok all ports are found, we are a ESX :)
384 # Says if we are a virtual machine or not
385 def is_vmware_vm(self
):
386 # special case : the esx host itself
387 if self
.is_vmware_esx():
389 # Else, look at the mac vendor
390 return self
.mac_vendor
== 'VMware'
393 # Fill the different os possibilities
394 def add_os_possibility(self
, os
, osgen
, accuracy
):
395 self
.os_possibilities
.append( (os
, osgen
, accuracy
) )
397 # Look at ours oses and see which one is the better
398 def compute_os(self
):
399 # bailout if we got no os :(
400 if len(self
.os_possibilities
) == 0:
404 for (os
, osgen
, accuracy
) in self
.os_possibilities
:
405 if accuracy
> max_accuracy
:
406 max_accuracy
= accuracy
408 # now get the entry with the max value
409 for (os
, osgen
, accuracy
) in self
.os_possibilities
:
410 if accuracy
== max_accuracy
:
411 self
.os
= (os
, osgen
)
414 xml_input
= opts
.xml_input
415 output_dir
= opts
.output_dir
416 cfg_output_dir
= opts
.cfg_output_dir
420 tree
.parse(xml_input
)
422 print "Error opening file '%s' : %s" % (xml_input
, exp
)
425 hosts
= tree
.findall('host')
426 print "Number of hosts", len(hosts
)
432 # Bypass non up hosts
438 # Now we get the ipaddr and the mac vendor
439 # for future VMWare matching
441 addrs
= h
.findall('address')
443 #print "Address", addr.__dict__
444 addrtype
= addr
.attrib
['addrtype']
445 if addrtype
== 'ipv4':
446 dh
.ip
= addr
.attrib
['addr']
447 if addrtype
== "mac":
448 if 'vendor' in addr
.attrib
:
449 dh
.mac_vendor
= addr
.attrib
['vendor']
452 # Now we've got the hostnames
453 host_names
= h
.findall('hostnames')
454 for h_name
in host_names
:
455 h_names
= h_name
.findall('hostname')
457 #print 'hname', h_n.__dict__
458 #print 'Host name', h_n.attrib['name']
459 dh
.set_host_name(h_n
.attrib
['name'])
462 # Now print the traceroute
463 traces
= h
.findall('trace')
465 #print trace.__dict__
466 hops
= trace
.findall('hop')
467 #print "Number of hops", len(hops)
471 ttl
= int(hop
.attrib
['ttl'])
472 #We search for the direct father
473 if ttl
== distance
-1:
475 print "Super hop", hop
.__dict
__
476 # Get the host name if possible, if not
478 if 'host' in hop
.attrib
:
479 dh
.parent
= hop
.attrib
['host']
481 dh
.parent
= hop
.attrib
['ipaddr']
484 # Now the OS detection
487 cls
= ios
.findall('osclass')
489 #print "Class", c.__dict__
490 family
= c
.attrib
['osfamily']
491 accuracy
= c
.attrib
['accuracy']
492 if 'osgen' in c
.attrib
:
493 osgen
= c
.attrib
['osgen']
496 #print "Type:", family, osgen, accuracy
497 dh
.add_os_possibility(family
, osgen
, accuracy
)
498 # Ok we can compute our OS now :)
503 allports
= h
.findall('ports')
505 ports
= ap
.findall('port')
507 #print "Port", p.__dict__
508 p_id
= p
.attrib
['portid']
511 state
= s
.attrib
['state']
513 dh
.open_ports
.append(int(p_id
))
526 print "Doing name", name
527 path
= os
.path
.join(output_dir
, name
+'.discover')
528 print "Want path", path
533 # And generate the configuration too
534 c
= ConfigurationManager(h
, cfg_output_dir
, criticity
)
535 c
.look_for_parent(all_hosts
)
537 c
.fill_ports_services()
538 c
.fill_system_services()
539 c
.write_host_configuration()
540 c
.write_services_configuration()