2020-04-07 17:45:16 +00:00
|
|
|
import uuid
|
2020-04-11 19:37:36 +00:00
|
|
|
import ipaddress
|
2020-04-07 17:45:16 +00:00
|
|
|
|
2020-03-02 06:17:04 +00:00
|
|
|
from django.db import models
|
2020-04-03 17:27:49 +00:00
|
|
|
from django.contrib.auth import get_user_model
|
|
|
|
from django.core.validators import MinValueValidator, MaxValueValidator
|
2020-10-25 21:43:34 +00:00
|
|
|
from django.core.exceptions import FieldError, ValidationError
|
2020-04-03 17:27:49 +00:00
|
|
|
|
2020-12-28 22:35:34 +00:00
|
|
|
from uncloud_pay.models import Order, Product
|
2020-04-06 20:30:01 +00:00
|
|
|
|
2020-12-13 10:38:41 +00:00
|
|
|
class WireGuardVPNPool(models.Model):
|
2020-04-03 17:27:49 +00:00
|
|
|
"""
|
|
|
|
Network address pools from which VPNs can be created
|
|
|
|
"""
|
|
|
|
|
2020-12-13 17:34:43 +00:00
|
|
|
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)
|
|
|
|
|
2020-04-07 17:45:16 +00:00
|
|
|
network = models.GenericIPAddressField(unique=True)
|
2020-12-13 10:38:41 +00:00
|
|
|
network_mask = models.IntegerField(validators=[MinValueValidator(0),
|
2020-04-03 17:27:49 +00:00
|
|
|
MaxValueValidator(128)])
|
|
|
|
|
2020-12-13 10:38:41 +00:00
|
|
|
subnetwork_mask = models.IntegerField(validators=[
|
|
|
|
MinValueValidator(0),
|
|
|
|
MaxValueValidator(128)
|
|
|
|
])
|
2020-04-06 20:30:01 +00:00
|
|
|
|
2020-12-13 10:38:41 +00:00
|
|
|
vpn_server_hostname = models.CharField(max_length=256)
|
2020-04-06 20:30:01 +00:00
|
|
|
wireguard_private_key = models.CharField(max_length=48)
|
2020-12-13 17:05:48 +00:00
|
|
|
wireguard_public_key = models.CharField(max_length=48)
|
2020-04-06 20:30:01 +00:00
|
|
|
|
2020-12-13 16:59:35 +00:00
|
|
|
@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):
|
2020-12-26 13:42:53 +00:00
|
|
|
"""
|
|
|
|
Return the IP network based on our address and mask
|
|
|
|
"""
|
2020-12-13 16:59:35 +00:00
|
|
|
return ipaddress.ip_network(f"{self.network}/{self.network_mask}")
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return f"{self.ip_network} (subnets: /{self.subnetwork_mask})"
|
|
|
|
|
2020-12-20 11:20:54 +00:00
|
|
|
@property
|
|
|
|
def wireguard_config(self):
|
2020-12-20 11:45:36 +00:00
|
|
|
wireguard_config = [ f"[Interface]\nListenPort = 51820\nPrivateKey = {self.wireguard_private_key}\n" ]
|
2020-12-20 11:20:54 +00:00
|
|
|
|
|
|
|
peers = []
|
|
|
|
|
|
|
|
for vpn in self.wireguardvpn_set.all():
|
|
|
|
public_key = vpn.wireguard_public_key
|
2020-12-20 11:45:36 +00:00
|
|
|
peer_network = f"{vpn.address}/{self.subnetwork_mask}"
|
2020-12-20 11:20:54 +00:00
|
|
|
owner = vpn.owner
|
|
|
|
|
2020-12-20 11:45:36 +00:00
|
|
|
peers.append(f"# Owner: {owner}\n[Peer]\nPublicKey = {public_key}\nAllowedIPs = {peer_network}\n\n")
|
2020-12-20 11:20:54 +00:00
|
|
|
|
|
|
|
wireguard_config.extend(peers)
|
|
|
|
|
|
|
|
return "\n".join(wireguard_config)
|
|
|
|
|
2020-12-13 16:59:35 +00:00
|
|
|
|
2020-12-13 10:38:41 +00:00
|
|
|
class WireGuardVPN(models.Model):
|
|
|
|
"""
|
|
|
|
Created VPNNetworks
|
2020-04-03 17:27:49 +00:00
|
|
|
"""
|
2020-12-13 12:28:43 +00:00
|
|
|
owner = models.ForeignKey(get_user_model(),
|
|
|
|
on_delete=models.CASCADE)
|
2020-12-13 10:38:41 +00:00
|
|
|
vpnpool = models.ForeignKey(WireGuardVPNPool,
|
2020-04-08 14:24:39 +00:00
|
|
|
on_delete=models.CASCADE)
|
[vpn] make a vpn creat-able!
[15:40] line:~% http -a nicoschottelius:$(pass ungleich.ch/nico.schottelius@ungleich.ch) http://localhost:8000/net/vpn/ network_size=48 wireguard_public_key=$(wg genkey | wg pubkey)
HTTP/1.1 201 Created
Allow: GET, POST, HEAD, OPTIONS
Content-Length: 206
Content-Type: application/json
Date: Sun, 12 Apr 2020 13:40:26 GMT
Server: WSGIServer/0.2 CPython/3.7.3
Vary: Accept
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
{
"extra_data": null,
"network": "2a0a:e5c1:203::",
"order": null,
"owner": 30,
"status": "PENDING",
"uuid": "8f977a8f-e06a-4346-94ae-8f525df58b7b",
"wireguard_public_key": "JvCuUTZHm9unasJkGsLKN0Bf/hu6ZSIv7dnIGPyJ6xA="
}
2020-04-12 13:40:39 +00:00
|
|
|
|
2020-12-13 16:59:35 +00:00
|
|
|
pool_index = models.IntegerField(unique=True)
|
2020-04-07 17:45:16 +00:00
|
|
|
|
2020-12-20 18:37:12 +00:00
|
|
|
wireguard_public_key = models.CharField(max_length=48, unique=True)
|
2020-04-12 20:55:22 +00:00
|
|
|
|
2020-12-24 16:26:53 +00:00
|
|
|
class Meta:
|
|
|
|
constraints = [
|
|
|
|
models.UniqueConstraint(fields=['vpnpool', 'wireguard_public_key'],
|
|
|
|
name='wg_key_unique_per_pool')
|
|
|
|
]
|
|
|
|
|
|
|
|
|
2020-12-13 16:59:35 +00:00
|
|
|
@property
|
|
|
|
def network_mask(self):
|
|
|
|
return self.vpnpool.subnetwork_mask
|
|
|
|
|
2020-12-13 17:05:48 +00:00
|
|
|
@property
|
|
|
|
def vpn_server(self):
|
|
|
|
return self.vpnpool.vpn_server_hostname
|
|
|
|
|
|
|
|
@property
|
|
|
|
def vpn_server_public_key(self):
|
|
|
|
return self.vpnpool.wireguard_public_key
|
|
|
|
|
2020-12-13 16:59:35 +00:00
|
|
|
@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})"
|
|
|
|
|
2020-12-28 22:35:34 +00:00
|
|
|
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=
|
|
|
|
|
2020-12-13 17:05:48 +00:00
|
|
|
|
2020-12-13 16:59:35 +00:00
|
|
|
class WireGuardVPNFreeLeases(models.Model):
|
|
|
|
"""
|
|
|
|
Previously used VPNNetworks
|
|
|
|
"""
|
|
|
|
vpnpool = models.ForeignKey(WireGuardVPNPool,
|
|
|
|
on_delete=models.CASCADE)
|
|
|
|
|
|
|
|
pool_index = models.IntegerField(unique=True)
|
2020-05-07 18:22:42 +00:00
|
|
|
|
2020-12-13 10:38:41 +00:00
|
|
|
################################################################################
|
2020-10-25 20:00:30 +00:00
|
|
|
|
2020-12-13 10:38:41 +00:00
|
|
|
class MACAdress(models.Model):
|
|
|
|
default_prefix = 0x420000000000
|
2020-10-25 20:00:30 +00:00
|
|
|
|
2020-12-09 19:22:33 +00:00
|
|
|
|
2020-10-25 20:00:30 +00:00
|
|
|
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)
|
2020-10-25 21:43:34 +00:00
|
|
|
|
2020-11-15 14:43:11 +00:00
|
|
|
@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
|
|
|
|
|
|
|
|
|
2020-10-25 21:43:34 +00:00
|
|
|
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
|
2020-11-15 14:43:11 +00:00
|
|
|
product = None
|
2020-10-25 21:43:34 +00:00
|
|
|
|
|
|
|
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
|
2020-11-15 14:43:11 +00:00
|
|
|
product = order.product
|
2020-10-25 21:43:34 +00:00
|
|
|
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}"
|