in between commit
Signed-off-by: Nico Schottelius <nico@nico-notebook.schottelius.org>
This commit is contained in:
parent
3d2f8574d3
commit
d3f2a3e071
4 changed files with 125 additions and 21 deletions
|
@ -11,3 +11,6 @@ parsedatetime
|
||||||
pyparsing
|
pyparsing
|
||||||
pydot
|
pydot
|
||||||
django-extensions
|
django-extensions
|
||||||
|
|
||||||
|
# PDF creating
|
||||||
|
django-hardcopy
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue