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.

235 lines
6.4 KiB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. import os
  4. import os.path
  5. import sqlite3
  6. import subprocess
  7. from paths import *
  8. __doc__= """
  9. Module of classes to handle certificate requests
  10. """
  11. class SignRequest(object):
  12. def __init__(self, req_id):
  13. self.req_id = req_id
  14. @property
  15. def name(self):
  16. raise NotImplementedError()
  17. @property
  18. def fields(self):
  19. raise NotImplementedError()
  20. class UserSSHRequest(SignRequest, object):
  21. def __init__(self, req_id, user_name, root_requested, key_data):
  22. super(UserSSHRequest, self).__init__(req_id)
  23. self.user_name = user_name
  24. self.root_requested = root_requested
  25. self.key_data = key_data
  26. @property
  27. def name(self):
  28. return "User: %s [R:%d]" % (self.user_name, int(self.root_requested))
  29. @property
  30. def fields(self):
  31. return [
  32. ("User name", self.user_name),
  33. ("Root access requested", 'yes' if self.root_requested else 'no')
  34. ]
  35. def __str__(self):
  36. return ("%s %s" % (self.req_id, self.user_name))
  37. class HostSSLRequest(SignRequest, object):
  38. def __init__(self, req_id, host_name, key_data):
  39. super(HostSSLRequest, self).__init__(req_id)
  40. self.host_name = host_name
  41. self.key_data = key_data
  42. @property
  43. def name(self):
  44. return "Hostname: %s" % self.host_name
  45. @property
  46. def fields(self):
  47. return [
  48. ("Hostname", self.host_name)
  49. ]
  50. def __str__(self):
  51. return ("%s %s" % (self.req_id, self.host_name))
  52. class HostSSHRequest(SignRequest, object):
  53. def __init__(self, req_id, host_name, key_data):
  54. super(HostSSHRequest, self).__init__(req_id)
  55. self.host_name = host_name
  56. self.key_data = key_data
  57. @property
  58. def name(self):
  59. return "Hostname: %s" % self.host_name
  60. @property
  61. def fields(self):
  62. return [
  63. ("Hostname", self.host_name)
  64. ]
  65. def __str__(self):
  66. return ("%s %s" % (self.req_id, self.host_name))
  67. class Authority(object):
  68. ca_type = None
  69. def __init__(self, ca_id, name, ca_dir):
  70. self.ca_id = ca_id
  71. self.name = name
  72. self.ca_dir = ca_dir
  73. @property
  74. def path(self):
  75. return os.path.join(self.ca_dir, self.ca_id)
  76. def generate(self):
  77. raise NotImplementedError()
  78. def sign(self, request):
  79. raise NotImplementedError()
  80. class SSHAuthority(Authority):
  81. ca_type = 'ssh'
  82. key_algorithm = 'ed25519'
  83. user_validity = '+52w'
  84. host_validity = '+52w'
  85. def generate(self):
  86. if os.path.exists(self.path):
  87. raise ValueError("A CA with the same id and type already exists")
  88. subprocess.check_output(['ssh-keygen',
  89. '-f', self.path,
  90. '-t', self.key_algorithm,
  91. '-C', self.name])
  92. with open(self.path + '.serial', 'w') as stream:
  93. stream.write(str(0))
  94. def sign(self, request):
  95. assert type(request) in [UserSSHRequest, HostSSHRequest]
  96. pub_key_path = os.path.join(OUTPUT_PATH, request.req_id + '.pub')
  97. cert_path = os.path.join(OUTPUT_PATH, request.req_id + '-cert.pub')
  98. with open(self.path + '.serial', 'r') as stream:
  99. next_serial = int(stream.read())
  100. with open(self.path + '.serial', 'w') as stream:
  101. stream.write(str(next_serial + 1))
  102. with open(pub_key_path, 'w') as stream:
  103. stream.write(request.key_data)
  104. ca_private_key = self.path
  105. if type(request) == UserSSHRequest:
  106. login_names = [request.user_name]
  107. if request.root_requested:
  108. login_names.append('root')
  109. subprocess.check_output(['ssh-keygen',
  110. '-s', ca_private_key,
  111. '-I', 'user_%s' % request.user_name,
  112. '-n', ','.join(login_names),
  113. '-V', self.user_validity,
  114. '-z', str(next_serial),
  115. pub_key_path])
  116. elif type(request) == HostSSHRequest:
  117. subprocess.check_output(['ssh-keygen',
  118. '-s', ca_private_key,
  119. '-I', 'host_%s' % request.host_name.replace('.', '_'),
  120. '-h',
  121. '-n', request.host_name,
  122. '-V', self.host_validity,
  123. '-z', str(next_serial),
  124. pub_key_path])
  125. return cert_path
  126. class SSLAuthority(Authority):
  127. ca_type = 'ssl'
  128. ca_key_algorithm = 'des3'
  129. key_length = '4096'
  130. key_algorithm = 'sha256'
  131. ca_validity = '365'
  132. cert_validity = '365'
  133. def generate(self):
  134. if os.path.exists(self.path):
  135. raise ValueError("A CA with the same id and type already exists")
  136. subprocess.check_output(['openssl',
  137. 'genrsa',
  138. '-%s'%self.ca_key_algorithm,
  139. '-out', '%s'%(self.path),
  140. self.key_length])
  141. subprocess.check_output(['openssl',
  142. 'req',
  143. '-new',
  144. '-x509',
  145. '-days', self.ca_validity,
  146. '-key', self.path,
  147. # '-extensions', 'v3_ca'
  148. '-out', "%s.pub"%self.path,
  149. # '-config', "%s.conf"%self.path
  150. ])
  151. with open(self.path + '.serial', 'w') as stream:
  152. stream.write(str(0))
  153. def sign(self, request):
  154. OUTPUT_PATH
  155. assert type(request) in [HostSSLRequest]
  156. pub_key_path = os.path.join(OUTPUT_PATH, request.req_id + '.pub')
  157. cert_path = os.path.join(OUTPUT_PATH, request.req_id + '-cert.pub')
  158. with open(self.path + '.serial', 'r') as stream:
  159. next_serial = int(stream.read())
  160. with open(self.path + '.serial', 'w') as stream:
  161. stream.write(str(next_serial + 1))
  162. with open(pub_key_path, 'w') as stream:
  163. stream.write(request.key_data)
  164. ca_private_key = self.path
  165. # openssl x509 -req -days 360 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt # print()
  166. subprocess.check_output(['openssl',
  167. 'x509',
  168. '-req',
  169. '-days', self.ca_validity,
  170. '-in', pub_key_path,
  171. '-CA', "%s.pub"%self.path,
  172. '-CAkey', self.path,
  173. '-CAcreateserial',
  174. '-out', cert_path,
  175. '-%s'%self.key_algorithm])
  176. return cert_path