Class: Chef::Knife::SslCheck

Inherits:
Chef::Knife show all
Defined in:
lib/chef/knife/ssl_check.rb

Constant Summary

Constants inherited from Chef::Knife

CHEF_ORGANIZATION_MANAGEMENT, OFFICIAL_PLUGINS, OPSCODE_HOSTED_CHEF_ACCESS_CONTROL

Instance Attribute Summary

Attributes inherited from Chef::Knife

#name_args, #ui

Instance Method Summary collapse

Methods inherited from Chef::Knife

#api_key, #apply_computed_config, category, chef_config_dir, #cli_keys, common_name, #config_file_settings, config_loader, #config_source, #configure_chef, #create_object, #delete_object, dependency_loaders, deps, #format_rest_error, guess_category, #humanize_exception, #humanize_http_exception, inherited, list_commands, load_commands, load_config, load_deps, #maybe_setup_fips, #merge_configs, msg, #noauth_rest, #parse_options, reset_config_loader!, reset_subcommands!, #rest, run, #run_with_pretty_exceptions, #server_url, #show_usage, snake_case_name, subcommand_category, subcommand_class_from, subcommand_files, subcommand_loader, subcommands, subcommands_by_category, #test_mandatory_field, ui, unnamed?, use_separate_defaults?, #username

Methods included from Mixin::ConvertToClassName

#constantize, #convert_to_class_name, #convert_to_snake_case, #filename_to_qualified_string, #normalize_snake_case_name, #snake_case_basename

Constructor Details

#initialize(*args) ⇒ SslCheck

Returns a new instance of SslCheck.



39
40
41
42
43
44
# File 'lib/chef/knife/ssl_check.rb', line 39

def initialize(*args)
  @host = nil
  @verify_peer_socket = nil
  @ssl_policy = HTTP::DefaultSSLPolicy
  super
end

Instance Method Details

#configurationObject



243
244
245
# File 'lib/chef/knife/ssl_check.rb', line 243

def configuration
  Chef::Config
end

#debug_chef_ssl_configObject



236
237
238
239
240
241
# File 'lib/chef/knife/ssl_check.rb', line 236

def debug_chef_ssl_config
  ui.err "#{Chef::Dist::PRODUCT} SSL Configuration:"
  ui.err "* ssl_ca_path: #{configuration.ssl_ca_path.inspect}"
  ui.err "* ssl_ca_file: #{configuration.ssl_ca_file.inspect}"
  ui.err "* trusted_certs_dir: #{configuration.trusted_certs_dir.inspect}"
end

#debug_invalid_certObject



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
# File 'lib/chef/knife/ssl_check.rb', line 179

