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.

vpngen.py 9.5KB

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