inspec 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +65 -0
- data/.travis.yml +23 -0
- data/CHANGELOG.md +38 -0
- data/Gemfile +33 -0
- data/LICENSE +201 -0
- data/MAINTAINERS.md +28 -0
- data/MAINTAINERS.toml +42 -0
- data/README.md +257 -0
- data/Rakefile +47 -0
- data/bin/inspec +109 -0
- data/docs/ctl_inspec.rst +195 -0
- data/docs/dsl_inspec.rst +182 -0
- data/docs/readme.rst +100 -0
- data/docs/resources.rst +4319 -0
- data/docs/template.rst +51 -0
- data/examples/test-kitchen/.kitchen.yml +20 -0
- data/examples/test-kitchen/Berksfile +3 -0
- data/examples/test-kitchen/Gemfile +21 -0
- data/examples/test-kitchen/README.md +27 -0
- data/examples/test-kitchen/metadata.rb +7 -0
- data/examples/test-kitchen/recipes/default.rb +6 -0
- data/examples/test-kitchen/recipes/nginx.rb +30 -0
- data/examples/test-kitchen/test/integration/default/web_spec.rb +28 -0
- data/inspec.gemspec +30 -0
- data/lib/inspec.rb +20 -0
- data/lib/inspec/backend.rb +42 -0
- data/lib/inspec/dsl.rb +151 -0
- data/lib/inspec/log.rb +34 -0
- data/lib/inspec/metadata.rb +79 -0
- data/lib/inspec/plugins.rb +9 -0
- data/lib/inspec/plugins/resource.rb +62 -0
- data/lib/inspec/profile.rb +138 -0
- data/lib/inspec/profile_context.rb +170 -0
- data/lib/inspec/resource.rb +76 -0
- data/lib/inspec/rspec_json_formatter.rb +27 -0
- data/lib/inspec/rule.rb +170 -0
- data/lib/inspec/runner.rb +154 -0
- data/lib/inspec/shell.rb +66 -0
- data/lib/inspec/targets.rb +9 -0
- data/lib/inspec/targets/core.rb +27 -0
- data/lib/inspec/targets/dir.rb +67 -0
- data/lib/inspec/targets/file.rb +29 -0
- data/lib/inspec/targets/folder.rb +43 -0
- data/lib/inspec/targets/tar.rb +34 -0
- data/lib/inspec/targets/url.rb +39 -0
- data/lib/inspec/targets/zip.rb +47 -0
- data/lib/inspec/version.rb +7 -0
- data/lib/matchers/matchers.rb +221 -0
- data/lib/resources/apache.rb +29 -0
- data/lib/resources/apache_conf.rb +113 -0
- data/lib/resources/apt.rb +140 -0
- data/lib/resources/audit_policy.rb +63 -0
- data/lib/resources/auditd_conf.rb +56 -0
- data/lib/resources/auditd_rules.rb +53 -0
- data/lib/resources/bond.rb +65 -0
- data/lib/resources/bridge.rb +114 -0
- data/lib/resources/command.rb +57 -0
- data/lib/resources/csv.rb +32 -0
- data/lib/resources/directory.rb +15 -0
- data/lib/resources/etc_group.rb +150 -0
- data/lib/resources/file.rb +110 -0
- data/lib/resources/gem.rb +46 -0
- data/lib/resources/group.rb +132 -0
- data/lib/resources/host.rb +143 -0
- data/lib/resources/inetd_conf.rb +56 -0
- data/lib/resources/interface.rb +127 -0
- data/lib/resources/iptables.rb +65 -0
- data/lib/resources/json.rb +64 -0
- data/lib/resources/kernel_module.rb +40 -0
- data/lib/resources/kernel_parameter.rb +55 -0
- data/lib/resources/limits_conf.rb +55 -0
- data/lib/resources/login_def.rb +60 -0
- data/lib/resources/mysql.rb +81 -0
- data/lib/resources/mysql_conf.rb +116 -0
- data/lib/resources/mysql_session.rb +52 -0
- data/lib/resources/npm.rb +44 -0
- data/lib/resources/ntp_conf.rb +58 -0
- data/lib/resources/oneget.rb +63 -0
- data/lib/resources/os.rb +22 -0
- data/lib/resources/os_env.rb +34 -0
- data/lib/resources/package.rb +169 -0
- data/lib/resources/parse_config.rb +75 -0
- data/lib/resources/passwd.rb +93 -0
- data/lib/resources/pip.rb +75 -0
- data/lib/resources/port.rb +296 -0
- data/lib/resources/postgres.rb +37 -0
- data/lib/resources/postgres_conf.rb +87 -0
- data/lib/resources/postgres_session.rb +59 -0
- data/lib/resources/processes.rb +57 -0
- data/lib/resources/registry_key.rb +54 -0
- data/lib/resources/script.rb +34 -0
- data/lib/resources/security_policy.rb +73 -0
- data/lib/resources/service.rb +379 -0
- data/lib/resources/ssh_conf.rb +75 -0
- data/lib/resources/user.rb +374 -0
- data/lib/resources/windows_feature.rb +77 -0
- data/lib/resources/yaml.rb +23 -0
- data/lib/resources/yum.rb +154 -0
- data/lib/utils/convert.rb +12 -0
- data/lib/utils/detect.rb +15 -0
- data/lib/utils/find_files.rb +36 -0
- data/lib/utils/hash.rb +13 -0
- data/lib/utils/modulator.rb +12 -0
- data/lib/utils/parser.rb +61 -0
- data/lib/utils/simpleconfig.rb +115 -0
- data/tasks/maintainers.rb +213 -0
- data/test/docker_run.rb +156 -0
- data/test/docker_test.rb +51 -0
- data/test/helper.rb +200 -0
- data/test/integration/.kitchen.yml +42 -0
- data/test/integration/Berksfile +4 -0
- data/test/integration/cookbooks/os_prepare/metadata.rb +8 -0
- data/test/integration/cookbooks/os_prepare/recipes/apt.rb +20 -0
- data/test/integration/cookbooks/os_prepare/recipes/default.rb +9 -0
- data/test/integration/cookbooks/os_prepare/recipes/file.rb +21 -0
- data/test/integration/cookbooks/os_prepare/recipes/package.rb +26 -0
- data/test/integration/default/_debug_spec.rb +1 -0
- data/test/integration/default/apt_spec.rb +42 -0
- data/test/integration/default/file_spec.rb +109 -0
- data/test/integration/default/group_spec.rb +32 -0
- data/test/integration/default/kernel_module_spec.rb +17 -0
- data/test/integration/default/kernel_parameter_spec.rb +56 -0
- data/test/integration/default/package_spec.rb +11 -0
- data/test/integration/default/service_spec.rb +28 -0
- data/test/integration/default/user_spec.rb +44 -0
- data/test/resource/command_test.rb +33 -0
- data/test/resource/dsl_test.rb +45 -0
- data/test/resource/file_test.rb +130 -0
- data/test/resource/ssh_config.rb +9 -0
- data/test/resource/sshd_config.rb +9 -0
- data/test/test-extra.yaml +11 -0
- data/test/test.yaml +11 -0
- data/test/unit/mock/cmd/Get-NetAdapter +24 -0
- data/test/unit/mock/cmd/GetUserAccount +33 -0
- data/test/unit/mock/cmd/GetWin32Group +23 -0
- data/test/unit/mock/cmd/PATH +1 -0
- data/test/unit/mock/cmd/Resolve-DnsName +26 -0
- data/test/unit/mock/cmd/Test-NetConnection +4 -0
- data/test/unit/mock/cmd/auditctl +7 -0
- data/test/unit/mock/cmd/auditpol +2 -0
- data/test/unit/mock/cmd/brew-info-jq +1 -0
- data/test/unit/mock/cmd/chage-l-root +7 -0
- data/test/unit/mock/cmd/dpkg-s-curl +21 -0
- data/test/unit/mock/cmd/dscl +5 -0
- data/test/unit/mock/cmd/etc-apt +7 -0
- data/test/unit/mock/cmd/find-etc-rc-d-name-S +12 -0
- data/test/unit/mock/cmd/find-net-interface +9 -0
- data/test/unit/mock/cmd/gem-list-local-a-q-rubocop +1 -0
- data/test/unit/mock/cmd/get-net-tcpconnection +24 -0
- data/test/unit/mock/cmd/get-netadapter-binding-bridge +4 -0
- data/test/unit/mock/cmd/get-package-firefox +30 -0
- data/test/unit/mock/cmd/get-package-ruby +18 -0
- data/test/unit/mock/cmd/get-service-dhcp +10 -0
- data/test/unit/mock/cmd/get-windows-feature +7 -0
- data/test/unit/mock/cmd/getent-hosts-example.com +1 -0
- data/test/unit/mock/cmd/getent-passwd-root +1 -0
- data/test/unit/mock/cmd/id-chartmann +1 -0
- data/test/unit/mock/cmd/id-root +1 -0
- data/test/unit/mock/cmd/initctl-show-config-ssh +3 -0
- data/test/unit/mock/cmd/initctl-status-ssh +1 -0
- data/test/unit/mock/cmd/iptables-s +6 -0
- data/test/unit/mock/cmd/launchctl-list +3 -0
- data/test/unit/mock/cmd/ls-1-etc-init.d +2 -0
- data/test/unit/mock/cmd/ls-sys-class-net-br +2 -0
- data/test/unit/mock/cmd/lsmod +2 -0
- data/test/unit/mock/cmd/lsof-np-itcp +4 -0
- data/test/unit/mock/cmd/netstat-tulpen +5 -0
- data/test/unit/mock/cmd/npm-ls-g--json-bower +9 -0
- data/test/unit/mock/cmd/pacman-qi-curl +21 -0
- data/test/unit/mock/cmd/ping-example.com +6 -0
- data/test/unit/mock/cmd/pip-show-jinja2 +11 -0
- data/test/unit/mock/cmd/ps-aux +3 -0
- data/test/unit/mock/cmd/pw-usershow-root-7 +1 -0
- data/test/unit/mock/cmd/reg_schedule +1 -0
- data/test/unit/mock/cmd/rpm-qia-curl +24 -0
- data/test/unit/mock/cmd/sbin_sysctl +1 -0
- data/test/unit/mock/cmd/secedit-export +7 -0
- data/test/unit/mock/cmd/service-e +2 -0
- data/test/unit/mock/cmd/service-sendmail-onestatus +3 -0
- data/test/unit/mock/cmd/service-sshd-status +1 -0
- data/test/unit/mock/cmd/sockstat +5 -0
- data/test/unit/mock/cmd/success +0 -0
- data/test/unit/mock/cmd/systemctl-show-all-sshd +6 -0
- data/test/unit/mock/cmd/win32_product +8 -0
- data/test/unit/mock/cmd/yum-repolist-all +52 -0
- data/test/unit/mock/files/auditd.conf +4 -0
- data/test/unit/mock/files/bond0 +37 -0
- data/test/unit/mock/files/etcgroup +3 -0
- data/test/unit/mock/files/example.csv +6 -0
- data/test/unit/mock/files/inetd.conf +2 -0
- data/test/unit/mock/files/kitchen.yml +7 -0
- data/test/unit/mock/files/limits.conf +5 -0
- data/test/unit/mock/files/login.defs +5 -0
- data/test/unit/mock/files/mysql.conf +8 -0
- data/test/unit/mock/files/mysql2.conf +2 -0
- data/test/unit/mock/files/ntp.conf +5 -0
- data/test/unit/mock/files/passwd +2 -0
- data/test/unit/mock/files/policyfile.lock.json +12 -0
- data/test/unit/mock/files/ssh_config +5 -0
- data/test/unit/mock/files/sshd_config +7 -0
- data/test/unit/mock/profiles/empty/metadata.rb +0 -0
- data/test/unit/mock/profiles/metadata/metadata.rb +1 -0
- data/test/unit/profile_context_test.rb +140 -0
- data/test/unit/profile_test.rb +49 -0
- data/test/unit/resources/apt_test.rb +46 -0
- data/test/unit/resources/audit_policy_test.rb +13 -0
- data/test/unit/resources/auditd_conf_test.rb +15 -0
- data/test/unit/resources/auditd_rules_test.rb +21 -0
- data/test/unit/resources/bond_test.rb +24 -0
- data/test/unit/resources/bridge_test.rb +56 -0
- data/test/unit/resources/csv_test.rb +35 -0
- data/test/unit/resources/etc_group_test.rb +37 -0
- data/test/unit/resources/gem_test.rb +20 -0
- data/test/unit/resources/group_test.rb +96 -0
- data/test/unit/resources/host_test.rb +38 -0
- data/test/unit/resources/inetd_conf_test.rb +15 -0
- data/test/unit/resources/interface_test.rb +54 -0
- data/test/unit/resources/iptables_test.rb +30 -0
- data/test/unit/resources/json_test.rb +36 -0
- data/test/unit/resources/kernel_module_test.rb +23 -0
- data/test/unit/resources/kernel_parameter_test.rb +13 -0
- data/test/unit/resources/limits_conf_test.rb +14 -0
- data/test/unit/resources/login_def_test.rb +16 -0
- data/test/unit/resources/mysql_conf_test.rb +14 -0
- data/test/unit/resources/npm_test.rb +20 -0
- data/test/unit/resources/ntp_conf_test.rb +16 -0
- data/test/unit/resources/oneget_test.rb +45 -0
- data/test/unit/resources/os_env_test.rb +13 -0
- data/test/unit/resources/package_test.rb +51 -0
- data/test/unit/resources/passwd_test.rb +24 -0
- data/test/unit/resources/pip_test.rb +15 -0
- data/test/unit/resources/port_test.rb +46 -0
- data/test/unit/resources/processes_test.rb +32 -0
- data/test/unit/resources/registry_key_test.rb +19 -0
- data/test/unit/resources/script_test.rb +19 -0
- data/test/unit/resources/security_policy_test.rb +16 -0
- data/test/unit/resources/service_test.rb +116 -0
- data/test/unit/resources/ssh_conf_test.rb +33 -0
- data/test/unit/resources/user_test.rb +93 -0
- data/test/unit/resources/windows_feature.rb +17 -0
- data/test/unit/resources/yaml_test.rb +34 -0
- data/test/unit/resources/yum_test.rb +68 -0
- data/test/unit/simpleconfig_test.rb +80 -0
- data/test/unit/utils/content_parser_test.rb +30 -0
- metadata +555 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# author: Christoph Hartmann
|
|
3
|
+
# author: Dominik Richter
|
|
4
|
+
|
|
5
|
+
# Usage:
|
|
6
|
+
# describe gem('rubocop') do
|
|
7
|
+
# it { should be_installed }
|
|
8
|
+
# end
|
|
9
|
+
class GemPackage < Inspec.resource(1)
|
|
10
|
+
name 'gem'
|
|
11
|
+
|
|
12
|
+
def initialize(package_name)
|
|
13
|
+
@package_name = package_name
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def info
|
|
17
|
+
return @info if defined?(@info)
|
|
18
|
+
|
|
19
|
+
cmd = inspec.command("gem list --local -a -q \^#{@package_name}\$")
|
|
20
|
+
@info = {
|
|
21
|
+
installed: cmd.exit_status == 0,
|
|
22
|
+
type: 'gem',
|
|
23
|
+
}
|
|
24
|
+
return @info unless @info[:installed]
|
|
25
|
+
|
|
26
|
+
# extract package name and version
|
|
27
|
+
# parses data like winrm (1.3.4, 1.3.3)
|
|
28
|
+
params = /^\s*([^\(]*?)\s*\((.*?)\)\s*$/.match(cmd.stdout.chomp)
|
|
29
|
+
versions = params[2].split(',')
|
|
30
|
+
@info[:name] = params[1]
|
|
31
|
+
@info[:version] = versions[0]
|
|
32
|
+
@info
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def installed?
|
|
36
|
+
info[:installed] == true
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def version
|
|
40
|
+
info[:version]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def to_s
|
|
44
|
+
"gem package #{@package_name}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# author: Christoph Hartmann
|
|
3
|
+
# author: Dominik Richter
|
|
4
|
+
|
|
5
|
+
# Usage:
|
|
6
|
+
# describe group('root') do
|
|
7
|
+
# it { should exist }
|
|
8
|
+
# its('gid') { should eq 0 }
|
|
9
|
+
# end
|
|
10
|
+
#
|
|
11
|
+
# deprecated has matcher
|
|
12
|
+
# describe group('root') do
|
|
13
|
+
# it { should have_gid 0 }
|
|
14
|
+
# end
|
|
15
|
+
|
|
16
|
+
class Group < Inspec.resource(1)
|
|
17
|
+
name 'group'
|
|
18
|
+
|
|
19
|
+
def initialize(groupname, domain = nil)
|
|
20
|
+
@group = groupname.downcase
|
|
21
|
+
@domain = domain
|
|
22
|
+
@domain = @domain.downcase unless @domain.nil?
|
|
23
|
+
|
|
24
|
+
@cache = nil
|
|
25
|
+
|
|
26
|
+
# select group manager
|
|
27
|
+
@group_provider = nil
|
|
28
|
+
if inspec.os.unix?
|
|
29
|
+
@group_provider = UnixGroup.new(inspec)
|
|
30
|
+
elsif inspec.os.windows?
|
|
31
|
+
@group_provider = WindowsGroup.new(inspec)
|
|
32
|
+
else
|
|
33
|
+
return skip_resource 'The `group` resource is not supported on your OS yet.'
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# verifies if a group exists
|
|
38
|
+
def exists?
|
|
39
|
+
# ensure that we found one group
|
|
40
|
+
!group_info.nil? && group_info.size > 0
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def gid
|
|
44
|
+
if group_info.nil? || group_info.size == 0
|
|
45
|
+
return nil
|
|
46
|
+
elsif group_info.size == 1
|
|
47
|
+
# the default case should be one group
|
|
48
|
+
return group_info[0][:gid]
|
|
49
|
+
else
|
|
50
|
+
# return array if we got multiple gids
|
|
51
|
+
return group_info.map { |grp| grp[:gid] }
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# implements rspec has matcher, to be compatible with serverspec
|
|
56
|
+
def has_gid?(compare_gid)
|
|
57
|
+
gid == compare_gid
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def local
|
|
61
|
+
if group_info.nil? || group_info.size == 0
|
|
62
|
+
return nil
|
|
63
|
+
elsif group_info.size == 1
|
|
64
|
+
# the default case should be one group
|
|
65
|
+
return group_info[0][:local]
|
|
66
|
+
else
|
|
67
|
+
# return array if we got multiple gids
|
|
68
|
+
return group_info.map { |grp| grp[:local] }
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def to_s
|
|
73
|
+
"Group #{@group}"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def group_info
|
|
79
|
+
return @cache if !@cache.nil?
|
|
80
|
+
@cache = @group_provider.group_info(@group, @domain) if !@group_provider.nil?
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
class GroupInfo
|
|
85
|
+
attr_reader :inspec
|
|
86
|
+
def initialize(inspec)
|
|
87
|
+
@inspec = inspec
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# implements generic unix groups via /etc/group
|
|
92
|
+
class UnixGroup < GroupInfo
|
|
93
|
+
def group_info(group, _domain = nil)
|
|
94
|
+
inspec.etc_group.where(name: group).entries.map { |grp|
|
|
95
|
+
{
|
|
96
|
+
name: grp['name'],
|
|
97
|
+
gid: grp['gid'],
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
class WindowsGroup < GroupInfo
|
|
104
|
+
def group_info(compare_group, compare_domain = nil)
|
|
105
|
+
cmd = inspec.command('Get-WmiObject Win32_Group | Select-Object -Property Caption, Domain, Name, SID, LocalAccount | ConvertTo-Json')
|
|
106
|
+
|
|
107
|
+
# cannot rely on exit code for now, successful command returns exit code 1
|
|
108
|
+
# return nil if cmd.exit_status != 0, try to parse json
|
|
109
|
+
begin
|
|
110
|
+
groups = JSON.parse(cmd.stdout)
|
|
111
|
+
rescue JSON::ParserError => _e
|
|
112
|
+
return nil
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# ensure we have an array of groups
|
|
116
|
+
groups = [groups] if !groups.is_a?(Array)
|
|
117
|
+
|
|
118
|
+
# reduce list
|
|
119
|
+
groups.each_with_object([]) do |grp, grp_collection|
|
|
120
|
+
# map object
|
|
121
|
+
grp_info = {
|
|
122
|
+
name: grp['Name'],
|
|
123
|
+
domain: grp['Domain'],
|
|
124
|
+
caption: grp['Caption'],
|
|
125
|
+
gid: nil,
|
|
126
|
+
sid: grp['SID'],
|
|
127
|
+
local: grp['LocalAccount'],
|
|
128
|
+
}
|
|
129
|
+
return grp_collection.push(grp_info) if grp_info[:name].casecmp(compare_group) == 0 && (compare_domain.nil? || grp_info[:domain].casecmp(compare_domain) == 0)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# author: Christoph Hartmann
|
|
3
|
+
# author: Dominik Richter
|
|
4
|
+
|
|
5
|
+
# Usage:
|
|
6
|
+
# describe host('example.com') do
|
|
7
|
+
# it { should be_resolvable }
|
|
8
|
+
# it { should be_reachable }
|
|
9
|
+
# its(:ipaddress) { should include '93.184.216.34' }
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
# To verify a hostname with protocol and port
|
|
13
|
+
# describe host('example.com', port: 53, proto: 'udp') do
|
|
14
|
+
# it { should be_reachable }
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# We do not support the following serverspec syntax:
|
|
18
|
+
# describe host('example.com') do
|
|
19
|
+
# it { should be_reachable.with( :port => 22 ) }
|
|
20
|
+
# it { should be_reachable.with( :port => 22, :proto => 'tcp' ) }
|
|
21
|
+
# it { should be_reachable.with( :port => 53, :proto => 'udp' ) }
|
|
22
|
+
#
|
|
23
|
+
# it { should be_resolvable.by('hosts') }
|
|
24
|
+
# it { should be_resolvable.by('dns') }
|
|
25
|
+
# end
|
|
26
|
+
|
|
27
|
+
class Host < Inspec.resource(1)
|
|
28
|
+
name 'host'
|
|
29
|
+
|
|
30
|
+
def initialize(hostname, params = {})
|
|
31
|
+
@hostname = hostname
|
|
32
|
+
@port = params[:port] || nil
|
|
33
|
+
@proto = params[:proto] || nil
|
|
34
|
+
|
|
35
|
+
@host_provider = nil
|
|
36
|
+
if inspec.os.linux?
|
|
37
|
+
@host_provider = LinuxHostProvider.new(inspec)
|
|
38
|
+
elsif inspec.os.windows?
|
|
39
|
+
@host_provider = WindowsHostProvider.new(inspec)
|
|
40
|
+
else
|
|
41
|
+
return skip_resource 'The `host` resource is not supported on your OS yet.'
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# if we get the IP adress, the host is resolvable
|
|
46
|
+
def resolvable?(type = nil)
|
|
47
|
+
warn "The `host` resource ignores #{type} parameters. Continue to resolve host." if !type.nil?
|
|
48
|
+
resolve.nil? || resolve.empty? ? false : true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def reachable?(port = nil, proto = nil, timeout = nil)
|
|
52
|
+
fail "Use `host` resource with host('#{@hostname}', port: #{port}, proto: '#{proto}') parameters." if !port.nil? || !proto.nil? || !timeout.nil?
|
|
53
|
+
ping.nil? ? false : ping
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# returns all A records of the IP adress, will return an array
|
|
57
|
+
def ipaddress
|
|
58
|
+
resolve.nil? || resolve.empty? ? nil : resolve
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def to_s
|
|
62
|
+
"Host #{@hostname}"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def ping
|
|
68
|
+
return @ping_cache if defined?(@ping_cache)
|
|
69
|
+
@ping_cache = @host_provider.ping(@hostname, @port, @proto) if !@host_provider.nil?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def resolve
|
|
73
|
+
return @ip_cache if defined?(@ip_cache)
|
|
74
|
+
@ip_cache = @host_provider.resolve(@hostname) if !@host_provider.nil?
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
class HostProvider
|
|
79
|
+
attr_reader :inspec
|
|
80
|
+
def initialize(inspec)
|
|
81
|
+
@inspec = inspec
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class LinuxHostProvider < HostProvider
|
|
86
|
+
# ping is difficult to achieve, since we are not sure
|
|
87
|
+
def ping(hostname, _port = nil, _proto = nil)
|
|
88
|
+
# fall back to ping, but we can only test ICMP packages with ping
|
|
89
|
+
# therefore we have to skip the test, if we do not have everything on the node to run the test
|
|
90
|
+
ping = inspec.command("ping -w 1 -c 1 #{hostname}")
|
|
91
|
+
ping.exit_status.to_i != 0 ? false : true
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def resolve(hostname)
|
|
95
|
+
# TODO: we rely on getent hosts for now, but it prefers to return IPv6, only then IPv4
|
|
96
|
+
cmd = inspec.command("getent hosts #{hostname}")
|
|
97
|
+
return nil if cmd.exit_status.to_i != 0
|
|
98
|
+
|
|
99
|
+
# extract ip adress
|
|
100
|
+
resolve = /^\s*(?<ip>\S+)\s+(.*)\s*$/.match(cmd.stdout.chomp)
|
|
101
|
+
[resolve[1]] if resolve
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Windows
|
|
106
|
+
# TODO: UDP is not supported yey, we need a custom ps1 script to add udp support
|
|
107
|
+
# @see http://blogs.technet.com/b/josebda/archive/2015/04/18/windows-powershell-equivalents-for-common-networking-commands-ipconfig-ping-nslookup.aspx
|
|
108
|
+
# @see http://blogs.technet.com/b/heyscriptingguy/archive/2014/03/19/creating-a-port-scanner-with-windows-powershell.aspx
|
|
109
|
+
class WindowsHostProvider < HostProvider
|
|
110
|
+
def ping(hostname, port = nil, proto = nil)
|
|
111
|
+
# TODO: abort if we cannot run it via udp
|
|
112
|
+
return nil if proto == 'udp'
|
|
113
|
+
|
|
114
|
+
# ICMP: Test-NetConnection www.microsoft.com
|
|
115
|
+
# TCP and port: Test-NetConnection -ComputerName www.microsoft.com -RemotePort 80
|
|
116
|
+
request = "Test-NetConnection -ComputerName #{hostname}"
|
|
117
|
+
request += " -RemotePort #{port}" unless port.nil?
|
|
118
|
+
request += '| Select-Object -Property ComputerName, RemoteAddress, RemotePort, SourceAddress, PingSucceeded | ConvertTo-Json'
|
|
119
|
+
p request
|
|
120
|
+
request += '| Select-Object -Property ComputerName, PingSucceeded | ConvertTo-Json'
|
|
121
|
+
cmd = inspec.command(request)
|
|
122
|
+
|
|
123
|
+
begin
|
|
124
|
+
ping = JSON.parse(cmd.stdout)
|
|
125
|
+
rescue JSON::ParserError => _e
|
|
126
|
+
return nil
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
ping['PingSucceeded']
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def resolve(hostname)
|
|
133
|
+
cmd = inspec.command("Resolve-DnsName –Type A #{hostname} | ConvertTo-Json")
|
|
134
|
+
begin
|
|
135
|
+
resolv = JSON.parse(cmd.stdout)
|
|
136
|
+
rescue JSON::ParserError => _e
|
|
137
|
+
return nil
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
resolv = [resolv] unless resolv.is_a?(Array)
|
|
141
|
+
resolv.map { |entry| entry['IPAddress'] }
|
|
142
|
+
end
|
|
143
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# copyright: 2015, Vulcano Security GmbH
|
|
3
|
+
# author: Christoph Hartmann
|
|
4
|
+
# author: Dominik Richter
|
|
5
|
+
# license: All rights reserved
|
|
6
|
+
|
|
7
|
+
require 'utils/simpleconfig'
|
|
8
|
+
|
|
9
|
+
# Usage:
|
|
10
|
+
#
|
|
11
|
+
# describe inetd_conf do
|
|
12
|
+
# its('shell') { should eq nil }
|
|
13
|
+
# its('login') { should eq nil }
|
|
14
|
+
# its('exec') { should eq nil }
|
|
15
|
+
# end
|
|
16
|
+
|
|
17
|
+
class InetdConf < Inspec.resource(1)
|
|
18
|
+
name 'inetd_conf'
|
|
19
|
+
|
|
20
|
+
def initialize(path = nil)
|
|
21
|
+
@conf_path = path || '/etc/inetd.conf'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def method_missing(name)
|
|
25
|
+
read_params[name.to_s]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def read_params
|
|
29
|
+
return @params if defined?(@params)
|
|
30
|
+
|
|
31
|
+
# read the file
|
|
32
|
+
file = inspec.file(@conf_path)
|
|
33
|
+
if !file.file?
|
|
34
|
+
skip_resource "Can't find file \"#{@conf_path}\""
|
|
35
|
+
return @params = {}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
content = file.content
|
|
39
|
+
if content.empty? && file.size > 0
|
|
40
|
+
skip_resource "Can't read file \"#{@conf_path}\""
|
|
41
|
+
return @params = {}
|
|
42
|
+
end
|
|
43
|
+
# parse the file
|
|
44
|
+
conf = SimpleConfig.new(
|
|
45
|
+
content,
|
|
46
|
+
assignment_re: /^\s*(\S+?)\s+(.*?)\s+(.*?)\s+(.*?)\s+(.*?)\s+(.*?)\s+(.*?)\s*$/,
|
|
47
|
+
key_vals: 6,
|
|
48
|
+
multiple_values: false,
|
|
49
|
+
)
|
|
50
|
+
@params = conf.params
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def to_s
|
|
54
|
+
'inetd.conf'
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# author: Christoph Hartmann
|
|
3
|
+
# author: Dominik Richter
|
|
4
|
+
|
|
5
|
+
# Usage:
|
|
6
|
+
# describe interface('eth0') do
|
|
7
|
+
# it { should exist }
|
|
8
|
+
# it { should be_up }
|
|
9
|
+
# its(:speed) { should eq 1000 }
|
|
10
|
+
# end
|
|
11
|
+
|
|
12
|
+
require 'utils/convert'
|
|
13
|
+
|
|
14
|
+
class NetworkInterface < Inspec.resource(1)
|
|
15
|
+
name 'interface'
|
|
16
|
+
|
|
17
|
+
def initialize(iface)
|
|
18
|
+
@iface = iface
|
|
19
|
+
|
|
20
|
+
@interface_provider = nil
|
|
21
|
+
if inspec.os.linux?
|
|
22
|
+
@interface_provider = LinuxInterface.new(inspec)
|
|
23
|
+
elsif inspec.os.windows?
|
|
24
|
+
@interface_provider = WindowsInterface.new(inspec)
|
|
25
|
+
else
|
|
26
|
+
return skip_resource 'The `interface` resource is not supported on your OS yet.'
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def exists?
|
|
31
|
+
!interface_info.nil? && !interface_info[:name].nil?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def up?
|
|
35
|
+
interface_info.nil? ? false : interface_info[:up]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# returns link speed in Mbits/sec
|
|
39
|
+
def speed
|
|
40
|
+
interface_info.nil? ? nil : interface_info[:speed]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def to_s
|
|
44
|
+
"Interface #{@iface}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def interface_info
|
|
50
|
+
return @cache if defined?(@cache)
|
|
51
|
+
@cache = @interface_provider.interface_info(@iface) if !@interface_provider.nil?
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class InterfaceInfo
|
|
56
|
+
include Converter
|
|
57
|
+
attr_reader :inspec
|
|
58
|
+
def initialize(inspec)
|
|
59
|
+
@inspec = inspec
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class LinuxInterface < InterfaceInfo
|
|
64
|
+
def interface_info(iface)
|
|
65
|
+
# will return "[mtu]\n1500\n[type]\n1"
|
|
66
|
+
cmd = inspec.command("find /sys/class/net/#{iface}/ -type f -maxdepth 1 -exec sh -c 'echo \"[$(basename {})]\"; cat {} || echo -n' \\;")
|
|
67
|
+
return nil if cmd.exit_status.to_i != 0
|
|
68
|
+
|
|
69
|
+
# parse values, we only recieve values, therefore we threat them as keys
|
|
70
|
+
params = SimpleConfig.new(cmd.stdout.chomp).params
|
|
71
|
+
|
|
72
|
+
# abort if we got an empty result-set
|
|
73
|
+
return nil if params.empty?
|
|
74
|
+
|
|
75
|
+
# parse state
|
|
76
|
+
state = false
|
|
77
|
+
if params.key?('operstate')
|
|
78
|
+
operstate, _value = params['operstate'].first
|
|
79
|
+
state = operstate == 'up'
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# parse speed
|
|
83
|
+
speed = nil
|
|
84
|
+
if params.key?('speed')
|
|
85
|
+
speed, _value = params['speed'].first
|
|
86
|
+
speed = convert_to_i(speed)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
{
|
|
90
|
+
name: iface,
|
|
91
|
+
up: state,
|
|
92
|
+
speed: speed,
|
|
93
|
+
}
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
class WindowsInterface < InterfaceInfo
|
|
98
|
+
def interface_info(iface)
|
|
99
|
+
# gather all network interfaces
|
|
100
|
+
cmd = inspec.command('Get-NetAdapter | Select-Object -Property Name, InterfaceDescription, Status, State, MacAddress, LinkSpeed, ReceiveLinkSpeed, TransmitLinkSpeed, Virtual | ConvertTo-Json')
|
|
101
|
+
|
|
102
|
+
# filter network interface
|
|
103
|
+
begin
|
|
104
|
+
net_adapter = JSON.parse(cmd.stdout)
|
|
105
|
+
rescue JSON::ParserError => _e
|
|
106
|
+
return nil
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# ensure we have an array of groups
|
|
110
|
+
net_adapter = [net_adapter] if !net_adapter.is_a?(Array)
|
|
111
|
+
|
|
112
|
+
# select the requested interface
|
|
113
|
+
adapters = net_adapter.each_with_object([]) do |adapter, adapter_collection|
|
|
114
|
+
# map object
|
|
115
|
+
info = {
|
|
116
|
+
name: adapter['Name'],
|
|
117
|
+
up: adapter['State'] == 2,
|
|
118
|
+
speed: adapter['ReceiveLinkSpeed'] / 1000,
|
|
119
|
+
}
|
|
120
|
+
adapter_collection.push(info) if info[:name].casecmp(iface) == 0
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
return nil if adapters.size == 0
|
|
124
|
+
warn "[Possible Error] detected multiple network interfaces with the name #{iface}" if adapters.size > 1
|
|
125
|
+
adapters[0]
|
|
126
|
+
end
|
|
127
|
+
end
|