inspec 0.34.1 → 1.0.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 +4 -4
- data/CHANGELOG.md +323 -6
- data/Gemfile +10 -0
- data/README.md +1 -1
- data/Rakefile +4 -55
- data/docs/.gitignore +2 -0
- data/docs/README.md +40 -0
- data/docs/cli.md +342 -0
- data/docs/dsl_inspec.md +245 -0
- data/docs/dsl_resource.md +93 -0
- data/docs/inspec_and_friends.md +105 -0
- data/docs/matchers.md +136 -0
- data/docs/plugin_kitchen_inspec.html.md +55 -0
- data/docs/profiles.md +285 -0
- data/docs/resources/apache_conf.md.erb +75 -0
- data/docs/resources/apt.md.erb +84 -0
- data/docs/resources/audit_policy.md.erb +61 -0
- data/docs/resources/auditd_conf.md.erb +79 -0
- data/docs/resources/auditd_rules.md.erb +132 -0
- data/docs/resources/bash.md.erb +84 -0
- data/docs/resources/bond.md.erb +97 -0
- data/docs/resources/bridge.md.erb +67 -0
- data/docs/resources/bsd_service.md.erb +76 -0
- data/docs/resources/command.md.erb +151 -0
- data/docs/resources/csv.md.erb +62 -0
- data/docs/resources/directory.md.erb +43 -0
- data/docs/resources/etc_group.md.erb +116 -0
- data/docs/resources/etc_passwd.md.erb +155 -0
- data/docs/resources/etc_shadow.md.erb +149 -0
- data/docs/resources/file.md.erb +460 -0
- data/docs/resources/gem.md.erb +73 -0
- data/docs/resources/group.md.erb +74 -0
- data/docs/resources/grub_conf.md.erb +115 -0
- data/docs/resources/host.md.erb +85 -0
- data/docs/resources/iis_site.md.erb +142 -0
- data/docs/resources/inetd_conf.md.erb +99 -0
- data/docs/resources/ini.md.erb +69 -0
- data/docs/resources/interface.md.erb +66 -0
- data/docs/resources/iptables.md.erb +70 -0
- data/docs/resources/json.md.erb +76 -0
- data/docs/resources/kernel_module.md.erb +60 -0
- data/docs/resources/kernel_parameter.md.erb +72 -0
- data/docs/resources/launchd_service.md.erb +76 -0
- data/docs/resources/limits_conf.md.erb +80 -0
- data/docs/resources/login_def.md.erb +77 -0
- data/docs/resources/mount.md.erb +83 -0
- data/docs/resources/mysql_conf.md.erb +102 -0
- data/docs/resources/mysql_session.md.erb +63 -0
- data/docs/resources/npm.md.erb +75 -0
- data/docs/resources/ntp_conf.md.erb +76 -0
- data/docs/resources/oneget.md.erb +67 -0
- data/docs/resources/os.md.erb +154 -0
- data/docs/resources/os_env.md.erb +98 -0
- data/docs/resources/package.md.erb +115 -0
- data/docs/resources/parse_config.md.erb +122 -0
- data/docs/resources/parse_config_file.md.erb +143 -0
- data/docs/resources/pip.md.erb +74 -0
- data/docs/resources/port.md.erb +150 -0
- data/docs/resources/postgres_conf.md.erb +90 -0
- data/docs/resources/postgres_session.md.erb +75 -0
- data/docs/resources/powershell.md.erb +116 -0
- data/docs/resources/process.md.erb +73 -0
- data/docs/resources/registry_key.md.erb +149 -0
- data/docs/resources/runit_service.md.erb +76 -0
- data/docs/resources/security_policy.md.erb +61 -0
- data/docs/resources/service.md.erb +135 -0
- data/docs/resources/ssh_config.md.erb +94 -0
- data/docs/resources/sshd_config.md.erb +97 -0
- data/docs/resources/ssl.md.erb +133 -0
- data/docs/resources/sys_info.md.erb +55 -0
- data/docs/resources/systemd_service.md.erb +76 -0
- data/docs/resources/sysv_service.md.erb +76 -0
- data/docs/resources/upstart_service.md.erb +76 -0
- data/docs/resources/user.md.erb +154 -0
- data/docs/resources/users.md.erb +140 -0
- data/docs/resources/vbscript.md.erb +69 -0
- data/docs/resources/windows_feature.md.erb +61 -0
- data/docs/resources/wmi.md.erb +95 -0
- data/docs/resources/xinetd_conf.md.erb +170 -0
- data/docs/resources/yaml.md.erb +69 -0
- data/docs/resources/yum.md.erb +103 -0
- data/docs/ruby_usage.md +154 -0
- data/docs/shared/matcher_be.md.erb +1 -0
- data/docs/shared/matcher_cmp.md.erb +45 -0
- data/docs/shared/matcher_eq.md.erb +3 -0
- data/docs/shared/matcher_include.md.erb +1 -0
- data/docs/shared/matcher_match.md.erb +1 -0
- data/docs/shell.md +150 -0
- data/examples/meta-profile/inspec.yml +2 -4
- data/inspec.gemspec +1 -1
- data/lib/bundles/inspec-compliance/api.rb +28 -18
- data/lib/bundles/inspec-compliance/cli.rb +19 -27
- data/lib/bundles/inspec-compliance/target.rb +15 -2
- data/lib/fetchers/git.rb +4 -0
- data/lib/fetchers/local.rb +16 -1
- data/lib/fetchers/mock.rb +4 -0
- data/lib/fetchers/url.rb +46 -20
- data/lib/inspec/base_cli.rb +14 -0
- data/lib/inspec/cached_fetcher.rb +67 -0
- data/lib/inspec/cli.rb +22 -8
- data/lib/inspec/control_eval_context.rb +12 -2
- data/lib/inspec/dependencies/{vendor_index.rb → cache.rb} +5 -4
- data/lib/inspec/dependencies/dependency_set.rb +8 -14
- data/lib/inspec/dependencies/lockfile.rb +5 -13
- data/lib/inspec/dependencies/requirement.rb +31 -41
- data/lib/inspec/dependencies/resolver.rb +3 -3
- data/lib/inspec/dsl.rb +16 -4
- data/lib/inspec/errors.rb +1 -0
- data/lib/inspec/fetcher.rb +25 -1
- data/lib/inspec/objects/each_loop.rb +5 -2
- data/lib/inspec/objects/test.rb +8 -2
- data/lib/inspec/plugins/fetcher.rb +13 -12
- data/lib/inspec/plugins/resource.rb +3 -0
- data/lib/inspec/profile.rb +62 -26
- data/lib/inspec/profile_context.rb +44 -8
- data/lib/inspec/resource.rb +18 -3
- data/lib/inspec/rspec_json_formatter.rb +91 -40
- data/lib/inspec/rule.rb +45 -2
- data/lib/inspec/runner.rb +51 -39
- data/lib/inspec/shell.rb +5 -16
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/apache_conf.rb +1 -1
- data/lib/resources/gem.rb +1 -0
- data/lib/resources/groups.rb +190 -0
- data/lib/resources/oneget.rb +1 -0
- data/lib/resources/os.rb +1 -1
- data/lib/resources/package.rb +3 -1
- data/lib/resources/parse_config.rb +18 -1
- data/lib/resources/pip.rb +1 -1
- data/lib/resources/security_policy.rb +54 -0
- data/lib/resources/ssl.rb +9 -11
- data/lib/resources/sys_info.rb +1 -1
- data/lib/resources/users.rb +81 -64
- metadata +91 -17
- data/docs/cli.rst +0 -442
- data/docs/dsl_inspec.rst +0 -259
- data/docs/dsl_resource.rst +0 -90
- data/docs/inspec_and_friends.rst +0 -85
- data/docs/matchers.rst +0 -137
- data/docs/profiles.rst +0 -169
- data/docs/readme.rst +0 -105
- data/docs/resources.rst +0 -4836
- data/docs/ruby_usage.rst +0 -145
- data/docs/shell.rst +0 -130
- data/docs/template.rst +0 -51
- data/lib/resources/group.rb +0 -137
data/docs/ruby_usage.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Using Ruby in InSpec
|
|
2
|
+
|
|
3
|
+
The InSpec DSL is a Ruby based DSL for writing audit controls, which
|
|
4
|
+
includes audit resources that you can invoke. Core and custom resources
|
|
5
|
+
are written as regular Ruby classes which inherit from
|
|
6
|
+
`Inspec.resource`.
|
|
7
|
+
|
|
8
|
+
Assuming we have a JSON file like this on the node to be tested:
|
|
9
|
+
|
|
10
|
+
```json
|
|
11
|
+
{
|
|
12
|
+
"keys":[
|
|
13
|
+
{"username":"john", "key":"/opt/keys/johnd.key"},
|
|
14
|
+
{"username":"jane", "key":"/opt/keys/janed.key"},
|
|
15
|
+
{"username":"sunny ", "key":"/opt/keys/sunnym.key"}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The following example shows how you can use pure Ruby code(variables,
|
|
21
|
+
loops, conditionals, regular expressions, etc) to run a few tests
|
|
22
|
+
against the above JSON file:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
control 'check-interns' do
|
|
26
|
+
# use the json inspec resource to get the file
|
|
27
|
+
json_obj = json('/opt/keys/interns.json')
|
|
28
|
+
describe json_obj do
|
|
29
|
+
its('keys') { should_not eq nil }
|
|
30
|
+
end
|
|
31
|
+
if json_obj['keys']
|
|
32
|
+
# loop over the keys array
|
|
33
|
+
json_obj['keys'].each do |intern|
|
|
34
|
+
username = intern['username'].strip
|
|
35
|
+
# check for white spaces chars in usernames
|
|
36
|
+
describe username do
|
|
37
|
+
it { should_not match(/\s/) }
|
|
38
|
+
end
|
|
39
|
+
# check key file owners and permissions
|
|
40
|
+
describe file(intern['key']) do
|
|
41
|
+
it { should be_owned_by username }
|
|
42
|
+
its('mode') { should cmp '0600' }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Execution
|
|
50
|
+
|
|
51
|
+
It's important to understand that Ruby code used in custom resources and
|
|
52
|
+
controls DSL is executed on the system that runs InSpec. This allows
|
|
53
|
+
InSpec to work without Ruby and rubygems being required on remote
|
|
54
|
+
targets(servers or containers).
|
|
55
|
+
|
|
56
|
+
For example, using `` `ls ``\` or `system('ls')` will result in the `ls`
|
|
57
|
+
command being run locally and not on the target(remote) system. In order
|
|
58
|
+
to process the output of `ls` executed on the target system, use
|
|
59
|
+
`inspec.command('ls')` or `inspec.powershell('ls')`
|
|
60
|
+
|
|
61
|
+
Similarly, use `inspec.file(PATH)` to access files or directories from
|
|
62
|
+
remote systems in your tests or custom resources.
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
## Using rubygems
|
|
66
|
+
|
|
67
|
+
Ruby gems are self-contained programs and libraries. If you create a custom
|
|
68
|
+
resource please vendor gems into the library. This ensures that all resources
|
|
69
|
+
are self-contained and complete and don't need any resolution at runtime. We
|
|
70
|
+
vendor resources and requirements through dependency resolution, which is
|
|
71
|
+
independent of programming languages and their resolver mechanisms.
|
|
72
|
+
|
|
73
|
+
## Interactive Debugging with Pry
|
|
74
|
+
|
|
75
|
+
Here's a sample InSpec control that users Ruby variables to instantiate
|
|
76
|
+
an InSpec resource once and use the content in multipLe tests.
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
control 'check-perl' do
|
|
80
|
+
impact 0.3
|
|
81
|
+
title 'Check perl compiled options and permissions'
|
|
82
|
+
perl_out = command('perl -V')
|
|
83
|
+
#require 'pry'; binding.pry;
|
|
84
|
+
describe perl_out do
|
|
85
|
+
its('exit_status') { should eq 0 }
|
|
86
|
+
its('stdout') { should match (/USE_64_BIT_ALL/) }
|
|
87
|
+
its('stdout') { should match (/useposix=true/) }
|
|
88
|
+
its('stdout') { should match (/-fstack-protector/) }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# extract an array of include directories
|
|
92
|
+
perl_inc = perl_out.stdout.partition('@INC:').last.strip.split("\n")
|
|
93
|
+
# ensure include directories are only writable by 'owner'
|
|
94
|
+
perl_inc.each do |path|
|
|
95
|
+
describe directory(path.strip) do
|
|
96
|
+
it { should_not be_writable.by('group') }
|
|
97
|
+
it { should_not be_writable.by('other') }
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
An **advanced** but very useful Ruby tip. In the previous example, I
|
|
104
|
+
commented out the `require 'pry'; binding.pry;` line. If you remove the
|
|
105
|
+
`#` prefix and run the control, the execution will stop at that line and
|
|
106
|
+
give you a `pry` shell. Use that to troubleshoot, print variables, see
|
|
107
|
+
methods available, etc. For the above example:
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
[1] pry> perl_out.exit_status
|
|
111
|
+
=> 0
|
|
112
|
+
[2] pry> perl_out.stderr
|
|
113
|
+
=> ""
|
|
114
|
+
[3] pry> ls perl_out
|
|
115
|
+
Inspec::Plugins::Resource#methods: inspect
|
|
116
|
+
Inspec::Resources::Cmd#methods: command exist? exit_status result stderr stdout to_s
|
|
117
|
+
Inspec::Plugins::ResourceCommon#methods: resource_skipped skip_resource
|
|
118
|
+
Inspec::Resource::Registry::Command#methods: inspec
|
|
119
|
+
instance variables: @__backend_runner__ @__resource_name__ @command @result
|
|
120
|
+
[4] pry> perl_out.stdout.partition('@INC:').last.strip.split("\n")
|
|
121
|
+
=> ["/Library/Perl/5.18/darwin-thread-multi-2level",
|
|
122
|
+
" /Library/Perl/5.18",
|
|
123
|
+
...REDACTED...
|
|
124
|
+
[5] pry> exit # or abort
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
You can use `pry` inside both the controls DSL and resources. Similarly,
|
|
128
|
+
for dev and test, you can use `inspec shell` which is based on `pry`,
|
|
129
|
+
for example:
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
$ inspec shell
|
|
133
|
+
Welcome to the interactive InSpec Shell
|
|
134
|
+
To find out how to use it, type: help
|
|
135
|
+
|
|
136
|
+
inspec> command('ls /home/gordon/git/inspec/docs').stdout
|
|
137
|
+
=> "ctl_inspec.rst\ndsl_inspec.rst\ndsl_resource.rst\n"
|
|
138
|
+
inspec> command('ls').stdout.split("\n")
|
|
139
|
+
=> ["ctl_inspec.rst", "dsl_inspec.rst", "dsl_resource.rst"]
|
|
140
|
+
|
|
141
|
+
inspec> help command
|
|
142
|
+
Name: command
|
|
143
|
+
|
|
144
|
+
Description:
|
|
145
|
+
Use the command InSpec audit resource to test an arbitrary command that is run on the system.
|
|
146
|
+
|
|
147
|
+
Example:
|
|
148
|
+
describe command('ls -al /') do
|
|
149
|
+
it { should exist }
|
|
150
|
+
its('stdout') { should match /bin/ }
|
|
151
|
+
its('stderr') { should eq '' }
|
|
152
|
+
its('exit_status') { should eq 0 }
|
|
153
|
+
end
|
|
154
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Use the `be` matcher to use a comparison operator---`=` (equal to), `>` (greater than), `<` (less than), `>=` (greater than or equal to), and `<=` (less than or equal to)---to compare two values: `its('value') { should be >= value }`, `its('value') { should be < value }`, and so on.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
Use the `cmp` matcher compare two values, such as comparing strings to numbers, comparing a single value to an array of values, comparing an array of strings to a regular expression, improving the printing of octal values, and comparing while ignoring case sensitivity.
|
|
2
|
+
|
|
3
|
+
Compare a single value to an array:
|
|
4
|
+
|
|
5
|
+
describe some_resource do
|
|
6
|
+
its('users') { should cmp 'root' }
|
|
7
|
+
its('users') { should cmp ['root'] }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
Compare strings and regular expressions:
|
|
11
|
+
|
|
12
|
+
describe some_resource do
|
|
13
|
+
its('setting') { should cmp /raw/i }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
Compare strings and numbers:
|
|
17
|
+
|
|
18
|
+
describe some_resource do
|
|
19
|
+
its('setting') { should eq '2' }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
vs:
|
|
23
|
+
|
|
24
|
+
describe some_resource do
|
|
25
|
+
its('setting') { should cmp '2' }
|
|
26
|
+
its('setting') { should cmp 2 }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
Ignoring case sensitivity:
|
|
30
|
+
|
|
31
|
+
.. code-block:: ruby
|
|
32
|
+
|
|
33
|
+
describe some_resource do
|
|
34
|
+
its('setting') { should cmp 'raw' }
|
|
35
|
+
its('setting') { should cmp 'RAW' }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
Printing octal values:
|
|
39
|
+
|
|
40
|
+
describe some_resource('/proc/cpuinfo') do
|
|
41
|
+
its('mode') { should cmp '0345' }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
expected: 0345
|
|
45
|
+
got: 0444
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Use the `include` matcher to verify that a string value is included in a list: `its('list') { should include 'string' }`.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Use the `match` matcher to check if a string matches a regular expression: `its('string') { should_not match /regex/ }`.
|
data/docs/shell.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: InSpec Shell
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# InSpec Shell
|
|
6
|
+
|
|
7
|
+
The InSpec interactive shell is a pry based REPL that can be used to
|
|
8
|
+
quickly run InSpec controls and tests without having to write it to a
|
|
9
|
+
file. Its functionality is similar to `chef shell` - it provides a way
|
|
10
|
+
to exercise the InSpec DSL, its resources, tests and plugins without
|
|
11
|
+
having to create a profile or write a test file. See
|
|
12
|
+
[http://pryrepl.org/](http://pryrepl.org/) for an introduction to what pry is and what it can
|
|
13
|
+
do.
|
|
14
|
+
|
|
15
|
+
## Launching the shell
|
|
16
|
+
|
|
17
|
+
If you are using InSpec from a platform-specific package (rpm, msi,
|
|
18
|
+
etc.) or from a chef prepared shell in ChefDK, you can directly launch
|
|
19
|
+
InSpec shell against your local machine using the following. See
|
|
20
|
+
<https://docs.chef.io/install_dk.html#set-system-ruby> for details.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
$ inspec shell
|
|
24
|
+
$ inspec help shell # This will describe inspec shell usage
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
If you wish to connect to a remote machine (called a target within
|
|
28
|
+
InSpec), you can use the `-t` flag. We support connecting using ssh,
|
|
29
|
+
WinRm and docker. If no target is provided, we implicitly support the
|
|
30
|
+
"local" target - i.e. tests running on the current machine running
|
|
31
|
+
InSpec. For an ssh connection, use `-i` for specifying ssh key files,
|
|
32
|
+
and the `--sudo*` commands for requesting a privelege escalation after
|
|
33
|
+
logging in. For a WinRM connection, use `--path` to change the login
|
|
34
|
+
path, `--ssl` to use SSL for transport layer encryption.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
$ inspec shell -t ssh://root@192.168.64.2:11022 # Login to remote machine using ssh as root.
|
|
38
|
+
$ inspec shell -t ssh://user@hostname:1234 -i /path/to/user_key # Login to hostname on port 1234 as user using given ssh key.
|
|
39
|
+
$ inspec shell -t winrm://UserName:Password@windowsmachine:1234 # Login to windowsmachine over WinRM as UserName.
|
|
40
|
+
$ inspec shell -t docker://container_id # Login to a docker container.
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Using Ruby in InSpec shell
|
|
44
|
+
|
|
45
|
+
Since InSpec shell is pry based, you may treat the shell as an
|
|
46
|
+
interactive Ruby session. You may write Ruby expressions and evaluate
|
|
47
|
+
them. Source high-lighting, automatic indentation and command history
|
|
48
|
+
(using the up and down arrow keys) are available to make your experience
|
|
49
|
+
more delightful. You can exit the shell using `exit`.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
$ inspec shell
|
|
53
|
+
Welcome to the interactive InSpec Shell
|
|
54
|
+
To find out how to use it, type: help
|
|
55
|
+
|
|
56
|
+
inspec> 1 + 2
|
|
57
|
+
=> 3
|
|
58
|
+
inspec> exit
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Using InSpec DSL in InSpec shell
|
|
62
|
+
|
|
63
|
+
InSpec shell will automatically evaluate the result of every command as
|
|
64
|
+
if it were a test file. If you type in a Ruby command that is not an
|
|
65
|
+
InSpec control or test, the shell will evaluate it as if it were a
|
|
66
|
+
regular ruby command.
|
|
67
|
+
|
|
68
|
+
Bare InSpec resources are instantiated and their help text is presented.
|
|
69
|
+
You may also access the resource contents or other matchers that they
|
|
70
|
+
define. Run `help <resource>` to get more help on using a particular
|
|
71
|
+
resource or see the InSpec resources documentation online.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
$ inspec shell
|
|
75
|
+
Welcome to the interactive InSpec Shell
|
|
76
|
+
To find out how to use it, type: help
|
|
77
|
+
|
|
78
|
+
inspec> file('/Users/ksubramanian').directory?
|
|
79
|
+
=> true
|
|
80
|
+
inspec> os_env('HOME')
|
|
81
|
+
=> Environment variable HOME
|
|
82
|
+
inspec> os_env('HOME').content
|
|
83
|
+
=> /Users/ksubramanian
|
|
84
|
+
inspec> exit
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
InSpec tests are immediately executed.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
inspec> describe file('/Users') # Empty test.
|
|
91
|
+
Summary: 0 successful, 0 failures, 0 skipped
|
|
92
|
+
inspec> describe file('/Users') do # Test with one check.
|
|
93
|
+
inspec> it { should exist }
|
|
94
|
+
inspec> end
|
|
95
|
+
✔ File /Users should exist
|
|
96
|
+
|
|
97
|
+
Summary: 1 successful, 0 failures, 0 skipped
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
All tests in a control are immediately executed as well. If a control is
|
|
101
|
+
redefined in the shell, the old control's tests are destroyed and
|
|
102
|
+
replaced with the redefinition and the control is re-run.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
inspec> control 'my_control' do
|
|
106
|
+
inspec> describe os_env('HOME') do
|
|
107
|
+
inspec> its('content') { should eq '/Users/ksubramanian' }
|
|
108
|
+
inspec> end
|
|
109
|
+
inspec> end
|
|
110
|
+
✔ my_control: Environment variable HOME content should eq "/Users/ksubramanian"
|
|
111
|
+
|
|
112
|
+
Summary: 1 successful, 0 failures, 0 skipped
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Syntax errors are illegal tests are also detected and reported.
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
inspec> control 'foo' do
|
|
119
|
+
inspec> thisisnonsense
|
|
120
|
+
inspec> end
|
|
121
|
+
NameError: undefined local variable or method `thisisnonsense' for #<#<Class:0x007fd63b571f98>:0x007fd639825cc8>
|
|
122
|
+
from /usr/local/lib/ruby/gems/2.3.0/gems/rspec-expectations-3.5.0/lib/rspec/matchers.rb:967:in `method_missing'
|
|
123
|
+
inspec> control 'foo' do
|
|
124
|
+
inspec> describe file('wut') do
|
|
125
|
+
inspec> its('thismakesnosense') { should cmp 'fail' }
|
|
126
|
+
inspec> end
|
|
127
|
+
inspec> end
|
|
128
|
+
✖ foo: File wut thismakesnosense (undefined method `thismakesnosense' for File wut:Inspec::Resource::Registry::File)
|
|
129
|
+
|
|
130
|
+
Summary: 0 successful, 1 failures, 0 skipped
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Running a single InSpec command
|
|
134
|
+
|
|
135
|
+
If you wish to run a single InSpec command and fetch its results, you
|
|
136
|
+
may use the `-c` flag. This is similar to using `bash -c`.
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
$ inspec shell -c 'describe file("/Users/ksubramanian") do it { should exist } end'}
|
|
140
|
+
Target: local://
|
|
141
|
+
|
|
142
|
+
✔ File /Users/ksubramanian should exist
|
|
143
|
+
|
|
144
|
+
Summary: 1 successful, 0 failures, 0 skipped
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
$ inspec shell --format json -c 'describe file("/Users/ksubramanian") do it { should exist } end'
|
|
149
|
+
{"version":"0.30.0","profiles":{"":{"supports":[],"controls":{"(generated from in_memory.rb:1 5aab65c33fb1f133d9244017958eef64)":{"title":null,"desc":null,"impact":0.5,"refs":[],"tags":{},"code":" rule = rule_class.new(id, profile_id, {}) do\n res = describe(*args, &block)\n end\n","source_location":{"ref":"/Users/ksubramanian/repo/chef/inspec/lib/inspec/profile_context.rb","line":184},"results":[{"status":"passed","code_desc":"File /Users/ksubramanian should exist","run_time":0.000747,"start_time":"2016-08-16 11:41:40 -0400"}]}},"groups":{"in_memory.rb":{"title":null,"controls":["(generated from in_memory.rb:1 5aab65c33fb1f133d9244017958eef64)"]}},"attributes":[]}},"other_checks":[],"summary":{"duration":0.001078,"example_count":1,"failure_count":0,"skip_count":0}}}
|
|
150
|
+
```
|
|
@@ -7,12 +7,10 @@ license: Apache 2
|
|
|
7
7
|
summary: InSpec Profile that is only consuming dependencies
|
|
8
8
|
version: 0.2.0
|
|
9
9
|
depends:
|
|
10
|
-
- name: ssh-hardening
|
|
11
|
-
supermarket: hardening/ssh-hardening
|
|
10
|
+
- name: hardening/ssh-hardening # defaults to supermarket
|
|
12
11
|
- name: os-hardening
|
|
13
12
|
url: https://github.com/dev-sec/tests-os-hardening/archive/master.zip
|
|
14
|
-
-
|
|
15
|
-
git: https://github.com/dev-sec/ssl-benchmark.git
|
|
13
|
+
- git: https://github.com/dev-sec/ssl-benchmark.git
|
|
16
14
|
- name: windows-patch-benchmark
|
|
17
15
|
git: https://github.com/chris-rock/windows-patch-benchmark.git
|
|
18
16
|
- name: linux
|
data/inspec.gemspec
CHANGED
|
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
|
|
|
24
24
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
25
25
|
spec.require_paths = ['lib']
|
|
26
26
|
|
|
27
|
-
spec.add_dependency 'train', '>=0.19.
|
|
27
|
+
spec.add_dependency 'train', '>=0.19.1', '<1.0'
|
|
28
28
|
spec.add_dependency 'thor', '~> 0.19'
|
|
29
29
|
spec.add_dependency 'json', '>= 1.8', '< 3.0'
|
|
30
30
|
spec.add_dependency 'rainbow', '~> 2'
|
|
@@ -9,18 +9,6 @@ module Compliance
|
|
|
9
9
|
# API Implementation does not hold any state by itself,
|
|
10
10
|
# everything will be stored in local Configuration store
|
|
11
11
|
class API
|
|
12
|
-
# login method for pre-1.0 compliance server
|
|
13
|
-
def self.legacy_login_post(url, username, password, insecure)
|
|
14
|
-
# form request
|
|
15
|
-
# TODO: reuse post function
|
|
16
|
-
uri = URI.parse(url)
|
|
17
|
-
req = Net::HTTP::Post.new(uri.path)
|
|
18
|
-
req.basic_auth(username, password)
|
|
19
|
-
req.form_data={}
|
|
20
|
-
|
|
21
|
-
send_request(uri, req, insecure)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
12
|
# return all compliance profiles available for the user
|
|
25
13
|
def self.profiles(config)
|
|
26
14
|
url = "#{config['server']}/user/compliance"
|
|
@@ -86,19 +74,19 @@ Please login using `inspec compliance login https://compliance.test --user admin
|
|
|
86
74
|
[res.is_a?(Net::HTTPSuccess), res.body]
|
|
87
75
|
end
|
|
88
76
|
|
|
89
|
-
|
|
77
|
+
# Use username and refresh_toke to get an API access token
|
|
78
|
+
def self.get_token_via_refresh_token(url, refresh_token, insecure)
|
|
90
79
|
uri = URI.parse("#{url}/login")
|
|
91
80
|
req = Net::HTTP::Post.new(uri.path)
|
|
92
|
-
|
|
93
|
-
req.body = { token: token }.to_json
|
|
81
|
+
req.body = { token: refresh_token }.to_json
|
|
94
82
|
access_token = nil
|
|
95
83
|
response = Compliance::HTTP.send_request(uri, req, insecure)
|
|
96
84
|
data = response.body
|
|
97
|
-
if
|
|
85
|
+
if response.code == '200'
|
|
98
86
|
begin
|
|
99
87
|
tokendata = JSON.parse(data)
|
|
100
88
|
access_token = tokendata['access_token']
|
|
101
|
-
msg = 'Successfully fetched access token'
|
|
89
|
+
msg = 'Successfully fetched API access token'
|
|
102
90
|
success = true
|
|
103
91
|
rescue JSON::ParserError => e
|
|
104
92
|
success = false
|
|
@@ -106,7 +94,29 @@ Please login using `inspec compliance login https://compliance.test --user admin
|
|
|
106
94
|
end
|
|
107
95
|
else
|
|
108
96
|
success = false
|
|
109
|
-
msg =
|
|
97
|
+
msg = "Failed to authenticate to #{url} \n\
|
|
98
|
+
Response code: #{response.code}\n Body: #{response.body}"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
[success, msg, access_token]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Use username and password to get an API access token
|
|
105
|
+
def self.get_token_via_password(url, username, password, insecure)
|
|
106
|
+
uri = URI.parse("#{url}/login")
|
|
107
|
+
req = Net::HTTP::Post.new(uri.path)
|
|
108
|
+
req.body = { userid: username, password: password }.to_json
|
|
109
|
+
access_token = nil
|
|
110
|
+
response = Compliance::HTTP.send_request(uri, req, insecure)
|
|
111
|
+
data = response.body
|
|
112
|
+
if response.code == '200'
|
|
113
|
+
access_token = data
|
|
114
|
+
msg = 'Successfully fetched an API access token valid for 12 hours'
|
|
115
|
+
success = true
|
|
116
|
+
else
|
|
117
|
+
success = false
|
|
118
|
+
msg = "Failed to authenticate to #{url} \n\
|
|
119
|
+
Response code: #{response.code}\n Body: #{response.body}"
|
|
110
120
|
end
|
|
111
121
|
|
|
112
122
|
[success, msg, access_token]
|
|
@@ -22,9 +22,9 @@ module Compliance
|
|
|
22
22
|
option :insecure, aliases: :k, type: :boolean,
|
|
23
23
|
desc: 'Explicitly allows InSpec to perform "insecure" SSL connections and transfers'
|
|
24
24
|
option :user, type: :string, required: false,
|
|
25
|
-
desc: 'Chef Compliance Username
|
|
25
|
+
desc: 'Chef Compliance Username'
|
|
26
26
|
option :password, type: :string, required: false,
|
|
27
|
-
desc: 'Chef Compliance Password
|
|
27
|
+
desc: 'Chef Compliance Password'
|
|
28
28
|
option :apipath, type: :string, default: '/api',
|
|
29
29
|
desc: 'Set the path to the API, defaults to /api'
|
|
30
30
|
option :token, type: :string, required: false,
|
|
@@ -38,7 +38,7 @@ module Compliance
|
|
|
38
38
|
url = options['server'] + options['apipath']
|
|
39
39
|
if !options['user'].nil? && !options['password'].nil?
|
|
40
40
|
# username / password
|
|
41
|
-
_success, msg =
|
|
41
|
+
_success, msg = login_username_password(url, options['user'], options['password'], options['insecure'])
|
|
42
42
|
elsif !options['user'].nil? && !options['token'].nil?
|
|
43
43
|
# access token
|
|
44
44
|
_success, msg = store_access_token(url, options['user'], options['token'], options['insecure'])
|
|
@@ -199,7 +199,7 @@ module Compliance
|
|
|
199
199
|
private
|
|
200
200
|
|
|
201
201
|
def login_refreshtoken(url, options)
|
|
202
|
-
success, msg, access_token = Compliance::API.
|
|
202
|
+
success, msg, access_token = Compliance::API.get_token_via_refresh_token(url, options['refresh_token'], options['insecure'])
|
|
203
203
|
if success
|
|
204
204
|
config = Compliance::Configuration.new
|
|
205
205
|
config['server'] = url
|
|
@@ -212,25 +212,17 @@ module Compliance
|
|
|
212
212
|
[success, msg]
|
|
213
213
|
end
|
|
214
214
|
|
|
215
|
-
def
|
|
215
|
+
def login_username_password(url, username, password, insecure)
|
|
216
216
|
config = Compliance::Configuration.new
|
|
217
|
-
success,
|
|
218
|
-
if
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
config.store
|
|
227
|
-
success = true
|
|
228
|
-
msg = 'Successfully authenticated'
|
|
229
|
-
else
|
|
230
|
-
msg = 'Response does not include a token'
|
|
231
|
-
end
|
|
232
|
-
else
|
|
233
|
-
msg = "Authentication failed for Server: #{url}"
|
|
217
|
+
success, msg, api_token = Compliance::API.get_token_via_password(url, username, password, insecure)
|
|
218
|
+
if success
|
|
219
|
+
config['server'] = url
|
|
220
|
+
config['user'] = username
|
|
221
|
+
config['token'] = api_token
|
|
222
|
+
config['insecure'] = insecure
|
|
223
|
+
config['version'] = Compliance::API.version(url, insecure)
|
|
224
|
+
config.store
|
|
225
|
+
success = true
|
|
234
226
|
end
|
|
235
227
|
[success, msg]
|
|
236
228
|
end
|
|
@@ -245,10 +237,10 @@ module Compliance
|
|
|
245
237
|
config['version'] = Compliance::API.version(url, insecure)
|
|
246
238
|
config.store
|
|
247
239
|
|
|
248
|
-
[true, 'access token stored']
|
|
240
|
+
[true, 'API access token stored']
|
|
249
241
|
end
|
|
250
242
|
|
|
251
|
-
# saves
|
|
243
|
+
# saves a refresh token supplied by the user
|
|
252
244
|
def store_refresh_token(url, refresh_token, verify, user, insecure)
|
|
253
245
|
config = Compliance::Configuration.new
|
|
254
246
|
config['server'] = url
|
|
@@ -260,13 +252,13 @@ module Compliance
|
|
|
260
252
|
if !verify
|
|
261
253
|
config.store
|
|
262
254
|
success = true
|
|
263
|
-
msg = 'refresh token stored'
|
|
255
|
+
msg = 'API refresh token stored'
|
|
264
256
|
else
|
|
265
|
-
success, msg, access_token = Compliance::API.
|
|
257
|
+
success, msg, access_token = Compliance::API.get_token_via_refresh_token(url, refresh_token, insecure)
|
|
266
258
|
if success
|
|
267
259
|
config['token'] = access_token
|
|
268
260
|
config.store
|
|
269
|
-
msg = 'token verified and stored'
|
|
261
|
+
msg = 'API access token verified and stored'
|
|
270
262
|
end
|
|
271
263
|
end
|
|
272
264
|
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
require 'uri'
|
|
6
6
|
require 'inspec/fetcher'
|
|
7
|
+
require 'inspec/errors'
|
|
7
8
|
|
|
8
9
|
# InSpec Target Helper for Chef Compliance
|
|
9
10
|
# reuses UrlHelper, but it knows the target server and the access token already
|
|
@@ -24,11 +25,23 @@ module Compliance
|
|
|
24
25
|
|
|
25
26
|
# check if we have a compliance token
|
|
26
27
|
config = Compliance::Configuration.new
|
|
27
|
-
|
|
28
|
+
if config['token'].nil?
|
|
29
|
+
fail Inspec::FetcherFailure, <<EOF
|
|
30
|
+
|
|
31
|
+
Cannot fetch #{uri} because your compliance token has not been
|
|
32
|
+
configured.
|
|
33
|
+
|
|
34
|
+
Please login using
|
|
35
|
+
|
|
36
|
+
inspec compliance login https://your_compliance_server --user admin --insecure --token 'PASTE TOKEN HERE'
|
|
37
|
+
EOF
|
|
38
|
+
end
|
|
28
39
|
|
|
29
40
|
# verifies that the target e.g base/ssh exists
|
|
30
41
|
profile = uri.host + uri.path
|
|
31
|
-
Compliance::API.exist?(config, profile)
|
|
42
|
+
if !Compliance::API.exist?(config, profile)
|
|
43
|
+
fail Inpsec::FetcherFailure, "The compliance profile #{profile} was not found on the configured compliance server"
|
|
44
|
+
end
|
|
32
45
|
new(target_url(profile, config), config)
|
|
33
46
|
rescue URI::Error => _e
|
|
34
47
|
nil
|
data/lib/fetchers/git.rb
CHANGED
data/lib/fetchers/local.rb
CHANGED
|
@@ -55,8 +55,23 @@ module Fetchers
|
|
|
55
55
|
@target
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
def writable?
|
|
59
|
+
File.directory?(@target)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def cache_key
|
|
63
|
+
sha256.to_s
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def sha256
|
|
67
|
+
return nil if File.directory?(@target)
|
|
68
|
+
@archive_shasum ||= Digest::SHA256.hexdigest File.read(@target)
|
|
69
|
+
end
|
|
70
|
+
|
|
58
71
|
def resolved_source
|
|
59
|
-
{ path: @target }
|
|
72
|
+
h = { path: @target }
|
|
73
|
+
h[:sha256] = sha256 if sha256
|
|
74
|
+
h
|
|
60
75
|
end
|
|
61
76
|
end
|
|
62
77
|
end
|