def debug_invalid_cert
  noverify_socket.connect
  issuer_info = noverify_socket.peer_cert.issuer
  ui.msg("Certificate issuer data: #{issuer_info}")

  ui.msg("\n#{ui.color("Configuration Info:", :bold)}\n\n")
  debug_ssl_settings
  debug_chef_ssl_config

  ui.err(<<~ADVICE)

    #{ui.color("TO FIX THIS ERROR:", :bold)}

    If the server you are connecting to uses a self-signed certificate, you must
    configure #{Chef::Dist::PRODUCT} to trust that server's certificate.

    By default, the certificate is stored in the following location on the host
    where your chef-server runs:

      /var/opt/opscode/nginx/ca/SERVER_HOSTNAME.crt

    Copy that file to your trusted_certs_dir (currently: #{configuration.trusted_certs_dir})
    using SSH/SCP or some other secure method, then re-run this command to confirm
    that the server's certificate is now trusted.

  ADVICE
end

#debug_invalid_hostObject



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/chef/knife/ssl_check.rb', line 207

def debug_invalid_host
  noverify_socket.connect
  subject = noverify_socket.peer_cert.subject
  cn_field_tuple = subject.to_a.find { |field| field[0] == "CN" }
  cn = cn_field_tuple[1]

  ui.error("You are attempting to connect to:   '#{host}'")
  ui.error("The server's certificate belongs to '#{cn}'")
  ui.err(<<~ADVICE)

    #{ui.color("TO FIX THIS ERROR:", :bold)}

    The solution for this issue depends on your networking configuration. If you
    are able to connect to this server using the hostname #{cn}
    instead of #{host}, then you can resolve this issue by updating chef_server_url
    in your configuration file.

    If you are not able to connect to the server using the hostname #{cn}
    you will have to update the certificate on the server to use the correct hostname.
  ADVICE
end

#debug_invalid_X509(cert_debug_msg) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/chef/knife/ssl_check.rb', line 150

def debug_invalid_X509(cert_debug_msg)
  ui.msg("\n#{ui.color("Configuration Info:", :bold)}\n\n")
  debug_ssl_settings
  debug_chef_ssl_config

  ui.warn(<<~BAD_CERTS)
    There are invalid certificates in your trusted_certs_dir.
    OpenSSL will not use the following certificates when verifying SSL connections:

    #{cert_debug_msg}

    #{ui.color("TO FIX THESE WARNINGS:", :bold)}

    We are working on documentation for resolving common issues uncovered here.

    * If the certificate is generated by the server, you may try redownloading the
    server's certificate. By default, the certificate is stored in the following
    location on the host where your chef-server runs:

      /var/opt/opscode/nginx/ca/SERVER_HOSTNAME.crt

    Copy that file to your trusted_certs_dir (currently: #{configuration.trusted_certs_dir})
    using SSH/SCP or some other secure method, then re-run this command to confirm
    that the server's certificate is now trusted.

  BAD_CERTS
  # @TODO: ^ needs URL once documentation is posted.
end

#debug_ssl_settingsObject



229
230
231
232
233
234
# File 'lib/chef/knife/ssl_check.rb', line 229

def debug_ssl_settings
  ui.err "OpenSSL Configuration:"
  ui.err "* Version: #{OpenSSL::OPENSSL_VERSION}"
  ui.err "* Certificate file: #{OpenSSL::X509::DEFAULT_CERT_FILE}"
  ui.err "* Certificate directory: #{OpenSSL::X509::DEFAULT_CERT_DIR}"
end

#given_uriObject



53
54
55
# File 'lib/chef/knife/ssl_check.rb', line 53

def given_uri
  (name_args[0] || Chef::Config.chef_server_url)
end

#hostObject



57
58
59
# File 'lib/chef/knife/ssl_check.rb', line 57

def host
  uri.host
end

#invalid_uri!Object



73
74
75
76
77
# File 'lib/chef/knife/ssl_check.rb', line 73

def invalid_uri!
  ui.error("Given URI: `#{given_uri}' is invalid")
  show_usage
  exit 1
end

#noverify_peer_ssl_contextObject



104
105
106
107
108
109
110
111
# File 'lib/chef/knife/ssl_check.rb', line 104

def noverify_peer_ssl_context
  @noverify_peer_ssl_context ||= begin
    noverify_peer_context = OpenSSL::SSL::SSLContext.new
    @ssl_policy.apply_to(noverify_peer_context)
    noverify_peer_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
    noverify_peer_context
  end
end

#noverify_socketObject



97
98
99
100
101
102
# File 'lib/chef/knife/ssl_check.rb', line 97

def noverify_socket
  @noverify_socket ||= begin
    tcp_connection = proxified_socket(host, port)
    OpenSSL::SSL::SSLSocket.new(tcp_connection, noverify_peer_ssl_context)
  end
end

#portObject



61
62
63
# File 'lib/chef/knife/ssl_check.rb', line 61

def port
  uri.port
end

#runObject



247
248
249
250
251
252
253
254
255
# File 'lib/chef/knife/ssl_check.rb', line 247

def run
  validate_uri

  if verify_X509 && verify_cert && verify_cert_host
    ui.msg "Successfully verified certificates from `#{host}'"
  else
    exit 1
  end
end

#uriObject



46
47
48
49
50
51
# File 'lib/chef/knife/ssl_check.rb', line 46

def uri
  @uri ||= begin
    Chef::Log.trace("Checking SSL cert on #{given_uri}")
    URI.parse(given_uri)
  end
end

#validate_uriObject



65
66
67
68
69
70
71
# File 'lib/chef/knife/ssl_check.rb', line 65

def validate_uri
  unless host && port
    invalid_uri!
  end
rescue URI::Error
  invalid_uri!
end

#verify_certObject



129
130
131
132
133
134
135
136
137
138
# File 'lib/chef/knife/ssl_check.rb', line 129

def verify_cert
  ui.msg("Connecting to host #{host}:#{port}")
  verify_peer_socket.connect
  true
rescue OpenSSL::SSL::SSLError => e
  ui.error "The SSL certificate of #{host} could not be verified"
  Chef::Log.trace e.message
  debug_invalid_cert
  false
end

#verify_cert_hostObject



140
141
142
143
144
145
146
147
148
# File 'lib/chef/knife/ssl_check.rb', line 140

def verify_cert_host
  verify_peer_socket.post_connection_check(host)
  true
rescue OpenSSL::SSL::SSLError => e
  ui.error "The SSL cert is signed by a trusted authority but is not valid for the given hostname"
  Chef::Log.trace(e)
  debug_invalid_host
  false
end

#verify_peer_socketObject



79
80
81
82
83
84
85
86
# File 'lib/chef/knife/ssl_check.rb', line 79

def verify_peer_socket
  @verify_peer_socket ||= begin
    tcp_connection = proxified_socket(host, port)
    ssl_client = OpenSSL::SSL::SSLSocket.new(tcp_connection, verify_peer_ssl_context)
    ssl_client.hostname = host
    ssl_client
  end
end

#verify_peer_ssl_contextObject



88
89
90
91
92
93
94
95
# File 'lib/chef/knife/ssl_check.rb', line 88

def verify_peer_ssl_context
  @verify_peer_ssl_context ||= begin
    verify_peer_context = OpenSSL::SSL::SSLContext.new
    @ssl_policy.apply_to(verify_peer_context)
    verify_peer_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
    verify_peer_context
  end
end

#verify_X509Object



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/chef/knife/ssl_check.rb', line 113

def verify_X509
  cert_debug_msg = ""
  trusted_certificates.each do |cert_name|
    message = check_X509_certificate(cert_name)
    unless message.nil?
      cert_debug_msg << File.expand_path(cert_name) + ": " + message + "\n"
    end
  end

  unless cert_debug_msg.empty?
    debug_invalid_X509(cert_debug_msg)
  end

  true # Maybe the bad certs won't hurt...
end