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 | ) |