Remplissage de la table NAT.

This commit is contained in:
Hugo LEVY-FALK 2019-01-16 22:57:50 +01:00
parent f5c9eeda5a
commit f160b51e33
4 changed files with 375 additions and 13 deletions

144
.gitignore vendored Normal file
View file

@ -0,0 +1,144 @@
Pipfile*
# Created by https://www.gitignore.io/api/vim,python
# Edit at https://www.gitignore.io/?templates=vim,python
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
### Python Patch ###
.venv/
### Vim ###
# Swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~
# End of https://www.gitignore.io/api/vim,python

View file

@ -73,10 +73,4 @@ table inet firewall {
} }
table nat { table nat {
# TODO : on met 27 Ips internes par ip publiques
# Users : 193.48.225.10-119
# Prerezotage 193.48.225.240-254
# Aloes : 193.48.225.120-129
# Federez : 193.48.225.130-199
# Server : 193.48.225.200-209
} }

View file

@ -89,12 +89,35 @@ class Parser:
return netaddr.EUI(mac, dialect=netaddr.mac_unix_expanded) return netaddr.EUI(mac, dialect=netaddr.mac_unix_expanded)
@staticmethod @staticmethod
def IPv4(ip): def IPv4(ip):
"""Check an IPv4 validity.""" """Check an IPv4 validity.
Args:
ip: can either be a tuple (in this case returns an IPRange), a
single IP address or a IP Network.
"""
if isinstance(ip, tuple):
begin, end = ip
return netaddr.IPRange(begin, end, version=4)
try:
return netaddr.IPAddress(ip, version=4) return netaddr.IPAddress(ip, version=4)
except ValueError:
return netaddr.IPNetwork(ip, version=4)
@staticmethod @staticmethod
def IPv6(ip): def IPv6(ip):
"""Check a IPv6 validity.""" """Check a IPv6 validity.
Args:
ip: can either be a tuple (in this case returns an IPRange), a
single IP address or a IP Network.
"""
if isinstance(ip, tuple):
begin, end = ip
return netaddr.IPRange(begin, end, version=6)
try:
return netaddr.IPAddress(ip, version=6) return netaddr.IPAddress(ip, version=6)
except ValueError:
return netaddr.IPNetwork(ip, version=6)
@staticmethod @staticmethod
def protocol(protocol): def protocol(protocol):
"""Check a protocol validity.""" """Check a protocol validity."""
@ -120,13 +143,17 @@ class NetfilterSet:
ADDRESS_FAMILIES = {'ip', 'ip6', 'inet', 'arp', 'bridge', 'netdev'} ADDRESS_FAMILIES = {'ip', 'ip6', 'inet', 'arp', 'bridge', 'netdev'}
FLAGS = {'constant', 'interval', 'timeout'}
def __init__(self, def __init__(self,
name, name,
type_, # e.g.: ('MAC', 'IPv4') type_, # e.g.: ('MAC', 'IPv4')
target_content=None, target_content=None,
use_sudo=True, use_sudo=True,
address_family='inet', # Manage both IPv4 and IPv6. address_family='inet', # Manage both IPv4 and IPv6.
table_name='filter'): table_name='filter',
flags = []
):
self.name = name self.name = name
self.content = set() self.content = set()
# self.type # self.type
@ -135,6 +162,7 @@ class NetfilterSet:
# self.address_family # self.address_family
self.set_address_family(address_family) self.set_address_family(address_family)
self.table = table_name self.table = table_name
self.set_flags(flags)
sudo = ["/usr/bin/sudo"] * int(bool(use_sudo)) sudo = ["/usr/bin/sudo"] * int(bool(use_sudo))
self.nft = [*sudo, "/usr/sbin/nft"] self.nft = [*sudo, "/usr/sbin/nft"]
if target_content: if target_content:
@ -167,6 +195,13 @@ class NetfilterSet:
'Invalid address_family: "{}".'.format(address_family)) 'Invalid address_family: "{}".'.format(address_family))
self.address_family = address_family self.address_family = address_family
def set_flags(self, flags_):
"""Check set flags validity before saving them."""
for f in flags_:
if f not in self.FLAGS:
raise ValueError('Invalid flag: "{}".'.format(f))
self.flags = _flags
def create_in_kernel(self): def create_in_kernel(self):
"""Create the set, removing existing set if needed.""" """Create the set, removing existing set if needed."""
# Delete set if it exists with wrong type # Delete set if it exists with wrong type
@ -191,10 +226,11 @@ class NetfilterSet:
"""Create the non-existing set, creating table if needed.""" """Create the non-existing set, creating table if needed."""
create_set = [ create_set = [
*self.nft, *self.nft,
'add set {addr_family} {table} {set_} {{ type {type_} ; }}'.format( 'add set {addr_family} {table} {set_} {{ type {type_} ; flags {flags}}}'.format(
addr_family=self.address_family, table=self.table, addr_family=self.address_family, table=self.table,
set_=self.name, set_=self.name,
type_=' . '.join(self.TYPES[i] for i in self.type)) type_=' . '.join(self.TYPES[i] for i in self.type)),
flags=', '.join(self.flags)
] ]
return_code = CommandExec.run(create_set, allowed_return_codes=(0, 1)) return_code = CommandExec.run(create_set, allowed_return_codes=(0, 1))
if return_code == 0: if return_code == 0:

