Kategória: Python

Zmenené: 3. jún 2014

Konverzia rozsahu IP na CIDR

Otvoriť port SSH vo verejnom internete je cesta k prakticky okamžitým pokusom o prihlásenie/prelomenie hesla. Časom som zistil, že väčšina pokusov prichádza z rovnakej siete (sietí) jedného poskytovateľa, pekne postupne stroj za strojom. A tak chcem blokovať celý rozsah IP poskytovateľa…

Nechcem tu dumať nad tým, čo to za siete sú, dokonca ani nad tým z akých krajín pochádzajú. Každopádne, keď sa mi zdá útokov z podobnej siete príliš veľa, začnem to skúmať. Prvé skúmanie, ktoré robím je pomocou whois:

whois 61.174.50.235 | grep inetnum
inetnum:        61.174.48.0 - 61.174.55.255

Áno, nezaujíma ma odkiaľ adresa IP pochádza (v tomto prípade z Číny), ale zaujíma ma rozsah adries IP. Poznať tento rozsah je síce zaujímavé, ale veľmi málo použiteľné, pretože väčšina nástrojov nedokáže s takto daným rozsahom pracovať, a preto je potrebné previesť ho do formátu CIDR.

Donedávna som tento prevod robil pomocou nástroja ipcalc, v ktorom som metódou pokus – omyl postupne menil zadávaný rozsah adries IP, až kým som nedostal želaný výsledok. Ako si iste dokážete predstaviť, miestami to bolo dosť zdĺhavé. Nedávno som sa nasrdil a rozhodol som sa pozrieť, či by to nešlo pohodlnejšie.

Samozrejme, ako prvé riešenie ma napadol jazyk Python, a tak som začal hľadať nejakú vhodnú knižnicu, aby som nemusel vymýšľať už vymyslené. Po niekoľkých neúspešných pokusoch som konečne našiel tú pravú, s názvom netaddr, pomocou ktorej som vytvoril jednoduchý skript:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  cidr.py
#
#  Copyright 2014 Slavko <linux@slavino.sk>
#  License GPL3+

from netaddr import *
from netaddr.core import AddrFormatError
import sys

STARTIP="Start IP address: "
ENDIP="End IP address: "

# Use arguments or ask for them
if len(sys.argv) == 3:
    ips = sys.argv[1:]
    print "%s\t%s" % (STARTIP, sys.argv[1])
    print "%s\t%s" % (ENDIP, sys.argv[2])
else:
    ips = []
    try:
        ips.append(raw_input(STARTIP))
        ips.append(raw_input(ENDIP))
    except EOFError:
        print "Canceled by user..."
        exit(0)

try:
    all_ips = list(iter_iprange(ips[0], ips[1]))
except (AddrFormatError, ValueError), chyba:
    print chyba
    exit(1)

ip_address = cidr_merge(all_ips)

# print output
cidr = []
for ip in ip_address:
    cidr.append(str(ip.cidr))

print "\nCIDR:"
if len(cidr) == 1:
    print "\t%s" % cidr[0]
else:
    for net in cidr:
        print "\t%s" % net

Skript pracuje jednoducho – môžete v príkazovom riadku zadať dve adresy IP (počiatočnú a koncovú), a skript zistí blok adries v zápise CIDR:

./cidr.py 192.168.0.0 192.168.0.255
Start IP address:   192.168.0.0
End IP address:     192.168.0.255

CIDR:
    192.168.0.0/24

Ak je zadaný rozsah adries, ktorý nemožno zahrnúť do jedného bloku, skript vypíše všetky bloky CIDR, ktoré sú potrebné na pokrytie rozsahu:

./cidr.py 192.168.0.0 192.168.0.250
Start IP address:   192.168.0.0
End IP address:     192.168.0.250

CIDR:
    192.168.0.0/25
    192.168.0.128/26
    192.168.0.192/27
    192.168.0.224/28
    192.168.0.240/29
    192.168.0.248/31
    192.168.0.250/32

Poznámka

V prípade, že nezadáte dosť argumentov (alebo i veľa), skript si v dialógu adresy IP vyžiada.

Pretože využívam na zistenie informácií o adrese(ách) IP nástroj whois, možno do skriptu priamo poslať jeho výpis malou konverziou:

./cidr.py $(whois 61.174.50.235 | grep inetnum | awk '{print $2 " " $4}')
Start IP address:   61.174.48.0
End IP address:     61.174.55.255

CIDR:
    61.174.48.0/21

takto mám ľahko a rýchlo IP adresu siete, z ktorej pochádzajú útoky a túto (pod)sieť môžem jednoducho pridať, či už do množiny ipset alebo do súboru .htaccess, a tak ľahko blokovať nespratníkov.