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,75 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# copyright: 2015, Vulcano Security GmbH
|
|
3
|
+
# author: Dominik Richter
|
|
4
|
+
# author: Christoph Hartmann
|
|
5
|
+
# license: All rights reserved
|
|
6
|
+
|
|
7
|
+
require 'utils/simpleconfig'
|
|
8
|
+
|
|
9
|
+
class SshConf < Inspec.resource(1)
|
|
10
|
+
name 'ssh_config'
|
|
11
|
+
|
|
12
|
+
def initialize(conf_path = nil, type = nil)
|
|
13
|
+
@conf_path = conf_path || '/etc/ssh/ssh_config'
|
|
14
|
+
typename = (@conf_path.include?('sshd') ? 'Server' : 'Client')
|
|
15
|
+
@type = type || "SSH #{typename} configuration #{conf_path}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def content
|
|
19
|
+
read_content
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def params(*opts)
|
|
23
|
+
opts.inject(read_params) do |res, nxt|
|
|
24
|
+
res.respond_to?(:key) ? res[nxt] : nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def method_missing(name)
|
|
29
|
+
param = read_params[name.to_s]
|
|
30
|
+
return nil if param.nil?
|
|
31
|
+
# extract first value if we have only one value in array
|
|
32
|
+
return param[0] if param.length == 1
|
|
33
|
+
param
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def to_s
|
|
37
|
+
'SSH Configuration'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def read_content
|
|
43
|
+
return @content if defined?(@content)
|
|
44
|
+
file = inspec.file(@conf_path)
|
|
45
|
+
if !file.file?
|
|
46
|
+
return skip_resource "Can't find file \"#{@conf_path}\""
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
@content = file.content
|
|
50
|
+
if @content.empty? && file.size > 0
|
|
51
|
+
return skip_resource "Can't read file \"#{@conf_path}\""
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
@content
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def read_params
|
|
58
|
+
return @params if defined?(@params)
|
|
59
|
+
return @params = {} if read_content.nil?
|
|
60
|
+
conf = SimpleConfig.new(
|
|
61
|
+
read_content,
|
|
62
|
+
assignment_re: /^\s*(\S+?)\s+(.*?)\s*$/,
|
|
63
|
+
multiple_values: true,
|
|
64
|
+
)
|
|
65
|
+
@params = conf.params
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class SshdConf < SshConf
|
|
70
|
+
name 'sshd_config'
|
|
71
|
+
|
|
72
|
+
def initialize(path = nil)
|
|
73
|
+
super(path || '/etc/ssh/sshd_config')
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# author: Christoph Hartmann
|
|
3
|
+
# author: Dominik Richter
|
|
4
|
+
|
|
5
|
+
# Usage:
|
|
6
|
+
#
|
|
7
|
+
# describe user('root') do
|
|
8
|
+
# it { should exist }
|
|
9
|
+
# its(:uid) { should eq 0 }
|
|
10
|
+
# its(:gid) { should eq 0 }
|
|
11
|
+
# its(:group) { should eq 'root' }
|
|
12
|
+
# its(:groups) { should eq ['root', 'wheel']}
|
|
13
|
+
# its(:home) { should eq '/root' }
|
|
14
|
+
# its(:shell) { should eq '/bin/bash' }
|
|
15
|
+
# its(:mindays) { should eq 0 }
|
|
16
|
+
# its(:maxdays) { should eq 99 }
|
|
17
|
+
# its(:warndays) { should eq 5 }
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# The following Serverspec matchers are deprecated in favor for direct value access
|
|
21
|
+
#
|
|
22
|
+
# describe user('root') do
|
|
23
|
+
# it { should belong_to_group 'root' }
|
|
24
|
+
# it { should have_uid 0 }
|
|
25
|
+
# it { should have_home_directory '/root' }
|
|
26
|
+
# it { should have_login_shell '/bin/bash' }
|
|
27
|
+
# its(:minimum_days_between_password_change) { should eq 0 }
|
|
28
|
+
# its(:maximum_days_between_password_change) { should eq 99 }
|
|
29
|
+
# end
|
|
30
|
+
|
|
31
|
+
# ServerSpec tests that are not supported:
|
|
32
|
+
#
|
|
33
|
+
# describe user('root') do
|
|
34
|
+
# it { should have_authorized_key 'ssh-rsa ADg54...3434 user@example.local' }
|
|
35
|
+
# its(:encrypted_password) { should eq 1234 }
|
|
36
|
+
# end
|
|
37
|
+
|
|
38
|
+
require 'utils/parser'
|
|
39
|
+
require 'utils/convert'
|
|
40
|
+
|
|
41
|
+
class User < Inspec.resource(1)
|
|
42
|
+
name 'user'
|
|
43
|
+
|
|
44
|
+
def initialize(user)
|
|
45
|
+
@user = user
|
|
46
|
+
|
|
47
|
+
# select package manager
|
|
48
|
+
@user_provider = nil
|
|
49
|
+
case inspec.os[:family]
|
|
50
|
+
when 'ubuntu', 'debian', 'redhat', 'fedora', 'centos', 'arch', 'opensuse'
|
|
51
|
+
@user_provider = LinuxUser.new(inspec)
|
|
52
|
+
when 'windows'
|
|
53
|
+
@user_provider = WindowsUser.new(inspec)
|
|
54
|
+
when 'darwin'
|
|
55
|
+
@user_provider = DarwinUser.new(inspec)
|
|
56
|
+
when 'freebsd'
|
|
57
|
+
@user_provider = FreeBSDUser.new(inspec)
|
|
58
|
+
else
|
|
59
|
+
return skip_resource 'The `user` resource is not supported on your OS yet.'
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def exists?
|
|
64
|
+
!identiy.nil? && !identiy[:user].nil?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def uid
|
|
68
|
+
identiy.nil? ? nil : identiy[:uid]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def gid
|
|
72
|
+
identiy.nil? ? nil : identiy[:gid]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def group
|
|
76
|
+
identiy.nil? ? nil : identiy[:group]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def groups
|
|
80
|
+
identiy.nil? ? nil : identiy[:groups]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def home
|
|
84
|
+
meta_info.nil? ? nil : meta_info[:home]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def shell
|
|
88
|
+
meta_info.nil? ? nil : meta_info[:shell]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# returns the minimum days between password changes
|
|
92
|
+
def mindays
|
|
93
|
+
credentials.nil? ? nil : credentials[:mindays]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# returns the maximum days between password changes
|
|
97
|
+
def maxdays
|
|
98
|
+
credentials.nil? ? nil : credentials[:maxdays]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# returns the days for password change warning
|
|
102
|
+
def warndays
|
|
103
|
+
credentials.nil? ? nil : credentials[:warndays]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# implement 'mindays' method to be compatible with serverspec
|
|
107
|
+
def minimum_days_between_password_change
|
|
108
|
+
deprecated('minimum_days_between_password_change', "Please use 'its(:mindays)'")
|
|
109
|
+
mindays
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# implement 'maxdays' method to be compatible with serverspec
|
|
113
|
+
def maximum_days_between_password_change
|
|
114
|
+
deprecated('maximum_days_between_password_change', "Please use 'its(:maxdays)'")
|
|
115
|
+
maxdays
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# implements rspec has matcher, to be compatible with serverspec
|
|
119
|
+
# @see: https://github.com/rspec/rspec-expectations/blob/master/lib/rspec/matchers/built_in/has.rb
|
|
120
|
+
def has_uid?(compare_uid)
|
|
121
|
+
deprecated('has_uid?')
|
|
122
|
+
uid == compare_uid
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def has_home_directory?(compare_home)
|
|
126
|
+
deprecated('has_home_directory?', "Please use 'its(:home)'")
|
|
127
|
+
home == compare_home
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def has_login_shell?(compare_shell)
|
|
131
|
+
deprecated('has_login_shell?', "Please use 'its(:shell)'")
|
|
132
|
+
shell == compare_shell
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def has_authorized_key?(_compare_key)
|
|
136
|
+
deprecated('has_authorized_key?')
|
|
137
|
+
fail NotImplementedError
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def deprecated(name, alternative = nil)
|
|
141
|
+
warn "[DEPRECATION] #{name} is deprecated. #{alternative}"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def to_s
|
|
145
|
+
"User #{@user}"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
private
|
|
149
|
+
|
|
150
|
+
def identiy
|
|
151
|
+
return @id_cache if defined?(@id_cache)
|
|
152
|
+
@id_cache = @user_provider.identity(@user) if !@user_provider.nil?
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def meta_info
|
|
156
|
+
return @meta_cache if defined?(@meta_cache)
|
|
157
|
+
@meta_cache = @user_provider.meta_info(@user) if !@user_provider.nil?
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def credentials
|
|
161
|
+
return @cred_cache if defined?(@cred_cache)
|
|
162
|
+
@cred_cache = @user_provider.credentials(@user) if !@user_provider.nil?
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
class UserInfo
|
|
167
|
+
include Converter
|
|
168
|
+
|
|
169
|
+
attr_reader :inspec
|
|
170
|
+
def initialize(inspec)
|
|
171
|
+
@inspec = inspec
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def credentials(_username)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# implements generic unix id handling
|
|
179
|
+
class UnixUser < UserInfo
|
|
180
|
+
# parse one id entry like '0(wheel)''
|
|
181
|
+
def parse_value(line)
|
|
182
|
+
SimpleConfig.new(
|
|
183
|
+
line,
|
|
184
|
+
line_separator: ',',
|
|
185
|
+
assignment_re: /^\s*([^\(]*?)\s*\(\s*(.*?)\)*$/,
|
|
186
|
+
group_re: nil,
|
|
187
|
+
multiple_values: false,
|
|
188
|
+
).params
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# extracts the identity
|
|
192
|
+
def identity(username)
|
|
193
|
+
cmd = inspec.command("id #{username}")
|
|
194
|
+
return nil if cmd.exit_status != 0
|
|
195
|
+
|
|
196
|
+
# parse words
|
|
197
|
+
params = SimpleConfig.new(
|
|
198
|
+
cmd.stdout.chomp,
|
|
199
|
+
line_separator: ' ',
|
|
200
|
+
assignment_re: /^\s*([^=]*?)\s*=\s*(.*?)\s*$/,
|
|
201
|
+
group_re: nil,
|
|
202
|
+
multiple_values: false,
|
|
203
|
+
).params
|
|
204
|
+
|
|
205
|
+
{
|
|
206
|
+
uid: convert_to_i(parse_value(params['uid']).keys[0]),
|
|
207
|
+
user: parse_value(params['uid']).values[0],
|
|
208
|
+
gid: convert_to_i(parse_value(params['gid']).keys[0]),
|
|
209
|
+
group: parse_value(params['gid']).values[0],
|
|
210
|
+
groups: parse_value(params['groups']).values,
|
|
211
|
+
}
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
class LinuxUser < UnixUser
|
|
216
|
+
include ContentParser
|
|
217
|
+
|
|
218
|
+
def meta_info(username)
|
|
219
|
+
cmd = inspec.command("getent passwd #{username}")
|
|
220
|
+
return nil if cmd.exit_status != 0
|
|
221
|
+
# returns: root:x:0:0:root:/root:/bin/bash
|
|
222
|
+
passwd = parse_passwd_line(cmd.stdout.chomp)
|
|
223
|
+
{
|
|
224
|
+
home: passwd['home'],
|
|
225
|
+
shell: passwd['shell'],
|
|
226
|
+
}
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def credentials(username)
|
|
230
|
+
cmd = inspec.command("chage -l #{username}")
|
|
231
|
+
return nil if cmd.exit_status != 0
|
|
232
|
+
|
|
233
|
+
params = SimpleConfig.new(
|
|
234
|
+
cmd.stdout.chomp,
|
|
235
|
+
assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
|
|
236
|
+
group_re: nil,
|
|
237
|
+
multiple_values: false,
|
|
238
|
+
).params
|
|
239
|
+
|
|
240
|
+
{
|
|
241
|
+
mindays: convert_to_i(params['Minimum number of days between password change']),
|
|
242
|
+
maxdays: convert_to_i(params['Maximum number of days between password change']),
|
|
243
|
+
warndays: convert_to_i(params['Number of days of warning before password expires']),
|
|
244
|
+
}
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# we do not use 'finger' for MacOS, because it is harder to parse data with it
|
|
249
|
+
# @see https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/fingerd.8.html
|
|
250
|
+
# instead we use 'dscl' to request user data
|
|
251
|
+
# @see https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/dscl.1.html
|
|
252
|
+
# @see http://superuser.com/questions/592921/mac-osx-users-vs-dscl-command-to-list-user
|
|
253
|
+
class DarwinUser < UnixUser
|
|
254
|
+
def meta_info(username)
|
|
255
|
+
cmd = inspec.command("dscl -q . -read /Users/#{username} NFSHomeDirectory PrimaryGroupID RecordName UniqueID UserShell")
|
|
256
|
+
return nil if cmd.exit_status != 0
|
|
257
|
+
|
|
258
|
+
params = SimpleConfig.new(
|
|
259
|
+
cmd.stdout.chomp,
|
|
260
|
+
assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
|
|
261
|
+
group_re: nil,
|
|
262
|
+
multiple_values: false,
|
|
263
|
+
).params
|
|
264
|
+
|
|
265
|
+
{
|
|
266
|
+
home: params['NFSHomeDirectory'],
|
|
267
|
+
shell: params['UserShell'],
|
|
268
|
+
}
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# FreeBSD recommends to use the 'pw' command for user management
|
|
273
|
+
# @see: https://www.freebsd.org/doc/handbook/users-synopsis.html
|
|
274
|
+
# @see: https://www.freebsd.org/cgi/man.cgi?pw(8)
|
|
275
|
+
# It offers the following commands:
|
|
276
|
+
# - adduser(8) The recommended command-line application for adding new users.
|
|
277
|
+
# - rmuser(8) The recommended command-line application for removing users.
|
|
278
|
+
# - chpass(1) A flexible tool for changing user database information.
|
|
279
|
+
# - passwd(1) The command-line tool to change user passwords.
|
|
280
|
+
class FreeBSDUser < UnixUser
|
|
281
|
+
include ContentParser
|
|
282
|
+
|
|
283
|
+
def meta_info(username)
|
|
284
|
+
cmd = inspec.command("pw usershow #{username} -7")
|
|
285
|
+
return nil if cmd.exit_status != 0
|
|
286
|
+
# returns: root:*:0:0:Charlie &:/root:/bin/csh
|
|
287
|
+
passwd = parse_passwd_line(cmd.stdout.chomp)
|
|
288
|
+
{
|
|
289
|
+
home: passwd['home'],
|
|
290
|
+
shell: passwd['shell'],
|
|
291
|
+
}
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# For now, we stick with WMI Win32_UserAccount
|
|
296
|
+
# @see https://msdn.microsoft.com/en-us/library/aa394507(v=vs.85).aspx
|
|
297
|
+
# @see https://msdn.microsoft.com/en-us/library/aa394153(v=vs.85).aspx
|
|
298
|
+
#
|
|
299
|
+
# using Get-AdUser would be the best command for domain machines, but it will not be installed
|
|
300
|
+
# on client machines by default
|
|
301
|
+
# @see https://technet.microsoft.com/en-us/library/ee617241.aspx
|
|
302
|
+
# @see https://technet.microsoft.com/en-us/library/hh509016(v=WS.10).aspx
|
|
303
|
+
# @see http://woshub.com/get-aduser-getting-active-directory-users-data-via-powershell/
|
|
304
|
+
# @see http://stackoverflow.com/questions/17548523/the-term-get-aduser-is-not-recognized-as-the-name-of-a-cmdlet
|
|
305
|
+
#
|
|
306
|
+
# Just for reference, we could also use ADSI (Active Directory Service Interfaces)
|
|
307
|
+
# @see https://mcpmag.com/articles/2015/04/15/reporting-on-local-accounts.aspx
|
|
308
|
+
class WindowsUser < UserInfo
|
|
309
|
+
# parse windows account name
|
|
310
|
+
def parse_windows_account(username)
|
|
311
|
+
account = username.split('\\')
|
|
312
|
+
name = account.pop
|
|
313
|
+
domain = account.pop if account.size > 0
|
|
314
|
+
[name, domain]
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def identity(username)
|
|
318
|
+
# extract domain/user information
|
|
319
|
+
account, domain = parse_windows_account(username)
|
|
320
|
+
|
|
321
|
+
# TODO: escape content
|
|
322
|
+
if !domain.nil?
|
|
323
|
+
filter = "Name = '#{account}' and Domain = '#{domain}'"
|
|
324
|
+
else
|
|
325
|
+
filter = "Name = '#{account}' and LocalAccount = true"
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
script = <<-EOH
|
|
329
|
+
# find user
|
|
330
|
+
$user = Get-WmiObject Win32_UserAccount -filter "#{filter}"
|
|
331
|
+
# get related groups
|
|
332
|
+
$groups = $user.GetRelated('Win32_Group') | Select-Object -Property Caption, Domain, Name, LocalAccount, SID, SIDType, Status
|
|
333
|
+
# filter user information
|
|
334
|
+
$user = $user | Select-Object -Property Caption, Description, Domain, Name, LocalAccount, Lockout, PasswordChangeable, PasswordExpires, PasswordRequired, SID, SIDType, Status
|
|
335
|
+
# build response object
|
|
336
|
+
New-Object -Type PSObject | `
|
|
337
|
+
Add-Member -MemberType NoteProperty -Name User -Value ($user) -PassThru | `
|
|
338
|
+
Add-Member -MemberType NoteProperty -Name Groups -Value ($groups) -PassThru | `
|
|
339
|
+
ConvertTo-Json
|
|
340
|
+
EOH
|
|
341
|
+
|
|
342
|
+
cmd = inspec.script(script)
|
|
343
|
+
|
|
344
|
+
# cannot rely on exit code for now, successful command returns exit code 1
|
|
345
|
+
# return nil if cmd.exit_status != 0, try to parse json
|
|
346
|
+
begin
|
|
347
|
+
params = JSON.parse(cmd.stdout)
|
|
348
|
+
rescue JSON::ParserError => _e
|
|
349
|
+
return nil
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
user = params['User']['Caption'] unless params['User'].nil?
|
|
353
|
+
groups = params['Groups']
|
|
354
|
+
# if groups is no array, generate one
|
|
355
|
+
groups = [groups] if !groups.is_a?(Array)
|
|
356
|
+
groups = groups.map { |grp| grp['Caption'] } unless params['Groups'].nil?
|
|
357
|
+
|
|
358
|
+
{
|
|
359
|
+
uid: nil,
|
|
360
|
+
user: user,
|
|
361
|
+
gid: nil,
|
|
362
|
+
group: nil,
|
|
363
|
+
groups: groups,
|
|
364
|
+
}
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# not implemented yet
|
|
368
|
+
def meta_info(_username)
|
|
369
|
+
{
|
|
370
|
+
home: nil,
|
|
371
|
+
shell: nil,
|
|
372
|
+
}
|
|
373
|
+
end
|
|
374
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# author: Christoph Hartmann
|
|
3
|
+
# author: Dominik Richter
|
|
4
|
+
|
|
5
|
+
# check for a Windows feature
|
|
6
|
+
# Usage:
|
|
7
|
+
# describe windows_feature('DHCP Server') do
|
|
8
|
+
# it{ should be_installed }
|
|
9
|
+
# end
|
|
10
|
+
#
|
|
11
|
+
# deprecated serverspec syntax:
|
|
12
|
+
# describe windows_feature('IIS-Webserver') do
|
|
13
|
+
# it{ should be_installed.by("dism") }
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# describe windows_feature('Web-Webserver') do
|
|
17
|
+
# it{ should be_installed.by("powershell") }
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# This implementation uses the Get-WindowsFeature commandlet:
|
|
21
|
+
# Get-WindowsFeature | Where-Object {$_.Name -eq 'XPS Viewer' -or $_.DisplayName -eq 'XPS Viewe
|
|
22
|
+
# r'} | Select-Object -Property Name,DisplayName,Description,Installed,InstallState | ConvertTo-Json
|
|
23
|
+
# {
|
|
24
|
+
# "Name": "XPS-Viewer",
|
|
25
|
+
# "DisplayName": "XPS Viewer",
|
|
26
|
+
# "Description": "The XPS Viewer is used to read, set permissions for, and digitally sign XPS documents.",
|
|
27
|
+
# "Installed": false,
|
|
28
|
+
# "InstallState": 0
|
|
29
|
+
# }
|
|
30
|
+
class WindowsFeature < Inspec.resource(1)
|
|
31
|
+
name 'windows_feature'
|
|
32
|
+
|
|
33
|
+
def initialize(feature)
|
|
34
|
+
@feature = feature
|
|
35
|
+
@cache = nil
|
|
36
|
+
|
|
37
|
+
# verify that this resource is only supported on Windows
|
|
38
|
+
return skip_resource 'The `windows_feature` resource is not supported on your OS.' if inspec.os[:family] != 'windows'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# returns true if the package is installed
|
|
42
|
+
def installed?(_provider = nil, _version = nil)
|
|
43
|
+
info[:installed] == true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# returns the package description
|
|
47
|
+
def info
|
|
48
|
+
return @cache if !@cache.nil?
|
|
49
|
+
features_cmd = "Get-WindowsFeature | Where-Object {$_.Name -eq '#{@feature}' -or $_.DisplayName -eq '#{@feature}'} | Select-Object -Property Name,DisplayName,Description,Installed,InstallState | ConvertTo-Json"
|
|
50
|
+
cmd = inspec.command(features_cmd)
|
|
51
|
+
|
|
52
|
+
@cache = {
|
|
53
|
+
name: @feature,
|
|
54
|
+
type: 'windows-feature',
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# cannot rely on exit code for now, successful command returns exit code 1
|
|
58
|
+
# return nil if cmd.exit_status != 0
|
|
59
|
+
# try to parse json
|
|
60
|
+
begin
|
|
61
|
+
params = JSON.parse(cmd.stdout)
|
|
62
|
+
rescue JSON::ParserError => _e
|
|
63
|
+
return @cache
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
@cache = {
|
|
67
|
+
name: params['Name'],
|
|
68
|
+
description: params['Description'],
|
|
69
|
+
installed: params['Installed'],
|
|
70
|
+
type: 'windows-feature',
|
|
71
|
+
}
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def to_s
|
|
75
|
+
"Windows Feature '#{@feature}'"
|
|
76
|
+
end
|
|
77
|
+
end
|