From 938f0a3390206745d3d99f2ee754246a40bf2d5c Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Tue, 7 Apr 2020 19:45:16 +0200 Subject: [PATCH] update to work on different computer Signed-off-by: Nico Schottelius --- .../uncloud/doc/README-vpn.org | 14 ++++ .../uncloud_net/management/commands/vpn.py | 44 ++++++++++++ .../uncloud_net/migrations/0001_initial.py | 25 ++++--- .../migrations/0002_auto_20200406_2021.py | 70 ------------------- .../uncloud/uncloud_net/models.py | 24 +++++-- .../uncloud/uncloud_net/serializers.py | 48 ++++++++++++- .../uncloud/uncloud_net/views.py | 2 + 7 files changed, 139 insertions(+), 88 deletions(-) create mode 100644 uncloud_django_based/uncloud/uncloud_net/management/commands/vpn.py delete mode 100644 uncloud_django_based/uncloud/uncloud_net/migrations/0002_auto_20200406_2021.py diff --git a/uncloud_django_based/uncloud/doc/README-vpn.org b/uncloud_django_based/uncloud/doc/README-vpn.org index 8f1f368..e7255d8 100644 --- a/uncloud_django_based/uncloud/doc/README-vpn.org +++ b/uncloud_django_based/uncloud/doc/README-vpn.org @@ -9,3 +9,17 @@ ** Route a /40 network to its IPv6 address ** Install wireguard on it ** TODO Enable wireguard on boot +** TODO Create a new VPNPool on uncloud with +*** the network address (selecting from our existing pool) +*** the network size (/...) +*** the vpn host that provides the network (selecting the created VM) +*** the wireguard private key of the vpn host (using wg genkey) +*** http command +``` +http -a nicoschottelius:$(pass + ungleich.ch/nico.schottelius@ungleich.ch) + http://localhost:8000/admin/vpnpool/ network=2a0a:e5c1:200:: \ + network_size=40 subnetwork_size=48 + vpn_hostname=vpn-2a0ae5c1200.ungleich.ch + wireguard_private_key=... +``` diff --git a/uncloud_django_based/uncloud/uncloud_net/management/commands/vpn.py b/uncloud_django_based/uncloud/uncloud_net/management/commands/vpn.py new file mode 100644 index 0000000..c63e5a0 --- /dev/null +++ b/uncloud_django_based/uncloud/uncloud_net/management/commands/vpn.py @@ -0,0 +1,44 @@ +import sys +from datetime import datetime + +from django.core.management.base import BaseCommand + +from django.contrib.auth import get_user_model + +from opennebula.models import VM as VMModel +from uncloud_vm.models import VMHost, VMProduct, VMNetworkCard, VMDiskImageProduct, VMDiskProduct, VMCluster + +import logging +log = logging.getLogger(__name__) + + +wireguard_template=""" + +[Interface] +ListenPort = 51820 +PrivateKey = {privatekey} + +# Nico, 2019-01-23, Switzerland +#[Peer] +#PublicKey = kL1S/Ipq6NkFf1MAsNRou4b9VoUsnnb4ZxgiBrH0zA8= +#AllowedIPs = 2a0a:e5c1:101::/48 +""" + +class Command(BaseCommand): + help = 'General uncloud commands' + + def add_arguments(self, parser): + parser.add_argument('--hostname', action='store_true', help='Name of this VPN Host', + required=True) + + def handle(self, *args, **options): +# for net + if options['bootstrap']: + self.bootstrap() + + self.create_vpn_config(options['hostname']) + + def create_vpn_config(self, hostname): + for pool in VPNPool.objects.filter(vpn_hostname + default_cluster = VPNNetwork.objects.get_or_create(name="default") +# local_host = diff --git a/uncloud_django_based/uncloud/uncloud_net/migrations/0001_initial.py b/uncloud_django_based/uncloud/uncloud_net/migrations/0001_initial.py index b40e0b3..940d63f 100644 --- a/uncloud_django_based/uncloud/uncloud_net/migrations/0001_initial.py +++ b/uncloud_django_based/uncloud/uncloud_net/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.0.5 on 2020-04-03 17:27 +# Generated by Django 3.0.5 on 2020-04-06 21:38 from django.conf import settings import django.contrib.postgres.fields.jsonb @@ -28,22 +28,23 @@ class Migration(migrations.Migration): name='VPNPool', fields=[ ('extra_data', django.contrib.postgres.fields.jsonb.JSONField(blank=True, editable=False, null=True)), - ('network', models.GenericIPAddressField(editable=False, primary_key=True, serialize=False)), + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('network', models.GenericIPAddressField(unique=True)), ('network_size', models.IntegerField(validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(128)])), + ('subnetwork_size', models.IntegerField(validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(128)])), + ('vpn_hostname', models.CharField(max_length=256)), + ('wireguard_private_key', models.CharField(max_length=48)), ], options={ 'abstract': False, }, ), migrations.CreateModel( - name='VPNProduct', + name='VPNNetworkReservation', fields=[ ('extra_data', django.contrib.postgres.fields.jsonb.JSONField(blank=True, editable=False, null=True)), - ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ('status', models.CharField(choices=[('PENDING', 'Pending'), ('AWAITING_PAYMENT', 'Awaiting payment'), ('BEING_CREATED', 'Being created'), ('SCHEDULED', 'Scheduled'), ('ACTIVE', 'Active'), ('MODIFYING', 'Modifying'), ('DELETED', 'Deleted'), ('DISABLED', 'Disabled'), ('UNUSABLE', 'Unusable')], default='PENDING', max_length=32)), - ('network', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='uncloud_net.VPNPool')), - ('order', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.Order')), - ('owner', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('address', models.GenericIPAddressField(primary_key=True, serialize=False)), + ('vpnpool', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='uncloud_net.VPNPool')), ], options={ 'abstract': False, @@ -53,8 +54,12 @@ class Migration(migrations.Migration): name='VPNNetwork', fields=[ ('extra_data', django.contrib.postgres.fields.jsonb.JSONField(blank=True, editable=False, null=True)), - ('network', models.GenericIPAddressField(editable=False, primary_key=True, serialize=False)), - ('vpnpool', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='uncloud_net.VPNPool')), + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('status', models.CharField(choices=[('PENDING', 'Pending'), ('AWAITING_PAYMENT', 'Awaiting payment'), ('BEING_CREATED', 'Being created'), ('SCHEDULED', 'Scheduled'), ('ACTIVE', 'Active'), ('MODIFYING', 'Modifying'), ('DELETED', 'Deleted'), ('DISABLED', 'Disabled'), ('UNUSABLE', 'Unusable')], default='PENDING', max_length=32)), + ('wireguard_public_key', models.CharField(max_length=48)), + ('network', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='uncloud_net.VPNNetworkReservation')), + ('order', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.Order')), + ('owner', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], options={ 'abstract': False, diff --git a/uncloud_django_based/uncloud/uncloud_net/migrations/0002_auto_20200406_2021.py b/uncloud_django_based/uncloud/uncloud_net/migrations/0002_auto_20200406_2021.py deleted file mode 100644 index 82e4c7d..0000000 --- a/uncloud_django_based/uncloud/uncloud_net/migrations/0002_auto_20200406_2021.py +++ /dev/null @@ -1,70 +0,0 @@ -# Generated by Django 3.0.5 on 2020-04-06 20:21 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import uuid - - -class Migration(migrations.Migration): - - dependencies = [ - ('uncloud_pay', '0001_initial'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('uncloud_net', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='vpnnetwork', - name='order', - field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.Order'), - ), - migrations.AddField( - model_name='vpnnetwork', - name='owner', - field=models.ForeignKey(default=0, editable=False, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - preserve_default=False, - ), - migrations.AddField( - model_name='vpnnetwork', - name='status', - field=models.CharField(choices=[('PENDING', 'Pending'), ('AWAITING_PAYMENT', 'Awaiting payment'), ('BEING_CREATED', 'Being created'), ('SCHEDULED', 'Scheduled'), ('ACTIVE', 'Active'), ('MODIFYING', 'Modifying'), ('DELETED', 'Deleted'), ('DISABLED', 'Disabled'), ('UNUSABLE', 'Unusable')], default='PENDING', max_length=32), - ), - migrations.AlterField( - model_name='vpnnetwork', - name='network', - field=models.GenericIPAddressField(editable=False, unique=True), - ), - migrations.AddField( - model_name='vpnnetwork', - name='uuid', - field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False), - ), - migrations.AddField( - model_name='vpnnetwork', - name='wireguard_public_key', - field=models.CharField(default='', max_length=48), - preserve_default=False, - ), - migrations.AddField( - model_name='vpnpool', - name='vpn_hostname', - field=models.CharField(default='', max_length=256), - preserve_default=False, - ), - migrations.AddField( - model_name='vpnpool', - name='wireguard_private_key', - field=models.CharField(default='', max_length=48), - preserve_default=False, - ), - migrations.AlterField( - model_name='vpnpool', - name='network', - field=models.GenericIPAddressField(primary_key=True, serialize=False), - ), - migrations.DeleteModel( - name='VPNProduct', - ), - ] diff --git a/uncloud_django_based/uncloud/uncloud_net/models.py b/uncloud_django_based/uncloud/uncloud_net/models.py index d811902..a3939ee 100644 --- a/uncloud_django_based/uncloud/uncloud_net/models.py +++ b/uncloud_django_based/uncloud/uncloud_net/models.py @@ -1,3 +1,5 @@ +import uuid + from django.db import models from django.contrib.auth import get_user_model from django.core.validators import MinValueValidator, MaxValueValidator @@ -15,24 +17,36 @@ class VPNPool(UncloudModel): Network address pools from which VPNs can be created """ - network = models.GenericIPAddressField(primary_key=True) + uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + network = models.GenericIPAddressField(unique=True) network_size = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(128)]) + subnetwork_size = models.IntegerField(validators=[MinValueValidator(0), + MaxValueValidator(128)]) + vpn_hostname = models.CharField(max_length=256) wireguard_private_key = models.CharField(max_length=48) -class VPNNetwork(Product): +class VPNNetworkReservation(UncloudModel): """ - A selected network. Used for tracking reservations / used networks + This class tracks the used VPN networks. It will be deleted, when the product is cancelled. """ vpnpool = models.ForeignKey(VPNPool, on_delete=models.CASCADE) - network = models.GenericIPAddressField(editable=False, - unique=True) + address = models.GenericIPAddressField(primary_key=True) + + +class VPNNetwork(Product): + """ + A selected network. Used for tracking reservations / used networks + """ + network = models.ForeignKey(VPNNetworkReservation, + on_delete=models.CASCADE, + editable=False) wireguard_public_key = models.CharField(max_length=48) diff --git a/uncloud_django_based/uncloud/uncloud_net/serializers.py b/uncloud_django_based/uncloud/uncloud_net/serializers.py index 7f3ab8e..2c54b4f 100644 --- a/uncloud_django_based/uncloud/uncloud_net/serializers.py +++ b/uncloud_django_based/uncloud/uncloud_net/serializers.py @@ -1,4 +1,7 @@ +import base64 + from django.contrib.auth import get_user_model +from django.utils.translation import gettext_lazy as _ from rest_framework import serializers from .models import * @@ -10,9 +13,48 @@ class VPNPoolSerializer(serializers.ModelSerializer): class VPNNetworkSerializer(serializers.ModelSerializer): - network_size = serializers.IntegerField(min_value=0, - max_value=128) - class Meta: model = VPNNetwork fields = '__all__' + + # This is required for finding the VPN pool, but does not + # exist in the model + network_size = serializers.IntegerField(min_value=0, + max_value=128) + + def validate_wireguard_public_key(self, value): + msg = _("Supplied key is not a valid wireguard public key") + + """ FIXME: verify that this does not create broken wireguard config files, + i.e. contains \n or similar! + We might even need to be more strict to not break wireguard... + """ + print(value) + + try: + base64.standard_b64decode(value) + except Exception as e: + raise serializers.ValidationError(msg) + + if '\n' in value: + raise serializers.ValidationError(msg) + + return value + + def validate(self, data): + + # FIXME: filter for status = active or similar + all_pools = VPNPool.objects.all() + sizes = [ p.subnetwork_size for p in all_pools ] + + pools = VPNPool.objects.filter(subnetwork_size=data['network_size']) + + if len(pools) == 0: + msg = _("No pool available for networks with size = {}. Available are: {}".format(data['network_size'], sizes)) + raise serializers.ValidationError(msg) + + + return data + + def create(self, validated_data): + from_pool = diff --git a/uncloud_django_based/uncloud/uncloud_net/views.py b/uncloud_django_based/uncloud/uncloud_net/views.py index 7afc99d..a3f5284 100644 --- a/uncloud_django_based/uncloud/uncloud_net/views.py +++ b/uncloud_django_based/uncloud/uncloud_net/views.py @@ -1,4 +1,6 @@ + from django.shortcuts import render + from rest_framework import viewsets, permissions