Fix issue with saving tenants
diff --git a/containers/synchronizer/Makefile b/containers/synchronizer/Makefile
index 733e7b8..352616a 100644
--- a/containers/synchronizer/Makefile
+++ b/containers/synchronizer/Makefile
@@ -1,6 +1,6 @@
IMAGE_NAME:=xosproject/xos-synchronizer-openstack
CONTAINER_NAME:=xos-synchronizer
-NO_DOCKER_CACHE?=true
+NO_DOCKER_CACHE?=false
.PHONY: build
build: ; sudo docker build --no-cache=${NO_DOCKER_CACHE} --rm -t ${IMAGE_NAME} .
diff --git a/xos/services/vpn/admin.py b/xos/services/vpn/admin.py
index 38737fa..bd33d99 100644
--- a/xos/services/vpn/admin.py
+++ b/xos/services/vpn/admin.py
@@ -20,7 +20,8 @@
super(VPNServiceForm, self).__init__(*args, **kwargs)
if self.instance:
- self.fields['exposed_ports'].initial = self.instance.exposed_ports
+ self.fields['exposed_ports'].initial = (
+ self.instance.exposed_ports_str)
def save(self, commit=True):
self.instance.exposed_ports = self.cleaned_data['exposed_ports']
@@ -28,6 +29,7 @@
def clean_exposed_ports(self):
exposed_ports = self.cleaned_data['exposed_ports']
+ self.instance.exposed_ports_str = exposed_ports
port_mapping = {"udp": [], "tcp": []}
parts = exposed_ports.split(",")
for part in parts:
@@ -37,7 +39,9 @@
elif " " in part:
(protocol, ports) = part.split(None, 1)
else:
- raise XOSValidationError('malformed port specifier %s, format example: "tcp 123, tcp 201:206, udp 333"' % part)
+ raise XOSValidationError(
+ 'malformed port specifier %s, format example: ' +
+ '"tcp 123, tcp 201:206, udp 333"' % part)
protocol = protocol.strip()
ports = ports.strip()
@@ -46,9 +50,11 @@
raise XOSValidationError('unknown protocol %s' % protocol)
if "-" in ports:
- port_mapping[protocol].extend(self.parse_port_range(ports, "-"))
+ port_mapping[protocol].extend(
+ self.parse_port_range(ports, "-"))
elif ":" in ports:
- port_mapping[protocol].extend(self.parse_port_range(ports, ":"))
+ port_mapping[protocol].extend(
+ self.parse_port_range(ports, ":"))
else:
port_mapping[protocol].append(int(ports))
@@ -75,7 +81,8 @@
list_display_links = ('backend_status_icon', 'name', )
fieldsets = [(None, {'fields': ['backend_status_text', 'name', 'enabled',
- 'versionNumber', 'description', "view_url", 'exposed_ports'],
+ 'versionNumber', 'description', "view_url",
+ 'exposed_ports'],
'classes':['suit-tab suit-tab-general']})]
readonly_fields = ('backend_status_text', )
@@ -102,11 +109,16 @@
"""The form used to create and edit a VPNTenant.
Attributes:
- creator (forms.ModelChoiceField): The XOS user that created this tenant.
- client_conf (forms.CharField): The readonly configuration used on the client to connect to this Tenant.
- server_address (forms.GenericIPAddressField): The ip address on the VPN of this Tenant.
- client_address (forms.GenericIPAddressField): The ip address on the VPN of the client.
- is_persistent (forms.BooleanField): Determines if this Tenant keeps this connection alive through failures.
+ creator (forms.ModelChoiceField): The XOS user that created this
+ tenant.
+ client_conf (forms.CharField): The readonly configuration used on the
+ client to connect to this Tenant.
+ server_address (forms.GenericIPAddressField): The ip address on the VPN
+ of this Tenant.
+ client_address (forms.GenericIPAddressField): The ip address on the VPN
+ of the client.
+ is_persistent (forms.BooleanField): Determines if this Tenant keeps
+ this connection alive through failures.
"""
creator = forms.ModelChoiceField(queryset=User.objects.all())
server_network = forms.GenericIPAddressField(
@@ -114,15 +126,18 @@
vpn_subnet = forms.GenericIPAddressField(protocol="IPv4", required=True)
is_persistent = forms.BooleanField(required=False)
clients_can_see_each_other = forms.BooleanField(required=False)
- failover_servers = forms.ModelMultipleChoiceField(queryset=VPNTenant.get_tenant_objects(), required=False)
- protocol = forms.ChoiceField(required=True, choices=[("udp", "udp"), ("tcp", "tcp")])
+ failover_servers = forms.ModelMultipleChoiceField(
+ queryset=VPNTenant.get_tenant_objects(), required=False)
+ protocol = forms.ChoiceField(required=True, choices=[
+ ("udp", "udp"), ("tcp", "tcp")])
def __init__(self, *args, **kwargs):
super(VPNTenantForm, self).__init__(*args, **kwargs)
self.fields['kind'].widget.attrs['readonly'] = True
# self.fields['script_name'].widget.attrs['readonly'] = True
self.fields[
- 'provider_service'].queryset = VPNService.get_service_objects().all()
+ 'provider_service'].queryset = (
+ VPNService.get_service_objects().all())
self.fields['kind'].initial = VPN_KIND
@@ -132,11 +147,15 @@
self.fields[
'server_network'].initial = self.instance.server_network
self.fields[
- 'clients_can_see_each_other'].initial = self.instance.clients_can_see_each_other
+ 'clients_can_see_each_other'].initial = (
+ self.instance.clients_can_see_each_other)
self.fields['is_persistent'].initial = self.instance.is_persistent
self.initial['protocol'] = self.instance.protocol
if (self.instance.failover_servers):
- self.initial['failover_servers'] = [model.pk for model in list(serializers.deserialize('json', self.instance.failover_servers))]
+ self.initial['failover_servers'] = [
+ model.pk for model in list(
+ serializers.deserialize(
+ 'json', self.instance.failover_servers))]
if (not self.instance) or (not self.instance.pk):
self.fields['creator'].initial = get_request().user
@@ -145,8 +164,8 @@
self.fields['clients_can_see_each_other'].initial = True
self.fields['is_persistent'].initial = True
if VPNService.get_service_objects().exists():
- self.fields["provider_service"].initial = VPNService.get_service_objects().all()[
- 0]
+ self.fields["provider_service"].initial = (
+ VPNService.get_service_objects().all()[0])
def save(self, commit=True):
self.instance.creator = self.cleaned_data.get("creator")
@@ -155,34 +174,50 @@
self.instance.server_network = self.cleaned_data.get('server_network')
self.instance.clients_can_see_each_other = self.cleaned_data.get(
'clients_can_see_each_other')
- self.instance.failover_servers = serializers.serialize("json", self.cleaned_data.get('failover_servers'))
+ self.instance.failover_servers = (
+ serializers.serialize(
+ "json", self.cleaned_data.get('failover_servers')))
self.instance.protocol = self.cleaned_data.get("protocol")
- self.instance.port_number = self.instance.provider_service.get_next_available_port(self.instance.protocol)
-
- if (not self.instance.ca_crt):
- self.instance.ca_crt = self.generate_ca_crt()
+ self.instance.port_number = (
+ self.instance.provider_service.get_next_available_port(
+ self.instance.protocol))
result = super(VPNTenantForm, self).save(commit=commit)
- pki_dir = "/opt/openvpn/easyrsa3/server-" + self.instance.id
+ pki_dir = "/opt/openvpn/easyrsa3/server-" + result.id
if (not os.path.isdir(pki_dir)):
os.makedirs(pki_dir)
shutil.copy2("/opt/openvpn/easyrsa3/openssl-1.0.cnf", pki_dir)
shutil.copy2("/opt/openvpn/easyrsa3/easyrsa", pki_dir)
- shutil.copytree("/opt/openvpn/easyrsa3/x509-types", pki_dir + "/x509-types")
- (stdout, stderr) = Popen(pki_dir + "/easyrsa --batch init-pki nopass", shell=True, stdout=PIPE, stderr=PIPE).communicate()
+ shutil.copytree("/opt/openvpn/easyrsa3/x509-types",
+ pki_dir + "/x509-types")
+ (stdout, stderr) = Popen(
+ pki_dir + "/easyrsa --batch init-pki nopass",
+ shell=True,
+ stdout=PIPE,
+ stderr=PIPE).communicate()
if (stderr):
- raise XOSConfigurationError("init-pki failed with standard out:" + str(stdout) + " and stderr: " + str(stderr))
- (stdout, stderr) = Popen(pki_dir + "/easyrsa --batch --req-cn=XOS build-ca nopass", shell=True, stdout=PIPE, stderr=PIPE).communicate()
+ raise XOSConfigurationError(
+ "init-pki failed with standard out:" + str(stdout) +
+ " and stderr: " + str(stderr))
+ (stdout, stderr) = Popen(
+ pki_dir + "/easyrsa --batch --req-cn=XOS build-ca nopass",
+ shell=True,
+ stdout=PIPE,
+ stderr=PIPE).communicate()
if (stderr):
- raise XOSConfigurationError("build-ca failed with standard out:" + str(stdout) + " and stderr: " + str(stderr))
-
- self.instance.ca_crt = self.generate_ca_crt(self.instance.id)
+ raise XOSConfigurationError(
+ "build-ca failed with standard out:" + str(stdout) +
+ " and stderr: " + str(stderr))
+ self.instance.ca_crt = self.generate_ca_crt(result.id)
+ return super(VPNTenantForm, self).save(commit=commit)
return result
def generate_ca_crt(self, server_id):
"""str: Generates the ca cert by reading from the ca file"""
- with open("/opt/openvpn/easyrsa3/server-" + server_id + "/pki/ca.crt") as crt:
+ with open(
+ "/opt/openvpn/easyrsa3/server-" + server_id + "/pki/ca.crt"
+ ) as crt:
return crt.readlines()
class Meta:
@@ -197,30 +232,43 @@
'instance', 'server_network', 'vpn_subnet')
fieldsets = [(None, {'fields': ['backend_status_text', 'kind',
'provider_service', 'instance', 'creator',
- 'server_network', 'vpn_subnet', 'is_persistent',
- 'clients_can_see_each_other', 'failover_servers', "protocol"],
+ 'server_network', 'vpn_subnet',
+ 'is_persistent',
+ 'clients_can_see_each_other',
+ 'failover_servers', "protocol"],
'classes': ['suit-tab suit-tab-general']})]
readonly_fields = ('backend_status_text', 'instance')
form = VPNTenantForm
inlines = [TenantPrivilegeInline]
- suit_form_tabs = (('general', 'Details'), ('tenantprivileges', 'Privileges'))
+ suit_form_tabs = (('general', 'Details'),
+ ('tenantprivileges', 'Privileges'))
def queryset(self, request):
return VPNTenant.get_tenant_objects_by_user(request.user)
def certificate_name(self, tenant_privilege):
- return str(tenant_privilege.user.email) + "-" + str(tenant_privilege.tenant.id)
+ return (str(tenant_privilege.user.email) +
+ "-" + str(tenant_privilege.tenant.id))
def save_formset(self, request, form, formset, change):
- super(VPNTenantAdmin, self).save_formset(request, form, formset, change)
+ super(VPNTenantAdmin, self).save_formset(
+ request, form, formset, change)
for obj in formset.deleted_objects:
- # If anything deleated was a TenantPrivilege then revoke the certificate
+ # If anything deleated was a TenantPrivilege then revoke the
+ # certificate
if type(obj) is TenantPrivilege:
certificate = self.certificate_name(obj)
- (stdout, stderr) = Popen("/opt/openvpn/easyrsa3/server-" + obj.tenant.id + "/easyrsa --batch revoke " + certificate, shell=True, stdout=PIPE, stderr=PIPE).communicate()
+ (stdout, stderr) = Popen(
+ "/opt/openvpn/easyrsa3/server-" + obj.tenant.id +
+ "/easyrsa --batch revoke " + certificate,
+ shell=True,
+ stdout=PIPE,
+ stderr=PIPE).communicate()
if (stderr):
- raise XOSConfigurationError("revoke failed with standard out:" + str(stdout) + " and stderr: " + str(stderr))
+ raise XOSConfigurationError(
+ "revoke failed with standard out:" + str(stdout) +
+ " and stderr: " + str(stderr))
# TODO(jermowery): determine if this is necessary.
# if type(obj) is VPNTenant:
# if the tenant was deleted revoke all certs assoicated
@@ -230,9 +278,17 @@
# If there were any new TenantPrivlege objects then create certs
if type(obj) is TenantPrivilege:
certificate = self.certificate_name(obj)
- (stdout, stderr) = Popen("/opt/openvpn/easyrsa3/server-" + obj.tenant.id + "/easyrsa --batch build-client-full " + certificate + " nopass", shell=True, stdout=PIPE, stderr=PIPE).communicate()
+ (stdout, stderr) = Popen(
+ "/opt/openvpn/easyrsa3/server-" + obj.tenant.id +
+ "/easyrsa --batch build-client-full " + certificate +
+ " nopass",
+ shell=True,
+ stdout=PIPE,
+ stderr=PIPE).communicate()
if (stderr):
- raise XOSConfigurationError("build-client-full failed with standard out:" + str(stdout) + " and stderr: " + str(stderr))
+ raise XOSConfigurationError(
+ "build-client-full failed with standard out:" +
+ str(stdout) + " and stderr: " + str(stderr))
# Associate the admin forms with the models.
admin.site.register(VPNService, VPNServiceAdmin)
diff --git a/xos/services/vpn/models.py b/xos/services/vpn/models.py
index a33f06f..76bc516 100644
--- a/xos/services/vpn/models.py
+++ b/xos/services/vpn/models.py
@@ -15,26 +15,42 @@
app_label = "vpn"
verbose_name = "VPN Service"
- default_attributes = {'exposed_ports': None}
+ default_attributes = {'exposed_ports': None,
+ 'exposed_ports_str': None}
@property
def exposed_ports(self):
return self.get_attribute("exposed_ports",
- self.default_attributes["exposed_ports"])
+ self.default_attributes["exposed_ports"])
@exposed_ports.setter
def exposed_ports(self, value):
self.set_attribute("exposed_ports", value)
+ @property
+ def exposed_ports_str(self):
+ return self.get_attribute("exposed_ports_str",
+ self.default_attributes["exposed_ports_str"])
+
+ @exposed_ports_str.setter
+ def exposed_ports_str(self, value):
+ self.set_attribute("exposed_ports_str", value)
+
def get_next_available_port(self, protocol):
if protocol != "udp" and protocol != "tcp":
raise XOSValidationError("Port protocol must be udp or tcp")
if not self.exposed_ports[protocol]:
- raise XOSValidationError("No availble ports for protocol: " + protocol)
- tenants = [tenant for tenant in VPNTenant.get_tenant_objects().all() if tenant.protocol == protocol]
+ raise XOSValidationError(
+ "No availble ports for protocol: " + protocol)
+ tenants = [
+ tenant for tenant in VPNTenant.get_tenant_objects().all()
+ if tenant.protocol == protocol]
port_numbers = self.exposed_ports[protocol]
for port_number in port_numbers:
- if count([tenant for tenant in tenants if tenant.port_number == port_number]) == 0:
+ if (
+ len([
+ tenant for tenant in tenants
+ if tenant.port_number == port_number]) == 0):
return port_number
@@ -76,7 +92,8 @@
@property
def protocol(self):
- return self.get_attribute("protocol", self.default_attributes["protocol"])
+ return self.get_attribute(
+ "protocol", self.default_attributes["protocol"])
@protocol.setter
def protocol(self, value):
@@ -84,7 +101,8 @@
@property
def addresses(self):
- """Mapping[str, str]: The ip, mac address, and subnet of the NAT network of this Tenant."""
+ """Mapping[str, str]: The ip, mac address, and subnet of the NAT
+ network of this Tenant."""
if (not self.id) or (not self.instance):
return {}
@@ -144,7 +162,8 @@
@property
def failover_servers(self):
- self.get_attribute("failover_servers", self.default_attributes["failover_servers"])
+ self.get_attribute(
+ "failover_servers", self.default_attributes["failover_servers"])
@failover_servers.setter
def failover_servers(self, value):
@@ -152,7 +171,8 @@
@property
def clients_can_see_each_other(self):
- """bool: True if the client can see the subnet of the server, false otherwise."""
+ """bool: True if the client can see the subnet of the server, false
+ otherwise."""
return self.get_attribute(
"clients_can_see_each_other",
self.default_attributes['clients_can_see_each_other'])
@@ -181,7 +201,8 @@
@property
def script_text(self):
- return self.get_attribute("script_text", self.default_attributes['script_text'])
+ return self.get_attribute(
+ "script_text", self.default_attributes['script_text'])
@script_text.setter
def script_text(self, value):
@@ -212,18 +233,24 @@
return script
def get_client_cert(self, client_name):
- return open("/opt/openvpn/easyrsa3/server-" + self.id + "/pki/issued/" + client_name + ".crt").readlines()
+ return open(
+ "/opt/openvpn/easyrsa3/server-" + self.id + "/pki/issued/" +
+ client_name + ".crt").readlines()
def get_client_key(self, client_name):
- return open("/opt/openvpn/easyrsa3/server-" + self.id + "/pki/private/" + client_name + ".key").readlines()
+ return open(
+ "/opt/openvpn/easyrsa3/server-" + self.id + "/pki/private/" +
+ client_name + ".key").readlines()
def generate_client_conf(self, client_name):
- """str: Generates the client configuration to use to connect to this VPN server.
+ """str: Generates the client configuration to use to connect to this
+ VPN server.
"""
conf = ("client\n" +
"dev tun\n" +
"proto " + self.protocol + "\n" +
- "remote " + str(self.nat_ip) + " " + str(self.port_number) + "\n" +
+ "remote " + str(self.nat_ip) + " " + str(self.port_number) +
+ "\n" +
"resolv-retry infinite\n" +
"nobind\n" +
"ca ca.crt\n" +