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.

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