Giuseppe Lavagetto has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/327189 )
Change subject: puppet-ecdsacert: various tweaks ...................................................................... puppet-ecdsacert: various tweaks - Unify puppet-ecdsacert and puppet-wildcardsign - Add logging - Add cleanup logic - Add the CN of the cert to the SAN list automatically Change-Id: I8c48eb6c4f59b17005036f4ffe9785a1e4a11dd4 --- M modules/puppetmaster/files/puppet_ecdsacert.rb D modules/puppetmaster/files/puppet_wildcardsign.rb M modules/puppetmaster/manifests/init.pp 3 files changed, 83 insertions(+), 54 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/operations/puppet refs/changes/89/327189/1 diff --git a/modules/puppetmaster/files/puppet_ecdsacert.rb b/modules/puppetmaster/files/puppet_ecdsacert.rb index 0a33719..168e7ec 100755 --- a/modules/puppetmaster/files/puppet_ecdsacert.rb +++ b/modules/puppetmaster/files/puppet_ecdsacert.rb @@ -1,16 +1,49 @@ #!/usr/bin/env ruby # Copyright (c) 2016 Giuseppe Lavagetto, Wikimedia Foundation # Loosely based on https://github.com/ripienaar/mcollective-choria/blob/master/lib/mcollective/util/choria.rb +require 'json' +require 'logger' require 'net/http' require 'openssl' -require 'yaml' require 'optparse' -require 'json' +require 'yaml' + +require 'puppet' +require 'puppet/ssl/certificate_authority' +require 'puppet/util/command_line' OpenSSL::PKey::EC.send(:alias_method, :private?, :private_key?) class PuppetECDSAGenError < StandardError end + +args = { + configfile: nil, + cert_dir: '/var/lib/puppet/ssl/certs', + key_dir: '/var/lib/puppet/ssl/private_keys', + organization: 'Wikimedia Foundation, Inc', + country: 'US', + state: 'California', + locality: 'San Francisco', + puppetca: 'puppet', + altnames: [], + asn1_oid: 'prime256v1' +} + + +Log = Logger.new(STDOUT) + +Log.level = Logger::INFO +Log.formatter = proc do |severity, datetime, progname, msg| + date_format = datetime.strftime("%Y-%m-%d %H:%M:%S") + progname = "puppet-ecdsacert" + if severity == "INFO" or severity == "WARN" + "[#{date_format}] #{severity} (#{progname}): #{msg}\n" + else + "[#{date_format}] #{severity} (#{progname}): #{msg}\n" + end +end + # Ecdsa certificates generator class # Generates the cert, the CSR, and sends the signing request to the puppetmaster @@ -28,7 +61,12 @@ parse_config args[:configfile] if args[:configfile] @common_name = args[:common_name] - @dns_alt_names = args[:altnames] + @dns_alt_names = args[:altnames].map { |domain| "DNS:#{domain}" } + # We need the CN in the SAN if we want to validate against it + @dns_alt_names << "DNS:#{@common_name}" + Log.info "Creating and signing ECDSA certificate for #{@common_name}" + Log.info "DNS subjectAltNames: #{@dns_alt_names}" + Log.info "Using NIST curve #{@config[:asn1_oid]}" end def parse_config(filename) @@ -46,6 +84,7 @@ 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 @@ -64,6 +103,7 @@ csr.public_key = ec_public csr_alt_names csr csr.sign ec_domain_key, OpenSSL::Digest::SHA256.new + Log.info "Generated CSR at #{csr_path}" File.open(csr_path, "w", 0o0644) {|f| f.write(csr.to_pem)} end @@ -79,10 +119,9 @@ end def csr_alt_names(csr) - san_list = @dns_alt_names.map { |domain| "DNS:#{domain}" } extensions = [ OpenSSL::X509::ExtensionFactory.new.create_extension( - 'subjectAltName', san_list.join(',') + 'subjectAltName', @dns_alt_names.join(',') ) ] # add SAN extension to the CSR @@ -109,27 +148,42 @@ 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) fail(PuppetECDSAGenError, format('Signing request to %s failed with code %s: %s', @config[:puppetca], resp.code, resp.body) ) unless resp.code == '200' - puts resp.message + Log.info "CSR request succeeded" + end + + def cleanup + Log.debug "Removing file #{csr_path} if present" + File.delete csr_path if File.exists? csr_path end end -args = { - configfile: nil, - cert_dir: '/var/lib/puppet/ssl/certs', - key_dir: '/var/lib/puppet/ssl/private_keys', - organization: 'Wikimedia Foundation', - country: 'US', - state: 'CA', - locality: 'San Francisco', - puppetca: 'puppet', - altnames: [], - asn1_oid: 'prime256v1' -} +module Puppet + module SSL + # Extend the signing checks + module CertificateAuthorityExtensions + def check_internal_signing_policies(hostname, csr, _allow_dns_alt_names) + super(hostname, csr, true) + rescue Puppet::SSL::CertificateAuthority::CertificateSigningError => e + if e.message.start_with?("CSR '#{csr.name}' subjectAltName contains a wildcard") + true + else + raise + end + end + end + # Extend the base class + class CertificateAuthority + prepend Puppet::SSL::CertificateAuthorityExtensions + end + end +end + OptionParser.new do |opts| opts.banner = "Usage: puppetecdsamanager [-c configfile] [-a SAN1,SAN2] common-name " @@ -143,7 +197,7 @@ args[:common_name] = ARGV.shift || '' -fail(PuppetECDSAGenError, 'You must provide a common name') unless args[:common_name] +fail(PuppetECDSAGenError, 'You must provide a common name') unless args[:common_name] != '' begin manager = PuppetECDSAGen.new args @@ -151,6 +205,13 @@ manager.generate_csr(ecdsa_key) manager.sign rescue PuppetECDSAGenError => e - puts "Error: #{e.message}" + Log.error "#{e.message}" + manager.cleanup exit 1 +else + manager.cleanup unless manager.nil? end + +Log.info "Now signing the certificate" +# Now sign the cert using puppet's own commandline interpreter +Puppet::Util::CommandLine.new('cert', ['sign', args[:common_name]]).execute diff --git a/modules/puppetmaster/files/puppet_wildcardsign.rb b/modules/puppetmaster/files/puppet_wildcardsign.rb deleted file mode 100755 index 6818bac..0000000 --- a/modules/puppetmaster/files/puppet_wildcardsign.rb +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env ruby -# Copyright (c) 2016 Giuseppe Lavagetto, Wikimedia Foundation -# Shameful hack to allow signing certs with wildcard SANs when we want it -# Useful for issuing internal certificates for exposing services. -require 'puppet' -require 'puppet/ssl/certificate_authority' -require 'puppet/util/command_line' -module Puppet - module SSL - # Extend the signing checks - module CertificateAuthorityExtensions - def check_internal_signing_policies(hostname, csr, _allow_dns_alt_names) - super(hostname, csr, true) - rescue Puppet::SSL::CertificateAuthority::CertificateSigningError => e - if e.message.start_with?("CSR '#{csr.name}' subjectAltName contains a wildcard") - true - else - raise - end - end - end - # Extend the base class - class CertificateAuthority - prepend Puppet::SSL::CertificateAuthorityExtensions - end - end -end - -name = ARGV.shift || fail('The name of the certificate to sign must be provided.') -Puppet::Util::CommandLine.new('cert', ['sign', name]).execute diff --git a/modules/puppetmaster/manifests/init.pp b/modules/puppetmaster/manifests/init.pp index 4f32975..263111d 100644 --- a/modules/puppetmaster/manifests/init.pp +++ b/modules/puppetmaster/manifests/init.pp @@ -183,15 +183,13 @@ } # Small utility to generate ECDSA certs and submit the CSR to the puppet master file { '/usr/local/bin/puppet-ecdsacert': + ensure => absent, source => 'puppet:///modules/puppetmaster/puppet_ecdsacert.rb', mode => '0550', owner => 'root', group => 'root', } file { '/usr/local/bin/puppet-wildcardsign': - source => 'puppet:///modules/puppetmaster/puppet_wildcardsign.rb', - mode => '0550', - owner => 'root', - group => 'root', + ensure => absent, } } -- To view, visit https://gerrit.wikimedia.org/r/327189 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I8c48eb6c4f59b17005036f4ffe9785a1e4a11dd4 Gerrit-PatchSet: 1 Gerrit-Project: operations/puppet Gerrit-Branch: production Gerrit-Owner: Giuseppe Lavagetto <[email protected]> _______________________________________________ MediaWiki-commits mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits
