Better error handling

v1
Aeris 2016-05-10 17:24:49 +02:00
parent e17e793070
commit 2b134ba712
12 changed files with 147 additions and 88 deletions

View File

@ -22,6 +22,7 @@ end
group :development, :test do
gem 'puma'
gem 'web-console'
gem 'awesome_print'
gem 'pry-rails'

View File

@ -8,12 +8,10 @@ class CheckController < ApplicationController
respond_to do |format|
format.html do
return render :processing if @result.pending
return render :no_tls if @result.no_tls
end
format.json do
render json: case
when @result.pending then :pending
when @result.no_tls then :no_tls
else @result
end
end
@ -34,7 +32,7 @@ class CheckController < ApplicationController
protected
def enqueue_host
Datastore.pending self.type, @id, @port
Datastore.pending self.type, @host, @port
self.worker.perform_async *(@port.blank? ? [@host] : [@host, @port])
@result = OpenStruct.new pending: true , date: Time.now
end

View File

@ -3,19 +3,20 @@ class Datastore
@@index.create unless @@index.exists?
def self.host(type, host, port)
result = @@index.type(type).get self.key(host, port)
key = self.key host, port
result = @@index.type(type).get key
result.date = Time.parse result.date
result
rescue Stretcher::RequestError::NotFound
end
def self.pending(type, host, port)
self.post type, host, port, { pending: true }
self.post type, host, port, { pending: true, date: DateTime.now }
end
def self.post(type, host, port, data)
data[:date] = DateTime.now
@@index.type(type).put self.key(host, port), data
key = self.key host, port
@@index.type(type).put key, data
end
private

View File

@ -1,12 +0,0 @@
<div id="check" class="container">
<div class="row">
<div class="col-sm-8 col-sm-offset-2">
<h1>
[<%= self.type.to_s.upcase %>] <%= @host %> ne supporte pas <%= self.tls_type %>
</h1>
<% if Time.now - @result.date >= Rails.configuration.refresh_delay %>
<%= link_to 'Rafraîchir', {action: :refresh}, class: %i(btn btn-default pull-right) %>
<% end %>
</div>
</div>
</div>

View File

@ -3,7 +3,6 @@
<div class="col-sm-11">
<h1>
[<%= self.type.to_s.upcase %>] <%= @host %> <span class="small">(<%= l @result.date %>)</span>
<%= rank_label @result.score.rank %>
</h1>
</div>
<% if Time.now - @result.date >= Rails.configuration.refresh_delay %>
@ -12,16 +11,35 @@
</div>
<% end %>
</div>
<br/>
<%
@result.hosts.each do |host|
if host.error
error, host = host.error, host.host
%>
<div class="row">
<div class="col-sm-12">
<h2><%= host.name %> - <%= host.ip %> : <%= host.port %></h2>
Error during analysis :
<span class="label label-error"><%= error %></span>
</div>
</div>
<%
else
host, grade, handshake = host.host, host.grade, host.handshake
%>
<div class="row">
<div class="col-sm-12">
<h2><%= host.name %> - <%= host.ip %> : <%= host.port %></h2>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<% scores = @result.score.details %>
<table class="table table-bordered table-condensed table-striped">
<thead>
<tr>
<th colspan="2">
Scores
<%= rank_label @result.score.rank %>
<%= rank_label grade.rank %>
</th>
</tr>
</thead>
@ -33,7 +51,7 @@
'Total' => 'score'}.each do |name, v| %>
<tr>
<th class="col-sm-4"><%= name %></th>
<td class="col-sm-8"><%= score_progress scores[v] %></td>
<td class="col-sm-8"><%= score_progress grade.details[v] %></td>
</tr>
<% end %>
</tbody>
@ -44,20 +62,20 @@
<tbody>
<tr>
<th class="col-sm-4">Protocoles</th>
<td class="col-sm-8"><%= protocol_labels @result.protocols %></td>
<td class="col-sm-8"><%= protocol_labels handshake.protocols %></td>
</tr>
<tr>
<th>Clefs</th>
<td>
<p>Certificat : <%= key_label @result[:key] %></p>
<p>Diffie Hellman : <%= key_labels @result.dh %></p>
<p>Certificat : <%= key_label handshake[:key] %></p>
<p>Diffie Hellman : <%= key_labels handshake.dh %></p>
</td>
</tr>
<% { 'Bonnes pratiques' => :success,
'Alertes' => :warning,
'Dangers' => :danger,
'Erreurs' => :error }.each do |name, color|
names = @result.score[color]
names = grade[color]
next if names.nil? or names.empty?
%>
<tr>
@ -100,8 +118,8 @@
</thead>
<tbody>
<% CryptCheck::Tls::Server::EXISTING_METHODS.each do |protocol|
ciphers = CryptCheck::Tls::Cipher.sort(@result.ciphers.select { |c| c.protocol == protocol.to_s }
.collect { |c| CryptCheck::Tls::Cipher.new protocol, [c.name, nil, c[:size]], c.dh, @result[:key] })
ciphers = CryptCheck::Tls::Cipher.sort(handshake.ciphers.select { |c| c.protocol == protocol.to_s }
.collect { |c| CryptCheck::Tls::Cipher.new protocol, [c.name, nil, c[:size]], c.dh, handshake[:key] })
unless ciphers.empty? %>
<tr>
<th colspan="12"><%= protocol_label protocol %></th>
@ -133,4 +151,6 @@
</table>
</div>
</div>
<% end
end %>
</div>

View File

@ -11,7 +11,27 @@
</div>
<% end %>
</div>
<br/>
<%
@result.hosts.each do |host|
if host.error
error, host = host.error, host.host
%>
<div class="row">
<div class="col-sm-12">
<h2><%= host.name %> - <%= host.ip %> : <%= host.port %></h2>
Error during analysis :
<span class="label label-error"><%= error %></span>
</div>
</div>
<%
else
host, server = host.host, host.handshake
%>
<div class="row">
<div class="col-sm-12">
<h2><%= host.name %> - <%= host.ip %> : <%= host.port %></h2>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<table class="table table-bordered table-condensed table-striped">
@ -19,7 +39,7 @@
<tr>
<th>Échange de clef</th>
</tr>
<% @result.kex.each do |kex| %>
<% server.kex.each do |kex| %>
<tr>
<td><%= kex_label kex %></td>
</tr>
@ -28,7 +48,7 @@
<tr>
<th>Chiffrement</th>
</tr>
<% @result.encryption.each do |cipher| %>
<% server.encryption.each do |cipher| %>
<tr>
<td><%= cipher_label cipher %></td>
</tr>
@ -37,7 +57,7 @@
<tr>
<th>HMAC</th>
</tr>
<% @result.hmac.each do |hmac| %>
<% server.hmac.each do |hmac| %>
<tr>
<td><%= hmac_label hmac %></td>
</tr>
@ -46,7 +66,7 @@
<tr>
<th>Compression</th>
</tr>
<% @result.compression.each do |compression| %>
<% server.compression.each do |compression| %>
<tr>
<td><%= compression_label compression %></td>
</tr>
@ -55,7 +75,7 @@
<tr>
<th>Clefs</th>
</tr>
<% @result['key'].each do |key| %>
<% server.key_.each do |key| %>
<tr>
<td><%= key_label key %></td>
</tr>
@ -64,4 +84,6 @@
</table>
</div>
</div>
<% end
end %>
</div>

View File

