Commit 6b7ffc655b74983648fd01515b0d00800891d1d9
Exists in
master
Adição dos comandos para ver como se comportam
Showing
7 changed files
with
197 additions
and
6 deletions
Show diff stats
@@ -0,0 +1,167 @@ | @@ -0,0 +1,167 @@ | ||
1 | +#!/bin/env python | ||
2 | +# -*- coding: utf-8 -*- | ||
3 | +__author__ = 'eduardo' | ||
4 | +import logging | ||
5 | +import os | ||
6 | +import os.path | ||
7 | +from paste.script import command | ||
8 | +from .. import Cocar | ||
9 | +from ..model import Base | ||
10 | +from ..model.network import Network | ||
11 | +from ..csv_utils import NetworkCSV | ||
12 | +from ..session import NmapSession | ||
13 | +from multiprocessing import Process, Queue | ||
14 | + | ||
15 | +log = logging.getLogger() | ||
16 | + | ||
17 | + | ||
18 | +class ScanCommands(command.Command): | ||
19 | + """ | ||
20 | + Comandos para realizar o scan da rede | ||
21 | + Usage:: | ||
22 | + paster scan create_db -c <path to config file> | ||
23 | + - Cria banco de dados | ||
24 | + paster scan drop_db -c <path to config file> | ||
25 | + - Remove banco de dados | ||
26 | + paster scan networks -c <path to config file> | ||
27 | + - Faz a busca das redes | ||
28 | + | ||
29 | + Os comandos devem ser executados a partir da raiz do módulo Cocar | ||
30 | + """ | ||
31 | + max_args = 1 | ||
32 | + min_args = 1 | ||
33 | + summary = __doc__.split('\n')[0] | ||
34 | + usage = __doc__ | ||
35 | + group_name = "Scan Commands" | ||
36 | + | ||
37 | + parser = command.Command.standard_parser(verbose=True) | ||
38 | + | ||
39 | + parser.add_option('-f', '--full', | ||
40 | + action='store', | ||
41 | + dest='full', | ||
42 | + help='Full scan or regular scan' | ||
43 | + ) | ||
44 | + | ||
45 | + def __init__(self, name): | ||
46 | + """ | ||
47 | + Constructor method | ||
48 | + | ||
49 | + """ | ||
50 | + super(ScanCommands, self).__init__(name) | ||
51 | + self.cocar = Cocar(environment='production') | ||
52 | + self.networks_dir = self.cocar.cocar_data_dir + "/networks" | ||
53 | + self.networks_csv = self.cocar.config.get('cocar', 'networks_csv') | ||
54 | + if not os.path.isdir(self.networks_dir): | ||
55 | + os.mkdir(self.networks_dir) | ||
56 | + | ||
57 | + def command(self): | ||
58 | + """ | ||
59 | + Parse command line arguments and call appropriate method. | ||
60 | + """ | ||
61 | + | ||
62 | + if not self.args or self.args[0] in ['--help', '-h', 'help']: | ||
63 | + print(ScanCommands.__doc__) | ||
64 | + return | ||
65 | + | ||
66 | + cmd = self.args[0] | ||
67 | + | ||
68 | + if cmd == 'create_db': | ||
69 | + self.create_db() | ||
70 | + return | ||
71 | + if cmd == 'drop_db': | ||
72 | + self.drop_db() | ||
73 | + return | ||
74 | + if cmd == 'load_networks': | ||
75 | + self.load_networks() | ||
76 | + return | ||
77 | + if cmd == 'scan_networks': | ||
78 | + self.scan_networks() | ||
79 | + return | ||
80 | + else: | ||
81 | + log.error('Command "%s" not recognized' % (cmd,)) | ||
82 | + | ||
83 | + def create_db(self): | ||
84 | + """ | ||
85 | + Create database | ||
86 | + """ | ||
87 | + Base.metadata.create_all(self.cocar.engine) | ||
88 | + | ||
89 | + def drop_db(self): | ||
90 | + """ | ||
91 | + Drop database | ||
92 | + """ | ||
93 | + Base.metadata.drop_all(self.cocar.engine) | ||
94 | + | ||
95 | + def load_networks(self): | ||
96 | + """ | ||
97 | + Load networks from CSV file | ||
98 | + """ | ||
99 | + networks_csv = NetworkCSV(csv_file=self.networks_csv) | ||
100 | + session = self.cocar.Session | ||
101 | + for elm in networks_csv.parse_csv(): | ||
102 | + results = session.query(Network).filter(Network.ip_network == elm.ip_network).first() | ||
103 | + if results is None: | ||
104 | + log.info("Adicionando a rede: %s", elm.network_ip) | ||
105 | + session.add(elm) | ||
106 | + else: | ||
107 | + log.info("Rede já cadastrada: %s", elm.network_ip) | ||
108 | + session.flush() | ||
109 | + session.close() | ||
110 | + | ||
111 | + def scan_networks(self): | ||
112 | + """ | ||
113 | + Scan all networks | ||
114 | + """ | ||
115 | + processes = int(self.cocar.config.get('cocar', 'processes')) | ||
116 | + # Create queues | ||
117 | + task_queue = Queue() | ||
118 | + done_queue = Queue() | ||
119 | + | ||
120 | + session = self.cocar.Session | ||
121 | + results = session.query(Network).all() | ||
122 | + for network in results: | ||
123 | + network.network_ip = network.ip_network | ||
124 | + if self.options.full is None: | ||
125 | + nmap_session = NmapSession( | ||
126 | + network.network_ip.cidr, | ||
127 | + outfile=self.networks_dir + "/" + str(network.network_ip.cidr).replace("/", "-") + ".xml", | ||
128 | + full=False | ||
129 | + ) | ||
130 | + else: | ||
131 | + nmap_session = NmapSession( | ||
132 | + network.network_ip.cidr, | ||
133 | + outfile=self.networks_dir + "/" + str(network.network_ip.cidr).replace("/", "-") + ".xml", | ||
134 | + full=True | ||
135 | + ) | ||
136 | + task_queue.put(nmap_session) | ||
137 | + | ||
138 | + #Start worker processes | ||
139 | + for i in range(processes): | ||
140 | + Process(target=worker, args=(task_queue, done_queue)).start() | ||
141 | + | ||
142 | + # Get and print results | ||
143 | + print 'Unordered results:' | ||
144 | + for i in range(len(results)): | ||
145 | + print '\t', done_queue.get() | ||
146 | + | ||
147 | + # Tell child processes to stop | ||
148 | + for i in range(processes): | ||
149 | + task_queue.put('STOP') | ||
150 | + | ||
151 | + | ||
152 | +def make_query(host): | ||
153 | + """This does the actual snmp query | ||
154 | + | ||
155 | + This is a bit fancy as it accepts both instances | ||
156 | + of SnmpSession and host/ip addresses. This | ||
157 | + allows a user to customize mass queries with | ||
158 | + subsets of different hostnames and community strings | ||
159 | + """ | ||
160 | + return host.scan() | ||
161 | + | ||
162 | + | ||
163 | +# Function run by worker processes | ||
164 | +def worker(inp, output): | ||
165 | + for func in iter(inp.get, 'STOP'): | ||
166 | + result = make_query(func) | ||
167 | + output.put(result) | ||
0 | \ No newline at end of file | 168 | \ No newline at end of file |
cocar/model/network.py
1 | #!/bin/env python | 1 | #!/bin/env python |
2 | # -*- coding: utf-8 -*- | 2 | # -*- coding: utf-8 -*- |
3 | __author__ = 'eduardo' | 3 | __author__ = 'eduardo' |
4 | +from iptools.ipv4 import netmask2prefix | ||
4 | from netaddr import IPNetwork, IPSet | 5 | from netaddr import IPNetwork, IPSet |
5 | from ..model import Base | 6 | from ..model import Base |
6 | from sqlalchemy.schema import Column | 7 | from sqlalchemy.schema import Column |
@@ -31,7 +32,7 @@ class Network(Base): | @@ -31,7 +32,7 @@ class Network(Base): | ||
31 | :param cidr: CIDR para calcular a máscara da rede | 32 | :param cidr: CIDR para calcular a máscara da rede |
32 | :param name: Nome da rede | 33 | :param name: Nome da rede |
33 | """ | 34 | """ |
34 | - self.network_ip = IPNetwork(network_ip) | 35 | + self.network_ip = network_ip |
35 | self.netmask = netmask | 36 | self.netmask = netmask |
36 | self.prefixlen = prefixlen | 37 | self.prefixlen = prefixlen |
37 | self.name = name | 38 | self.name = name |
@@ -44,6 +45,19 @@ class Network(Base): | @@ -44,6 +45,19 @@ class Network(Base): | ||
44 | # SQLAlchemy attribute | 45 | # SQLAlchemy attribute |
45 | self.ip_network = str(self.network_ip.ip) | 46 | self.ip_network = str(self.network_ip.ip) |
46 | 47 | ||
48 | + @property | ||
49 | + def network_ip(self): | ||
50 | + return self._network_ip | ||
51 | + | ||
52 | + @network_ip.setter | ||
53 | + def network_ip(self, value): | ||
54 | + self._network_ip = IPNetwork(value) | ||
55 | + if self._network_ip.prefixlen == 32: | ||
56 | + if self.netmask is not None: | ||
57 | + prefixlen = netmask2prefix(self.netmask) | ||
58 | + self._network_ip = IPNetwork(str(self._network_ip.ip) + "/" + str(prefixlen)) | ||
59 | + self.prefixlen = prefixlen | ||
60 | + | ||
47 | def ip_list(self): | 61 | def ip_list(self): |
48 | """ | 62 | """ |
49 | Método que encontra a lista de IP's da subrede | 63 | Método que encontra a lista de IP's da subrede |
cocar/query.py
@@ -50,7 +50,7 @@ def main(): | @@ -50,7 +50,7 @@ def main(): | ||
50 | for i in range(NUMBER_OF_PROCESSES): | 50 | for i in range(NUMBER_OF_PROCESSES): |
51 | Process(target=worker, args=(task_queue, done_queue)).start() | 51 | Process(target=worker, args=(task_queue, done_queue)).start() |
52 | 52 | ||
53 | - # Get and print results | 53 | + # Get and print results |
54 | print 'Unordered results:' | 54 | print 'Unordered results:' |
55 | for i in range(len(hosts)): | 55 | for i in range(len(hosts)): |
56 | print '\t', done_queue.get().query | 56 | print '\t', done_queue.get().query |
cocar/session.py
@@ -62,7 +62,7 @@ class SnmpSession(Cocar): | @@ -62,7 +62,7 @@ class SnmpSession(Cocar): | ||
62 | return self.hostrec | 62 | return self.hostrec |
63 | 63 | ||
64 | 64 | ||
65 | -class NmapSession(Cocar): | 65 | +class NmapSession(object): |
66 | """ | 66 | """ |
67 | Realiza busca Nmap num ativo de rede | 67 | Realiza busca Nmap num ativo de rede |
68 | Inspirado em https://github.com/c0r3dump3d/pylanos | 68 | Inspirado em https://github.com/c0r3dump3d/pylanos |
@@ -75,13 +75,12 @@ class NmapSession(Cocar): | @@ -75,13 +75,12 @@ class NmapSession(Cocar): | ||
75 | """ | 75 | """ |
76 | Parâmetros obrigatórios | 76 | Parâmetros obrigatórios |
77 | """ | 77 | """ |
78 | - Cocar.__init__(self) | ||
79 | self.host = host | 78 | self.host = host |
80 | self.full = full | 79 | self.full = full |
81 | if outfile is not None: | 80 | if outfile is not None: |
82 | self.outfile = outfile | 81 | self.outfile = outfile |
83 | else: | 82 | else: |
84 | - self.outfile = self.cocar_data_dir + "/" + str(self.host).replace("/", "-") + ".xml" | 83 | + self.outfile = str(self.host).replace("/", "-") + ".xml" |
85 | 84 | ||
86 | def scan(self): | 85 | def scan(self): |
87 | """ | 86 | """ |
development.ini-dist
1 | [cocar] | 1 | [cocar] |
2 | data_dir = /srv/cocar-agente/cocar_data | 2 | data_dir = /srv/cocar-agente/cocar_data |
3 | +netorks_csv = /srv/cocar-agente/cocar_data/networks.csv | ||
4 | +processes = 4 | ||
3 | 5 | ||
4 | [sqlalchemy] | 6 | [sqlalchemy] |
5 | url = sqlite:////srv/cocar-agente/cocar-data/tests/cocar-test.db | 7 | url = sqlite:////srv/cocar-agente/cocar-data/tests/cocar-test.db |
setup.py
@@ -7,7 +7,9 @@ requires = [ | @@ -7,7 +7,9 @@ requires = [ | ||
7 | 'netaddr', | 7 | 'netaddr', |
8 | 'netifaces', | 8 | 'netifaces', |
9 | 'lxml', | 9 | 'lxml', |
10 | - 'sqlalchemy' | 10 | + 'sqlalchemy', |
11 | + 'PasteScript', | ||
12 | + 'iptools' | ||
11 | ] | 13 | ] |
12 | 14 | ||
13 | 15 | ||
@@ -23,4 +25,9 @@ setup( | @@ -23,4 +25,9 @@ setup( | ||
23 | description='Agente coletor do software Cocar', | 25 | description='Agente coletor do software Cocar', |
24 | test_suite='cocar', | 26 | test_suite='cocar', |
25 | install_requires=requires, | 27 | install_requires=requires, |
28 | + entry_points="""\ | ||
29 | + [paste.paster_command] | ||
30 | + scan = cocar.commands:ScanCommands | ||
31 | + """, | ||
32 | + | ||
26 | ) | 33 | ) |