mirror of
https://github.com/inspec/inspec
synced 2024-11-22 20:53:11 +00:00
api: Complete overhaul of resources and backends
* Created general plugin system * Add backend plugins: Specinfra and Mock * Add resource plugins: All Vulcanosec resources * Add integration testing suite * Removed Serverspec as a dependency This will be ready once the remaining resources from Serverspec are integrated. So far command and file have been largely migrated. See unit and integration tests.
This commit is contained in:
commit
d769659d48
56 changed files with 1406 additions and 673 deletions
4
.tests.yaml
Normal file
4
.tests.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
images:
|
||||
- ubuntu-1204-20150612
|
||||
- ubuntu-1404-20150320
|
||||
- ubuntu-latest
|
24
Rakefile
Normal file
24
Rakefile
Normal file
|
@ -0,0 +1,24 @@
|
|||
require 'rake/testtask'
|
||||
|
||||
task :default => :test
|
||||
Rake::TestTask.new do |t|
|
||||
t.libs << 'test'
|
||||
t.pattern = 'test/**/*_test.rb'
|
||||
t.warning = true
|
||||
t.verbose = true
|
||||
t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
|
||||
end
|
||||
|
||||
namespace :test do
|
||||
task :isolated do
|
||||
Dir.glob("test/**/*_test.rb").all? do |file|
|
||||
sh(Gem.ruby, '-w', '-Ilib:test', file)
|
||||
end or raise "Failures"
|
||||
end
|
||||
|
||||
task :integration do
|
||||
tests = Dir["test/resource/*.rb"]
|
||||
return if tests.empty?
|
||||
sh(Gem.ruby, 'test/docker.rb', *tests)
|
||||
end
|
||||
end
|
11
bin/vulcano
11
bin/vulcano
|
@ -45,9 +45,9 @@ class VulcanoCLI < Thor
|
|||
desc "exec PATHS", "run all test files"
|
||||
option :id, type: :string,
|
||||
desc: 'Attach a profile ID to all test results'
|
||||
option :target, type: :string, default: nil,
|
||||
option :target, aliases: :t, type: :string, default: nil,
|
||||
desc: 'Simple targeting option using URIs, e.g. ssh://user:pass@host:port'
|
||||
option :backend, type: :string, default: nil,
|
||||
option :backend, aliases: :b, type: :string, default: nil,
|
||||
desc: 'Choose a backend: exec (run locally), ssh, winrm.'
|
||||
option :host, type: :string,
|
||||
desc: 'Specify a remote host which is tested.'
|
||||
|
@ -69,10 +69,13 @@ class VulcanoCLI < Thor
|
|||
desc: 'Allow remote scans with WinRM to run on self-signed certificates.'
|
||||
option :winrm_ssl, type: :boolean, default: false,
|
||||
desc: 'Configure WinRM scans to run via SSL instead of pure HTTP.'
|
||||
def exec(*resources)
|
||||
option :format, type: :string, default: 'progress'
|
||||
def exec(*tests)
|
||||
runner = Vulcano::Runner.new(options[:id], options)
|
||||
runner.add_resources(resources)
|
||||
runner.add_tests(tests)
|
||||
runner.run
|
||||
rescue RuntimeError => e
|
||||
puts e.message
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
require 'utils/simpleconfig'
|
||||
require 'utils/find_files'
|
||||
|
||||
class ApacheConf
|
||||
class ApacheConf < Vulcano.resource(1)
|
||||
name 'apache_conf'
|
||||
|
||||
def initialize( conf_path )
|
||||
@runner = Specinfra::Runner
|
||||
@conf_path = conf_path
|
||||
@conf_dir = File.expand_path(File.dirname @conf_path)
|
||||
@conf_dir = File.dirname(@conf_path)
|
||||
@files_contents = {}
|
||||
@content = nil
|
||||
@params = nil
|
||||
|
@ -31,13 +31,13 @@ class ApacheConf
|
|||
end
|
||||
|
||||
def filter_comments data
|
||||
contents = ""
|
||||
content = ""
|
||||
data.each_line do |line|
|
||||
if (!line.match(/^\s*#/)) then
|
||||
contents << line
|
||||
content << line
|
||||
end
|
||||
end
|
||||
return contents
|
||||
return content
|
||||
end
|
||||
|
||||
def read_content
|
||||
|
@ -45,11 +45,13 @@ class ApacheConf
|
|||
@params = {}
|
||||
|
||||
# skip if the main configuration file doesn't exist
|
||||
if !@runner.check_file_is_file(@conf_path)
|
||||
file = vulcano.file(@conf_path)
|
||||
if !file.file?
|
||||
return skip_resource "Can't find file \"#{@conf_path}\""
|
||||
end
|
||||
raw_conf = read_file(@conf_path)
|
||||
if raw_conf.empty? && @runner.get_file_size(@conf_path).stdout.strip.to_i > 0
|
||||
|
||||
raw_conf = file.content
|
||||
if raw_conf.empty? && file.size > 0
|
||||
return skip_resource("Can't read file \"#{@conf_path}\"")
|
||||
end
|
||||
|
||||
|
@ -100,6 +102,6 @@ class ApacheConf
|
|||
end
|
||||
|
||||
def read_file(path)
|
||||
@files_contents[path] ||= @runner.get_file_content(path).stdout
|
||||
@files_contents[path] ||= vulcano.file(path).content
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
# license: All rights reserved
|
||||
|
||||
## Advanced Auditing
|
||||
# As soon as you start applying Advanced Audit Configuration Policy, legacy policies will be completely ignored.
|
||||
# As soon as you start applying Advanced Audit Configuration Policy, legacy policies will be completely ignored.
|
||||
# reference: https://technet.microsoft.com/en-us/library/cc753632.aspx
|
||||
# use:
|
||||
# use:
|
||||
# - list all categories: Auditpol /list /subcategory:* /r
|
||||
# - list parameters: Auditpol /get /category:"System" /subcategory:"IPsec Driver"
|
||||
# - list specific parameter: Auditpol /get /subcategory:"IPsec Driver"
|
||||
#
|
||||
# @link: http://blogs.technet.com/b/askds/archive/2011/03/11/getting-the-effective-audit-policy-in-windows-7-and-2008-r2.aspx
|
||||
#
|
||||
# @link: http://blogs.technet.com/b/askds/archive/2011/03/11/getting-the-effective-audit-policy-in-windows-7-and-2008-r2.aspx
|
||||
|
||||
=begin
|
||||
Category/Subcategory,GUID
|
||||
|
@ -92,9 +92,8 @@ Further information is available at: https://msdn.microsoft.com/en-us/library/dd
|
|||
|
||||
=end
|
||||
|
||||
include Serverspec::Type
|
||||
|
||||
class AuditPolicy < Serverspec::Type::Base
|
||||
class AuditPolicy < Vulcano.resource(1)
|
||||
name 'audit_policy'
|
||||
|
||||
def method_missing(method)
|
||||
key = method.to_s
|
||||
|
@ -102,12 +101,11 @@ class AuditPolicy < Serverspec::Type::Base
|
|||
# expected result:
|
||||
# Machine Name,Policy Target,Subcategory,Subcategory GUID,Inclusion Setting,Exclusion Setting
|
||||
# WIN-MB8NINQ388J,System,Kerberos Authentication Service,{0CCE9242-69AE-11D9-BED3-505054503030},No Auditing,
|
||||
command_result ||= @runner.run_command("Auditpol /get /subcategory:'#{key}' /r")
|
||||
result = command_result.stdout
|
||||
result ||= vulcano.run_command("Auditpol /get /subcategory:'#{key}' /r").stdout
|
||||
|
||||
# find line
|
||||
target = nil
|
||||
result.each_line {|s|
|
||||
result.each_line {|s|
|
||||
target = s.strip if s.match(/\b.*#{key}.*\b/)
|
||||
}
|
||||
|
||||
|
@ -115,7 +113,7 @@ class AuditPolicy < Serverspec::Type::Base
|
|||
if target != nil
|
||||
# split csv values and return value
|
||||
value = target.split(',')[4]
|
||||
else
|
||||
else
|
||||
value = nil
|
||||
end
|
||||
|
||||
|
@ -123,13 +121,7 @@ class AuditPolicy < Serverspec::Type::Base
|
|||
end
|
||||
|
||||
def to_s
|
||||
%Q[Windows Advanced Auditing]
|
||||
'Windows Advanced Auditing'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def audit_policy()
|
||||
AuditPolicy.new()
|
||||
end
|
||||
end
|
|
@ -4,10 +4,10 @@
|
|||
|
||||
require 'utils/simpleconfig'
|
||||
|
||||
class AuditDaemonConf < Vulcano::Resource
|
||||
class AuditDaemonConf < Vulcano.resource(1)
|
||||
name 'audit_daemon_conf'
|
||||
|
||||
def initialize
|
||||
@runner = Specinfra::Runner
|
||||
@conf_path = '/etc/audit/auditd.conf'
|
||||
@files_contents = {}
|
||||
@content = nil
|
||||
|
@ -26,27 +26,19 @@ class AuditDaemonConf < Vulcano::Resource
|
|||
|
||||
def read_content
|
||||
# read the file
|
||||
if !@runner.check_file_is_file(@conf_path)
|
||||
file = vulcano.file(@conf_path)
|
||||
if !file.file?
|
||||
return skip_resource "Can't find file \"#{@conf_path}\""
|
||||
end
|
||||
@content = read_file(@conf_path)
|
||||
if @content.empty? && @runner.get_file_size(@conf_path).stdout.strip.to_i > 0
|
||||
|
||||
@content = file.content
|
||||
if @content.empty? && file.size > 0
|
||||
return skip_resource "Can't read file \"#{@conf_path}\""
|
||||
end
|
||||
# parse the file
|
||||
@params = SimpleConfig.new(@content,
|
||||
multiple_values: false
|
||||
).params
|
||||
@content
|
||||
end
|
||||
|
||||
def read_file(path)
|
||||
@files_contents[path] ||= @runner.get_file_content(path).stdout
|
||||
end
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def audit_daemon_conf()
|
||||
AuditDaemonConf.new()
|
||||
end
|
||||
end
|
|
@ -2,25 +2,24 @@
|
|||
# copyright: 2015, Vulcano Security GmbH
|
||||
# license: All rights reserved
|
||||
|
||||
include Serverspec::Type
|
||||
|
||||
class AuditDaemonRules < Vulcano::Resource
|
||||
class AuditDaemonRules < Vulcano.resource(1)
|
||||
name 'audit_daemon_rules'
|
||||
|
||||
def initialize
|
||||
@runner = Specinfra::Runner
|
||||
@command_result ||= @runner.run_command("/sbin/auditctl -l")
|
||||
@content = @command_result.stdout.chomp
|
||||
@content = vulcano.run_command("/sbin/auditctl -l").stdout.chomp
|
||||
|
||||
@opts = {
|
||||
assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
|
||||
multiple_values: true
|
||||
}
|
||||
@params = SimpleConfig.new(@content, @opts).params
|
||||
|
||||
end
|
||||
|
||||
def params
|
||||
@params ||= SimpleConfig.new(@content, @opts).params
|
||||
end
|
||||
|
||||
def method_missing name
|
||||
@params[name.to_s]
|
||||
params[name.to_s]
|
||||
end
|
||||
|
||||
def status name
|
||||
|
@ -28,7 +27,7 @@ class AuditDaemonRules < Vulcano::Resource
|
|||
assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
|
||||
multiple_values: false
|
||||
}
|
||||
@status_content ||= @runner.run_command("/sbin/auditctl -s").stdout.chomp
|
||||
@status_content ||= vulcano.run_command("/sbin/auditctl -s").stdout.chomp
|
||||
@status_params = SimpleConfig.new(@status_content, @status_opts).params
|
||||
status = @status_params["AUDIT_STATUS"]
|
||||
if (status == nil) then return nil end
|
||||
|
@ -38,13 +37,7 @@ class AuditDaemonRules < Vulcano::Resource
|
|||
end
|
||||
|
||||
def to_s
|
||||
%Q[AuditDaemonRules]
|
||||
'Audit Daemon Rules'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def audit_daemon_rules()
|
||||
AuditDaemonRules.new()
|
||||
end
|
||||
end
|
|
@ -1,14 +1,32 @@
|
|||
# encoding: utf-8
|
||||
# copyright: 2015, Vulcano Security GmbH
|
||||
# encoding: utf-8
|
||||
# license: All rights reserved
|
||||
|
||||
module Serverspec::Type
|
||||
class Command < Base
|
||||
# Check if a given command (executable) exists
|
||||
# in the default path
|
||||
def exists?
|
||||
cmd = @name
|
||||
Command.new("type \"#{cmd}\" > /dev/null").exit_status == 0
|
||||
end
|
||||
class Command < Vulcano.resource(1)
|
||||
name 'command'
|
||||
def initialize(cmd)
|
||||
@command = cmd
|
||||
end
|
||||
end
|
||||
|
||||
def result
|
||||
@result ||= vulcano.run_command(@command)
|
||||
end
|
||||
|
||||
def stdout
|
||||
result.stdout
|
||||
end
|
||||
|
||||
def stderr
|
||||
result.stderr
|
||||
end
|
||||
|
||||
def exit_status
|
||||
result.exit_status.to_i
|
||||
end
|
||||
|
||||
def exists?
|
||||
res = vulcano.run_command("type \"#{@command}\" > /dev/null")
|
||||
res.exit_status.to_i == 0
|
||||
end
|
||||
|
||||
end
|
||||
|
|
9
lib/resources/directory.rb
Normal file
9
lib/resources/directory.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'resources/file'
|
||||
|
||||
module Vulcano::Resources
|
||||
class Directory < File
|
||||
name 'directory'
|
||||
end
|
||||
end
|
|
@ -2,40 +2,31 @@
|
|||
# copyright: 2015, Vulcano Security GmbH
|
||||
# license: All rights reserved
|
||||
|
||||
include Serverspec::Type
|
||||
class OsEnv < Vulcano.resource(1)
|
||||
name 'os_env'
|
||||
|
||||
class EnvironmentVariable < Serverspec::Type::Base
|
||||
|
||||
def method_missing(method)
|
||||
@command_result ||= @runner.run_command("su - root -c 'echo $#{name}'")
|
||||
end
|
||||
|
||||
def content
|
||||
command_result.stdout.chomp
|
||||
attr_reader :content
|
||||
def initialize(field)
|
||||
@command_result = vulcano.run_command("su - root -c 'echo $#{name}'")
|
||||
@content = @command_result.stdout.chomp
|
||||
end
|
||||
|
||||
def split
|
||||
# -1 is required to catch cases like dir1::dir2:
|
||||
# where we have a trailing :
|
||||
command_result.stdout.chomp.split(':', -1)
|
||||
@content.split(':', -1)
|
||||
end
|
||||
|
||||
def stderr
|
||||
command_result.stderr
|
||||
@command_result.stderr
|
||||
end
|
||||
|
||||
def exit_status
|
||||
command_result.exit_status.to_i
|
||||
@command_result.exit_status.to_i
|
||||
end
|
||||
|
||||
def to_s
|
||||
%Q[Environment Variable]
|
||||
"Environment variable #{field}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def os_env(name)
|
||||
EnvironmentVariable.new(name)
|
||||
end
|
||||
end
|
|
@ -8,24 +8,17 @@
|
|||
# - gid
|
||||
# - group list, comma seperated list
|
||||
|
||||
include Serverspec::Type
|
||||
class EtcGroup < Vulcano.resource(1)
|
||||
name 'etc_group'
|
||||
|
||||
class EtcGroup < Serverspec::Type::File
|
||||
|
||||
attr_accessor :gid
|
||||
attr_accessor :gid, :entries
|
||||
def initialize(path = nil)
|
||||
@path = path || '/etc/group'
|
||||
@entries = parse(@path)
|
||||
end
|
||||
|
||||
def to_s
|
||||
%Q[/etc/group]
|
||||
end
|
||||
|
||||
def parse
|
||||
content().split("\n").map do |line|
|
||||
line.split(':')
|
||||
end
|
||||
end
|
||||
|
||||
def entries
|
||||
@entries ||= parse
|
||||
@path
|
||||
end
|
||||
|
||||
def groups
|
||||
|
@ -61,10 +54,13 @@ class EtcGroup < Serverspec::Type::File
|
|||
self
|
||||
end
|
||||
|
||||
end
|
||||
private
|
||||
|
||||
module Serverspec::Type
|
||||
def etc_group(path = nil)
|
||||
EtcGroup.new(path || '/etc/group')
|
||||
def parse(path)
|
||||
@content = vulcano.file(path).content
|
||||
@content.split("\n").map do |line|
|
||||
line.split(':')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -2,12 +2,68 @@
|
|||
# copyright: 2015, Vulcano Security GmbH
|
||||
# license: All rights reserved
|
||||
|
||||
module Serverspec::Type
|
||||
class File < Base
|
||||
# Overwrite the to_s method to show path
|
||||
# instead of type
|
||||
def to_s
|
||||
%Q.Path "#{@name}".
|
||||
module Vulcano::Resources
|
||||
class File < Vulcano.resource(1)
|
||||
name 'file'
|
||||
|
||||
def initialize(path)
|
||||
@path = path
|
||||
@file = vulcano.file(@path)
|
||||
end
|
||||
|
||||
%w{
|
||||
type exists? file? block_device? character_device? socket? directory?
|
||||
symlink? pipe?
|
||||
mode mode? owner owned_by? group grouped_into? link_target linked_to?
|
||||
content mtime size selinux_label
|
||||
mounted? immutable? product_version file_version version?
|
||||
md5sum sha256sum
|
||||
}.each do |m|
|
||||
define_method m.to_sym do |*args|
|
||||
@file.method(m.to_sym).call(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def contain(pattern, from, to)
|
||||
raise ' not yet implemented '
|
||||
end
|
||||
|
||||
def readable?(by_owner, by_user)
|
||||
if by_user.nil?
|
||||
m = unix_mode_mask(by_owner, 'r') ||
|
||||
raise("#{by_owner} is not a valid unix owner.")
|
||||
( @file.mask & m ) != 0
|
||||
else
|
||||
# TODO: REMOVE THIS FALLBACK
|
||||
Specinfra::Runner.check_file_is_accessible_by_user(@path, by_user, 'r')
|
||||
end
|
||||
end
|
||||
|
||||
def writable?(by_owner, by_user)
|
||||
if by_user.nil?
|
||||
m = unix_mode_mask(by_owner, 'w') ||
|
||||
raise("#{by_owner} is not a valid unix owner.")
|
||||
( @file.mask & m ) != 0
|
||||
else
|
||||
# TODO: REMOVE THIS FALLBACK
|
||||
Specinfra::Runner.check_file_is_accessible_by_user(@path, by_user, 'w')
|
||||
end
|
||||
end
|
||||
|
||||
def executable?(by_owner, by_user)
|
||||
if by_user.nil?
|
||||
m = unix_mode_mask(by_owner, 'x') ||
|
||||
raise("#{by_owner} is not a valid unix owner.")
|
||||
( @file.mask & m ) != 0
|
||||
else
|
||||
# TODO: REMOVE THIS FALLBACK
|
||||
Specinfra::Runner.check_file_is_accessible_by_user(@path, by_user, 'x')
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Path '#{@path}'"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
require 'json'
|
||||
|
||||
include Serverspec::Type
|
||||
|
||||
# return JSON object
|
||||
def gpo (policy_path, policy_name)
|
||||
file = ::File.read(::File.join ::File.dirname(__FILE__), "gpo.json")
|
||||
|
@ -15,12 +13,13 @@ def gpo (policy_path, policy_name)
|
|||
end
|
||||
|
||||
# Group Policy
|
||||
class GroupPolicy < Serverspec::Type::Base
|
||||
class GroupPolicy < Vulcano.resource(1)
|
||||
name 'group_policy'
|
||||
|
||||
def getRegistryValue(entry)
|
||||
keys = entry['registry_information'][0]
|
||||
cmd = "(Get-Item 'Registry::#{keys['path']}').GetValue('#{keys['key']}')"
|
||||
command_result ||= @runner.run_command(cmd)
|
||||
command_result ||= vulcano.run_command(cmd)
|
||||
val = { :exit_code => command_result.exit_status.to_i, :data => command_result.stdout }
|
||||
val
|
||||
end
|
||||
|
@ -52,9 +51,3 @@ class GroupPolicy < Serverspec::Type::Base
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def group_policy(policy_path)
|
||||
GroupPolicy.new(policy_path)
|
||||
end
|
||||
end
|
|
@ -4,10 +4,10 @@
|
|||
|
||||
require 'utils/simpleconfig'
|
||||
|
||||
class InetdConf < Vulcano::Resource
|
||||
class InetdConf < Vulcano.resource(1)
|
||||
name 'inetd_config'
|
||||
|
||||
def initialize(path)
|
||||
@runner = Specinfra::Runner
|
||||
@conf_path = path
|
||||
@files_contents = {}
|
||||
@content = nil
|
||||
|
@ -26,11 +26,12 @@ class InetdConf < Vulcano::Resource
|
|||
|
||||
def read_content
|
||||
# read the file
|
||||
if !@runner.check_file_is_file(@conf_path)
|
||||
file = vulcano.file(@conf_path)
|
||||
if !file.file?
|
||||
return skip_resource "Can't find file \"#{@conf_path}\""
|
||||
end
|
||||
@content = read_file(@conf_path)
|
||||
if @content.empty? && @runner.get_file_size(@conf_path).stdout.strip.to_i > 0
|
||||
@content = file.content
|
||||
if @content.empty? && file.size > 0
|
||||
return skip_resource "Can't read file \"#{@conf_path}\""
|
||||
end
|
||||
# parse the file
|
||||
|
@ -42,15 +43,4 @@ class InetdConf < Vulcano::Resource
|
|||
@content
|
||||
end
|
||||
|
||||
def read_file(path)
|
||||
@files_contents[path] ||= @runner.get_file_content(path).stdout
|
||||
end
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def inetd_conf(path = nil)
|
||||
@inetd_conf ||= {}
|
||||
dpath = path || '/etc/inetd.conf'
|
||||
@inetd_conf[dpath] = InetdConf.new(dpath)
|
||||
end
|
||||
end
|
|
@ -4,10 +4,10 @@
|
|||
|
||||
require 'utils/simpleconfig'
|
||||
|
||||
class LimitsConf < Vulcano::Resource
|
||||
class LimitsConf < Vulcano.resource(1)
|
||||
name 'limits_conf'
|
||||
|
||||
def initialize(path)
|
||||
@runner = Specinfra::Runner
|
||||
@conf_path = path
|
||||
@files_contents = {}
|
||||
@content = nil
|
||||
|
@ -26,11 +26,12 @@ class LimitsConf < Vulcano::Resource
|
|||
|
||||
def read_content
|
||||
# read the file
|
||||
if !@runner.check_file_is_file(@conf_path)
|
||||
file = vulcano.file(@conf_path)
|
||||
if !file.file?
|
||||
return skip_resource "Can't find file \"#{@conf_path}\""
|
||||
end
|
||||
@content = read_file(@conf_path)
|
||||
if @content.empty? && @runner.get_file_size(@conf_path).stdout.strip.to_i > 0
|
||||
@content = file.content
|
||||
if @content.empty? && file.size > 0
|
||||
return skip_resource "Can't read file \"#{@conf_path}\""
|
||||
end
|
||||
# parse the file
|
||||
|
@ -42,15 +43,4 @@ class LimitsConf < Vulcano::Resource
|
|||
@content
|
||||
end
|
||||
|
||||
def read_file(path)
|
||||
@files_contents[path] ||= @runner.get_file_content(path).stdout
|
||||
end
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def limits_conf(path = nil)
|
||||
@limits_conf ||= {}
|
||||
dpath = path || '/etc/security/limits.conf'
|
||||
@limits_conf[dpath] ||= LimitsConf.new(dpath)
|
||||
end
|
||||
end
|
|
@ -4,10 +4,10 @@
|
|||
|
||||
require 'utils/simpleconfig'
|
||||
|
||||
class LoginDef < Vulcano::Resource
|
||||
class LoginDef < Vulcano.resource(1)
|
||||
name 'login_defs'
|
||||
|
||||
def initialize(path = nil)
|
||||
@runner = Specinfra::Runner
|
||||
@conf_path = path || '/etc/login.defs'
|
||||
@files_contents = {}
|
||||
@content = nil
|
||||
|
@ -26,11 +26,12 @@ class LoginDef < Vulcano::Resource
|
|||
|
||||
def read_content
|
||||
# read the file
|
||||
if !@runner.check_file_is_file(@conf_path)
|
||||
file = vulcano.file(@conf_path)
|
||||
if !file.file?
|
||||
return skip_resource "Can't find file \"#{@conf_path}\""
|
||||
end
|
||||
@content = read_file(@conf_path)
|
||||
if @content.empty? && @runner.get_file_size(@conf_path).stdout.strip.to_i > 0
|
||||
@content = file.content
|
||||
if @content.empty? && file.size > 0
|
||||
return skip_resource "Can't read file \"#{@conf_path}\""
|
||||
end
|
||||
# parse the file
|
||||
|
@ -41,13 +42,4 @@ class LoginDef < Vulcano::Resource
|
|||
@content
|
||||
end
|
||||
|
||||
def read_file(path)
|
||||
@files_contents[path] ||= @runner.get_file_content(path).stdout
|
||||
end
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def login_def(path = nil)
|
||||
LoginDef.new(path)
|
||||
end
|
||||
end
|
|
@ -2,7 +2,9 @@
|
|||
# copyright: 2015, Vulcano Security GmbH
|
||||
# license: All rights reserved
|
||||
|
||||
class Mysql
|
||||
class Mysql < Vulcano.resource(1)
|
||||
name 'mysql'
|
||||
|
||||
attr_reader :package, :service, :conf_dir, :conf_path, :data_dir, :log_dir, :log_path, :log_group, :log_dir_group
|
||||
def initialize
|
||||
# set OS-dependent filenames and paths
|
||||
|
@ -55,9 +57,3 @@ class Mysql
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def mysql
|
||||
@mysql ||= Mysql.new()
|
||||
end
|
||||
end
|
|
@ -8,16 +8,17 @@ require 'resources/mysql'
|
|||
|
||||
class MysqlConfEntry
|
||||
def initialize( path, params )
|
||||
@runner = Specinfra::Runner
|
||||
@params = params
|
||||
@path = path
|
||||
end
|
||||
|
||||
def method_missing name, *args
|
||||
k = name.to_s
|
||||
res = @params[k]
|
||||
return true if res.nil? && @params.key?(k)
|
||||
@params[k]
|
||||
end
|
||||
|
||||
def to_s
|
||||
group = ' '
|
||||
group = "[#{@path.join('][')}] " unless @path.nil? or @path.empty?
|
||||
|
@ -25,10 +26,10 @@ class MysqlConfEntry
|
|||
end
|
||||
end
|
||||
|
||||
class MysqlConf < Vulcano::Resource
|
||||
class MysqlConf < Vulcano.resource(1)
|
||||
name 'mysql_conf'
|
||||
|
||||
def initialize( conf_path )
|
||||
@runner = Specinfra::Runner
|
||||
@conf_path = conf_path
|
||||
@files_contents = {}
|
||||
@content = nil
|
||||
|
@ -59,11 +60,11 @@ class MysqlConf < Vulcano::Resource
|
|||
@params = {}
|
||||
|
||||
# skip if the main configuration file doesn't exist
|
||||
if !@runner.check_file_is_file(@conf_path)
|
||||
if !vulcano.file(@conf_path).file?
|
||||
return skip_resource "Can't find file \"#{@conf_path}\""
|
||||
end
|
||||
raw_conf = read_file(@conf_path)
|
||||
if raw_conf.empty? && @runner.get_file_size(@conf_path).stdout.strip.to_i > 0
|
||||
if raw_conf.empty? && vulcano.file(@conf_path).size > 0
|
||||
return skip_resource("Can't read file \"#{@conf_path}\"")
|
||||
end
|
||||
|
||||
|
@ -91,14 +92,6 @@ class MysqlConf < Vulcano::Resource
|
|||
end
|
||||
|
||||
def read_file(path)
|
||||
@files_contents[path] ||= @runner.get_file_content(path).stdout
|
||||
@files_contents[path] ||= vulcano.file(path).content
|
||||
end
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def mysql_conf(path = nil)
|
||||
@mysql_conf ||= {}
|
||||
dpath = path || mysql.conf_path
|
||||
@mysql_conf[dpath] ||= MysqlConf.new( dpath )
|
||||
end
|
||||
end
|
|
@ -4,11 +4,12 @@
|
|||
|
||||
$__SCOPE = self
|
||||
|
||||
class MysqlSession < Vulcano::Resource
|
||||
class MysqlSession < Vulcano.resource(1)
|
||||
name 'mysql_session'
|
||||
|
||||
def initialize user, pass
|
||||
@user = user
|
||||
@pass = pass
|
||||
@runner = Specinfra::Runner
|
||||
initialize_fallback if user.nil? or pass.nil?
|
||||
skip_resource("Can't run MySQL SQL checks without authentication") if @user.nil? or @pass.nil?
|
||||
end
|
||||
|
@ -18,7 +19,7 @@ class MysqlSession < Vulcano::Resource
|
|||
# that does this securely
|
||||
escaped_query = query.gsub(/\\/, '\\\\').gsub(/"/,'\\"').gsub(/\$/,'\\$')
|
||||
# run the query
|
||||
cmd = Serverspec::Type::Command.new("mysql -u#{@user} -p#{@pass} #{db} -s -e \"#{escaped_query}\"")
|
||||
cmd = vulcano.run_command("mysql -u#{@user} -p#{@pass} #{db} -s -e \"#{escaped_query}\"")
|
||||
out = cmd.stdout + "\n" + cmd.stderr
|
||||
if out =~ /Can't connect to .* MySQL server/ or
|
||||
out.downcase =~ /^error/
|
||||
|
@ -33,7 +34,7 @@ class MysqlSession < Vulcano::Resource
|
|||
|
||||
def initialize_fallback
|
||||
# support debian mysql administration login
|
||||
debian = @runner.run_command("test -f /etc/mysql/debian.cnf && cat /etc/mysql/debian.cnf").stdout
|
||||
debian = vulcano.run_command("test -f /etc/mysql/debian.cnf && cat /etc/mysql/debian.cnf").stdout
|
||||
unless debian.empty?
|
||||
user = debian.match(/^\s*user\s*=\s*([^ ]*)\s*$/)
|
||||
pass = debian.match(/^\s*password\s*=\s*([^ ]*)\s*$/)
|
||||
|
@ -44,9 +45,3 @@ class MysqlSession < Vulcano::Resource
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def mysql_session( user=nil, password=nil )
|
||||
MysqlSession.new(user, password)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
|
||||
require 'utils/simpleconfig'
|
||||
|
||||
class NtpConf < Vulcano::Resource
|
||||
class NtpConf < Vulcano.resource(1)
|
||||
name 'ntp_conf'
|
||||
|
||||
def initialize(path = nil)
|
||||
@runner = Specinfra::Runner
|
||||
@conf_path = path || '/etc/ntp.conf'
|
||||
@files_contents = {}
|
||||
@content = nil
|
||||
|
@ -26,11 +26,11 @@ class NtpConf < Vulcano::Resource
|
|||
|
||||
def read_content
|
||||
# read the file
|
||||
if !@runner.check_file_is_file(@conf_path)
|
||||
if !vulcano.file(@conf_path).file?
|
||||
return skip_resource "Can't find file \"#{@conf_path}\""
|
||||
end
|
||||
@content = read_file(@conf_path)
|
||||
if @content.empty? && @runner.get_file_size(@conf_path).stdout.strip.to_i > 0
|
||||
@content = vulcano.file(@conf_path).content
|
||||
if @content.empty? && vulcano.file(@conf_path).size > 0
|
||||
return skip_resource "Can't read file \"#{@conf_path}\""
|
||||
end
|
||||
# parse the file
|
||||
|
@ -41,13 +41,4 @@ class NtpConf < Vulcano::Resource
|
|||
@content
|
||||
end
|
||||
|
||||
def read_file(path)
|
||||
@files_contents[path] ||= @runner.get_file_content(path).stdout
|
||||
end
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def ntp_conf(path = nil)
|
||||
NtpConf.new(path)
|
||||
end
|
||||
end
|
|
@ -11,13 +11,13 @@
|
|||
# }
|
||||
# describe parse_config(audit, options ) do
|
||||
|
||||
class PConfig < Vulcano::Resource
|
||||
class PConfig < Vulcano.resource(1)
|
||||
name 'parse_config'
|
||||
|
||||
def initialize ( content=nil, useropts = {} )
|
||||
|
||||
default_options = {}
|
||||
@opts = default_options.merge(useropts)
|
||||
@runner = Specinfra::Runner
|
||||
@content = content
|
||||
@files_contents = {}
|
||||
@params = nil
|
||||
|
@ -40,11 +40,11 @@ class PConfig < Vulcano::Resource
|
|||
@conf_path = conf_path
|
||||
|
||||
# read the file
|
||||
if !@runner.check_file_is_file(conf_path)
|
||||
if !vulcano.file(conf_path).file?
|
||||
return skip_resource "Can't find file \"#{conf_path}\""
|
||||
end
|
||||
@content = read_file(conf_path)
|
||||
if @content.empty? && @runner.get_file_size(conf_path).stdout.strip.to_i > 0
|
||||
if @content.empty? && vulcano.file(conf_path).size > 0
|
||||
return skip_resource "Can't read file \"#{conf_path}\""
|
||||
end
|
||||
|
||||
|
@ -52,7 +52,7 @@ class PConfig < Vulcano::Resource
|
|||
end
|
||||
|
||||
def read_file(path)
|
||||
@files_contents[path] ||= @runner.get_file_content(path).stdout
|
||||
@files_contents[path] ||= vulcano.file(path).content
|
||||
end
|
||||
|
||||
def read_content
|
||||
|
@ -62,14 +62,11 @@ class PConfig < Vulcano::Resource
|
|||
end
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def parse_config(content, opts={})
|
||||
PConfig.new(content, opts)
|
||||
end
|
||||
class PConfigFile < PConfig
|
||||
name 'parse_config_file'
|
||||
|
||||
def parse_config_file(file, opts={})
|
||||
p = PConfig.new(nil, opts)
|
||||
p.parse_file(file)
|
||||
p
|
||||
def initialize(path, opts)
|
||||
super(nil, opts)
|
||||
parse_file(path)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,20 +11,24 @@
|
|||
# - home directory
|
||||
# - command
|
||||
|
||||
include Serverspec::Type
|
||||
|
||||
class Passwd < Serverspec::Type::File
|
||||
class Passwd < Vulcano.resource(1)
|
||||
name 'passwd'
|
||||
|
||||
attr_accessor :uid
|
||||
|
||||
def initialize(path = nil, uid: nil)
|
||||
@path = path || '/etc/passwd'
|
||||
@content = vulcano.file(@path).content
|
||||
@parsed = parse(@content)
|
||||
end
|
||||
|
||||
def to_s
|
||||
%Q[/etc/passwd]
|
||||
@path
|
||||
end
|
||||
|
||||
def determine_uid ()
|
||||
parsed = parse()
|
||||
uids = Array.new
|
||||
parsed.each {|x|
|
||||
@parsed.each {|x|
|
||||
if ( x.at(2) == "#{@uid}") then
|
||||
uids.push(x.at(0))
|
||||
end
|
||||
|
@ -43,8 +47,7 @@ class Passwd < Serverspec::Type::File
|
|||
end
|
||||
|
||||
def map_data (id)
|
||||
parsed = parse()
|
||||
parsed.map {|x|
|
||||
@parsed.map {|x|
|
||||
x.at(id)
|
||||
}
|
||||
end
|
||||
|
@ -66,8 +69,7 @@ class Passwd < Serverspec::Type::File
|
|||
end
|
||||
|
||||
def users
|
||||
parsed = parse()
|
||||
parsed.map {|x|
|
||||
@parsed.map {|x|
|
||||
{
|
||||
"name" => x.at(0),
|
||||
"password" => x.at(1),
|
||||
|
@ -80,20 +82,12 @@ class Passwd < Serverspec::Type::File
|
|||
}
|
||||
end
|
||||
|
||||
def parse
|
||||
entries = Array.new
|
||||
content().split("\n").each do |line|
|
||||
entries.push(line.split(':'))
|
||||
private
|
||||
|
||||
def parse(content)
|
||||
content.split("\n").map do |line|
|
||||
line.split(':')
|
||||
end
|
||||
entries
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def passwd(uid=nil)
|
||||
i = Passwd.new('/etc/passwd')
|
||||
i.uid = uid
|
||||
i
|
||||
end
|
||||
end
|
|
@ -2,14 +2,16 @@
|
|||
# copyright: 2015, Vulcano Security GmbH
|
||||
# license: All rights reserved
|
||||
|
||||
class Postgres
|
||||
class Postgres < Vulcano.resource(1)
|
||||
name 'postgres'
|
||||
|
||||
attr_reader :service, :data_dir, :conf_dir, :conf_path
|
||||
def initialize
|
||||
case os[:family]
|
||||
when 'ubuntu', 'debian'
|
||||
@service = 'postgresql'
|
||||
@data_dir = '/var/lib/postgresql'
|
||||
@version = command('ls /etc/postgresql/').stdout.chomp
|
||||
@version = vulcano.run_command('ls /etc/postgresql/').stdout.chomp
|
||||
@conf_dir = "/etc/postgresql/#{@version}/main"
|
||||
@conf_path = File.join @conf_dir, 'postgresql.conf'
|
||||
|
||||
|
@ -27,9 +29,3 @@ class Postgres
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def postgres
|
||||
@postgres ||= Postgres.new()
|
||||
end
|
||||
end
|
|
@ -6,10 +6,10 @@ require 'utils/simpleconfig'
|
|||
require 'utils/find_files'
|
||||
require 'resources/postgres'
|
||||
|
||||
class PostgresConf
|
||||
class PostgresConf < Vulcano.resource(1)
|
||||
name 'postgres_conf'
|
||||
|
||||
def initialize( conf_path )
|
||||
@runner = Specinfra::Runner
|
||||
@conf_path = conf_path
|
||||
@conf_dir = File.expand_path(File.dirname @conf_path)
|
||||
@files_contents = {}
|
||||
|
@ -36,11 +36,11 @@ class PostgresConf
|
|||
@params = {}
|
||||
|
||||
# skip if the main configuration file doesn't exist
|
||||
if !@runner.check_file_is_file(@conf_path)
|
||||
if !vulcano.file(@conf_path).file?
|
||||
return skip_resource "Can't find file \"#{@conf_path}\""
|
||||
end
|
||||
raw_conf = read_file(@conf_path)
|
||||
if raw_conf.empty? && @runner.get_file_size(@conf_path).stdout.strip.to_i > 0
|
||||
if raw_conf.empty? && vulcano.file(@conf_path).size > 0
|
||||
return skip_resource("Can't read file \"#{@conf_path}\"")
|
||||
end
|
||||
|
||||
|
@ -70,15 +70,6 @@ class PostgresConf
|
|||
end
|
||||
|
||||
def read_file(path)
|
||||
@files_contents[path] ||= @runner.get_file_content(path).stdout
|
||||
@files_contents[path] ||= vulcano.file(path).content
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
module Serverspec::Type
|
||||
def postgres_conf(path = nil)
|
||||
@postgres_conf ||= {}
|
||||
dpath = path || postgres.conf_path
|
||||
@postgres_conf[dpath] ||= PostgresConf.new( dpath )
|
||||
end
|
||||
end
|
|
@ -2,25 +2,22 @@
|
|||
# copyright: 2015, Vulcano Security GmbH
|
||||
# license: All rights reserved
|
||||
|
||||
module Serverspec end
|
||||
module Serverspec::Type
|
||||
class Lines
|
||||
def initialize raw, desc
|
||||
@raw = raw
|
||||
@desc = desc
|
||||
end
|
||||
class Lines
|
||||
def initialize raw, desc
|
||||
@raw = raw
|
||||
@desc = desc
|
||||
end
|
||||
|
||||
def output
|
||||
@raw
|
||||
end
|
||||
def output
|
||||
@raw
|
||||
end
|
||||
|
||||
def lines
|
||||
@raw.split("\n")
|
||||
end
|
||||
def lines
|
||||
@raw.split("\n")
|
||||
end
|
||||
|
||||
def to_s
|
||||
@desc
|
||||
end
|
||||
def to_s
|
||||
@desc
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -36,7 +33,7 @@ class PostgresSession
|
|||
# that does this securely
|
||||
escaped_query = query.gsub(/\\/, '\\\\').gsub(/"/,'\\"').gsub(/\$/,'\\$')
|
||||
# run the query
|
||||
cmd = Serverspec::Type::Command.new("PGPASSWORD='#{@pass}' psql -U #{@user} #{dbs} -c \"#{escaped_query}\"")
|
||||
cmd = vulcano.run_command("PGPASSWORD='#{@pass}' psql -U #{@user} #{dbs} -c \"#{escaped_query}\"")
|
||||
out = cmd.stdout + "\n" + cmd.stderr
|
||||
if out =~ /could not connect to .*/ or
|
||||
out.downcase =~ /^error/
|
||||
|
@ -51,15 +48,9 @@ class PostgresSession
|
|||
sub(/(.*\n)+([-]+[+])*[-]+\n/,'').
|
||||
# remove the tail
|
||||
sub(/\n[^\n]*\n\n$/,'')
|
||||
l = Serverspec::Type::Lines.new(lines.strip, "PostgreSQL query: #{query}")
|
||||
l = Lines.new(lines.strip, "PostgreSQL query: #{query}")
|
||||
RSpec.__send__( 'describe', l, &block )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def postgres_session( user, password )
|
||||
PostgresSession.new(user, password)
|
||||
end
|
||||
end
|
|
@ -2,17 +2,19 @@
|
|||
# copyright: 2015, Vulcano Security GmbH
|
||||
# license: All rights reserved
|
||||
|
||||
include Serverspec::Type
|
||||
class Processes < Vulcano.resource(1)
|
||||
name 'processes'
|
||||
|
||||
class Processes < Serverspec::Type::Base
|
||||
def initialize grep
|
||||
attr_reader :list
|
||||
def initialize(grep)
|
||||
# turn into a regexp if it isn't one yet
|
||||
if grep.class == String
|
||||
grep = '(/[^/]*)*'+grep if grep[0] != '/'
|
||||
grep = Regexp.new('^'+grep+'(\s|$)')
|
||||
end
|
||||
|
||||
# get all running processes
|
||||
cmd = Serverspec::Type::Command.new('ps aux')
|
||||
cmd = vulcano.run_command('ps aux')
|
||||
all = cmd.stdout.split("\n")[1..-1]
|
||||
all_cmds = all.map do |line|
|
||||
# user 32296 0.0 0.0 42592 7972 pts/15 Ss+ Apr06 0:00 zsh
|
||||
|
@ -37,10 +39,5 @@ class Processes < Serverspec::Type::Base
|
|||
hm[:command] =~ grep
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def processes( grep )
|
||||
Processes.new(grep)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,14 +2,20 @@
|
|||
# copyright: 2015, Vulcano Security GmbH
|
||||
# license: All rights reserved
|
||||
|
||||
include Serverspec::Type
|
||||
require 'json'
|
||||
|
||||
# Registry Key Helper
|
||||
class RegistryKey < Serverspec::Type::Base
|
||||
class RegistryKey < Vulcano.resource(1)
|
||||
name 'registry_key'
|
||||
|
||||
attr_accessor :reg_key
|
||||
|
||||
def initialize(name, reg_key = nil)
|
||||
# if we have one parameter, we use it as name
|
||||
reg_key = name if reg_key == nil
|
||||
@name = name
|
||||
@reg_key = reg_key
|
||||
end
|
||||
|
||||
def getRegistryValue(path, key)
|
||||
cmd = "(Get-Item 'Registry::#{path}').GetValue('#{key}')"
|
||||
command_result ||= @runner.run_command(cmd)
|
||||
|
@ -42,17 +48,3 @@ class RegistryKey < Serverspec::Type::Base
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def registry_key(name, reg_key=nil)
|
||||
# if we have one parameter, we use it as name
|
||||
if reg_key == nil
|
||||
reg_key = name
|
||||
end
|
||||
|
||||
# initialize variable
|
||||
i = RegistryKey.new(name)
|
||||
i.reg_key = reg_key
|
||||
i
|
||||
end
|
||||
end
|
|
@ -1,40 +0,0 @@
|
|||
# encoding: utf-8
|
||||
# copyright: 2015, Vulcano Security GmbH
|
||||
# license: All rights reserved
|
||||
|
||||
require 'resources/apache_conf'
|
||||
require 'resources/audit_policy'
|
||||
require 'resources/auditd_conf'
|
||||
require 'resources/auditd_rules'
|
||||
require 'resources/command'
|
||||
require 'resources/env'
|
||||
require 'resources/etc_group'
|
||||
require 'resources/file'
|
||||
require 'resources/group_policy'
|
||||
require 'resources/inetd_conf'
|
||||
require 'resources/limits_conf'
|
||||
require 'resources/login_def'
|
||||
require 'resources/mysql'
|
||||
require 'resources/mysql_conf'
|
||||
require 'resources/mysql_session'
|
||||
require 'resources/ntp_conf'
|
||||
require 'resources/parse_config'
|
||||
require 'resources/passwd'
|
||||
require 'resources/postgres'
|
||||
require 'resources/postgres_conf'
|
||||
require 'resources/postgres_session'
|
||||
require 'resources/processes'
|
||||
require 'resources/registry_key'
|
||||
require 'resources/security_policy'
|
||||
require 'resources/ssh_conf'
|
||||
|
||||
# extend serverspec types
|
||||
module Serverspec
|
||||
module Type
|
||||
|
||||
def directory(name)
|
||||
Directory.new(name)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -4,14 +4,13 @@
|
|||
# secedit /export /cfg secpol.cfg
|
||||
#
|
||||
# @link http://www.microsoft.com/en-us/download/details.aspx?id=25250
|
||||
#
|
||||
#
|
||||
# In Windows, some security options are managed differently that the local GPO
|
||||
# All local GPO parameters can be examined via Registry, but not all security
|
||||
# All local GPO parameters can be examined via Registry, but not all security
|
||||
# parameters. Therefore we need a combination of Registry and secedit output
|
||||
|
||||
include Serverspec::Type
|
||||
|
||||
class SecurityPolicy < Serverspec::Type::Base
|
||||
class SecurityPolicy < Vulcano.resource(1)
|
||||
name 'security_policy'
|
||||
|
||||
# static variable, shared across all instances
|
||||
@@loaded = false
|
||||
|
@ -46,7 +45,7 @@ class SecurityPolicy < Serverspec::Type::Base
|
|||
# find line with key
|
||||
key = method.to_s
|
||||
target = ""
|
||||
@@policy.each_line {|s|
|
||||
@@policy.each_line {|s|
|
||||
target = s.strip if s.match(/\b#{key}\s*=\s*(.*)\b/)
|
||||
}
|
||||
|
||||
|
@ -57,9 +56,9 @@ class SecurityPolicy < Serverspec::Type::Base
|
|||
val = result[:value]
|
||||
val = val.to_i if val.match(/^\d+$/)
|
||||
else
|
||||
# TODO we may need to return skip or failure if the
|
||||
# TODO we may need to return skip or failure if the
|
||||
# requested value is not available
|
||||
val = nil
|
||||
val = nil
|
||||
end
|
||||
|
||||
val
|
||||
|
@ -70,9 +69,3 @@ class SecurityPolicy < Serverspec::Type::Base
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def security_policy()
|
||||
SecurityPolicy.new()
|
||||
end
|
||||
end
|
|
@ -4,16 +4,12 @@
|
|||
|
||||
require 'utils/simpleconfig'
|
||||
|
||||
class SshConf < Vulcano::Resource
|
||||
class SshConf < Vulcano.resource(1)
|
||||
name 'ssh_config'
|
||||
|
||||
def initialize( conf_path, type = nil )
|
||||
@runner = Specinfra::Runner
|
||||
@conf_path = conf_path
|
||||
@conf_dir = File.expand_path(File.dirname @conf_path)
|
||||
@files_contents = {}
|
||||
@content = nil
|
||||
@params = nil
|
||||
typename = ( conf_path.include?('sshd') ? 'Server' : 'Client' )
|
||||
def initialize( conf_path = nil, type = nil )
|
||||
@conf_path = conf_path || '/etc/ssh/ssh_config'
|
||||
typename = ( @conf_path.include?('sshd') ? 'Server' : 'Client' )
|
||||
@type = type || "SSH #{typename} configuration #{conf_path}"
|
||||
read_content
|
||||
end
|
||||
|
@ -23,11 +19,10 @@ class SshConf < Vulcano::Resource
|
|||
end
|
||||
|
||||
def content
|
||||
@content ||= read_content
|
||||
@conf.content
|
||||
end
|
||||
|
||||
def params *opts
|
||||
@params || read_content
|
||||
res = @params
|
||||
opts.each do |opt|
|
||||
res = res[opt] unless res.nil?
|
||||
|
@ -36,40 +31,35 @@ class SshConf < Vulcano::Resource
|
|||
end
|
||||
|
||||
def method_missing name
|
||||
@params || read_content
|
||||
@params[name.to_s]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def read_content
|
||||
@conf = vulcano.file(@conf_path)
|
||||
# read the file
|
||||
if !@runner.check_file_is_file(@conf_path)
|
||||
if !@conf.file?
|
||||
return skip_resource "Can't find file \"#{@conf_path}\""
|
||||
end
|
||||
@content = read_file(@conf_path)
|
||||
if @content.empty? && @runner.get_file_size(@conf_path).stdout.strip.to_i > 0
|
||||
|
||||
if @conf.content.empty? && @conf.size > 0
|
||||
return skip_resource "Can't read file \"#{@conf_path}\""
|
||||
end
|
||||
|
||||
# parse the file
|
||||
@params = SimpleConfig.new(@content,
|
||||
@params = SimpleConfig.new(@conf.content,
|
||||
assignment_re: /^\s*(\S+?)\s+(.*?)\s*$/,
|
||||
multiple_values: false
|
||||
).params
|
||||
@content
|
||||
end
|
||||
|
||||
def read_file(path)
|
||||
@files_contents[path] ||= @runner.get_file_content(path).stdout
|
||||
end
|
||||
end
|
||||
|
||||
module Serverspec::Type
|
||||
def ssh_config( path = nil )
|
||||
@ssh_config ||= {}
|
||||
dpath = path || '/etc/ssh/ssh_config'
|
||||
@ssh_config[dpath] ||= SshConf.new(dpath)
|
||||
end
|
||||
class SshdConf < SshConf
|
||||
name 'sshd_config'
|
||||
|
||||
def sshd_config( path = nil )
|
||||
ssh_config( path || '/etc/ssh/sshd_config' )
|
||||
def initialize(path = nil)
|
||||
super(path || '/etc/ssh/sshd_config')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -45,7 +45,7 @@ if os[:family] == 'windows'
|
|||
release = versions[version]
|
||||
end
|
||||
|
||||
# hijack os-detection from serverspec
|
||||
# print OS detection infos
|
||||
puts JSON.dump({
|
||||
os_family: os[:family],
|
||||
os_release: release || os[:release],
|
||||
|
|
|
@ -1,19 +1,8 @@
|
|||
# Copyright 2014 Dominik Richter. All rights reserved.
|
||||
# Spec file for Vulcano specs
|
||||
|
||||
module Serverspec
|
||||
end
|
||||
|
||||
# Get types
|
||||
module DummyServerspecTypes
|
||||
sgem = Gem::Specification.find_by_name("serverspec")
|
||||
types = Dir[File.join sgem.gem_dir, 'lib', 'serverspec', 'type', '*'].
|
||||
map{|x| File.basename(x).sub(/\.rb$/,'')}
|
||||
types.each do |name|
|
||||
define_method name do |*arg|
|
||||
end
|
||||
end
|
||||
|
||||
module DummyTestTypes
|
||||
# a few commands with special handling
|
||||
def describe *args; end
|
||||
def context *args; end
|
||||
|
|
|
@ -9,12 +9,12 @@ libdir = File.dirname(__FILE__)
|
|||
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
||||
|
||||
require 'vulcano/version'
|
||||
require 'vulcano/backend'
|
||||
require 'vulcano/resource'
|
||||
require 'vulcano/rspec_json_formatter'
|
||||
require 'vulcano/rule'
|
||||
require 'vulcano/runner'
|
||||
|
||||
require 'resources/resources'
|
||||
require 'matchers/matchers'
|
||||
|
||||
# Dummy module for handling additional attributes
|
||||
|
|
|
@ -1,6 +1,41 @@
|
|||
# encoding: utf-8
|
||||
require 'vulcano/backend/core'
|
||||
require 'vulcano/backend/docker'
|
||||
require 'vulcano/backend/exec'
|
||||
require 'vulcano/backend/ssh'
|
||||
require 'vulcano/backend/winrm'
|
||||
require 'uri'
|
||||
require 'vulcano/plugins'
|
||||
|
||||
module Vulcano
|
||||
class Backend
|
||||
# Expose all registered backends
|
||||
def self.registry
|
||||
@registry ||= {}
|
||||
end
|
||||
|
||||
# Resolve target configuration in URI-scheme into
|
||||
# all respective fields and merge with existing configuration.
|
||||
# e.g. ssh://bob@remote => backend: ssh, user: bob, host: remote
|
||||
def self.target_config( config )
|
||||
conf = config.dup
|
||||
|
||||
return conf if conf['target'].to_s.empty?
|
||||
|
||||
uri = URI::parse(conf['target'].to_s)
|
||||
conf['backend'] = conf['backend'] || uri.scheme
|
||||
conf['host'] = conf['host'] || uri.host
|
||||
conf['port'] = conf['port'] || uri.port
|
||||
conf['user'] = conf['user'] || uri.user
|
||||
conf['password'] = conf['password'] || uri.password
|
||||
|
||||
# return the updated config
|
||||
conf
|
||||
end
|
||||
end
|
||||
|
||||
def self.backend(version = 1)
|
||||
if version != 1
|
||||
raise "Only backend version 1 is supported!"
|
||||
end
|
||||
Vulcano::Plugins::Backend
|
||||
end
|
||||
end
|
||||
|
||||
require 'vulcano/backend/mock'
|
||||
require 'vulcano/backend/specinfra'
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
# encoding: utf-8
|
||||
require 'utils/modulator'
|
||||
|
||||
module Vulcano
|
||||
class Backend
|
||||
extend Modulator
|
||||
|
||||
def initialize(conf)
|
||||
@conf = conf
|
||||
end
|
||||
|
||||
def resolve_target_options
|
||||
return if @conf[:target].to_s.empty?
|
||||
uri = URI::parse(@conf[:target].to_s)
|
||||
@conf[:backend] = @conf[:backend] || uri.scheme
|
||||
@conf[:host] = @conf[:host] || uri.host
|
||||
@conf[:port] = @conf[:port] || uri.port
|
||||
@conf[:user] = @conf[:user] || uri.user
|
||||
@conf[:password] = @conf[:password] || uri.password
|
||||
end
|
||||
|
||||
def configure_shared_options
|
||||
Specinfra::Backend::Cmd.send(:include, Specinfra::Helper::Set)
|
||||
si = Specinfra.configuration
|
||||
si.os = nil
|
||||
if @conf['disable_sudo']
|
||||
si.disable_sudo = true
|
||||
else
|
||||
si.sudo_password = @conf['sudo_password']
|
||||
si.sudo_options = @conf['sudo_options']
|
||||
end
|
||||
end
|
||||
|
||||
def configure_target
|
||||
t = @conf[:backend] || 'exec'
|
||||
m = Vulcano::Backend.modules[t]
|
||||
raise "Don't understand backend '#{t}'" if m.nil?
|
||||
m.configure(@conf)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,13 +0,0 @@
|
|||
# encoding: utf-8
|
||||
|
||||
module Vulcano::Backend::Docker
|
||||
|
||||
def self.configure(conf)
|
||||
host = conf['host'].to_s
|
||||
Specinfra.configuration.backend = :docker
|
||||
Specinfra.configuration.docker_container = host
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Vulcano::Backend.add_module('docker', Vulcano::Backend::Docker)
|
|
@ -1,11 +0,0 @@
|
|||
# encoding: utf-8
|
||||
|
||||
module Vulcano::Backend::Exec
|
||||
|
||||
def self.configure(conf)
|
||||
Specinfra.configuration.backend = :exec
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Vulcano::Backend.add_module('exec', Vulcano::Backend::Exec)
|
75
lib/vulcano/backend/mock.rb
Normal file
75
lib/vulcano/backend/mock.rb
Normal file
|
@ -0,0 +1,75 @@
|
|||
# encoding: utf-8
|
||||
|
||||
module Vulcano::Backends
|
||||
class Mock < Vulcano.backend(1)
|
||||
name 'mock'
|
||||
|
||||
def initialize( conf )
|
||||
@conf = conf
|
||||
@files = {}
|
||||
end
|
||||
|
||||
def file(path)
|
||||
puts "--> get file #{path}"
|
||||
@files[path] ||= File.new(self, path)
|
||||
end
|
||||
|
||||
def run_command(cmd)
|
||||
Command.new(self, cmd)
|
||||
end
|
||||
|
||||
def to_s
|
||||
'Mock Backend Runner'
|
||||
end
|
||||
end
|
||||
|
||||
class Mock
|
||||
class File
|
||||
|
||||
def initialize(runtime, path)
|
||||
@path = path
|
||||
@exists = (rand < 0.8) ? true : false
|
||||
@is_file = (@exists && rand < 0.7) ? true : false
|
||||
@size = 0
|
||||
@content = ''
|
||||
if @exists && @is_file
|
||||
@size = ( rand ** 3 * 1000 ).to_i
|
||||
@size = 0 if rand < 0.2
|
||||
end
|
||||
if @size > 0
|
||||
@content = (0...50).map { ('a'..'z').to_a[rand(26)] }.join
|
||||
end
|
||||
end
|
||||
|
||||
def size
|
||||
puts "----> get file #{@path} size: #{@size}"
|
||||
@size
|
||||
end
|
||||
|
||||
def content
|
||||
puts "----> get file #{@path} content: #{@content}"
|
||||
@content
|
||||
end
|
||||
|
||||
def file?
|
||||
puts "----> is file #{@path} a file? #{@is_file}"
|
||||
@is_file
|
||||
end
|
||||
|
||||
def exists?
|
||||
puts "----> does file #{@path} exist? #{@exists}"
|
||||
@exists
|
||||
end
|
||||
end
|
||||
|
||||
class Command
|
||||
attr_reader :stdout, :stderr, :exit_status
|
||||
def initialize(runtime, cmd)
|
||||
@exit_code = (rand < 0.7) ? 0 : (100 * rand).to_i
|
||||
@stdout = (0...50).map { ('a'..'z').to_a[rand(26)] }.join
|
||||
@stderr = (0...50).map { ('a'..'z').to_a[rand(26)] }.join
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
250
lib/vulcano/backend/specinfra.rb
Normal file
250
lib/vulcano/backend/specinfra.rb
Normal file
|
@ -0,0 +1,250 @@
|
|||
# encoding: utf-8
|
||||
require 'shellwords'
|
||||
|
||||
module Vulcano::Backends
|
||||
|
||||
class SpecinfraHelper < Vulcano.backend(1)
|
||||
name 'specinfra'
|
||||
|
||||
def initialize(conf)
|
||||
@conf = conf
|
||||
@files = {}
|
||||
type = @conf['backend'].to_s
|
||||
|
||||
reset_backend(type)
|
||||
configure_shared_options
|
||||
|
||||
# configure the given backend, if we can handle it
|
||||
# e.g. backend = exec ==> try to call configure_exec
|
||||
# if we don't support it, error out
|
||||
m = "configure_#{type}"
|
||||
if self.respond_to?(m.to_sym)
|
||||
self.send(m)
|
||||
else
|
||||
raise "Cannot configure Specinfra backend #{type}: it isn't supported yet."
|
||||
end
|
||||
end
|
||||
|
||||
def file(path)
|
||||
@files[path] ||= File.new(path)
|
||||
end
|
||||
|
||||
def run_command(cmd)
|
||||
Specinfra::Runner.run_command(cmd)
|
||||
end
|
||||
|
||||
def to_s
|
||||
'SpecInfra Backend Runner'
|
||||
end
|
||||
|
||||
def reset_backend(type)
|
||||
# may be less nice, but avoid eval...
|
||||
case type
|
||||
when 'exec'
|
||||
Specinfra::Backend::Exec.instance_variable_set(:@instance, nil)
|
||||
when 'docker'
|
||||
Specinfra::Backend::Docker.instance_variable_set(:@instance, nil)
|
||||
when 'ssh'
|
||||
Specinfra::Backend::Ssh.instance_variable_set(:@instance, nil)
|
||||
when 'winrm'
|
||||
Specinfra::Backend::Winrm.instance_variable_set(:@instance, nil)
|
||||
end
|
||||
end
|
||||
|
||||
def configure_shared_options
|
||||
Specinfra::Backend::Cmd.send(:include, Specinfra::Helper::Set)
|
||||
si = Specinfra.configuration
|
||||
si.os = nil
|
||||
if @conf['disable_sudo']
|
||||
si.disable_sudo = true
|
||||
else
|
||||
si.sudo_password = @conf['sudo_password']
|
||||
si.sudo_options = @conf['sudo_options']
|
||||
end
|
||||
end
|
||||
|
||||
def configure_docker
|
||||
host = @conf['host'].to_s
|
||||
Specinfra.configuration.backend = :docker
|
||||
Specinfra.configuration.docker_container = host
|
||||
end
|
||||
|
||||
def configure_exec
|
||||
Specinfra.configuration.backend = :exec
|
||||
end
|
||||
|
||||
def configure_ssh
|
||||
si = Specinfra.configuration
|
||||
si.backend = :ssh
|
||||
si.request_pty = true
|
||||
|
||||
host = @conf['host'].to_s
|
||||
RSpec.configuration.host = host
|
||||
|
||||
ssh_opts = {
|
||||
port: @conf['port'] || 22,
|
||||
auth_methods: ['none'],
|
||||
user_known_hosts_file: "/dev/null",
|
||||
global_known_hosts_file: "/dev/null",
|
||||
number_of_password_prompts: 0,
|
||||
user: @conf['user'],
|
||||
password: @conf['password'],
|
||||
keys: [@conf['key_file']].compact,
|
||||
}
|
||||
|
||||
if host.empty?
|
||||
raise "You must configure a target host."
|
||||
end
|
||||
unless ssh_opts[:port] > 0
|
||||
raise "Port must be > 0 (not #{ssh_opts[:port]})"
|
||||
end
|
||||
if ssh_opts[:user].to_s.empty?
|
||||
raise "User must not be empty."
|
||||
end
|
||||
unless ssh_opts[:keys].empty?
|
||||
ssh_opts[:auth_methods].push('publickey')
|
||||
ssh_opts[:keys_only] = true if ssh_opts[:password].nil?
|
||||
end
|
||||
unless ssh_opts[:password].nil?
|
||||
ssh_opts[:auth_methods].push('password')
|
||||
end
|
||||
if ssh_opts[:keys].empty? and ssh_opts[:password].nil?
|
||||
raise "You must configure at least one authentication method" +
|
||||
": Password or key."
|
||||
end
|
||||
|
||||
si.ssh_options = ssh_opts
|
||||
|
||||
end
|
||||
|
||||
def configure_winrm
|
||||
si = Specinfra.configuration
|
||||
si.backend = :winrm
|
||||
si.os = { family: 'windows'}
|
||||
|
||||
# common options
|
||||
host = conf['host'].to_s
|
||||
port = conf['port']
|
||||
user = conf['user'].to_s
|
||||
pass = conf['password'].to_s
|
||||
|
||||
# SSL configuration
|
||||
if conf['winrm_ssl']
|
||||
scheme = 'https'
|
||||
port = port || 5986
|
||||
else
|
||||
scheme = 'http'
|
||||
port = port || 5985
|
||||
end
|
||||
|
||||
# validation
|
||||
if host.empty?
|
||||
raise "You must configure a target host."
|
||||
end
|
||||
unless port > 0
|
||||
raise "Port must be > 0 (not #{port})"
|
||||
end
|
||||
if user.empty?
|
||||
raise "You must configure a WinRM user for login."
|
||||
end
|
||||
if pass.empty?
|
||||
raise "You must configure a WinRM password."
|
||||
end
|
||||
|
||||
# create the connection
|
||||
endpoint = "#{scheme}://#{host}:#{port}/wsman"
|
||||
winrm = ::WinRM::WinRMWebService.new(
|
||||
endpoint,
|
||||
:ssl,
|
||||
user: user,
|
||||
pass: pass,
|
||||
basic_auth_only: true,
|
||||
no_ssl_peer_verification: conf['winrm_self_signed'],
|
||||
)
|
||||
si.winrm = winrm
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class SpecinfraHelper
|
||||
|
||||
class File < FileCommon
|
||||
TYPES = {
|
||||
socket: 00140000,
|
||||
symlink: 00120000,
|
||||
file: 00100000,
|
||||
block_device: 00060000,
|
||||
directory: 00040000,
|
||||
character_device: 00020000,
|
||||
pipe: 00010000,
|
||||
}
|
||||
def initialize(path)
|
||||
@path = path
|
||||
end
|
||||
|
||||
def type
|
||||
path = Shellwords.escape(@path)
|
||||
raw_type = Specinfra::Runner.run_command("stat -c %f #{path}").stdout
|
||||
tmask = raw_type.to_i(16)
|
||||
res = TYPES.find{|x, mask| mask & tmask == mask}
|
||||
return :unknown if res.nil?
|
||||
res[0]
|
||||
end
|
||||
|
||||
def exists?
|
||||
Specinfra::Runner.check_file_exists(@path)
|
||||
end
|
||||
|
||||
def mode
|
||||
Specinfra::Runner.get_file_mode(@path).stdout.to_i(8)
|
||||
end
|
||||
|
||||
def owner
|
||||
Specinfra::Runner.get_file_owner_user(@path).stdout.strip
|
||||
end
|
||||
|
||||
def group
|
||||
Specinfra::Runner.get_file_owner_group(@path).stdout.strip
|
||||
end
|
||||
|
||||
def link_target
|
||||
path = Shellwords.escape(@path)
|
||||
Specinfra::Runner.run_command("readlink #{path}").stdout.strip
|
||||
end
|
||||
|
||||
def content
|
||||
Specinfra::Runner.get_file_content(@path).stdout
|
||||
end
|
||||
|
||||
def mtime
|
||||
Specinfra::Runner.get_file_mtime(@path).stdout.strip
|
||||
end
|
||||
|
||||
def size
|
||||
Specinfra::Runner.get_file_size(@path).stdout.strip.to_i
|
||||
end
|
||||
|
||||
def selinux_label
|
||||
Specinfra::Runner.get_file_selinuxlabel(@path).stdout.strip
|
||||
end
|
||||
|
||||
def mounted?(opts = {}, only_with = nil)
|
||||
Specinfra::Runner.check_file_is_mounted(@path, opts, only_with)
|
||||
end
|
||||
|
||||
def immutable?
|
||||
Specinfra::Runner.get_file_immutable(@path)
|
||||
end
|
||||
|
||||
def product_version
|
||||
Specinfra::Runner.run_command("(Get-Command '#{@path}').FileVersionInfo.ProductVersion").stdout.strip
|
||||
end
|
||||
|
||||
def file_version
|
||||
Specinfra::Runner.run_command("(Get-Command '#{@path}').FileVersionInfo.FileVersion").stdout.strip
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,51 +0,0 @@
|
|||
# encoding: utf-8
|
||||
|
||||
module Vulcano::Backend::SSH
|
||||
|
||||
def self.configure(conf)
|
||||
si = Specinfra.configuration
|
||||
si.backend = :ssh
|
||||
si.request_pty = true
|
||||
|
||||
host = conf['host'].to_s
|
||||
RSpec.configuration.host = host
|
||||
|
||||
ssh_opts = {
|
||||
port: conf['port'] || 22,
|
||||
auth_methods: ['none'],
|
||||
user_known_hosts_file: "/dev/null",
|
||||
global_known_hosts_file: "/dev/null",
|
||||
number_of_password_prompts: 0,
|
||||
user: conf['user'],
|
||||
password: conf['password'],
|
||||
keys: [conf['key_file']].compact,
|
||||
}
|
||||
|
||||
if host.empty?
|
||||
raise "You must configure a target host."
|
||||
end
|
||||
unless ssh_opts[:port] > 0
|
||||
raise "Port must be > 0 (not #{ssh_opts[:port]})"
|
||||
end
|
||||
if ssh_opts[:user].to_s.empty?
|
||||
raise "User must not be empty."
|
||||
end
|
||||
unless ssh_opts[:keys].empty?
|
||||
ssh_opts[:auth_methods].push('publickey')
|
||||
ssh_opts[:keys_only] = true if ssh_opts[:password].nil?
|
||||
end
|
||||
unless ssh_opts[:password].nil?
|
||||
ssh_opts[:auth_methods].push('password')
|
||||
end
|
||||
if ssh_opts[:keys].empty? and ssh_opts[:password].nil?
|
||||
raise "You must configure at least one authentication method" +
|
||||
": Password or key."
|
||||
end
|
||||
|
||||
si.ssh_options = ssh_opts
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Vulcano::Backend.add_module('ssh', Vulcano::Backend::SSH)
|
|
@ -1,55 +0,0 @@
|
|||
# encoding: utf-8
|
||||
require 'winrm'
|
||||
|
||||
module Vulcano::Backend::WinRM
|
||||
|
||||
def self.configure(conf)
|
||||
si = Specinfra.configuration
|
||||
si.backend = :winrm
|
||||
si.os = { family: 'windows'}
|
||||
|
||||
# common options
|
||||
host = conf['host'].to_s
|
||||
port = conf['port']
|
||||
user = conf['user'].to_s
|
||||
pass = conf['password'].to_s
|
||||
|
||||
# SSL configuration
|
||||
if conf['winrm_ssl']
|
||||
scheme = 'https'
|
||||
port = port || 5986
|
||||
else
|
||||
scheme = 'http'
|
||||
port = port || 5985
|
||||
end
|
||||
|
||||
# validation
|
||||
if host.empty?
|
||||
raise "You must configure a target host."
|
||||
end
|
||||
unless port > 0
|
||||
raise "Port must be > 0 (not #{port})"
|
||||
end
|
||||
if user.empty?
|
||||
raise "You must configure a WinRM user for login."
|
||||
end
|
||||
if pass.empty?
|
||||
raise "You must configure a WinRM password."
|
||||
end
|
||||
|
||||
# create the connection
|
||||
endpoint = "#{scheme}://#{host}:#{port}/wsman"
|
||||
winrm = ::WinRM::WinRMWebService.new(
|
||||
endpoint,
|
||||
:ssl,
|
||||
user: user,
|
||||
pass: pass,
|
||||
basic_auth_only: true,
|
||||
no_ssl_peer_verification: conf['winrm_self_signed'],
|
||||
)
|
||||
si.winrm = winrm
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Vulcano::Backend.add_module('winrm', Vulcano::Backend::WinRM)
|
8
lib/vulcano/plugins.rb
Normal file
8
lib/vulcano/plugins.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
# encoding: utf-8
|
||||
|
||||
module Vulcano
|
||||
module Plugins
|
||||
autoload :Resource, 'vulcano/plugins/resource'
|
||||
autoload :Backend, 'vulcano/plugins/backend'
|
||||
end
|
||||
end
|
130
lib/vulcano/plugins/backend.rb
Normal file
130
lib/vulcano/plugins/backend.rb
Normal file
|
@ -0,0 +1,130 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'digest'
|
||||
|
||||
module Vulcano::Plugins
|
||||
|
||||
class Backend
|
||||
def self.name( name )
|
||||
Vulcano::Plugins::Backend.__register(name, self)
|
||||
end
|
||||
|
||||
def self.__register(id, obj)
|
||||
# raise errors for all missing methods
|
||||
%w{ file run_command os }.each do |m|
|
||||
next if obj.public_method_defined?(m.to_sym)
|
||||
obj.send(:define_method, m.to_sym) do |*args|
|
||||
raise NotImplementedError.new("Backend must implement the #{m}() method.")
|
||||
end
|
||||
end
|
||||
|
||||
Vulcano::Backend.registry[id] = obj
|
||||
end
|
||||
|
||||
class FileCommon
|
||||
# interface methods: these fields should be implemented by every
|
||||
# backend File
|
||||
%w{
|
||||
exists? mode owner group link_target content mtime size
|
||||
selinux_label product_version file_version
|
||||
}
|
||||
|
||||
def type
|
||||
:unknown
|
||||
end
|
||||
|
||||
# The following methods can be overwritten by a derived class
|
||||
# if desired, to e.g. achieve optimizations.
|
||||
|
||||
def md5sum
|
||||
res = Digest::MD5.new
|
||||
res.update(content)
|
||||
res.hexdigest
|
||||
end
|
||||
|
||||
def sha256sum
|
||||
res = Digest::SHA256.new
|
||||
res.update(content)
|
||||
res.hexdigest
|
||||
end
|
||||
|
||||
# Additional methods for convenience
|
||||
|
||||
def file?
|
||||
type == :file
|
||||
end
|
||||
|
||||
def block_device?
|
||||
type == :block_device
|
||||
end
|
||||
|
||||
def character_device?
|
||||
type == :character_device
|
||||
end
|
||||
|
||||
def socket?
|
||||
type == :socket
|
||||
end
|
||||
|
||||
def directory?
|
||||
type == :directory
|
||||
end
|
||||
|
||||
def symlink?
|
||||
type == :symlink
|
||||
end
|
||||
|
||||
def pipe?
|
||||
type == :pipe?
|
||||
end
|
||||
|
||||
def mode?(mode)
|
||||
mode == mode
|
||||
end
|
||||
|
||||
def owned_by?(owner)
|
||||
owner == owner
|
||||
end
|
||||
|
||||
def grouped_into?(group)
|
||||
group == group
|
||||
end
|
||||
|
||||
def linked_to?(dst)
|
||||
link_target == dst
|
||||
end
|
||||
|
||||
def version?(version)
|
||||
product_version == version or
|
||||
file_version == version
|
||||
end
|
||||
|
||||
# helper methods provided to any implementing class
|
||||
private
|
||||
|
||||
UNIX_MODE_OWNERS = {
|
||||
owner: 00700,
|
||||
group: 00070,
|
||||
other: 00007,
|
||||
}
|
||||
|
||||
UNIX_MODE_TYPES = {
|
||||
r: 00444,
|
||||
w: 00222,
|
||||
x: 00111,
|
||||
}
|
||||
|
||||
def unix_mode_mask(owner, type)
|
||||
o = UNIX_MODE_OWNERS[owner.to_sym]
|
||||
return nil if o.nil?
|
||||
|
||||
t = UNIX_MODE_TYPES[type.to_sym]
|
||||
return nil if t.nil?
|
||||
|
||||
t & o
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
39
lib/vulcano/plugins/resource.rb
Normal file
39
lib/vulcano/plugins/resource.rb
Normal file
|
@ -0,0 +1,39 @@
|
|||
# encoding: utf-8
|
||||
|
||||
module Vulcano
|
||||
module Plugins
|
||||
|
||||
class Resource
|
||||
def self.name( name )
|
||||
Vulcano::Plugins::Resource.__register(name, self)
|
||||
end
|
||||
|
||||
def self.__register(name, obj)
|
||||
cl = Class.new(obj) do
|
||||
# add some common methods
|
||||
include Vulcano::Plugins::ResourceCommon
|
||||
def initialize(backend, *args)
|
||||
# attach the backend to this instance
|
||||
self.class.send(:define_method, :vulcano){backend}
|
||||
# call the resource initializer
|
||||
super(*args)
|
||||
end
|
||||
end
|
||||
|
||||
# add the resource to the registry by name
|
||||
Vulcano::Resource.registry[name] = cl
|
||||
end
|
||||
end
|
||||
|
||||
module ResourceCommon
|
||||
def resource_skipped
|
||||
@resource_skipped
|
||||
end
|
||||
|
||||
def skip_resource message
|
||||
@resource_skipped = message
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
72
lib/vulcano/profile_context.rb
Normal file
72
lib/vulcano/profile_context.rb
Normal file
|
@ -0,0 +1,72 @@
|
|||
require 'vulcano/backend'
|
||||
|
||||
module Vulcano
|
||||
|
||||
class ProfileContext
|
||||
|
||||
attr_reader :rules, :only_ifs
|
||||
def initialize(profile_id, backend, profile_registry: {}, only_ifs: [])
|
||||
if backend.nil?
|
||||
raise "ProfileContext is initiated with a backend == nil. " +
|
||||
"This is a backend error which must be fixed upstream."
|
||||
end
|
||||
|
||||
@profile_id = profile_id
|
||||
@rules = profile_registry
|
||||
@only_ifs = only_ifs
|
||||
__CTX = self
|
||||
|
||||
# This is the heart of the profile context
|
||||
# An instantiated object which has all resources registered to it
|
||||
# and exposes them to the a test file.
|
||||
ctx = Class.new do
|
||||
include Vulcano::DSL
|
||||
|
||||
define_method :__register_rule do |*args|
|
||||
__CTX.register_rule(*args)
|
||||
end
|
||||
define_method :__unregister_rule do |*args|
|
||||
__CTX.unregister_rule(*args)
|
||||
end
|
||||
|
||||
Vulcano::Resource.registry.each do |id,r|
|
||||
define_method id.to_sym do |*args|
|
||||
r.new(backend, *args)
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
'Profile Context Run'
|
||||
end
|
||||
end
|
||||
@profile_context = ctx.new
|
||||
|
||||
end
|
||||
|
||||
def load(content, source, line)
|
||||
@profile_context.instance_eval(content, source, line)
|
||||
end
|
||||
|
||||
def unregister_rule id
|
||||
full_id = VulcanoBaseRule::full_id(@profile_id, id)
|
||||
@rules[full_id] = nil
|
||||
end
|
||||
|
||||
def register_rule r
|
||||
# get the full ID
|
||||
full_id = VulcanoBaseRule::full_id(@profile_id, r)
|
||||
if full_id.nil?
|
||||
# TODO error
|
||||
return
|
||||
end
|
||||
# add the rule to the registry
|
||||
existing = @rules[full_id]
|
||||
if existing.nil?
|
||||
@rules[full_id] = r
|
||||
else
|
||||
VulcanoBaseRule::merge(existing, r)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,17 +1,46 @@
|
|||
# encoding: utf-8
|
||||
# copyright: 2015, Vulcano Security GmbH
|
||||
# license: All rights reserved
|
||||
require 'vulcano/plugins'
|
||||
|
||||
module Vulcano
|
||||
class Resource
|
||||
|
||||
def resource_skipped
|
||||
@resource_skipped
|
||||
def self.registry
|
||||
@registry ||= {}
|
||||
end
|
||||
|
||||
def skip_resource message
|
||||
@resource_skipped = message
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def self.resource(version)
|
||||
if version != 1
|
||||
raise "Only resource version 1 is supported!"
|
||||
end
|
||||
Vulcano::Plugins::Resource
|
||||
end
|
||||
end
|
||||
|
||||
require 'resources/apache_conf'
|
||||
require 'resources/audit_policy'
|
||||
require 'resources/auditd_conf'
|
||||
require 'resources/auditd_rules'
|
||||
require 'resources/command'
|
||||
require 'resources/directory'
|
||||
require 'resources/env'
|
||||
require 'resources/etc_group'
|
||||
require 'resources/file'
|
||||
require 'resources/group_policy'
|
||||
require 'resources/inetd_conf'
|
||||
require 'resources/limits_conf'
|
||||
require 'resources/login_def'
|
||||
require 'resources/mysql'
|
||||
require 'resources/mysql_conf'
|
||||
require 'resources/mysql_session'
|
||||
require 'resources/ntp_conf'
|
||||
require 'resources/parse_config'
|
||||
require 'resources/passwd'
|
||||
require 'resources/postgres'
|
||||
require 'resources/postgres_conf'
|
||||
require 'resources/postgres_session'
|
||||
require 'resources/processes'
|
||||
require 'resources/registry_key'
|
||||
require 'resources/security_policy'
|
||||
require 'resources/ssh_conf'
|
||||
|
|
|
@ -2,12 +2,9 @@
|
|||
# copyright: 2015, Dominik Richter
|
||||
# license: All rights reserved
|
||||
require 'vulcano/base_rule'
|
||||
require 'serverspec'
|
||||
|
||||
module Vulcano
|
||||
class Rule < VulcanoBaseRule
|
||||
include Serverspec::Helper::Type
|
||||
extend Serverspec::Helper::Type
|
||||
include RSpec::Core::DSL
|
||||
|
||||
# Override RSpec methods to add
|
||||
|
@ -173,43 +170,6 @@ module Vulcano::DSL
|
|||
|
||||
end
|
||||
|
||||
module Vulcano
|
||||
class ProfileContext
|
||||
|
||||
include Serverspec::Helper::Type
|
||||
extend Serverspec::Helper::Type
|
||||
include Vulcano::DSL
|
||||
|
||||
def initialize profile_id, profile_registry, only_ifs
|
||||
@profile_id = profile_id
|
||||
@rules = profile_registry
|
||||
@only_ifs = only_ifs
|
||||
end
|
||||
|
||||
def __unregister_rule id
|
||||
full_id = VulcanoBaseRule::full_id(@profile_id, id)
|
||||
@rules[full_id] = nil
|
||||
end
|
||||
|
||||
def __register_rule r
|
||||
# get the full ID
|
||||
full_id = VulcanoBaseRule::full_id(@profile_id, r)
|
||||
if full_id.nil?
|
||||
# TODO error
|
||||
return
|
||||
end
|
||||
# add the rule to the registry
|
||||
existing = @rules[full_id]
|
||||
if existing.nil?
|
||||
@rules[full_id] = r
|
||||
else
|
||||
VulcanoBaseRule::merge(existing, r)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
module Vulcano::GlobalDSL
|
||||
def __register_rule r
|
||||
# make sure the profile id is attached to the rule
|
||||
|
|
|
@ -5,15 +5,13 @@
|
|||
require 'uri'
|
||||
require 'vulcano/backend'
|
||||
require 'vulcano/targets'
|
||||
require 'vulcano/profile_context'
|
||||
# spec requirements
|
||||
require 'rspec'
|
||||
require 'rspec/its'
|
||||
require 'specinfra'
|
||||
require 'specinfra/helper'
|
||||
require 'specinfra/helper/set'
|
||||
require 'serverspec/helper'
|
||||
require 'serverspec/matcher'
|
||||
require 'serverspec/subject'
|
||||
require 'vulcano/rspec_json_formatter'
|
||||
|
||||
module Vulcano
|
||||
|
@ -23,37 +21,67 @@ module Vulcano
|
|||
def initialize(profile_id, conf)
|
||||
@rules = []
|
||||
@profile_id = profile_id
|
||||
@conf = conf.dup
|
||||
@conf = Vulcano::Backend.target_config(normalize_map(conf))
|
||||
|
||||
# RSpec.configuration.output_stream = $stdout
|
||||
# RSpec.configuration.error_stream = $stderr
|
||||
RSpec.configuration.add_formatter(:json)
|
||||
# global reset
|
||||
RSpec.world.reset
|
||||
|
||||
# specinfra
|
||||
backend = Vulcano::Backend.new(@conf)
|
||||
backend.resolve_target_options
|
||||
backend.configure_shared_options
|
||||
backend.configure_target
|
||||
configure_output
|
||||
configure_backend
|
||||
end
|
||||
|
||||
def add_resources(resources)
|
||||
items = resources.map do |resource|
|
||||
Vulcano::Targets.resolve(resource)
|
||||
def normalize_map(hm)
|
||||
res = {}
|
||||
hm.each{|k,v|
|
||||
res[k.to_s] = v
|
||||
}
|
||||
res
|
||||
end
|
||||
|
||||
def configure_output
|
||||
# RSpec.configuration.output_stream = $stdout
|
||||
# RSpec.configuration.error_stream = $stderr
|
||||
RSpec.configuration.add_formatter(@conf['format'] || 'progress')
|
||||
end
|
||||
|
||||
def configure_backend
|
||||
backend_name = ( @conf['backend'] ||= 'exec' )
|
||||
# @TODO all backends except for mock revert to specinfra for now
|
||||
unless %w{ mock }.include? backend_name
|
||||
backend_class = Vulcano::Backend.registry['specinfra']
|
||||
else
|
||||
backend_class = Vulcano::Backend.registry[backend_name]
|
||||
end
|
||||
|
||||
# Return on failure
|
||||
if backend_class.nil?
|
||||
raise "Can't find command backend '#{backend_name}'."
|
||||
end
|
||||
|
||||
# create the backend based on the config
|
||||
@backend = backend_class.new(@conf)
|
||||
end
|
||||
|
||||
def add_tests(tests)
|
||||
# retrieve the raw ruby code of all tests
|
||||
items = tests.map do |test|
|
||||
Vulcano::Targets.resolve(test)
|
||||
end
|
||||
|
||||
# add all tests (raw) to the runtime
|
||||
items.flatten.each do |item|
|
||||
add_content(item[:content], item[:ref], item[:line])
|
||||
end
|
||||
end
|
||||
|
||||
def add_content(content, source, line = nil)
|
||||
ctx = Vulcano::ProfileContext.new(@profile_id, {}, [])
|
||||
ctx = Vulcano::ProfileContext.new(@profile_id, @backend)
|
||||
|
||||
# evaluate all tests
|
||||
ctx.instance_eval(content, source, line || 1)
|
||||
ctx.load(content, source, line || 1)
|
||||
|
||||
# process the resulting rules
|
||||
rules = ctx.instance_variable_get(:@rules)
|
||||
rules.each do |rule_id, rule|
|
||||
ctx.rules.each do |rule_id, rule|
|
||||
#::Vulcano::DSL.execute_rule(rule, profile_id)
|
||||
checks = rule.instance_variable_get(:@checks)
|
||||
checks.each do |m,a,b|
|
||||
|
|
70
test/docker.rb
Normal file
70
test/docker.rb
Normal file
|
@ -0,0 +1,70 @@
|
|||
require 'docker'
|
||||
require 'yaml'
|
||||
require_relative '../lib/vulcano'
|
||||
|
||||
tests = ARGV
|
||||
if tests.empty?
|
||||
puts 'Nothing to do.'
|
||||
exit 0
|
||||
end
|
||||
|
||||
class DockerTester
|
||||
def initialize(tests)
|
||||
@tests = tests
|
||||
@images = docker_images_by_tag
|
||||
@conf = tests_conf
|
||||
end
|
||||
|
||||
def run
|
||||
# test all images
|
||||
@conf['images'].each{|n|
|
||||
test_image(n)
|
||||
}.all? or raise "Test failures"
|
||||
end
|
||||
|
||||
def docker_images_by_tag
|
||||
# get all docker image tags
|
||||
images = {}
|
||||
Docker::Image.all.map do |img|
|
||||
Array(img.info['RepoTags']).each do |tag|
|
||||
images[tag] = img
|
||||
end
|
||||
end
|
||||
images
|
||||
end
|
||||
|
||||
def tests_conf
|
||||
# get the test configuration
|
||||
conf_path = File::join(File::dirname(__FILE__), '..', '.tests.yaml')
|
||||
raise "Can't find tests config in #{conf_path}" unless File::file?(conf_path)
|
||||
conf = YAML.load(File::read(conf_path))
|
||||
end
|
||||
|
||||
def test_container(container_id)
|
||||
opts = { 'target' => "docker://#{container_id}" }
|
||||
runner = Vulcano::Runner.new(nil, opts)
|
||||
runner.add_tests(@tests)
|
||||
runner.run
|
||||
end
|
||||
|
||||
def test_image(name)
|
||||
dname = "docker-#{name}:latest"
|
||||
image = @images[dname]
|
||||
raise "Can't find docker image #{dname}" if image.nil?
|
||||
|
||||
container = Docker::Container.create(
|
||||
'Cmd' => [ '/bin/bash' ],
|
||||
'Image' => image.id,
|
||||
'OpenStdin' => true,
|
||||
)
|
||||
container.start
|
||||
|
||||
res = test_container(container.id)
|
||||
|
||||
container.kill
|
||||
container.delete(force: true)
|
||||
res
|
||||
end
|
||||
end
|
||||
|
||||
DockerTester.new(tests).run
|
4
test/helper.rb
Normal file
4
test/helper.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
require 'minitest/autorun'
|
||||
require 'minitest/spec'
|
||||
|
||||
require 'vulcano/backend'
|
30
test/resource/command.rb
Normal file
30
test/resource/command.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
|
||||
describe command('echo hello') do
|
||||
its(:stdout) { should eq "hello\n" }
|
||||
its(:stderr) { should eq "" }
|
||||
its(:exit_status) { should eq 0 }
|
||||
end
|
||||
|
||||
describe command('>&2 echo error') do
|
||||
its(:stdout) { should eq "" }
|
||||
its(:stderr) { should eq "error\n" }
|
||||
its(:exit_status) { should eq 0 }
|
||||
end
|
||||
|
||||
describe command('exit 123') do
|
||||
its(:stdout) { should eq "" }
|
||||
its(:stderr) { should eq "" }
|
||||
its(:exit_status) { should eq 123 }
|
||||
end
|
||||
|
||||
describe command('/bin/sh').exists? do
|
||||
it { should eq true }
|
||||
end
|
||||
|
||||
describe command('sh').exists? do
|
||||
it { should eq true }
|
||||
end
|
||||
|
||||
describe command('this is not existing').exists? do
|
||||
it { should eq false }
|
||||
end
|
126
test/resource/file.rb
Normal file
126
test/resource/file.rb
Normal file
|
@ -0,0 +1,126 @@
|
|||
|
||||
describe file('/tmp') do
|
||||
it { should exist }
|
||||
end
|
||||
|
||||
describe file('/tmpest') do
|
||||
it { should_not exist }
|
||||
end
|
||||
|
||||
describe file('/tmp') do
|
||||
its(:type) { should eq :directory }
|
||||
it { should be_directory }
|
||||
end
|
||||
|
||||
describe file('/proc/version') do
|
||||
its(:type) { should eq :file }
|
||||
it { should be_file }
|
||||
it { should_not be_directory }
|
||||
end
|
||||
|
||||
describe file('/dev/stdout') do
|
||||
its(:type) { should eq :symlink }
|
||||
it { should be_symlink }
|
||||
it { should_not be_file }
|
||||
it { should_not be_directory }
|
||||
end
|
||||
|
||||
describe file('/dev/zero') do
|
||||
its(:type) { should eq :character_device }
|
||||
it { should be_character_device }
|
||||
it { should_not be_file }
|
||||
it { should_not be_directory }
|
||||
end
|
||||
|
||||
# describe file('...') do
|
||||
# its(:type) { should eq :block_device }
|
||||
# it { should be_block_device }
|
||||
# end
|
||||
|
||||
# describe file('...') do
|
||||
# its(:type) { should eq :socket }
|
||||
# it { should be_socket }
|
||||
# end
|
||||
|
||||
# describe file('...') do
|
||||
# its(:type) { should eq :pipe }
|
||||
# it { should be_pipe }
|
||||
# end
|
||||
|
||||
describe file('/dev') do
|
||||
its(:mode) { should eq 00755 }
|
||||
end
|
||||
|
||||
describe file('/dev') do
|
||||
it { should be_mode 00755 }
|
||||
end
|
||||
|
||||
describe file('/root') do
|
||||
its(:owner) { should eq 'root' }
|
||||
end
|
||||
|
||||
describe file('/dev') do
|
||||
it { should be_owned_by 'root' }
|
||||
end
|
||||
|
||||
describe file('/root') do
|
||||
its(:group) { should eq 'root' }
|
||||
end
|
||||
|
||||
describe file('/dev') do
|
||||
it { should be_grouped_into 'root' }
|
||||
end
|
||||
|
||||
describe file('/dev/kcore') do
|
||||
its(:link_target) { should eq '/proc/kcore' }
|
||||
end
|
||||
|
||||
describe file('/dev/kcore') do
|
||||
it { should be_linked_to '/proc/kcore' }
|
||||
end
|
||||
|
||||
describe file('/proc/cpuinfo') do
|
||||
its(:content) { should match /^processor/ }
|
||||
end
|
||||
|
||||
describe file('/').mtime.to_i do
|
||||
it { should <= Time.now.to_i }
|
||||
it { should >= Time.now.to_i - 1000}
|
||||
end
|
||||
|
||||
describe file('/') do
|
||||
its(:size) { should be > 64 }
|
||||
its(:size) { should be < 10240 }
|
||||
end
|
||||
|
||||
describe file('/proc/cpuinfo') do
|
||||
its(:size) { should be 0 }
|
||||
end
|
||||
|
||||
# @TODO selinux_label
|
||||
|
||||
describe file('/proc') do
|
||||
it { should be_mounted }
|
||||
end
|
||||
|
||||
describe file('/proc/cpuinfo') do
|
||||
it { should_not be_mounted }
|
||||
end
|
||||
|
||||
# @TODO immutable?
|
||||
# @TODO product_version
|
||||
# @TODO file_version
|
||||
# @TODO version?
|
||||
|
||||
require 'digest'
|
||||
cpuinfo = file('/proc/cpuinfo').content
|
||||
|
||||
md5sum = Digest::MD5.hexdigest(cpuinfo)
|
||||
describe file('/proc/cpuinfo') do
|
||||
its(:md5sum) { should eq md5sum }
|
||||
end
|
||||
|
||||
sha256sum = Digest::SHA256.hexdigest(cpuinfo)
|
||||
describe file('/proc/cpuinfo') do
|
||||
its(:sha256sum) { should eq sha256sum }
|
||||
end
|
66
test/unit/backend_test.rb
Normal file
66
test/unit/backend_test.rb
Normal file
|
@ -0,0 +1,66 @@
|
|||
require 'helper'
|
||||
|
||||
describe 'Vulcano::Backend' do
|
||||
|
||||
it 'should have a populated registry' do
|
||||
reg = Vulcano::Backend.registry
|
||||
reg.must_be_kind_of Hash
|
||||
reg.keys.must_include 'mock'
|
||||
reg.keys.must_include 'specinfra'
|
||||
end
|
||||
|
||||
|
||||
describe 'target config helper' do
|
||||
it 'configures resolves target' do
|
||||
org = {
|
||||
'target' => 'ssh://user:pass@host.com:123',
|
||||
}
|
||||
res = Vulcano::Backend.target_config(org)
|
||||
res['backend'].must_equal 'ssh'
|
||||
res['host'].must_equal 'host.com'
|
||||
res['user'].must_equal 'user'
|
||||
res['password'].must_equal 'pass'
|
||||
res['port'].must_equal 123
|
||||
res['target'].must_equal org['target']
|
||||
org.keys.must_equal ['target']
|
||||
end
|
||||
|
||||
it 'resolves a target while keeping existing fields' do
|
||||
org = {
|
||||
'target' => 'ssh://user:pass@host.com:123',
|
||||
'backend' => rand,
|
||||
'host' => rand,
|
||||
'user' => rand,
|
||||
'password' => rand,
|
||||
'port' => rand,
|
||||
'target' => rand,
|
||||
}
|
||||
res = Vulcano::Backend.target_config(org)
|
||||
res.must_equal org
|
||||
end
|
||||
|
||||
it 'keeps the configuration when incorrect target is supplied' do
|
||||
org = {
|
||||
'target' => 'wrong',
|
||||
}
|
||||
res = Vulcano::Backend.target_config(org)
|
||||
res['backend'].must_be_nil
|
||||
res['host'].must_be_nil
|
||||
res['user'].must_be_nil
|
||||
res['password'].must_be_nil
|
||||
res['port'].must_be_nil
|
||||
res['target'].must_equal org['target']
|
||||
end
|
||||
end
|
||||
|
||||
describe 'helper method for creating backends' do
|
||||
it 'creates v1 backends by default' do
|
||||
Vulcano.backend.must_equal Vulcano::Plugins::Backend
|
||||
end
|
||||
|
||||
it 'creates v1 backends' do
|
||||
Vulcano.backend(1).must_equal Vulcano::Plugins::Backend
|
||||
end
|
||||
end
|
||||
|
||||
end
|
31
test/unit/plugins_v1_backend_file_test.rb
Normal file
31
test/unit/plugins_v1_backend_file_test.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
require 'helper'
|
||||
|
||||
describe 'Vulcano::Plugins::Backend::FileCommon' do
|
||||
let(:cls) { Vulcano::Plugins::Backend::FileCommon }
|
||||
let(:backend) { cls.new }
|
||||
|
||||
it 'default type is :unkown' do
|
||||
backend.type.must_equal :unknown
|
||||
end
|
||||
|
||||
describe 'with non-empty content' do
|
||||
let(:backend) {
|
||||
Class.new(cls) do
|
||||
def content; 'Hello World'; end
|
||||
end.new
|
||||
}
|
||||
|
||||
it 'must return raw content' do
|
||||
backend.content.must_equal 'Hello World'
|
||||
end
|
||||
|
||||
it 'must calculate the md5sum of content' do
|
||||
backend.md5sum.must_equal 'b10a8db164e0754105b7a99be72e3fe5'
|
||||
end
|
||||
|
||||
it 'must calculate the sha256sum of content' do
|
||||
backend.sha256sum.must_equal 'a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
43
test/unit/plugins_v1_backend_test.rb
Normal file
43
test/unit/plugins_v1_backend_test.rb
Normal file
|
@ -0,0 +1,43 @@
|
|||
require 'helper'
|
||||
|
||||
describe 'Vulcano::Plugins::Backend' do
|
||||
let(:cls) { Vulcano::Plugins::Backend }
|
||||
let(:child) { Class.new(cls) }
|
||||
|
||||
it 'provides a name method for registering' do
|
||||
child.must_respond_to :name
|
||||
end
|
||||
|
||||
describe 'when registering a plugin' do
|
||||
let(:registry) { Vulcano::Backend.registry }
|
||||
|
||||
before do
|
||||
child.name 'test'
|
||||
end
|
||||
|
||||
after do
|
||||
registry.delete('test')
|
||||
end
|
||||
|
||||
it 'must have the backend registered' do
|
||||
registry.keys.must_include 'test'
|
||||
registry['test'].must_equal child
|
||||
end
|
||||
|
||||
it 'must raise an error if file is not implemented' do
|
||||
t = registry['test'].new
|
||||
proc { t.run_command }.must_raise NotImplementedError
|
||||
end
|
||||
|
||||
it 'must raise an error if run_command is not implemented' do
|
||||
t = registry['test'].new
|
||||
proc { t.file }.must_raise NotImplementedError
|
||||
end
|
||||
|
||||
it 'must raise an error if os is not implemented' do
|
||||
t = registry['test'].new
|
||||
proc { t.os }.must_raise NotImplementedError
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -20,14 +20,13 @@ Gem::Specification.new do |spec|
|
|||
|
||||
spec.add_development_dependency "bundler", "~> 1.5"
|
||||
spec.add_development_dependency "minitest", "~> 5.5"
|
||||
spec.add_development_dependency "rspec", "~> 3.2"
|
||||
spec.add_development_dependency "rspec", "~> 3.3"
|
||||
spec.add_development_dependency "rake", "~> 10"
|
||||
spec.add_development_dependency "pry", "~> 0.10"
|
||||
|
||||
spec.add_dependency 'thor', '~> 0.19'
|
||||
spec.add_dependency 'json', '~> 1.8'
|
||||
spec.add_dependency 'rainbow', '~> 2'
|
||||
spec.add_dependency 'serverspec', '~> 2.18'
|
||||
spec.add_dependency 'method_source', '~> 0.8'
|
||||
spec.add_dependency 'rubyzip', '~> 1.1'
|
||||
spec.add_dependency 'rspec', '~> 3.3'
|
||||
|
|
Loading…
Reference in a new issue