@ -7,40 +7,56 @@ class CheckWorker
end
def perform(host, port=nil)
host = SimpleIDN.to_ascii host.downcase
result = begin
grade = self.analyze *(port ? [host, port] : [host])
raise CryptCheck::Tls::Server::TLSNotAvailableException if grade.is_a? CryptCheck::Tls::TlsNotSupportedGrade
server = grade.server
result = {
key: key_to_json(server.key),
dh: server.dh.collect { |k| key_to_json k },
protocols: server.supported_protocols,
ciphers: server.supported_ciphers.collect { |c| { protocol: c.protocol, name: c.name, size: c.size, dh: key_to_json(c.dh) } },
score: {
rank: grade.grade,
details: {
score: grade.score,
protocol: grade.protocol_score,
key_exchange: grade.key_exchange_score,
cipher_strengths: grade.cipher_strengths_score
},
error: grade.error,
danger: grade.danger,
warning: grade.warning,
success: grade.success
}
}
host = SimpleIDN.to_ascii host.downcase
hosts = self.analyze *(port ? [host, port] : [host])
hosts = hosts.collect do |host, result|
name, ip, p = host
host = { name: name, ip: ip, port: p }
self.result server, grade, result
rescue CryptCheck::Tls::Server::TLSNotAvailableException
{ no_tls: true }
if result.is_a? CryptCheck::AnalysisFailure
next {
host: host,
error: result.to_s
}
end
grade = result
server = grade.server
{
host: host,
handshake: to_json(server),
grade: grade_to_json(grade)
}
end
result = { date: DateTime.now, hosts: hosts }
Datastore.post self.type, host, port, result
end
protected
def result(_, _, result)
result
def to_json(server)
{
key: key_to_json(server.key),
dh: server.dh.collect { |k| key_to_json k },
protocols: server.supported_protocols,
ciphers: server.supported_ciphers.collect { |c| { protocol: c.protocol, name: c.name, size: c.size, dh: key_to_json(c.dh) } },
}
end
private
def grade_to_json(grade)
{
rank: grade.grade,
details: {
score: grade.score,
protocol: grade.protocol_score,
key_exchange: grade.key_exchange_score,
cipher_strengths: grade.cipher_strengths_score
},
error: grade.error,
danger: grade.danger,
warning: grade.warning,
success: grade.success
}
end
end

View File

@ -10,8 +10,9 @@ class HTTPSWorker < CheckWorker
:https
end
def result(server, _, hash)
hash[:hsts] = server.hsts
hash
def to_json(server)
result = super
result[:hsts] = server.hsts
result
end
end

View File

@ -2,8 +2,8 @@ class SMTPWorker < CheckWorker
sidekiq_options retry: false
protected
def analyze(host, port=25)
CryptCheck::Tls::Smtp.analyze host, port
def analyze(host)
CryptCheck::Tls::Smtp.analyze_domain host
end
def type

View File

@ -1,21 +1,26 @@
class SSHWorker
include Sidekiq::Worker
class SSHWorker < CheckWorker
sidekiq_options retry: false
def perform(host, port=nil)
host = SimpleIDN.to_ascii host.downcase
result = begin
server = CryptCheck::Ssh.analyze host, port
{
kex: server.kex,
encryption: server.encryption,
hmac: server.hmac,
compression: server.compression,
key: server.key
}
rescue CryptCheck::Ssh::Server::SshNotAvailableException
{ no_tls: true }
end
Datastore.post :ssh, host, port, result
protected
def analyze(host, port=22)
CryptCheck::Ssh.analyze host, port
end
def type
:ssh
end
def to_json(server)
{
kex: server.kex,
encryption: server.encryption,
hmac: server.hmac,
compression: server.compression,
key_: server.key
}
end
def grade_to_json(grade)
nil
end
end

View File

@ -3,10 +3,16 @@ class XMPPWorker < CheckWorker
protected
def analyze(host)
CryptCheck::Tls::Xmpp.analyze host
CryptCheck::Tls::Xmpp.analyze_domain host
end
def type
:xmpp
end
def to_json(server)
result = super
result[:required] = server.required?
result
end
end

View File

@ -6,4 +6,5 @@ options = {
}
client = Sidekiq::Client.new Sidekiq::RedisConnection.create options
clazz, *args = ARGV
clazz += 'Worker'
client.push({ 'class' => clazz, 'args' => args })