Easy CA management
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.

310 lines
8.2 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. import cmd
  4. import hashlib
  5. import json
  6. import os
  7. import os.path
  8. import shutil
  9. import sqlite3
  10. import tempfile
  11. from certificate_requests import *
  12. from paths import *
  13. __doc__= """
  14. Define class to interact with certificate requests and Certification Authority
  15. """
  16. class CAManager(object):
  17. """
  18. Middleware to interact with ssh-keygen
  19. """
  20. def __init__(self, path):
  21. self.path = path
  22. self.ca = CALookup(self.ssh_ca_dir, self.ssl_ca_dir)
  23. def __enter__(self):
  24. """
  25. Enter a context block, connect to database
  26. """
  27. self.conn = sqlite3.connect(self.db_path)
  28. self.ca.conn = self.conn
  29. return self
  30. def __exit__(self, exc_type, exc_value, traceback):
  31. """
  32. Exit a context block, disconnect from database
  33. """
  34. if exc_type is not None:
  35. print(exc_type, exc_value)
  36. print(traceback)
  37. self.ca.conn = None
  38. self.conn.close()
  39. @property
  40. def db_path(self):
  41. return os.path.join(self.path, 'ca_manager.db')
  42. @property
  43. def ssh_ca_dir(self):
  44. return os.path.join(self.path, 'ssh_cas')
  45. def _get_ssh_ca_path(self, ca_id):
  46. cas_dir = self.ssh_ca_dir
  47. return os.path.join(cas_dir, ca_id)
  48. @property
  49. def ssl_ca_dir(self):
  50. return os.path.join(self.path, 'ssl_cas')
  51. def _get_ssl_ca_path(self, ca_id):
  52. cas_dir = self.ssl_ca_dir
  53. return os.path.join(cas_dir, ca_id)
  54. def create_ssh_ca(self, ca_id, ca_name):
  55. """
  56. Create a new ssh certification authority, insert
  57. it into the database
  58. """
  59. ca_path = self._get_ssh_ca_path(ca_id)
  60. authority = SSHAuthority(ca_id, ca_name, ca_path)
  61. authority.generate()
  62. c = self.conn.cursor()
  63. c.execute("""INSERT INTO cas VALUES (?, ?, 'ssh')""",
  64. (ca_id, ca_name))
  65. self.conn.commit()
  66. def create_ssl_ca(self, ca_id, ca_name):
  67. """
  68. Create a new ssl certification authority, insert
  69. it into the database
  70. """
  71. ca_path = self._get_ssl_ca_path(ca_id)
  72. authority = SSLAuthority(ca_id, ca_name, ca_path)
  73. authority.generate()
  74. c = self.conn.cursor()
  75. c.execute("""INSERT INTO cas VALUES (?, ?, 'ssl')""",
  76. (ca_id, ca_name))
  77. self.conn.commit()
  78. def get_cas_list(self):
  79. """
  80. Get all the certification authorities saved in
  81. the database
  82. """
  83. c = self.conn.cursor()
  84. c.execute("""SELECT id, name, type FROM cas""")
  85. return c.fetchall()
  86. def get_ca(self, ca_id):
  87. """
  88. Get a specific certification authority from the database
  89. """
  90. c = self.conn.cursor()
  91. c.execute("""SELECT name, type FROM cas WHERE id = ?""", (ca_id, ))
  92. result = c.fetchone()
  93. if not result:
  94. raise ValueError('Unknown CA "%s"'%ca_id)
  95. ca_name, ca_type = result
  96. if ca_type == 'ssh':
  97. ca_path = self._get_ssh_ca_path(ca_id)
  98. return SSHAuthority(ca_id, ca_name, ca_path)
  99. elif ca_type == 'ssl':
  100. ca_path = self._get_ssl_ca_path(ca_id)
  101. return SSLAuthority(ca_id, ca_name, ca_path)
  102. def get_requests(self, ca_type=None):
  103. req_objs = []
  104. for request_name in os.listdir(REQUESTS_PATH):
  105. request_path = os.path.join(REQUESTS_PATH, request_name)
  106. with open(request_path, 'r') as stream:
  107. req = json.load(stream)
  108. if ca_type and not req['keyType'].startswith("%s_"%ca_type):
  109. continue
  110. if req['keyType'] == 'ssh_user':
  111. user_name = req['userName']
  112. root_requested = req['rootRequested']
  113. key_data = req['keyData']
  114. req_objs.append(
  115. UserSSHRequest(
  116. request_name, user_name, root_requested, key_data))
  117. elif req['keyType'] == 'ssh_host':
  118. host_name = req['hostName']
  119. key_data = req['keyData']
  120. req_objs.append(
  121. HostSSHRequest(
  122. request_name, host_name, key_data))
  123. elif req['keyType'] == 'ssl_host':
  124. host_name = req['hostName']
  125. key_data = req['keyData']
  126. req_objs.append(
  127. HostSSLRequest(
  128. request_name, host_name, key_data))
  129. return req_objs
  130. def drop_request(self, request):
  131. os.unlink(os.path.join(REQUESTS_PATH, request.req_id))
  132. class CALookup(object):
  133. """
  134. Proxy to interact with the database, get CA as element or as list
  135. """
  136. def __init__(self, ssh_ca_dir, ssl_ca_dir):
  137. """
  138. The connection attribute is setted by the CAManager instance
  139. when used
  140. """
  141. self.conn = None
  142. self.ssh_ca_dir = ssh_ca_dir
  143. self.ssl_ca_dir = ssl_ca_dir
  144. def __iter__(self):
  145. c = self.conn.cursor()
  146. c.execute("""SELECT id, name, type FROM cas""")
  147. return iter(c.fetchall())
  148. def __delitem__(self, ca_id):
  149. """
  150. Delete a specific certification authority from the database
  151. """
  152. c = self.conn.cursor()
  153. c.execute("""DELETE FROM cas WHERE id = ?""", (ca_id, ))
  154. def __getitem__(self, ca_id):
  155. """
  156. Get a specific certification authority from the database
  157. """
  158. c = self.conn.cursor()
  159. c.execute("""SELECT name, type FROM cas WHERE id = ?""", (ca_id, ))
  160. result = c.fetchone()
  161. if not result:
  162. raise ValueError('Unknown CA "%s"' % ca_id)
  163. ca_name, ca_type = result
  164. if ca_type.lower() == 'ssh':
  165. return SSHAuthority(ca_id, ca_name, self.ssh_ca_dir)
  166. elif ca_type.lower() == 'ssl':
  167. return SSLAuthority(ca_id, ca_name, self.ssl_ca_dir)
  168. def __setitem__(self, ca_id, ca_value):
  169. """
  170. Create a new certification authority, insert
  171. it into the database
  172. """
  173. def init_manager(paths):
  174. """
  175. Initiate the manager by creating the
  176. directories to store CAs and requests.
  177. Create a database to store the information
  178. """
  179. db_path = os.path.join(paths[0], 'ca_manager.db')
  180. directories = ['ssh_cas', 'ssl_cas']
  181. # ensure the directories needed by CAManager
  182. # exists
  183. for dirpath in paths:
  184. if not os.path.exists(dirpath):
  185. os.makedirs(dirpath)
  186. # ensure ssh_cas ad ssl_cas directories
  187. # exists in MANAGER_PATH
  188. for dirname in directories:
  189. dirpath = os.path.join(paths[0], dirname)
  190. if not os.path.exists(dirpath):
  191. os.mkdir(dirpath)
  192. # ensure the database exists
  193. # in MANAGER_PATH
  194. if not os.path.exists(db_path):
  195. conn = sqlite3.connect(db_path)
  196. c = conn.cursor()
  197. c.execute("""CREATE TABLE cas (id text, name text, type text)""")
  198. conn.commit()
  199. conn.close()
  200. def list_cas(ca_manager):
  201. for ca_id, ca_name, ca_type in ca_manager.get_cas_list():
  202. print("- [%3s] %-15s (%s)" % (ca_type, ca_id, ca_name))
  203. def sign_request(ca_manager, request_name, authority_name):
  204. request = None
  205. try:
  206. authority = ca_manager.get_ca(authority_name)
  207. except IndexError:
  208. print("Could not find CA '%d'" % choosen_ca)
  209. return
  210. requests = ca_manager.get_requests()
  211. for i in requests:
  212. if str(i) == request_name:
  213. request = i
  214. if request is None:
  215. raise(IndexError)
  216. h = hashlib.sha256()
  217. h.update(request.key_data.encode('utf-8'))
  218. print("Request hash: %s" % h.hexdigest())
  219. print("You are about to sign this request with the following CA:")
  220. confirm = input('Proceed? (type yes)> ')
  221. if confirm != 'yes':
  222. print ("user aborT")
  223. return
  224. cert_path = authority.sign(request)
  225. ca_manager.drop_request(request)
  226. shutil.copy(cert_path, os.path.join(RESULTS_PATH, request.req_id))
  227. if __name__ == '__main__':
  228. from ca_shell import CAManagerShell
  229. init_manager([
  230. MANAGER_PATH,
  231. REQUESTS_PATH,
  232. OUTPUT_PATH,
  233. RESULTS_PATH,
  234. ])
  235. with CAManager(MANAGER_PATH) as ca_manager:
  236. CAManagerShell(ca_manager).cmdloop()