in between commit

Signed-off-by: Nico Schottelius <nico@nico-notebook.schottelius.org>
This commit is contained in:
Nico Schottelius 2020-04-08 16:24:39 +02:00
parent 3d2f8574d3
commit d3f2a3e071
4 changed files with 125 additions and 21 deletions

View file

@ -11,3 +11,6 @@ parsedatetime
pyparsing pyparsing
pydot pydot
django-extensions django-extensions
# PDF creating
django-hardcopy

View file

@ -13,32 +13,52 @@ log = logging.getLogger(__name__)
wireguard_template=""" wireguard_template="""
[Interface] [Interface]
ListenPort = 51820 ListenPort = 51820
PrivateKey = {privatekey} PrivateKey = {privatekey}
"""
# Nico, 2019-01-23, Switzerland peer_template="""
#[Peer] # {username}
#PublicKey = kL1S/Ipq6NkFf1MAsNRou4b9VoUsnnb4ZxgiBrH0zA8= [Peer]
#AllowedIPs = 2a0a:e5c1:101::/48 PublicKey = {public_key}
AllowedIPs = {vpnnetwork}
""" """
class Command(BaseCommand): class Command(BaseCommand):
help = 'General uncloud commands' help = 'General uncloud commands'
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument('--hostname', action='store_true', help='Name of this VPN Host', parser.add_argument('--hostname',
action='store_true',
help='Name of this VPN Host',
required=True) required=True)
def handle(self, *args, **options): def handle(self, *args, **options):
# for net
if options['bootstrap']: if options['bootstrap']:
self.bootstrap() self.bootstrap()
self.create_vpn_config(options['hostname']) self.create_vpn_config(options['hostname'])
def create_vpn_config(self, hostname): def create_vpn_config(self, hostname):
for pool in VPNPool.objects.filter(vpn_hostname configs = []
default_cluster = VPNNetwork.objects.get_or_create(name="default")
# local_host = for pool in VPNPool.objects.filter(vpn_hostname=hostname):
pool_config = {
'private_key': pool.wireguard_private_key,
'subnetwork_size': pool.subnetwork_size,
'config_file': '/etc/wireguard/{}.conf'.format(pool.network),
'peers': []
}
for vpnnetwork in VPNNetworkReservation.objects.filter(vpnpool=pool):
pool_config['peers'].append({
'vpnnetwork': "{}/{}".format(vpnnetwork.address,
pool_config['subnetwork_size']),
'public_key': vpnnetwork.wireguard_public_key,
}
)
configs.append(pool_config)
print(configs)

View file

@ -23,30 +23,96 @@ class VPNPool(UncloudModel):
network_size = models.IntegerField(validators=[MinValueValidator(0), network_size = models.IntegerField(validators=[MinValueValidator(0),
MaxValueValidator(128)]) MaxValueValidator(128)])
subnetwork_size = models.IntegerField(validators=[MinValueValidator(0), subnetwork_size = models.IntegerField(validators=[
MaxValueValidator(128)]) MinValueValidator(0),
MaxValueValidator(128)
])
vpn_hostname = models.CharField(max_length=256) vpn_hostname = models.CharField(max_length=256)
wireguard_private_key = models.CharField(max_length=48) wireguard_private_key = models.CharField(max_length=48)
@property
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**(subnetwork_size - network_size)
@property
def used_networks(self):
return self.vpnnetworkreservation_set.objects.filter(vpnpool=self, status='used')
@property
def num_used_networks(self):
return len(self.used_networks)
@property
def num_free_networks(self):
return self.num_maximum_networks - self.num_used_networks
@property
def next_free_network(self):
free_net = self.vpnnetworkreservation_set.objects.filter(vpnpool=self,
status='free')
last_net = self.vpnnetworkreservation_set.objects.filter(vpnpool=self,
status='used')
if num_free_networks == 0:
raise Exception("No free networks")
if len(free_net) > 0:
return free_net[0].address
if len(used_net) > 0:
"""
sample:
pool = 2a0a:e5c1:200::/40
last_used = 2a0a:e5c1:204::/48
next:
"""
last_ip = last_net.address
# next_ip =
class VPNNetworkReservation(UncloudModel): class VPNNetworkReservation(UncloudModel):
""" """
This class tracks the used VPN networks. It will be deleted, when the product is cancelled. This class tracks the used VPN networks. It will be deleted, when the product is cancelled.
""" """
vpnpool = models.ForeignKey(VPNPool, vpnpool = models.ForeignKey(VPNPool,
on_delete=models.CASCADE) on_delete=models.CASCADE)
address = models.GenericIPAddressField(primary_key=True) address = models.GenericIPAddressField(primary_key=True)
status = models.CharField(max_length=256,
choices = (
('used', 'used'),
('free', 'free')
)
)
class VPNNetwork(Product): class VPNNetwork(Product):
""" """
A selected network. Used for tracking reservations / used networks A selected network. Used for tracking reservations / used networks
""" """
network = models.ForeignKey(VPNNetworkReservation, network = models.ForeignKey(VPNNetworkReservation,
on_delete=models.CASCADE, on_delete=models.CASCADE,
editable=False) editable=False)
wireguard_public_key = models.CharField(max_length=48) wireguard_public_key = models.CharField(max_length=48)

View file

@ -12,7 +12,6 @@ class VPNPoolSerializer(serializers.ModelSerializer):
fields = '__all__' fields = '__all__'
class VPNNetworkSerializer(serializers.ModelSerializer): class VPNNetworkSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = VPNNetwork model = VPNNetwork
fields = '__all__' fields = '__all__'
@ -53,8 +52,24 @@ class VPNNetworkSerializer(serializers.ModelSerializer):
msg = _("No pool available for networks with size = {}. Available are: {}".format(data['network_size'], sizes)) msg = _("No pool available for networks with size = {}. Available are: {}".format(data['network_size'], sizes))
raise serializers.ValidationError(msg) raise serializers.ValidationError(msg)
return data return data
def create(self, validated_data): def create(self, validated_data):
from_pool = """
Creating a new vpnnetwork - there are a couple of race conditions,
especially when run in parallel.
"""
pools = VPNPool.objects.filter(subnetwork_size=data['network_size'])
found_pool = False
for pool in pools:
if pool.num_free_networks > 0:
found_pool = True
# address = pool.
# reservation = VPNNetworkReservation(vpnpool=pool,
pool = VPNPool.objects.first(subnetwork_size=data['network_size'])
return VPNNetwork(**validated_data)