Merge branch 'master' of code.ungleich.ch:uncloud/uncloud
This commit is contained in:
commit
1838eaf7dd
7 changed files with 139 additions and 88 deletions
|
@ -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=...
|
||||
```
|
||||
|
|
|
@ -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 =
|
|
@ -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,
|
||||
|
|
|
@ -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',
|
||||
),
|
||||
]
|
|
@ -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)
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
from django.shortcuts import render
|
||||
|
||||
from rest_framework import viewsets, permissions
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue