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.

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