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.

150 lines
3.8 KiB

8 years ago
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. from peewee import *
  4. from datetime import datetime
  5. import os.path
  6. import subprocess
  7. from models.authority import Authority
  8. from models.certificate import Certificate
  9. from models.request import SignRequest
  10. from paths import *
  11. class UserSSHRequest(SignRequest):
  12. def __init__(self, req_id, user_name, root_requested, key_data):
  13. super(UserSSHRequest, self).__init__(req_id)
  14. self.user_name = user_name
  15. self.root_requested = root_requested
  16. self.key_data = key_data
  17. @property
  18. def name(self):
  19. return "User: %s [R:%d]" % (self.user_name, int(self.root_requested))
  20. @property
  21. def fields(self):
  22. return [
  23. ("User name", self.user_name),
  24. ("Root access requested", 'yes' if self.root_requested else 'no')
  25. ]
  26. @property
  27. def receiver(self):
  28. return self.user_name
  29. class HostSSHRequest(SignRequest):
  30. def __init__(self, req_id, host_name, key_data):
  31. super(HostSSHRequest, self).__init__(req_id)
  32. self.host_name = host_name
  33. self.key_data = key_data
  34. @property
  35. def name(self):
  36. return "Hostname: %s" % self.host_name
  37. @property
  38. def fields(self):
  39. return [
  40. ("Hostname", self.host_name)
  41. ]
  42. @property
  43. def receiver(self):
  44. return self.host_name
  45. class SSHAuthority(Authority):
  46. request_allowed = [ UserSSHRequest, HostSSHRequest, ]
  47. key_algorithm = 'ed25519'
  48. cert_validity = '+52w'
  49. def __bool__(self):
  50. """
  51. Check if key pair already exists
  52. """
  53. keys_pair_exist = os.path.exists(self.path) and os.path.exists(self.path + '.pub')
  54. return keys_pair_exist
  55. def generate(self):
  56. """
  57. Generate a SSHAuthority if the files associated
  58. do not exists
  59. """
  60. # check if the public key exists
  61. if not self:
  62. # let ssh-keygen do its job
  63. subprocess.check_output(['ssh-keygen',
  64. '-f', self.path,
  65. '-t', self.key_algorithm,
  66. '-C', self.name])
  67. else:
  68. raise ValueError('A CA with the same id already exists')
  69. def sign(self, request):
  70. """
  71. Sign a *SSHRequest with this certification authority
  72. """
  73. assert type(request) in self.request_allowed
  74. pub_key_path = request.destination
  75. cert = Certificate(
  76. signed_by = self,
  77. cert_id = request.req_id,
  78. date_issued = datetime.now(),
  79. receiver = self.receiver,
  80. serial_number = self.serial,
  81. validity_interval = self.user_validity,
  82. )
  83. # write the key data from the request into
  84. # the output folder
  85. with open(request.destination, 'w') as stream:
  86. stream.write(request.key_data)
  87. ca_private_key = self.path
  88. if type(request) == UserSSHRequest:
  89. login_names = [ request.user_name, ]
  90. if request.root_requested:
  91. login_names.append('root')
  92. subprocess.check_output(['ssh-keygen',
  93. '-s', ca_private_key,
  94. '-I', 'user_%s' % request.user_name,
  95. '-n', ','.join(login_names),
  96. '-V', self.user_validity,
  97. '-z', str(self.serial),
  98. pub_key_path])
  99. elif type(request) == HostSSHRequest:
  100. subprocess.check_output(['ssh-keygen',
  101. '-s', ca_private_key,
  102. '-I', 'host_%s' % request.host_name.replace('.', '_'),
  103. '-h',
  104. '-n', request.host_name,
  105. '-V', self.host_validity,
  106. '-z', str(self.serial),
  107. pub_key_path])
  108. self.serial += 1
  109. cert.save()
  110. return cert.path