Move to using a PKI per server
diff --git a/containers/xos/Dockerfile.devel b/containers/xos/Dockerfile.devel
index 1ff2646..17ae9c9 100644
--- a/containers/xos/Dockerfile.devel
+++ b/containers/xos/Dockerfile.devel
@@ -100,7 +100,6 @@
 RUN git -C /opt/openvpn pull origin master
 RUN echo "set_var EASYRSA	/opt/openvpn/easyrsa3" | tee /opt/openvpn/easyrsa3/vars
 RUN /opt/openvpn/easyrsa3/easyrsa --batch init-pki
-RUN /opt/openvpn/easyrsa3/easyrsa --batch --req-cn=XOS build-ca nopass
 RUN /opt/openvpn/easyrsa3/easyrsa --batch gen-dh
 RUN /opt/openvpn/easyrsa3/easyrsa --batch gen-crl
 RUN chmod 777 /opt/openvpn/easyrsa3/pki/dh.pem
diff --git a/xos/services/vpn/admin.py b/xos/services/vpn/admin.py
index a99e365..9e7255b 100644
--- a/xos/services/vpn/admin.py
+++ b/xos/services/vpn/admin.py
@@ -1,3 +1,6 @@
+import os
+import shutil
+
 from core.admin import ReadOnlyAwareAdmin, SliceInline, TenantPrivilegeInline
 from core.middleware import get_request
 from core.models import TenantPrivilege, User
@@ -131,7 +134,7 @@
             self.fields[
                 'clients_can_see_each_other'].initial = self.instance.clients_can_see_each_other
             self.fields['is_persistent'].initial = self.instance.is_persistent
-            self.fields['protocol'].initial = self.instance.protocol
+            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))]
 
@@ -160,11 +163,18 @@
         if (not self.instance.ca_crt):
             self.instance.ca_crt = self.generate_ca_crt()
 
-        return super(VPNTenantForm, self).save(commit=commit)
+        result = super(VPNTenantForm, self).save(commit=commit)
+        pki_dir = "/opt/openvpn/easyrsa3/server-" + self.instance.id
+        if (not os.path.isdir(pki_dir)):
+            os.makedirs(pki_dir)
+            shutil.copy2("/opt/openvpn/easyrsa3/", pki_dir)
+            Popen(pki_dir + "/easyrsa --batch --req-cn=XOS build-ca nopass", shell=True, stdout=PIPE).communicate()
+            self.instance.ca_crt = self.generate_ca_crt(self.instance.id)
+        return result
 
-    def generate_ca_crt(self):
+    def generate_ca_crt(self, server_id):
         """str: Generates the ca cert by reading from the ca file"""
-        with open("/opt/openvpn/easyrsa3/pki/ca.crt") as crt:
+        with open("/opt/openvpn/easyrsa3/server-" + server_id + "/ca.crt") as crt:
             return crt.readlines()
 
     class Meta:
@@ -200,7 +210,7 @@
             # If anything deleated was a TenantPrivilege then revoke the certificate
             if type(obj) is TenantPrivilege:
                 certificate = self.certificate_name(obj)
-                Popen("/opt/openvpn/easyrsa3/easyrsa --batch revoke " + certificate, shell=True, stdout=PIPE).communicate()
+                Popen("/opt/openvpn/easyrsa3/server-" + obj.tenant.id + "/easyrsa --batch revoke " + certificate, shell=True, stdout=PIPE).communicate()
             # TODO(jermowery): determine if this is necessary.
             # if type(obj) is VPNTenant:
                 # if the tenant was deleted revoke all certs assoicated
@@ -210,7 +220,7 @@
             # If there were any new TenantPrivlege objects then create certs
             if type(obj) is TenantPrivilege:
                 certificate = self.certificate_name(obj)
-                Popen("/opt/openvpn/easyrsa3/easyrsa --batch build-client-full " + certificate + " nopass", shell=True, stdout=PIPE).communicate()
+                Popen("/opt/openvpn/easyrsa3/server-" + obj.tenant.id + "/easyrsa --batch build-client-full " + certificate + " nopass", shell=True, stdout=PIPE).communicate()
 
 
 # Associate the admin forms with the models.
diff --git a/xos/services/vpn/models.py b/xos/services/vpn/models.py
index 94e62b6..72a6407 100644
--- a/xos/services/vpn/models.py
+++ b/xos/services/vpn/models.py
@@ -1,6 +1,6 @@
 from core.models import Service, TenantWithContainer
 from django.db import transaction
-from xos.exceptions import XOSConfigurationError, XOSValidationError
+from xos.exceptions import XOSValidationError
 
 VPN_KIND = "vpn"
 
@@ -28,7 +28,7 @@
 
     def get_next_available_port(self, protocol):
         if protocol != "udp" and protocol != "tcp":
-            raise XOSConfigurationError("Port protocol must be udp or 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]
@@ -198,10 +198,10 @@
             script += (line.rstrip() + r"\n")
         script += ("\" > ca.crt\n")
         script += ("printf \"%b\" \"")
-        for line in self.generate_client_cert(client_name):
+        for line in self.get_client_cert(client_name):
             script += (line.rstrip() + r"\n")
         script += ("\" > " + client_name + ".crt\n")
-        for line in self.generate_client_key(client_name):
+        for line in self.get_client_key(client_name):
             script += (line.rstrip() + r"\n")
         script += ("\" > " + client_name + ".key\n")
         # make sure openvpn is installed
@@ -211,11 +211,11 @@
         # close the script
         return script
 
-    def generate_client_cert(self, client_name):
-        return open("/opt/openvpn/easyrsa3/pki/issued/" + client_name + ".crt").readlines()
+    def get_client_cert(self, client_name):
+        return open("/opt/openvpn/easyrsa3/server-" + self.id + "/issued/" + client_name + ".crt").readlines()
 
-    def generate_client_key(self, client_name):
-        return open("/opt/openvpn/easyrsa3/pki/private/" + client_name + ".key").readlines()
+    def get_client_key(self, client_name):
+        return open("/opt/openvpn/easyrsa3/server-" + self.id + "/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.