Class: PuppetECDSAGen

Inherits:
Object show all
Defined in:
modules/puppetmaster/files/puppet_ecdsacert.rb

Overview

ECDSA certificates generator class Generates the cert, the CSR, and sends the signing request to the puppetmaster

Class Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args) ⇒ PuppetECDSAGen

Returns a new instance of PuppetECDSAGen.



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'modules/puppetmaster/files/puppet_ecdsacert.rb', line 47

def initialize(args)
  @config = {}
  self.class.confkeys.each do |key|
    @config[key] = args[key]
  end

  parse_config args[:configfile] if args[:configfile]

  @common_name = args[:common_name]
  @all_alt_names = args[:altnames].map do |name|
    begin
      _ = IPAddr.new name
      "IP:#{name}"
    rescue IPAddr::InvalidAddressError
      "DNS:#{name}"
    end
  end

  # We need the CN in the SAN if we want to validate against it
  @all_alt_names << "DNS:#{@common_name}"

  Log.info "Creating and signing ECDSA certificate for #{@common_name}"
  Log.debug "subjectAltNames: #{@all_alt_names}"
  Log.debug "Using NIST curve #{@config[:asn1_oid]}"
end

Class Attribute Details

.confkeysObject

Returns the value of attribute confkeys.



43
44
45
# File 'modules/puppetmaster/files/puppet_ecdsacert.rb', line 43

def confkeys
  @confkeys
end

Instance Method Details

#cleanupObject



163
164
165
166
# File 'modules/puppetmaster/files/puppet_ecdsacert.rb', line 163

def cleanup
  Log.debug "Removing file #{csr_path} if present"
  File.delete csr_path if File.exists? csr_path
end

#csr_alt_names(csr) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'modules/puppetmaster/files/puppet_ecdsacert.rb', line 122

def csr_alt_names(csr)
  extensions = [
    OpenSSL::X509::ExtensionFactory.new.create_extension(
      'subjectAltName', @all_alt_names.join(',')
    )
  ]
  # add SAN extension to the CSR
  attribute_values = OpenSSL::ASN1::Set [OpenSSL::ASN1::Sequence(extensions)]
  [
    OpenSSL::X509::Attribute.new('extReq', attribute_values),
    OpenSSL::X509::Attribute.new('msExtReq', attribute_values)
  ].each do |attribute|
    csr.add_attribute attribute
  end
end

#csr_pathObject



93
94
95
# File 'modules/puppetmaster/files/puppet_ecdsacert.rb', line 93

def csr_path
  File.join '/tmp', "#{@common_name}.csr.pem"
end

#generate_csr(ec_domain_key) ⇒ Object

Generates and writes out the CSR



98
99
100
101
102
103
104
105
106
107
108
109
# File 'modules/puppetmaster/files/puppet_ecdsacert.rb', line 98

def generate_csr(ec_domain_key)
  ec_public = OpenSSL::PKey::EC.new(@config[:asn1_oid])
  ec_public.public_key = ec_domain_key.public_key
  csr = OpenSSL::X509::Request.new
  csr.version = 0
  csr.subject = subject
  csr.public_key = ec_public
  csr_alt_names csr
  csr.sign ec_domain_key, OpenSSL::Digest::SHA256.new
  Log.debug "Generated CSR at #{csr_path}"
  File.open(csr_path, "w", 0o0644) {|f| f.write(csr.to_pem)}
end

#generate_ecdsa_keyObject

Generates and writes out the private key



83
84
85
86
87
88
89
90
91
# File 'modules/puppetmaster/files/puppet_ecdsacert.rb', line 83

def generate_ecdsa_key
  ec_domain_key = OpenSSL::PKey::EC.new(@config[:asn1_oid])
  ec_domain_key.generate_key

  private_key_file = File.join @config[:key_dir], "#{@common_name}.key"
  Log.info "Storing the private key in #{private_key_file}"
  File.open(private_key_file, 'w', 0o0640) { |f| f.write(ec_domain_key.to_pem) }
  ec_domain_key
end

#httpsObject



138
139
140
141
142
143
144
145
146
# File 'modules/puppetmaster/files/puppet_ecdsacert.rb', line 138

def https
  http = Net::HTTP.new(@config[:puppetca], 8140)
  http.use_ssl = true

  # TODO: fix this
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE

  http
end

#parse_config(filename) ⇒ Object



73
74
75
76
77
78
79
80
# File 'modules/puppetmaster/files/puppet_ecdsacert.rb', line 73

def parse_config(filename)
  data = File.read filename
  conffile = YAML.load(data)
  conffile.each do |key, val|
    k = key.to_sym
    @config[k] = val if self.class.confkeys.include? k
  end
end

#signObject



148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'modules/puppetmaster/files/puppet_ecdsacert.rb', line 148

def sign
  req = Net::HTTP::Put.new("/production/certificate_request/#{@common_name}",
                           'Content-Type' => 'text/plain')
  req.body = File.read csr_path
  Log.info "Submitting CSR for signing to #{@config[:puppetca]}"
  resp, _ = https.request(req)
  unless resp.code == '200'
    fail(PuppetECDSAGenError,
         format('Signing request to %s failed with code %s: %s',
                @config[:puppetca], resp.code, resp.body)
        )
  end
  Log.info "CSR request succeeded"
end

#subjectObject



111
112
113
114
115
116
117
118
119
120
# File 'modules/puppetmaster/files/puppet_ecdsacert.rb', line 111

def subject
  subject = OpenSSL::X509::Name.new [
    ['CN', @common_name],
    ['O', @config[:organization]],
    ['C', @config[:country]],
    ['ST', @config[:state]],
    ['L', @config[:locality]]
  ]
  subject
end