forked from uncloud/uncloud
begin phasing in config of vpn via cdist
This commit is contained in:
parent
e2b36c8bca
commit
054886fd9c
5 changed files with 119 additions and 39 deletions
|
@ -170,7 +170,7 @@ VPNNetworks can be managed by all authenticated users.
|
||||||
* Developer Handbook
|
* Developer Handbook
|
||||||
The following section describe decisions / architecture of
|
The following section describe decisions / architecture of
|
||||||
uncloud. These chapters are intended to be read by developers.
|
uncloud. These chapters are intended to be read by developers.
|
||||||
** Documentation
|
** This Documentation
|
||||||
This documentation is written in org-mode. To compile it to
|
This documentation is written in org-mode. To compile it to
|
||||||
html/pdf, just open emacs and press *C-c C-e l p*.
|
html/pdf, just open emacs and press *C-c C-e l p*.
|
||||||
** Models
|
** Models
|
||||||
|
@ -234,6 +234,53 @@ VPNNetworks can be managed by all authenticated users.
|
||||||
*** Decision
|
*** Decision
|
||||||
We use integers, because they are easy.
|
We use integers, because they are easy.
|
||||||
|
|
||||||
|
** Distributing/Dispatching/Orchestrating
|
||||||
|
*** Variant 1: using cdist
|
||||||
|
- The uncloud server can git commit things
|
||||||
|
- The uncloud server loads cdist and configures the server
|
||||||
|
- Advantages
|
||||||
|
- Fully integrated into normal flow
|
||||||
|
- Disadvantage
|
||||||
|
- web frontend has access to more data than it needs
|
||||||
|
- On compromise of the machine, more data leaks
|
||||||
|
- Some cdist usual delay
|
||||||
|
*** Variant 2: via celery
|
||||||
|
- The uncloud server dispatches via celery
|
||||||
|
- Every decentral node also runs celery/connects to the broker
|
||||||
|
- Summary brokers:
|
||||||
|
- If local only celery -> good to use redis - Broker
|
||||||
|
- If remote: probably better to use rabbitmq
|
||||||
|
- redis
|
||||||
|
- simpler
|
||||||
|
- rabbitmq
|
||||||
|
- more versatile
|
||||||
|
- made for remote connections
|
||||||
|
- quorom queues would be nice, but not clear if supported
|
||||||
|
- https://github.com/celery/py-amqp/issues/302
|
||||||
|
- https://github.com/celery/celery/issues/6067
|
||||||
|
- Cannot be installed on alpine Linux at the moment
|
||||||
|
- Advantage
|
||||||
|
- Very python / django integrated
|
||||||
|
- Rather instant
|
||||||
|
- Disadvantages
|
||||||
|
- Every decentral node needs to have the uncloud code available
|
||||||
|
- Decentral nodes *might* need to access the database
|
||||||
|
- Tasks can probably be written to work without that
|
||||||
|
(i.e. only strings/bytes)
|
||||||
|
|
||||||
|
**** log/tests
|
||||||
|
(venv) [19:54] vpn-2a0ae5c1200:~/uncloud$ celery -A uncloud -b redis://bridge.place7.ungleich.ch worker -n worker1@%h --logfile ~/celery.log -
|
||||||
|
Q vpn-2a0ae5c1200.ungleich.ch
|
||||||
|
|
||||||
|
|
||||||
|
*** Variant 3: dedicated cdist instance via message broker
|
||||||
|
- A separate VM/machine
|
||||||
|
- Has Checkout of ~/.cdist
|
||||||
|
- Has cdist checkout
|
||||||
|
- Tiny API for management
|
||||||
|
- Not directly web accessible
|
||||||
|
- "cdist" queue
|
||||||
|
|
||||||
** Milestones :uncloud:
|
** Milestones :uncloud:
|
||||||
*** 1.1 (cleanup 1)
|
*** 1.1 (cleanup 1)
|
||||||
**** TODO [#C] Unify ValidationError, FieldError - define proper Exception
|
**** TODO [#C] Unify ValidationError, FieldError - define proper Exception
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/3.0/ref/settings/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import ldap
|
import ldap
|
||||||
|
|
||||||
from django.core.management.utils import get_random_secret_key
|
from django.core.management.utils import get_random_secret_key
|
||||||
|
@ -199,14 +200,29 @@ AUTH_LDAP_USER_SEARCH = LDAPSearch("dc=example,dc=com",
|
||||||
# where to create customers
|
# where to create customers
|
||||||
LDAP_CUSTOMER_DN="ou=customer,dc=example,dc=com"
|
LDAP_CUSTOMER_DN="ou=customer,dc=example,dc=com"
|
||||||
|
|
||||||
CELERY_TASK_ROUTES = {
|
def route_task(name, args, kwargs, options, task=None, **kw):
|
||||||
'*': {
|
print(f"{name} - {args} - {kwargs}")
|
||||||
'queue': 'vpn1'
|
# if name == 'myapp.tasks.compress_video':
|
||||||
}
|
return {'queue': 'vpn1' }
|
||||||
}
|
# 'exchange_type': 'topic',
|
||||||
|
# 'routing_key': 'video.compress'}
|
||||||
|
|
||||||
CELERY_BROKER_URL = 'redis://bridge.place7.ungleich.ch:6379/0'
|
|
||||||
CELERY_RESULT_BACKEND = 'redis://bridge.place7.ungleich.ch:6379/0'
|
CELERY_TASK_ROUTES = (route_task,)
|
||||||
|
|
||||||
|
# CELERY_TASK_ROUTES = {
|
||||||
|
# '*': {
|
||||||
|
# 'queue': 'vpn1'
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
|
|
||||||
|
CELERY_BROKER_URL = 'redis://:uncloud.example.com:6379/0'
|
||||||
|
CELERY_RESULT_BACKEND = 'redis://:uncloud.example.com:6379/0'
|
||||||
|
|
||||||
|
CELERY_TASK_ROUTES = {
|
||||||
|
(re.compile(r'.*\.cdist\..*'), { 'queue': 'cdist' }
|
||||||
|
}
|
||||||
|
|
||||||
# CELERY_TASK_CREATE_MISSING_QUEUES = False
|
# CELERY_TASK_CREATE_MISSING_QUEUES = False
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,29 @@ class WireGuardVPNPool(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.ip_network} (subnets: /{self.subnetwork_mask})"
|
return f"{self.ip_network} (subnets: /{self.subnetwork_mask})"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def wireguard_config(self):
|
||||||
|
wireguard_config = [
|
||||||
|
"[Interface]\nListenPort = 51820\nPrivateKey = {self.wireguard_private_key}\n".format(
|
||||||
|
privatekey=self.wireguard_private_key)
|
||||||
|
]
|
||||||
|
|
||||||
|
peers = []
|
||||||
|
|
||||||
|
for vpn in self.wireguardvpn_set.all():
|
||||||
|
public_key = vpn.wireguard_public_key
|
||||||
|
peer_network = "{}/{}".format(vpn.address, self.subnetwork_mask)
|
||||||
|
owner = vpn.owner
|
||||||
|
|
||||||
|
peers.append("# Owner: {owner}\n[Peer]\nPublicKey = {public_key}\nAllowedIPs = {peer_network}\n\n".format(
|
||||||
|
owner=owner,
|
||||||
|
public_key=public_key,
|
||||||
|
peer_network=peer_network))
|
||||||
|
|
||||||
|
wireguard_config.extend(peers)
|
||||||
|
|
||||||
|
return "\n".join(wireguard_config)
|
||||||
|
|
||||||
|
|
||||||
class WireGuardVPN(models.Model):
|
class WireGuardVPN(models.Model):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -8,38 +8,30 @@ def whereami():
|
||||||
print(os.uname())
|
print(os.uname())
|
||||||
return os.uname()
|
return os.uname()
|
||||||
|
|
||||||
|
def configure_wireguard_server(wireguardvpnpool):
|
||||||
|
"""
|
||||||
|
- Create wireguard config (DB query -> string)
|
||||||
|
- Submit config to cdist worker
|
||||||
|
- Change config locally on worker / commit / shared
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
config = wireguardvpnpool.wireguard_config
|
||||||
|
server = wireguardvpnpool.vpn_server_hostname
|
||||||
|
|
||||||
|
print(f"Configuring {vpnpool.vpn_server_hostname}: {osa}")
|
||||||
|
cdist_configure_wireguard_server(config, server):
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def configure_wireguard_server(vpnpool):
|
def cdist_configure_wireguard_server(config, server):
|
||||||
print(f"Configuring {vpnpool.vpn_server_hostname}")
|
"""
|
||||||
|
Create config and configure server.
|
||||||
|
|
||||||
wireguard_config_filename = '/etc/wireguard/{}.conf'.format(vpnpool.network)
|
To be executed on the cdist workers.
|
||||||
|
"""
|
||||||
|
|
||||||
@property
|
fname = f"/home/app/.cdist/type/__ungleich_wireguard/files/{server}"
|
||||||
def wireguard_config(self):
|
|
||||||
wireguard_config = [
|
|
||||||
"""
|
|
||||||
[Interface]
|
|
||||||
ListenPort = 51820
|
|
||||||
PrivateKey = {privatekey}
|
|
||||||
""".format(privatekey=self.wireguard_private_key) ]
|
|
||||||
|
|
||||||
peers = []
|
with open(fname, "w") as fd:
|
||||||
|
fd.write(config)
|
||||||
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)
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ from .serializers import *
|
||||||
from .selectors import *
|
from .selectors import *
|
||||||
from .services import *
|
from .services import *
|
||||||
from .forms import *
|
from .forms import *
|
||||||
|
from .tasks import *
|
||||||
|
|
||||||
# class VPNPoolViewSet(viewsets.ModelViewSet):
|
# class VPNPoolViewSet(viewsets.ModelViewSet):
|
||||||
# serializer_class = VPNPoolSerializer
|
# serializer_class = VPNPoolSerializer
|
||||||
|
@ -39,6 +40,7 @@ class WireGuardVPNViewSet(viewsets.ModelViewSet):
|
||||||
public_key=serializer.validated_data['wireguard_public_key'],
|
public_key=serializer.validated_data['wireguard_public_key'],
|
||||||
network_mask=serializer.validated_data['network_mask']
|
network_mask=serializer.validated_data['network_mask']
|
||||||
)
|
)
|
||||||
|
configure_wireguard_server.apply_async((vpn.vpnpool,))
|
||||||
return Response(WireGuardVPNSerializer(vpn).data)
|
return Response(WireGuardVPNSerializer(vpn).data)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue