uncloud-mravi/uncloud_net/models.py
2020-12-28 23:35:34 +01:00

208 lines
5.7 KiB
Python

import uuid
import ipaddress
from django.db import models
from django.contrib.auth import get_user_model
from django.core.validators import MinValueValidator, MaxValueValidator
from django.core.exceptions import FieldError, ValidationError
from uncloud_pay.models import Order, Product
class WireGuardVPNPool(models.Model):
"""
Network address pools from which VPNs can be created
"""
class Meta:
constraints = [
models.UniqueConstraint(fields=['wg_name', 'vpn_server_hostname' ],
name='unique_interface_name_per_host')
]
# Linux interface naming is restricing to max 15 characters
wg_name = models.CharField(max_length=15)
network = models.GenericIPAddressField(unique=True)
network_mask = models.IntegerField(validators=[MinValueValidator(0),
MaxValueValidator(128)])
subnetwork_mask = models.IntegerField(validators=[
MinValueValidator(0),
MaxValueValidator(128)
])
vpn_server_hostname = models.CharField(max_length=256)
wireguard_private_key = models.CharField(max_length=48)
wireguard_public_key = models.CharField(max_length=48)
@property
def max_pool_index(self):
"""
Return the highest possible network / last network id
"""
bits = self.subnetwork_mask - self.network_mask
return (2**bits)-1
@property
def ip_network(self):
"""
Return the IP network based on our address and mask
"""
return ipaddress.ip_network(f"{self.network}/{self.network_mask}")
def __str__(self):
return f"{self.ip_network} (subnets: /{self.subnetwork_mask})"
@property
def wireguard_config(self):
wireguard_config = [ f"[Interface]\nListenPort = 51820\nPrivateKey = {self.wireguard_private_key}\n" ]
peers = []
for vpn in self.wireguardvpn_set.all():
public_key = vpn.wireguard_public_key
peer_network = f"{vpn.address}/{self.subnetwork_mask}"
owner = vpn.owner
peers.append(f"# Owner: {owner}\n[Peer]\nPublicKey = {public_key}\nAllowedIPs = {peer_network}\n\n")
wireguard_config.extend(peers)
return "\n".join(wireguard_config)
class WireGuardVPN(models.Model):
"""
Created VPNNetworks
"""
owner = models.ForeignKey(get_user_model(),
on_delete=models.CASCADE)
vpnpool = models.ForeignKey(WireGuardVPNPool,
on_delete=models.CASCADE)
pool_index = models.IntegerField(unique=True)
wireguard_public_key = models.CharField(max_length=48, unique=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=['vpnpool', 'wireguard_public_key'],
name='wg_key_unique_per_pool')
]
@property
def network_mask(self):
return self.vpnpool.subnetwork_mask
@property
def vpn_server(self):
return self.vpnpool.vpn_server_hostname
@property
def vpn_server_public_key(self):
return self.vpnpool.wireguard_public_key
@property
def address(self):
"""
Locate the correct subnet in the supernet
First get the network itself
"""
net = self.vpnpool.ip_network
subnet = net[(2**(128-self.vpnpool.subnetwork_mask)) * self.pool_index]
return str(subnet)
def __str__(self):
return f"{self.address} ({self.pool_index})"
def create_product(self):
"""
Ensure we have a product for the WireguardVPN
"""
pass
# Product.objects.get_or_create(
# name="WireGuardVPN",
# description="Wireguard VPN",
# currency=Currency.CHF,
# config=
class WireGuardVPNFreeLeases(models.Model):
"""
Previously used VPNNetworks
"""
vpnpool = models.ForeignKey(WireGuardVPNPool,
on_delete=models.CASCADE)
pool_index = models.IntegerField(unique=True)
################################################################################
class MACAdress(models.Model):
default_prefix = 0x420000000000
class ReverseDNSEntry(models.Model):
"""
A reverse DNS entry
"""
owner = models.ForeignKey(get_user_model(),
on_delete=models.CASCADE)
ip_address = models.GenericIPAddressField(null=False, unique=True)
name = models.CharField(max_length=253, null=False)
@property
def reverse_pointer(self):
return ipaddress.ip_address(self.ip_address).reverse_pointer
def implement(self):
"""
The implement function implements the change
"""
# Get all DNS entries (?) / update this DNS entry
# convert to DNS name
#
pass
def save(self, *args, **kwargs):
# Product.objects.filter(config__parameters__contains='reverse_dns_network')
# FIXME: check if order is still active / not replaced
allowed = False
product = None
for order in Order.objects.filter(config__parameters__reverse_dns_network__isnull=False,
owner=self.owner):
network = order.config['parameters']['reverse_dns_network']
net = ipaddress.ip_network(network)
addr = ipaddress.ip_address(self.ip_address)
if addr in net:
allowed = True
product = order.product
break
if not allowed:
raise ValidationError(f"User {self.owner} does not have the right to create reverse DNS entry for {self.ip_address}")
super().save(*args, **kwargs)
def __str__(self):
return f"{self.ip_address} - {self.name}"