You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. import glob
  2. import json
  3. import os
  4. import os.path
  5. import re
  6. import shutil
  7. from enum import Enum
  8. from subprocess import call
  9. class VpnGenError(Enum):
  10. Success = 0,
  11. VpnAlreadyExists = 1,
  12. VpnDoesNotExists = 2,
  13. ClientAlreadyExists = 3,
  14. ClientDoesNotExists = 4
  15. class VpnGen:
  16. default_config_base_dir = ""
  17. default_config_file = ""
  18. default_client_config_file = ""
  19. ovpn_config_path = ""
  20. def __init__(self, default_config_path, ovpn_config_path):
  21. self.default_config_base_dir = os.path.abspath(default_config_path)
  22. self.default_config_file = "%s.conf" % self.default_config_base_dir
  23. self.default_client_config_file = "%s%sclients%sclient.conf" % (self.default_config_base_dir, os.sep, os.sep)
  24. self.ovpn_config_path = os.path.abspath(ovpn_config_path)
  25. def f7(self, seq):
  26. seen = set()
  27. seen_add = seen.add
  28. return [x for x in seq if not (x in seen or seen_add(x))]
  29. def get_vpn_vars(self):
  30. with open(self.default_config_file, "r") as f:
  31. default_config = f.read()
  32. variables = re.findall('\$\{([^}]+)}', default_config)
  33. variables += ["KEY_COUNTRY", "KEY_PROVINCE", "KEY_CITY", "KEY_ORG", "KEY_EMAIL"]
  34. variables = self.f7(variables)
  35. return variables
  36. def get_client_vars(self, vpn_name):
  37. default_client_config_path = self.get_client_default_config_path(vpn_name)
  38. if not os.path.exists(default_client_config_path):
  39. return None
  40. with open(default_client_config_path, "r") as f:
  41. default_config = f.read()
  42. variables = re.findall('\$\{([^}]+)}', default_config)
  43. variables = self.f7(variables)
  44. return variables
  45. def get_base_dir(self, vpn_name):
  46. return "%s%s%s%s" % (self.ovpn_config_path, os.sep, vpn_name, os.sep)
  47. def get_config_path(self, vpn_name):
  48. return "%s%s%s.conf" % (self.ovpn_config_path, os.sep, vpn_name)
  49. def get_vpn_variables_path(self, vpn_name):
  50. base_dir = self.get_base_dir(vpn_name)
  51. return "%svpngen.json" % base_dir
  52. def get_easy_rsa_dir(self, vpn_name):
  53. base_dir = self.get_base_dir(vpn_name)
  54. return "%seasy-rsa%s" % (base_dir, os.sep)
  55. def get_easy_rsa_key_dir(self, vpn_name):
  56. easyrsadir = self.get_easy_rsa_dir(vpn_name)
  57. return "%skeys%s" % (easyrsadir, os.sep)
  58. def get_pkitool_path(self, vpn_name):
  59. easyrsadir = self.get_easy_rsa_dir(vpn_name)
  60. return "%spkitool" % easyrsadir
  61. def get_client_default_config_path(self, vpn_name):
  62. base_dir = self.get_base_dir(vpn_name)
  63. return "%s%sclients/client.conf" % (base_dir, os.sep)
  64. def get_client_dir(self, vpn_name, client_name):
  65. base_dir = self.get_base_dir(vpn_name)
  66. return "%sclients%s%s-%s%s" % (base_dir, os.sep, client_name, vpn_name, os.sep)
  67. def get_client_config_path(self, vpn_name, client_name):
  68. client_dir = self.get_client_dir(vpn_name, client_name)
  69. return "%s%s-%s.conf" % (client_dir, client_name, vpn_name)
  70. def get_client_generated_files_paths(self, vpn_name, client_name):
  71. keys_dir = self.get_easy_rsa_key_dir(vpn_name,)
  72. return [
  73. "%s%s.crt" % (keys_dir, client_name),
  74. "%s%s.key" % (keys_dir, client_name)
  75. ]
  76. def get_client_tarball_path(self, vpn_name, client_name):
  77. base_dir = self.get_base_dir(vpn_name)
  78. return "%sclients%s%s-%s.tar.bz2" % (base_dir, os.sep, client_name, vpn_name)
  79. def get_server_needed_files_paths(self, vpn_name):
  80. keys_dir = self.get_easy_rsa_key_dir(vpn_name)
  81. return [
  82. "%sca.crt" % keys_dir,
  83. "%sta.key" % keys_dir
  84. ]
  85. def get_all_needed_files_paths(self, vpn_name, client_name):
  86. return self.get_client_generated_files_paths(vpn_name, client_name) +\
  87. self.get_server_needed_files_paths(vpn_name)
  88. def setup_vars(self, vpn_name, variables):
  89. easyrsadir = self.get_easy_rsa_dir(vpn_name)
  90. os.environ["KEY_COUNTRY"] = variables['KEY_COUNTRY']
  91. os.environ["KEY_PROVINCE"] = variables['KEY_PROVINCE']
  92. os.environ["KEY_CITY"] = variables['KEY_CITY']
  93. os.environ["KEY_ORG"] = variables['KEY_ORG']
  94. os.environ["KEY_OU"] = variables['KEY_ORG']
  95. os.environ["KEY_CN"] = variables['KEY_ORG']
  96. os.environ["KEY_NAME"] = variables['KEY_ORG']
  97. os.environ["KEY_EMAIL"] = variables['KEY_EMAIL']
  98. os.environ["KEY_SIZE"] = variables['KEY_SIZE']
  99. os.environ["CA_EXPIRE"] = variables['CA_EXPIRE']
  100. os.environ["KEY_EXPIRE"] = variables['KEY_EXPIRE']
  101. os.environ["EASY_RSA"] = easyrsadir
  102. os.environ["OPENSSL"] = "openssl"
  103. os.environ["PKCS11TOOL"] = "pkcs11-tool"
  104. os.environ["GREP"] = "grep"
  105. os.environ["KEY_CONFIG"] = "%s%s" % (easyrsadir, "openssl.cnf")
  106. os.environ["KEY_DIR"] = "%s%s" % (easyrsadir, "keys")
  107. os.environ["PKCS11_MODULE_PATH"] = "dummy"
  108. os.environ["PKCS11_PIN"] = "dummy"
  109. def create_vpn(self, vpn_name, variables):
  110. base_dir = self.get_base_dir(vpn_name)
  111. conf_file = self.get_config_path(vpn_name)
  112. conf_vpngen_file = self.get_vpn_variables_path(vpn_name)
  113. if os.path.exists(base_dir) or os.path.exists(conf_file):
  114. return VpnGenError.VpnAlreadyExists
  115. with open(self.default_config_file, "r") as f:
  116. default_config = f.read()
  117. variables['name'] = vpn_name
  118. for variable in variables:
  119. default_config = default_config.replace("${%s}" % variable, variables[variable])
  120. os.makedirs(base_dir)
  121. with open(conf_file, "w") as f:
  122. f.write(default_config)
  123. os.rmdir(base_dir)
  124. shutil.copytree(self.default_config_base_dir, base_dir)
  125. curdir = os.curdir
  126. easyrsadir = self.get_easy_rsa_dir(vpn_name)
  127. pkitool = self.get_pkitool_path(vpn_name)
  128. os.chdir(easyrsadir)
  129. self.setup_vars(vpn_name, variables)
  130. call([".%sclean-all" % os.sep])
  131. call([pkitool, "--initca", "-batch"])
  132. call([pkitool, "--server", "server", "-batch"])
  133. call([".%sbuild-dh" % os.sep])
  134. call(["openssl", "ca", "-gencrl",
  135. "-keyfile", "keys%sca.key" % os.sep,
  136. "-cert", "keys%sca.crt" % os.sep,
  137. "-out", "keys%scrl.pem" % os.sep,
  138. "-config", "openssl.cnf"])
  139. del os.environ["KEY_OU"]
  140. del os.environ["KEY_CN"]
  141. del os.environ["KEY_NAME"]
  142. call(["openvpn", "--genkey", "--secret", "keys%sta.key" % os.sep])
  143. with open(conf_vpngen_file, "w") as f:
  144. json.dump({'variables': variables}, f, indent=4, separators=(',', ': '))
  145. os.chdir(curdir)
  146. return VpnGenError.Success
  147. def remove_vpn(self, vpn_name):
  148. base_dir = self.get_base_dir(vpn_name)
  149. conf_file = self.get_config_path(vpn_name)
  150. if not os.path.exists(base_dir) and not os.path.exists(conf_file):
  151. return VpnGenError.VpnDoesNotExists
  152. os.remove(conf_file)
  153. shutil.rmtree(base_dir)
  154. return VpnGenError.Success
  155. def create_client(self, vpn_name, client_name, variables):
  156. base_dir = self.get_base_dir(vpn_name)
  157. if not os.path.exists(base_dir):
  158. return VpnGenError.VpnDoesNotExists
  159. client_dir = self.get_client_dir(vpn_name, client_name)
  160. if os.path.exists(client_dir):
  161. return VpnGenError.ClientAlreadyExists
  162. curdir = os.curdir
  163. easyrsadir = self.get_easy_rsa_dir(vpn_name)
  164. pkitool = self.get_pkitool_path(vpn_name)
  165. os.chdir(easyrsadir)
  166. self.setup_vars(vpn_name, variables)
  167. os.environ["KEY_CN"] = client_name
  168. os.environ["KEY_NAME"] = client_name
  169. call([pkitool, client_name])
  170. os.chdir(curdir)
  171. self.build_client(vpn_name, client_name, variables)
  172. return VpnGenError.Success
  173. def remove_client(self, vpn_name, client_name):
  174. base_dir = self.get_base_dir(vpn_name)
  175. if not os.path.exists(base_dir):
  176. return VpnGenError.VpnDoesNotExists
  177. client_dir = self.get_client_dir(vpn_name, client_name)
  178. if not os.path.exists(client_dir):
  179. return VpnGenError.ClientDoesNotExists
  180. return VpnGenError.Success
  181. def rebuild_clients(self, vpn_name):
  182. base_dir = self.get_base_dir(vpn_name)
  183. return VpnGenError.Success
  184. def build_client(self, vpn_name, client_name, variables):
  185. client_dir = self.get_client_dir(vpn_name, client_name)
  186. client_conf_file = self.get_client_config_path(vpn_name, client_name)
  187. client_default_config_path = self.get_client_default_config_path(vpn_name)
  188. with open(client_default_config_path, "r") as f:
  189. client_default_config = f.read()
  190. variables['name'] = vpn_name
  191. variables['client'] = client_name
  192. for variable in variables:
  193. client_default_config = client_default_config.replace("${%s}" % variable, variables[variable])
  194. os.makedirs(client_dir)
  195. with open(client_conf_file, "w") as f:
  196. f.write(client_default_config)
  197. files_paths = self.get_all_needed_files_paths(vpn_name, client_name)
  198. for file_path in files_paths:
  199. split = os.path.splitext(file_path)
  200. dest = "%s%s-%s%s" % (client_dir, os.path.basename(split[0]), vpn_name, split[1])
  201. shutil.copy(file_path, dest)
  202. files_names = glob.glob("%s%s*" % (client_dir, os.sep))
  203. files_names = list(map(lambda file_path: os.path.basename(file_path), files_names))
  204. call(["tar", "cfj", self.get_client_tarball_path(vpn_name, client_name),
  205. "-C", client_dir] + files_names)