forked from uncloud/uncloud
phasing in celery
for configuring the vpn server
This commit is contained in:
parent
aec79cba74
commit
2d62388eb1
8 changed files with 160 additions and 80 deletions
|
@ -23,3 +23,6 @@ uritemplate
|
||||||
# Comprehensive interface to validate VAT numbers, making use of the VIES
|
# Comprehensive interface to validate VAT numbers, making use of the VIES
|
||||||
# service for European countries.
|
# service for European countries.
|
||||||
vat-validator
|
vat-validator
|
||||||
|
|
||||||
|
# Tasks
|
||||||
|
celery
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
import decimal
|
import decimal
|
||||||
|
from .celery import app as celery_app
|
||||||
|
|
||||||
# Define DecimalField properties, used to represent amounts of money.
|
# Define DecimalField properties, used to represent amounts of money.
|
||||||
AMOUNT_MAX_DIGITS=10
|
AMOUNT_MAX_DIGITS=10
|
||||||
|
@ -248,3 +249,6 @@ COUNTRIES = (
|
||||||
('ZR', _('Zaire')),
|
('ZR', _('Zaire')),
|
||||||
('ZW', _('Zimbabwe')),
|
('ZW', _('Zimbabwe')),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ('celery_app',)
|
||||||
|
|
22
uncloud/celery.py
Normal file
22
uncloud/celery.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from celery import Celery
|
||||||
|
|
||||||
|
# set the default Django settings module for the 'celery' program.
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'uncloud.settings')
|
||||||
|
|
||||||
|
app = Celery('uncloud')
|
||||||
|
|
||||||
|
# Using a string here means the worker doesn't have to serialize
|
||||||
|
# the configuration object to child processes.
|
||||||
|
# - namespace='CELERY' means all celery-related configuration keys
|
||||||
|
# should have a `CELERY_` prefix.
|
||||||
|
app.config_from_object('django.conf:settings', namespace='CELERY')
|
||||||
|
|
||||||
|
# Load task modules from all registered Django app configs.
|
||||||
|
app.autodiscover_tasks()
|
||||||
|
|
||||||
|
|
||||||
|
@app.task(bind=True)
|
||||||
|
def debug_task(self):
|
||||||
|
print(f'Request: {self.request!r}')
|
19
uncloud_net/migrations/0003_wireguardvpnpool_wg_name.py
Normal file
19
uncloud_net/migrations/0003_wireguardvpnpool_wg_name.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 3.1 on 2020-12-13 17:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('uncloud_net', '0002_wireguardvpnpool_wireguard_public_key'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='wireguardvpnpool',
|
||||||
|
name='wg_name',
|
||||||
|
field=models.CharField(default='wg0', max_length=15),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
17
uncloud_net/migrations/0004_auto_20201213_1734.py
Normal file
17
uncloud_net/migrations/0004_auto_20201213_1734.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 3.1 on 2020-12-13 17:34
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('uncloud_net', '0003_wireguardvpnpool_wg_name'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name='wireguardvpnpool',
|
||||||
|
constraint=models.UniqueConstraint(fields=('wg_name', 'vpn_server_hostname'), name='unique_interface_name_per_host'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -13,6 +13,16 @@ class WireGuardVPNPool(models.Model):
|
||||||
Network address pools from which VPNs can be created
|
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 = models.GenericIPAddressField(unique=True)
|
||||||
network_mask = models.IntegerField(validators=[MinValueValidator(0),
|
network_mask = models.IntegerField(validators=[MinValueValidator(0),
|
||||||
MaxValueValidator(128)])
|
MaxValueValidator(128)])
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django.db import transaction
|
||||||
|
|
||||||
from .models import *
|
from .models import *
|
||||||
from .selectors import *
|
from .selectors import *
|
||||||
|
from .tasks import *
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def create_wireguard_vpn(owner, public_key, network_mask):
|
def create_wireguard_vpn(owner, public_key, network_mask):
|
||||||
|
@ -9,27 +10,7 @@ def create_wireguard_vpn(owner, public_key, network_mask):
|
||||||
pool = get_suitable_pools(network_mask)[0]
|
pool = get_suitable_pools(network_mask)[0]
|
||||||
count = pool.wireguardvpn_set.count()
|
count = pool.wireguardvpn_set.count()
|
||||||
|
|
||||||
# First object
|
# Try re-using previously used networks first
|
||||||
if count == 0:
|
|
||||||
return WireGuardVPN.objects.create(owner=owner,
|
|
||||||
vpnpool=pool,
|
|
||||||
pool_index=0,
|
|
||||||
wireguard_public_key=public_key)
|
|
||||||
|
|
||||||
|
|
||||||
else: # Select last network and try +1 it
|
|
||||||
last_net = WireGuardVPN.objects.filter(vpnpool=pool).order_by('pool_index').last()
|
|
||||||
|
|
||||||
next_index = last_net.pool_index + 1
|
|
||||||
|
|
||||||
if next_index <= pool.max_pool_index:
|
|
||||||
return WireGuardVPN.objects.create(owner=owner,
|
|
||||||
vpnpool=pool,
|
|
||||||
pool_index=next_index,
|
|
||||||
wireguard_public_key=public_key)
|
|
||||||
|
|
||||||
|
|
||||||
# Still there? Then we need to lookup previously used networks
|
|
||||||
try:
|
try:
|
||||||
free_lease = WireGuardVPNFreeLeases.objects.get(vpnpool=pool)
|
free_lease = WireGuardVPNFreeLeases.objects.get(vpnpool=pool)
|
||||||
|
|
||||||
|
@ -40,69 +21,27 @@ def create_wireguard_vpn(owner, public_key, network_mask):
|
||||||
|
|
||||||
free_lease.delete()
|
free_lease.delete()
|
||||||
|
|
||||||
return vpn
|
|
||||||
|
|
||||||
except WireGuardVPNFreeLeases.DoesNotExist:
|
except WireGuardVPNFreeLeases.DoesNotExist:
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
# First object
|
||||||
def wireguard_config_filename(self):
|
if count == 0:
|
||||||
return '/etc/wireguard/{}.conf'.format(self.network)
|
vpn = WireGuardVPN.objects.create(owner=owner,
|
||||||
|
vpnpool=pool,
|
||||||
|
pool_index=0,
|
||||||
|
wireguard_public_key=public_key)
|
||||||
|
|
||||||
@property
|
else: # Select last network and try +1 it
|
||||||
def wireguard_config(self):
|
last_net = WireGuardVPN.objects.filter(vpnpool=pool).order_by('pool_index').last()
|
||||||
wireguard_config = [
|
|
||||||
"""
|
|
||||||
[Interface]
|
|
||||||
ListenPort = 51820
|
|
||||||
PrivateKey = {privatekey}
|
|
||||||
""".format(privatekey=self.wireguard_private_key) ]
|
|
||||||
|
|
||||||
peers = []
|
next_index = last_net.pool_index + 1
|
||||||
|
|
||||||
for reservation in self.vpnnetworkreservation_set.filter(status='used'):
|
if next_index <= pool.max_pool_index:
|
||||||
public_key = reservation.vpnnetwork_set.first().wireguard_public_key
|
vpn = WireGuardVPN.objects.create(owner=owner,
|
||||||
peer_network = "{}/{}".format(reservation.address, self.subnetwork_size)
|
vpnpool=pool,
|
||||||
owner = reservation.vpnnetwork_set.first().owner
|
pool_index=next_index,
|
||||||
|
wireguard_public_key=public_key)
|
||||||
peers.append("""
|
|
||||||
# Owner: {owner}
|
|
||||||
[Peer]
|
|
||||||
PublicKey = {public_key}
|
|
||||||
AllowedIPs = {peer_network}
|
|
||||||
""".format(
|
|
||||||
owner=owner,
|
|
||||||
public_key=public_key,
|
|
||||||
peer_network=peer_network))
|
|
||||||
|
|
||||||
wireguard_config.extend(peers)
|
|
||||||
|
|
||||||
return "\n".join(wireguard_config)
|
|
||||||
|
|
||||||
|
|
||||||
def configure_wireguard_vpnserver(self):
|
|
||||||
"""
|
|
||||||
This method is designed to run as a celery task and should
|
|
||||||
not be called directly from the web
|
|
||||||
"""
|
|
||||||
|
|
||||||
# subprocess, ssh
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def num_maximum_networks(self):
|
configure_wireguard_server(pool)
|
||||||
"""
|
return vpn
|
||||||
sample:
|
|
||||||
network_size = 40
|
|
||||||
subnetwork_size = 48
|
|
||||||
maximum_networks = 2^(48-40)
|
|
||||||
|
|
||||||
2nd sample:
|
|
||||||
network_size = 8
|
|
||||||
subnetwork_size = 24
|
|
||||||
maximum_networks = 2^(24-8)
|
|
||||||
"""
|
|
||||||
|
|
||||||
return 2**(self.subnetwork_mask - self.network_mask)
|
|
||||||
|
|
66
uncloud_net/tasks.py
Normal file
66
uncloud_net/tasks.py
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
from celery import shared_task
|
||||||
|
from .models import *
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def configure_wireguard_server(vpnpool):
|
||||||
|
print(f"Configuring {vpnpool.vpn_server_hostname}")
|
||||||
|
|
||||||
|
wireguard_config_filename = '/etc/wireguard/{}.conf'.format(vpnpool.network)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def wireguard_config(self):
|
||||||
|
wireguard_config = [
|
||||||
|
"""
|
||||||
|
[Interface]
|
||||||
|
ListenPort = 51820
|
||||||
|
PrivateKey = {privatekey}
|
||||||
|
""".format(privatekey=self.wireguard_private_key) ]
|
||||||
|
|
||||||
|
peers = []
|
||||||
|
|
||||||
|
for reservation in self.vpnnetworkreservation_set.filter(status='used'):
|
||||||
|
public_key = reservation.vpnnetwork_set.first().wireguard_public_key
|
||||||
|
peer_network = "{}/{}".format(reservation.address, self.subnetwork_size)
|
||||||
|
owner = reservation.vpnnetwork_set.first().owner
|
||||||
|
|
||||||
|
peers.append("""
|
||||||
|
# Owner: {owner}
|
||||||
|
[Peer]
|
||||||
|
PublicKey = {public_key}
|
||||||
|
AllowedIPs = {peer_network}
|
||||||
|
""".format(
|
||||||
|
owner=owner,
|
||||||
|
public_key=public_key,
|
||||||
|
peer_network=peer_network))
|
||||||
|
|
||||||
|
wireguard_config.extend(peers)
|
||||||
|
|
||||||
|
return "\n".join(wireguard_config)
|
||||||
|
|
||||||
|
|
||||||
|
def configure_wireguard_vpnserver(self):
|
||||||
|
"""
|
||||||
|
This method is designed to run as a celery task and should
|
||||||
|
not be called directly from the web
|
||||||
|
"""
|
||||||
|
|
||||||
|
# subprocess, ssh
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def num_maximum_networks(self):
|
||||||
|
"""
|
||||||
|
sample:
|
||||||
|
network_size = 40
|
||||||
|
subnetwork_size = 48
|
||||||
|
maximum_networks = 2^(48-40)
|
||||||
|
|
||||||
|
2nd sample:
|
||||||
|
network_size = 8
|
||||||
|
subnetwork_size = 24
|
||||||
|
maximum_networks = 2^(24-8)
|
||||||
|
"""
|
||||||
|
|
||||||
|
return 2**(self.subnetwork_mask - self.network_mask)
|
Loading…
Reference in a new issue