Class: Chef::Knife::WindowsCertGenerate

Inherits:
Chef::Knife show all
Includes:
Mixin::PowershellExec
Defined in:
lib/chef/knife/windows_cert_generate.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#hostnameObject

Returns the value of attribute hostname.



28
29
30
# File 'lib/chef/knife/windows_cert_generate.rb', line 28

def hostname
  @hostname
end

#thumbprintObject

Returns the value of attribute thumbprint.



28
29
30
# File 'lib/chef/knife/windows_cert_generate.rb', line 28

def thumbprint
  @thumbprint
end

Instance Method Details

#certificates_already_exist?(file_path) ⇒ Boolean



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/chef/knife/windows_cert_generate.rb', line 159

def certificates_already_exist?(file_path)
  certs_exists = false
  %w{pem pfx b64}.each do |extn|
    if File.exist?("#{file_path}.#{extn}")
      certs_exists = true
      break
    end
  end

  if certs_exists
    begin
      confirm("Do you really want to overwrite existing certificates")
    rescue SystemExit # Need to handle this as confirming with N/n raises SystemExit exception
      exit!
    end
  end
end

#check_for_local_passwordObject

in the world of No Certs On Disk, we store a password for a p12/pfx in Keychain or the Registry. A p12/Pfx MUST have a password associated with it because it holds a private key Here we check to see if that password is already set.



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/chef/knife/windows_cert_generate.rb', line 126

def check_for_local_password
  if ChefUtils.windows?
    powershell_code = <<-CHECKFORPASSWORD
      Try {
        $localpass =  Get-ItemPropertyValue -Path "HKLM:\\Software\Progress\Authenticator" -Name "PfxPass" -ErrorAction Stop
        return $localpass
      }
      Catch {
        return $false
      }
    CHECKFORPASSWORD
    powershell_exec!(powershell_code).result
  elsif ChefUtils.macos?
    nil
  end
end

#generate_certificate(rsa_key) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/chef/knife/windows_cert_generate.rb', line 86

def generate_certificate(rsa_key)
  @hostname = config[:hostname] if config[:hostname]

  # Create a self-signed X509 certificate from the rsa_key (unencrypted)
  cert = OpenSSL::X509::Certificate.new
  cert.version = 2
  cert.serial = Random.rand(65534) + 1 # 2 digit byte range random number for better security aspect

  cert.subject = OpenSSL::X509::Name.parse "/CN=#{@hostname}"
  cert.issuer = cert.subject
  cert.public_key = rsa_key.public_key
  cert.not_before = Time.now
  cert.not_after = cert.not_before + 2 * 365 * config[:cert_validity].to_i * 60 * 60 # 2 years validity
  ef = OpenSSL::X509::ExtensionFactory.new
  ef.subject_certificate = cert
  ef.issuer_certificate = cert
  cert.add_extension(ef.create_extension("subjectKeyIdentifier", "hash", false))
  cert.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always", false))
  cert.add_extension(ef.create_extension("extendedKeyUsage", "1.3.6.1.5.5.7.3.1", false))
  cert.sign(rsa_key, OpenSSL::Digest.new("SHA1"))
  @thumbprint = OpenSSL::Digest::SHA1.new(cert.to_der)
  cert
end

#generate_keypairObject



68
69
70
# File 'lib/chef/knife/windows_cert_generate.rb', line 68

def generate_keypair
  OpenSSL::PKey::RSA.new(config[:key_length].to_i)
end

#prompt_for_passphraseObject



72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/chef/knife/windows_cert_generate.rb', line 72

def prompt_for_passphrase
  passphrase = ""
  begin
    print "Passphrases do not match.  Try again.\n" unless passphrase.empty?
    print "Enter certificate passphrase (empty for no passphrase):"
    passphrase = STDIN.gets
    return passphrase.strip if passphrase == "\n"

    print "Enter same passphrase again:"
    confirm_passphrase = STDIN.gets
  end until passphrase == confirm_passphrase
  passphrase.strip
end

#runObject



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/chef/knife/windows_cert_generate.rb', line 177

def run
  STDOUT.sync = STDERR.sync = true

  # takes user specified first cli value as a destination file path for generated cert.
  # allowing for output_file to be ommitted
  if config[:output_file] == nil?
    config[:output_file] = File.join(::Chef::Config[:file_cache_path], "chef-#{config[:hostname]}")
  end

  file_path = @name_args.empty? ? config[:output_file].sub(/\.(\w+)$/, "") : @name_args.first

  # check if certs already exists at given file path
  certificates_already_exist? file_path

  if config[:store_in_certstore] == "true" || config[:store_in_certstore] == "True" || config[:store_in_certstore] == "TRUE"
    store_key = true
  else
    store_key = false
  end

  begin
    filename = File.basename(file_path)
    rsa_key = generate_keypair
    cert = generate_certificate rsa_key
    write_certificate_to_file cert, file_path, rsa_key, store_key
    ui.info "Generated Certificates:"
    ui.info "- #{filename}.pfx - PKCS12 format key pair. Contains public and private keys, can be used with an SSL server."
    ui.info "- #{filename}.b64 - Base64 encoded PKCS12 key pair. Contains public and private keys, used by some cloud provider API's to configure SSL servers."
    ui.info "- #{filename}.pem - Base64 encoded public certificate only. Required by the client to connect to the server."
    ui.info "Certificate Thumbprint: #{@thumbprint.to_s.upcase}"
  rescue => e
    puts "ERROR: + #{e}"
  end
end

#set_local_password(password) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/chef/knife/windows_cert_generate.rb', line 143

def set_local_password(password)
  print "The password you just specified is being stored in the Registry. It will be used as the default until explicitly updated\n"
  more_powershell_code = <<-SETTHEPASSWORD
  $my_pwd = ConvertTo-SecureString -String "#{password}" -Force -AsPlainText;
  if (-not (Test-Path HKLM:\\SOFTWARE\\Progress)){
    New-Item -Path "HKLM:\\SOFTWARE\\Progress\\Authenticator" -Force
    New-ItemProperty  -path "HKLM:\\SOFTWARE\\Progress\\Authenticator" -name "PfxPass" -value $my_pwd -PropertyType String
  }
  else{
    Set-ItemProperty  -path "HKLM:\\SOFTWARE\\Progress\\Authenticator" -name "PfxPass" -value $my_pwd
  }

  SETTHEPASSWORD
  powershell_exec!(more_powershell_code)
end

#write_certificate_to_file(cert, file_path, rsa_key, store_key) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/chef/knife/windows_cert_generate.rb', line 110

def write_certificate_to_file(cert, file_path, rsa_key, store_key)
  File.open(file_path + ".pem", "wb") { |f| f.print cert.to_pem }

  config[:cert_passphrase] = prompt_for_passphrase unless config[:cert_passphrase]

  if store_key == true
    set_local_password(config[:cert_passphrase])
  end

  pfx = OpenSSL::PKCS12.create("#{config[:cert_passphrase]}", file_path, rsa_key, cert)
  File.open(file_path + ".pfx", "wb") { |f| f.print pfx.to_der }
  File.open(file_path + ".b64", "wb") { |f| f.print Base64.strict_encode64(pfx.to_der) }
end