Module: Beaker::DSL::InstallUtils::CAUtils

Included in:
PE
Defined in:
lib/beaker-pe/install/ca_utils.rb

Constant Summary collapse

PRIVATE_KEY_LENGTH =
2048
FIVE_YEARS =
5 * 365 * 24 * 60 * 60
CA_EXTENSIONS =
[
["basicConstraints", "CA:TRUE", true],
["keyUsage", "keyCertSign, cRLSign", true],
["subjectKeyIdentifier", "hash", false],
["authorityKeyIdentifier", "keyid:always", false]
]
NODE_EXTENSIONS =
[
["keyUsage", "digitalSignature", true],
["subjectKeyIdentifier", "hash", false]
]
DEFAULT_SIGNING_DIGEST =
OpenSSL::Digest::SHA256.new
DEFAULT_REVOCATION_REASON =
OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE
ROOT_CA_NAME =
"/CN=root-ca"
INT_CA_NAME =
"/CN=intermediate-ca"
EXPLANATORY_TEXT =
<<-EOT
# Root Issuer: #{ROOT_CA_NAME}
# Intermediate Issuer: #{INT_CA_NAME}
EOT

Instance Method Summary collapse

Instance Method Details

#bundle(*items) ⇒ Object


185
186
187
# File 'lib/beaker-pe/install/ca_utils.rb', line 185

def bundle(*items)
  items.map {|i| EXPLANATORY_TEXT + i.to_pem }.join("\n")
end

#create_chained_pkiObject


151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/beaker-pe/install/ca_utils.rb', line 151

def create_chained_pki
  root_key = create_private_key
  root_cert = self_signed_ca(root_key, ROOT_CA_NAME)
  root_crl = create_crl_for(root_cert, root_key)

  int_key = create_private_key
  int_csr = create_csr(int_key, INT_CA_NAME)
  int_cert = sign(root_key, root_cert, int_csr, CA_EXTENSIONS)
  int_crl = create_crl_for(int_cert, int_key)

  int_ca_bundle = bundle(int_cert, root_cert)
  int_crl_chain = bundle(int_crl, root_crl)

  {
      :root_cert => root_cert,
      :int_cert => int_cert,
      :int_ca_bundle => int_ca_bundle,
      :int_key  => int_key,
      :int_crl_chain => int_crl_chain,
  }
end

#create_crl_for(ca_cert, ca_key) ⇒ Object


109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/beaker-pe/install/ca_utils.rb', line 109

def create_crl_for(ca_cert, ca_key)
  crl = OpenSSL::X509::CRL.new
  crl.version = 1
  crl.issuer = ca_cert.subject

  ef = extension_factory_for(ca_cert)
  crl.add_extension(
    ef.create_extension(["authorityKeyIdentifier", "keyid:always", false]))
  crl.add_extension(
    OpenSSL::X509::Extension.new("crlNumber", OpenSSL::ASN1::Integer(0)))

  not_before = just_now
  crl.last_update = not_before
  crl.next_update = not_before + FIVE_YEARS
  crl.sign(ca_key, DEFAULT_SIGNING_DIGEST)

  crl
end

#create_csr(key, name) ⇒ Object


74
75
76
77
78
79
80
81
82
83
# File 'lib/beaker-pe/install/ca_utils.rb', line 74

def create_csr(key, name)
  csr = OpenSSL::X509::Request.new

  csr.public_key = key.public_key
  csr.subject = OpenSSL::X509::Name.parse(name)
  csr.version = 2
  csr.sign(key, DEFAULT_SIGNING_DIGEST)

  csr
end

#create_private_key(length = PRIVATE_KEY_LENGTH) ⇒ Object


46
47
48
# File 'lib/beaker-pe/install/ca_utils.rb', line 46

def create_private_key(length = PRIVATE_KEY_LENGTH)
  OpenSSL::PKey::RSA.new(length)
end

#extension_factory_for(ca, cert = nil) ⇒ Object


177
178
179
180
181
182
183
# File 'lib/beaker-pe/install/ca_utils.rb', line 177

def extension_factory_for(ca, cert = nil)
  ef = OpenSSL::X509::ExtensionFactory.new
  ef.issuer_certificate  = ca
  ef.subject_certificate = cert if cert

  ef
end

#generate_ca_bundle_on(host = master, targetdir = '/tmp/ca_bundle') ⇒ Hash

Generate CA bundle with root and intermediate certs, as well as CRL chain and private key for the intermediate CA, pushed to the host for use during PE install with pe_install::signing_ca

Parameters:

  • host (Host) (defaults to: master)

    The host to create CA bundle files on. Defaults to global 'master' object.

  • targetdir (String) (defaults to: '/tmp/ca_bundle')

    Location to save files on host, to be referenced in pe.conf for install.

Returns:

  • (Hash)

    File names => where they were put on the host


35
36
37
38
39
40
41
42
43
44
# File 'lib/beaker-pe/install/ca_utils.rb', line 35

def generate_ca_bundle_on(host = master, targetdir = '/tmp/ca_bundle')
  files = {}
  pki = create_chained_pki
  on(host, "mkdir -p #{targetdir}", :acceptable_exit_codes => [0])
  pki.each do |name,cert|
    create_remote_file(host, "#{targetdir}/#{name}", cert.to_s, :acceptable_exit_codes => [0])
    files["#{name}".to_sym] = "#{targetdir}/#{name}"
  end
  files
end

#just_nowObject


173
174
175
# File 'lib/beaker-pe/install/ca_utils.rb', line 173

def just_now
  Time.now - 1
end

#revoke(serial, crl, ca_key) ⇒ Object


128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/beaker-pe/install/ca_utils.rb', line 128

def revoke(serial, crl, ca_key)
  revoked = OpenSSL::X509::Revoked.new
  revoked.serial = serial
  revoked.time = Time.now
  revoked.add_extension(
    OpenSSL::X509::Extension.new("CRLReason",
                                OpenSSL::ASN1::Enumerated(DEFAULT_REVOCATION_REASON)))

  crl.add_revoked(revoked)
  extensions = crl.extensions.group_by{|e| e.oid == 'crlNumber' }
  crl_number = extensions[true].first
  unchanged_exts = extensions[false]

  next_crl_number = crl_number.value.to_i + 1
  new_crl_number_ext = OpenSSL::X509::Extension.new("crlNumber",
                                                    OpenSSL::ASN1::Integer(next_crl_number))

  crl.extensions = unchanged_exts + [new_crl_number_ext]
  crl.sign(ca_key, DEFAULT_SIGNING_DIGEST)

  crl
end

#self_signed_ca(key, name) ⇒ Object


50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/beaker-pe/install/ca_utils.rb', line 50

def self_signed_ca(key, name)
  cert = OpenSSL::X509::Certificate.new

  cert.public_key = key.public_key
  cert.subject = OpenSSL::X509::Name.parse(name)
  cert.issuer = cert.subject
  cert.version = 2
  cert.serial = rand(2**128)

  not_before = just_now
  cert.not_before = not_before
  cert.not_after = not_before + FIVE_YEARS

  ext_factory = extension_factory_for(cert, cert)
  CA_EXTENSIONS.each do |ext|
    extension = ext_factory.create_extension(*ext)
    cert.add_extension(extension)
  end

  cert.sign(key, DEFAULT_SIGNING_DIGEST)

  cert
end

#sign(ca_key, ca_cert, csr, extensions = NODE_EXTENSIONS) ⇒ Object


85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/beaker-pe/install/ca_utils.rb', line 85

def sign(ca_key, ca_cert, csr, extensions = NODE_EXTENSIONS)
  cert = OpenSSL::X509::Certificate.new

  cert.public_key = csr.public_key
  cert.subject = csr.subject
  cert.issuer = ca_cert.subject
  cert.version = 2
  cert.serial = rand(2**128)

  not_before = just_now
  cert.not_before = not_before
  cert.not_after = not_before + FIVE_YEARS

  ext_factory = extension_factory_for(ca_cert, cert)
  extensions.each do |ext|
    extension = ext_factory.create_extension(*ext)
    cert.add_extension(extension)
  end

  cert.sign(ca_key, DEFAULT_SIGNING_DIGEST)

  cert
end