blob: 1c7af1b911a9515d971c7369f7bc165647e99b4b [file] [log] [blame]
Jeremy Mowery8b664f72015-12-04 11:52:16 -07001from core.models import Service, TenantWithContainer
2from django.db import transaction
3
4VPN_KIND = "vpn"
5
Jeremy Mowery82760822016-01-08 16:36:22 -07006
Jeremy Mowery8b664f72015-12-04 11:52:16 -07007class VPNService(Service):
Jeremy Mowery82760822016-01-08 16:36:22 -07008 """Defines the Service for creating VPN servers."""
Jeremy Mowery8b664f72015-12-04 11:52:16 -07009 KIND = VPN_KIND
10
11 class Meta:
12 proxy = True
13 # The name used to find this service, all directories are named this
14 app_label = "vpn"
15 verbose_name = "VPN Service"
16
Jeremy Mowery82760822016-01-08 16:36:22 -070017
Jeremy Mowery7ced7382015-12-04 23:58:38 -070018class VPNTenant(TenantWithContainer):
Jeremy Mowery82760822016-01-08 16:36:22 -070019 """Defines the Tenant for creating VPN servers."""
Jeremy Mowery8b664f72015-12-04 11:52:16 -070020
21 class Meta:
22 proxy = True
23 verbose_name = "VPN Tenant"
24
25 KIND = VPN_KIND
26
27 sync_attributes = ("nat_ip", "nat_mac",)
28
Jeremy Mowery3f6adcd2016-02-21 15:36:32 -070029 default_attributes = {'vpn_subnet': None,
Jeremy Moweryfd081292016-02-07 17:07:55 -070030 'server_network': None,
31 'clients_can_see_each_other': True,
Jeremy Mowerye02d4b62016-01-10 15:21:52 -070032 'is_persistent': True,
Jeremy Mowery290714e2016-02-25 14:36:29 -070033 'ca_crt': None,
Jeremy Mowery47c93742016-03-02 00:01:25 -070034 'port': None,
Jeremy Mowery65c60702016-03-16 11:22:02 -070035 'script_text': None,
36 'failover_servers': []}
Jeremy Mowery8b664f72015-12-04 11:52:16 -070037
38 def __init__(self, *args, **kwargs):
39 vpn_services = VPNService.get_service_objects().all()
40 if vpn_services:
41 self._meta.get_field(
42 "provider_service").default = vpn_services[0].id
43 super(VPNTenant, self).__init__(*args, **kwargs)
44
45 def save(self, *args, **kwargs):
46 super(VPNTenant, self).save(*args, **kwargs)
47 model_policy_vpn_tenant(self.pk)
48
49 def delete(self, *args, **kwargs):
50 self.cleanup_container()
51 super(VPNTenant, self).delete(*args, **kwargs)
52
53 @property
Jeremy Mowery8b664f72015-12-04 11:52:16 -070054 def addresses(self):
Jeremy Mowery5055c7b2016-01-08 17:25:33 -070055 """Mapping[str, str]: The ip, mac address, and subnet of the NAT network of this Tenant."""
Jeremy Mowery8b664f72015-12-04 11:52:16 -070056 if (not self.id) or (not self.instance):
57 return {}
58
59 addresses = {}
Jeremy Mowery8b664f72015-12-04 11:52:16 -070060 for ns in self.instance.ports.all():
61 if "nat" in ns.network.name.lower():
Jeremy Mowery5055c7b2016-01-08 17:25:33 -070062 addresses["ip"] = ns.ip
63 addresses["mac"] = ns.mac
Jeremy Mowery5055c7b2016-01-08 17:25:33 -070064 break
65
Jeremy Mowery8b664f72015-12-04 11:52:16 -070066 return addresses
67
68 # This getter is necessary because nat_ip is a sync_attribute
69 @property
70 def nat_ip(self):
Jeremy Mowery82760822016-01-08 16:36:22 -070071 """str: The IP of this Tenant on the NAT network."""
Jeremy Mowery5055c7b2016-01-08 17:25:33 -070072 return self.addresses.get("ip", None)
Jeremy Mowery8b664f72015-12-04 11:52:16 -070073
74 # This getter is necessary because nat_mac is a sync_attribute
75 @property
76 def nat_mac(self):
Jeremy Mowery82760822016-01-08 16:36:22 -070077 """str: The MAC address of this Tenant on the NAT network."""
Jeremy Mowery5055c7b2016-01-08 17:25:33 -070078 return self.addresses.get("mac", None)
Jeremy Mowerya9b673b2016-01-07 21:25:50 -070079
80 @property
Jeremy Moweryfd081292016-02-07 17:07:55 -070081 def server_network(self):
Jeremy Mowery82760822016-01-08 16:36:22 -070082 """str: The IP address of the server on the VPN."""
Jeremy Mowerybd2ed3a2016-01-05 16:52:43 -070083 return self.get_attribute(
Jeremy Moweryfd081292016-02-07 17:07:55 -070084 'server_network',
85 self.default_attributes['server_network'])
Jeremy Mowerybd2ed3a2016-01-05 16:52:43 -070086
Jeremy Moweryfd081292016-02-07 17:07:55 -070087 @server_network.setter
88 def server_network(self, value):
89 self.set_attribute("server_network", value)
Jeremy Mowerybd2ed3a2016-01-05 16:52:43 -070090
91 @property
Jeremy Moweryfd081292016-02-07 17:07:55 -070092 def vpn_subnet(self):
Jeremy Mowery82760822016-01-08 16:36:22 -070093 """str: The IP address of the client on the VPN."""
Jeremy Mowerybd2ed3a2016-01-05 16:52:43 -070094 return self.get_attribute(
Jeremy Moweryfd081292016-02-07 17:07:55 -070095 'vpn_subnet',
96 self.default_attributes['vpn_subnet'])
Jeremy Mowerybd2ed3a2016-01-05 16:52:43 -070097
Jeremy Moweryfd081292016-02-07 17:07:55 -070098 @vpn_subnet.setter
99 def vpn_subnet(self, value):
100 self.set_attribute("vpn_subnet", value)
Jeremy Mowerybd2ed3a2016-01-05 16:52:43 -0700101
102 @property
Jeremy Mowery4a23e7d2016-01-06 15:16:33 -0700103 def is_persistent(self):
Jeremy Mowery82760822016-01-08 16:36:22 -0700104 """bool: True if the VPN connection is persistence, false otherwise."""
Jeremy Mowery4a23e7d2016-01-06 15:16:33 -0700105 return self.get_attribute(
106 "is_persistent",
107 self.default_attributes['is_persistent'])
108
109 @is_persistent.setter
110 def is_persistent(self, value):
111 self.set_attribute("is_persistent", value)
112
113 @property
Jeremy Mowery65c60702016-03-16 11:22:02 -0700114 def failover_servers(self):
115 self.get_attribute("failover_servers", self.default_attributes["failover_servers"])
116
117 @failover_servers.setter
118 def failover_servers(self, value):
119 self.set_attribute("failover_servers", value)
120
121 @property
Jeremy Moweryfd081292016-02-07 17:07:55 -0700122 def clients_can_see_each_other(self):
Jeremy Mowery82760822016-01-08 16:36:22 -0700123 """bool: True if the client can see the subnet of the server, false otherwise."""
Jeremy Mowery4a23e7d2016-01-06 15:16:33 -0700124 return self.get_attribute(
Jeremy Moweryfd081292016-02-07 17:07:55 -0700125 "clients_can_see_each_other",
126 self.default_attributes['clients_can_see_each_other'])
Jeremy Mowery4a23e7d2016-01-06 15:16:33 -0700127
Jeremy Moweryfd081292016-02-07 17:07:55 -0700128 @clients_can_see_each_other.setter
129 def clients_can_see_each_other(self, value):
130 self.set_attribute("clients_can_see_each_other", value)
Jeremy Mowery4a23e7d2016-01-06 15:16:33 -0700131
Jeremy Mowerye02d4b62016-01-10 15:21:52 -0700132 @property
Jeremy Mowery3b1caba2016-02-02 23:53:50 -0700133 def ca_crt(self):
Jeremy Mowery1eab4fa2016-02-02 17:17:20 -0700134 """str: the string for the ca certificate"""
Jeremy Mowery3b1caba2016-02-02 23:53:50 -0700135 return self.get_attribute("ca_crt", self.default_attributes['ca_crt'])
Jeremy Mowery1eab4fa2016-02-02 17:17:20 -0700136
Jeremy Mowery3b1caba2016-02-02 23:53:50 -0700137 @ca_crt.setter
138 def ca_crt(self, value):
139 self.set_attribute("ca_crt", value)
140
Jeremy Mowery290714e2016-02-25 14:36:29 -0700141 @property
142 def port_number(self):
143 """int: the integer representing the port number for this server"""
144 return self.get_attribute("port", self.default_attributes['port'])
145
146 @port_number.setter
147 def port_number(self, value):
148 self.set_attribute("port", value)
149
Jeremy Mowery47c93742016-03-02 00:01:25 -0700150 @property
151 def script_text(self):
152 return self.get_attribute("script_text", self.default_attributes['script_text'])
153
154 @script_text.setter
155 def script_text(self, value):
156 self.set_attribute("script_text", value)
157
Jeremy Mowerycf1c5192016-03-15 20:28:04 -0700158 def create_client_script(self, client_certificate):
Jeremy Mowerybd3dfae2016-03-01 19:15:40 -0700159 script = ""
Jeremy Mowery01e3a5b2016-02-25 14:50:58 -0700160 # write the configuration portion
Jeremy Mowerybd3dfae2016-03-01 19:15:40 -0700161 script += ("printf \"%b\" \"")
Jeremy Mowerycf1c5192016-03-15 20:28:04 -0700162 script += self.generate_client_conf(client_certificate)
Jeremy Mowerybd3dfae2016-03-01 19:15:40 -0700163 script += ("\" > client.conf\n")
164 script += ("printf \"%b\" \"")
Jeremy Mowery01e3a5b2016-02-25 14:50:58 -0700165 for line in self.ca_crt:
Jeremy Mowerybd3dfae2016-03-01 19:15:40 -0700166 script += (line.rstrip() + r"\n")
167 script += ("\" > ca.crt\n")
Jeremy Mowerycf1c5192016-03-15 20:28:04 -0700168 script += ("printf \"%b\" \"")
169 for line in self.generate_client_cert(client_certificate):
170 script += (line.rstrip() + r"\n")
171 script += ("\" > " + client_certificate + ".crt\n")
172 for line in self.generate_client_key(client_certificate):
173 script += (line.rstrip() + r"\n")
174 script += ("\" > " + client_certificate + ".key\n")
Jeremy Mowery01e3a5b2016-02-25 14:50:58 -0700175 # make sure openvpn is installed
Jeremy Mowerybd3dfae2016-03-01 19:15:40 -0700176 script += ("apt-get update\n")
177 script += ("apt-get install openvpn\n")
178 script += ("openvpn client.conf &\n")
Jeremy Mowery01e3a5b2016-02-25 14:50:58 -0700179 # close the script
Jeremy Mowerybd3dfae2016-03-01 19:15:40 -0700180 return script;
Jeremy Mowery01e3a5b2016-02-25 14:50:58 -0700181
Jeremy Mowerycf1c5192016-03-15 20:28:04 -0700182 def generate_client_cert(self, client_certificate):
183 return open("/opt/openvpn/easyrsa3/pki/issued/" + client_certificate + ".crt").readlines()
Jeremy Mowery01e3a5b2016-02-25 14:50:58 -0700184
Jeremy Mowerycf1c5192016-03-15 20:28:04 -0700185 def generate_client_key(self, client_certificate):
186 return open("/opt/openvpn/easyrsa3/pki/private/" + client_certificate + ".key").readlines()
187
188 def generate_client_conf(self, client_certificate):
Jeremy Mowery01e3a5b2016-02-25 14:50:58 -0700189 """str: Generates the client configuration to use to connect to this VPN server.
190 """
191 conf = ("client\n" +
Jeremy Mowery01e3a5b2016-02-25 14:50:58 -0700192 "dev tun\n" +
193 "proto udp\n" +
194 "remote " + str(self.nat_ip) + " " + str(self.port_number) + "\n" +
195 "resolv-retry infinite\n" +
196 "nobind\n" +
197 "ca ca.crt\n" +
Jeremy Mowerycf1c5192016-03-15 20:28:04 -0700198 "cert " + client_certificate + ".crt\n" +
199 "key " + client_certificate + ".key\n" +
Jeremy Mowery01e3a5b2016-02-25 14:50:58 -0700200 "comp-lzo\n" +
201 "verb 3\n")
202
203 if self.is_persistent:
204 conf += "persist-tun\n"
205 conf += "persist-key\n"
206
207 return conf
208
Jeremy Mowery290714e2016-02-25 14:36:29 -0700209
Jeremy Mowery8b664f72015-12-04 11:52:16 -0700210
211def model_policy_vpn_tenant(pk):
Jeremy Mowery82760822016-01-08 16:36:22 -0700212 """Manages the contain for the VPN Tenant."""
Jeremy Mowery8b664f72015-12-04 11:52:16 -0700213 # This section of code is atomic to prevent race conditions
214 with transaction.atomic():
215 # We find all of the tenants that are waiting to update
216 tenant = VPNTenant.objects.select_for_update().filter(pk=pk)
217 if not tenant:
218 return
219 # Since this code is atomic it is safe to always use the first tenant
220 tenant = tenant[0]
221 tenant.manage_container()