From c4282ab6b2e50de62ed61cffc8cd950609bebed8 Mon Sep 17 00:00:00 2001 From: Dominik Richter Date: Sun, 14 Aug 2016 22:15:06 -0700 Subject: [PATCH] add ssl resource (early access) --- inspec.gemspec | 1 + lib/inspec/resource.rb | 1 + lib/resources/ssl.rb | 94 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 lib/resources/ssl.rb diff --git a/inspec.gemspec b/inspec.gemspec index 42caaaee6..1f067eff5 100644 --- a/inspec.gemspec +++ b/inspec.gemspec @@ -35,6 +35,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'pry', '~> 0' spec.add_dependency 'hashie', '~> 3.4' spec.add_dependency 'molinillo', '~> 0' + spec.add_dependency 'sslshake', '~> 1' spec.add_development_dependency 'mocha', '~> 1.1' end diff --git a/lib/inspec/resource.rb b/lib/inspec/resource.rb index 8d047a506..e266c5753 100644 --- a/lib/inspec/resource.rb +++ b/lib/inspec/resource.rb @@ -97,6 +97,7 @@ require 'resources/registry_key' require 'resources/security_policy' require 'resources/service' require 'resources/shadow' +require 'resources/ssl' require 'resources/ssh_conf' require 'resources/user' require 'resources/vbscript' diff --git a/lib/resources/ssl.rb b/lib/resources/ssl.rb new file mode 100644 index 000000000..5953fdb64 --- /dev/null +++ b/lib/resources/ssl.rb @@ -0,0 +1,94 @@ +# encoding: utf-8 +# copyright: 2015, Chef Software Inc. +# license: All rights reserved +# author: Dominik Richter +# author: Christoph Hartmann + +require 'sslshake' +require 'utils/filter' + +# Custom resource based on the InSpec resource DSL +class SSL < Inspec.resource(1) + name 'ssl' + + desc " + SSL test resource + " + + example " + describe ssl(port: 443) do + it { should be_enabled } + end + + # protocols: ssl2, ssl3, tls1.0, tls1.1, tls1.2 + describe ssl(port: 443).protocols('ssl2') do + it { should_not be_enabled } + end + + # any ciphers, filter by name or regex + describe ssl(port: 443).ciphers(/rc4/i) do + it { should_not be_enabled } + end + " + + VERSIONS = [ + 'ssl2', + 'ssl3', + 'tls1.0', + 'tls1.1', + 'tls1.2', + ].freeze + + attr_reader :host, :port + + def initialize(opts = {}) + @host = opts[:host] || + inspec.backend.instance_variable_get(:@hostname) + if @host.nil? && inspec.backend.class.to_s == 'Train::Transports::Local::Connection' + @host = 'localhost' + end + if @host.nil? + fail 'Cannot determine host for SSL test. Please specify it or use a different target.' + end + @port = opts[:port] || 443 + @timeout = opts[:timeout] + @retries = opts[:retries] + end + + filter = FilterTable.create + filter.add_accessor(:where) + .add_accessor(:entries) + .add(:ciphers, field: 'cipher') + .add(:protocols, field: 'protocol') + .add(:enabled?) { |x| x.handshake.values.any? { |i| i['success'] } } + .add(:handshake) { |x| + groups = x.entries.group_by(&:protocol) + res = groups.map do |proto, e| + [proto, SSLShake.hello(x.resource.host, port: x.resource.port, + protocol: proto, ciphers: e.map(&:cipher), + timeout: @timeout, retries: @retries)] + end + Hash[res] + } + .connect(self, :scan_config) + + def to_s + "SSL/TLS on #{@host}:#{@port}" + end + + private + + def scan_config + [ + { 'protocol' => 'ssl2', 'ciphers' => SSLShake::SSLv2::CIPHERS.keys }, + { 'protocol' => 'ssl3', 'ciphers' => SSLShake::TLS::SSL3_CIPHERS.keys }, + { 'protocol' => 'tls1.0', 'ciphers' => SSLShake::TLS::TLS10_CIPHERS.keys }, + { 'protocol' => 'tls1.1', 'ciphers' => SSLShake::TLS::TLS10_CIPHERS.keys }, + { 'protocol' => 'tls1.2', 'ciphers' => SSLShake::TLS::TLS_CIPHERS.keys }, + ].map do |line| + line['ciphers'].map do |cipher| + { 'protocol' => line['protocol'], 'cipher' => cipher } + end + end.flatten + end +end