188
nat.py Normal file
View file

@ -0,0 +1,188 @@
#! /usr/bin/python3
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Copyright © 2019 Hugo Levy-Falk <me@klafyvel.me>
"""
Creates the nat set.
"""
import logging
from configparser import ConfigParser
import netaddr
from firewall import NetfilterSet
CONFIG = ConfigParser()
CONFIG.read('config.ini')
def create_nat(name, range_in, range_out, first_port, last_port):
"""Create two nftables tables for the nat:
- <name>_address : which link a (or a range of) local address to a
public address;
- <name>_port : which links a local address to a
range of ports.
Args:
name: name of the sets
range_in: an IPRange with the private IP address
range_out: an IPRange with the public IP address
first_port: the first port used for the nat
last_port: the last port used for the nat
Returns:
(<name>_address, <name>_port) which are NetfilterSet
"""
assert last_port >= first_port, (name + ": Your first_port "
"is lower than your last_port")
nb_private_by_public = range_in.size / range_out.size
nb_port_by_ip = (last_port - first_port + 1) / nb_private_by_public
ports = []
ips = []
port = first_port
for ip, port in range_in:
ports.append((
str(netaddr.IPAddress(ip)),
"%d-%d" % (port, port+nb_port_by_ip)
))
port += nb_port_by_ip + 1
if port >= last_port:
port = first_port
ip = range_in.first
for ip_out in range_out:
ips.append((
'-'.join([
str(netaddr.IPAddress(ip)),
str(netaddr.IPAddress(ip+nb_private_by_public))
]),
str(ip_out)
))
ip += nb_private_by_public + 1
return (
NetfilterSet(
target_content=ips,
type_=('IPv4', 'IPv4'),
name=name,
table_name='nat',
),
NetfilterSet(
target_content=ports,
type_=('IPv4', 'port'),
name=name,
table_name='nat',
),
)
def create_nat_adherent():
range_in = netaddr.IPRange(CONFIG['range_in_adherent'])
range_out = netaddr.IPRange(CONFIG['range_out_adherent'])
first_port = CONFIG['first_port_adherent']
last_port = CONFIG['last_port_adherent']
return create_nat(
'adherent',
range_in,
range_out,
first_port,
last_port
)
def create_nat_federez():
range_in = netaddr.IPRange(CONFIG['range_in_federez'])
range_out = netaddr.IPRange(CONFIG['range_out_federez'])
first_port = CONFIG['first_port_federez']
last_port = CONFIG['last_port_federez']
return create_nat(
'federez',
range_in,
range_out,
first_port,
last_port
)
def create_nat_aloes():
range_in = netaddr.IPRange(CONFIG['range_in_aloes'])
range_out = netaddr.IPRange(CONFIG['range_out_aloes'])
first_port = CONFIG['first_port_aloes']
last_port = CONFIG['last_port_aloes']
return create_nat(
'aloes',
range_in,
range_out,
first_port,
last_port
)
def create_nat_admin():
range_in = netaddr.IPRange(CONFIG['range_in_admin'])
range_out = netaddr.IPRange(CONFIG['range_out_admin'])
first_port = CONFIG['first_port_admin']
last_port = CONFIG['last_port_admin']
return create_nat(
'admin',
range_in,
range_out,
first_port,
last_port
)
def create_nat_prerezotage():
range_in = netaddr.IPRange(CONFIG['range_in_prerezotage'])
range_out = netaddr.IPRange(CONFIG['range_out_prerezotage'])
first_port = CONFIG['first_port_prerezotage']
last_port = CONFIG['last_port_prerezotage']
return create_nat(
'prerezotage',
range_in,
range_out,
first_port,
last_port
)
def main():
logging.info("Creating adherent nat...")
address, port = create_nat_adherent()
address.manage()
port.manage()
logging.info("Done.")
logging.info("Creating federez nat...")
address, port = create_nat_federez()
address.manage()
port.manage()
logging.info("Done.")
logging.info("Creating aloes nat...")
address, port = create_nat_aloes()
address.manage()
port.manage()
logging.info("Done.")
logging.info("Creating admin nat...")
address, port = create_nat_admin()
address.manage()
port.manage()
logging.info("Done.")
logging.info("Creating prerezotage nat...")
address, port = create_nat_prerezotage()
address.manage()
port.manage()
logging.info("Done.")