inspec 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (247) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rubocop.yml +65 -0
  4. data/.travis.yml +23 -0
  5. data/CHANGELOG.md +38 -0
  6. data/Gemfile +33 -0
  7. data/LICENSE +201 -0
  8. data/MAINTAINERS.md +28 -0
  9. data/MAINTAINERS.toml +42 -0
  10. data/README.md +257 -0
  11. data/Rakefile +47 -0
  12. data/bin/inspec +109 -0
  13. data/docs/ctl_inspec.rst +195 -0
  14. data/docs/dsl_inspec.rst +182 -0
  15. data/docs/readme.rst +100 -0
  16. data/docs/resources.rst +4319 -0
  17. data/docs/template.rst +51 -0
  18. data/examples/test-kitchen/.kitchen.yml +20 -0
  19. data/examples/test-kitchen/Berksfile +3 -0
  20. data/examples/test-kitchen/Gemfile +21 -0
  21. data/examples/test-kitchen/README.md +27 -0
  22. data/examples/test-kitchen/metadata.rb +7 -0
  23. data/examples/test-kitchen/recipes/default.rb +6 -0
  24. data/examples/test-kitchen/recipes/nginx.rb +30 -0
  25. data/examples/test-kitchen/test/integration/default/web_spec.rb +28 -0
  26. data/inspec.gemspec +30 -0
  27. data/lib/inspec.rb +20 -0
  28. data/lib/inspec/backend.rb +42 -0
  29. data/lib/inspec/dsl.rb +151 -0
  30. data/lib/inspec/log.rb +34 -0
  31. data/lib/inspec/metadata.rb +79 -0
  32. data/lib/inspec/plugins.rb +9 -0
  33. data/lib/inspec/plugins/resource.rb +62 -0
  34. data/lib/inspec/profile.rb +138 -0
  35. data/lib/inspec/profile_context.rb +170 -0
  36. data/lib/inspec/resource.rb +76 -0
  37. data/lib/inspec/rspec_json_formatter.rb +27 -0
  38. data/lib/inspec/rule.rb +170 -0
  39. data/lib/inspec/runner.rb +154 -0
  40. data/lib/inspec/shell.rb +66 -0
  41. data/lib/inspec/targets.rb +9 -0
  42. data/lib/inspec/targets/core.rb +27 -0
  43. data/lib/inspec/targets/dir.rb +67 -0
  44. data/lib/inspec/targets/file.rb +29 -0
  45. data/lib/inspec/targets/folder.rb +43 -0
  46. data/lib/inspec/targets/tar.rb +34 -0
  47. data/lib/inspec/targets/url.rb +39 -0
  48. data/lib/inspec/targets/zip.rb +47 -0
  49. data/lib/inspec/version.rb +7 -0
  50. data/lib/matchers/matchers.rb +221 -0
  51. data/lib/resources/apache.rb +29 -0
  52. data/lib/resources/apache_conf.rb +113 -0
  53. data/lib/resources/apt.rb +140 -0
  54. data/lib/resources/audit_policy.rb +63 -0
  55. data/lib/resources/auditd_conf.rb +56 -0
  56. data/lib/resources/auditd_rules.rb +53 -0
  57. data/lib/resources/bond.rb +65 -0
  58. data/lib/resources/bridge.rb +114 -0
  59. data/lib/resources/command.rb +57 -0
  60. data/lib/resources/csv.rb +32 -0
  61. data/lib/resources/directory.rb +15 -0
  62. data/lib/resources/etc_group.rb +150 -0
  63. data/lib/resources/file.rb +110 -0
  64. data/lib/resources/gem.rb +46 -0
  65. data/lib/resources/group.rb +132 -0
  66. data/lib/resources/host.rb +143 -0
  67. data/lib/resources/inetd_conf.rb +56 -0
  68. data/lib/resources/interface.rb +127 -0
  69. data/lib/resources/iptables.rb +65 -0
  70. data/lib/resources/json.rb +64 -0
  71. data/lib/resources/kernel_module.rb +40 -0
  72. data/lib/resources/kernel_parameter.rb +55 -0
  73. data/lib/resources/limits_conf.rb +55 -0
  74. data/lib/resources/login_def.rb +60 -0
  75. data/lib/resources/mysql.rb +81 -0
  76. data/lib/resources/mysql_conf.rb +116 -0
  77. data/lib/resources/mysql_session.rb +52 -0
  78. data/lib/resources/npm.rb +44 -0
  79. data/lib/resources/ntp_conf.rb +58 -0
  80. data/lib/resources/oneget.rb +63 -0
  81. data/lib/resources/os.rb +22 -0
  82. data/lib/resources/os_env.rb +34 -0
  83. data/lib/resources/package.rb +169 -0
  84. data/lib/resources/parse_config.rb +75 -0
  85. data/lib/resources/passwd.rb +93 -0
  86. data/lib/resources/pip.rb +75 -0
  87. data/lib/resources/port.rb +296 -0
  88. data/lib/resources/postgres.rb +37 -0
  89. data/lib/resources/postgres_conf.rb +87 -0
  90. data/lib/resources/postgres_session.rb +59 -0
  91. data/lib/resources/processes.rb +57 -0
  92. data/lib/resources/registry_key.rb +54 -0
  93. data/lib/resources/script.rb +34 -0
  94. data/lib/resources/security_policy.rb +73 -0
  95. data/lib/resources/service.rb +379 -0
  96. data/lib/resources/ssh_conf.rb +75 -0
  97. data/lib/resources/user.rb +374 -0
  98. data/lib/resources/windows_feature.rb +77 -0
  99. data/lib/resources/yaml.rb +23 -0
  100. data/lib/resources/yum.rb +154 -0
  101. data/lib/utils/convert.rb +12 -0
  102. data/lib/utils/detect.rb +15 -0
  103. data/lib/utils/find_files.rb +36 -0
  104. data/lib/utils/hash.rb +13 -0
  105. data/lib/utils/modulator.rb +12 -0
  106. data/lib/utils/parser.rb +61 -0
  107. data/lib/utils/simpleconfig.rb +115 -0
  108. data/tasks/maintainers.rb +213 -0
  109. data/test/docker_run.rb +156 -0
  110. data/test/docker_test.rb +51 -0
  111. data/test/helper.rb +200 -0
  112. data/test/integration/.kitchen.yml +42 -0
  113. data/test/integration/Berksfile +4 -0
  114. data/test/integration/cookbooks/os_prepare/metadata.rb +8 -0
  115. data/test/integration/cookbooks/os_prepare/recipes/apt.rb +20 -0
  116. data/test/integration/cookbooks/os_prepare/recipes/default.rb +9 -0
  117. data/test/integration/cookbooks/os_prepare/recipes/file.rb +21 -0
  118. data/test/integration/cookbooks/os_prepare/recipes/package.rb +26 -0
  119. data/test/integration/default/_debug_spec.rb +1 -0
  120. data/test/integration/default/apt_spec.rb +42 -0
  121. data/test/integration/default/file_spec.rb +109 -0
  122. data/test/integration/default/group_spec.rb +32 -0
  123. data/test/integration/default/kernel_module_spec.rb +17 -0
  124. data/test/integration/default/kernel_parameter_spec.rb +56 -0
  125. data/test/integration/default/package_spec.rb +11 -0
  126. data/test/integration/default/service_spec.rb +28 -0
  127. data/test/integration/default/user_spec.rb +44 -0
  128. data/test/resource/command_test.rb +33 -0
  129. data/test/resource/dsl_test.rb +45 -0
  130. data/test/resource/file_test.rb +130 -0
  131. data/test/resource/ssh_config.rb +9 -0
  132. data/test/resource/sshd_config.rb +9 -0
  133. data/test/test-extra.yaml +11 -0
  134. data/test/test.yaml +11 -0
  135. data/test/unit/mock/cmd/Get-NetAdapter +24 -0
  136. data/test/unit/mock/cmd/GetUserAccount +33 -0
  137. data/test/unit/mock/cmd/GetWin32Group +23 -0
  138. data/test/unit/mock/cmd/PATH +1 -0
  139. data/test/unit/mock/cmd/Resolve-DnsName +26 -0
  140. data/test/unit/mock/cmd/Test-NetConnection +4 -0
  141. data/test/unit/mock/cmd/auditctl +7 -0
  142. data/test/unit/mock/cmd/auditpol +2 -0
  143. data/test/unit/mock/cmd/brew-info-jq +1 -0
  144. data/test/unit/mock/cmd/chage-l-root +7 -0
  145. data/test/unit/mock/cmd/dpkg-s-curl +21 -0
  146. data/test/unit/mock/cmd/dscl +5 -0
  147. data/test/unit/mock/cmd/etc-apt +7 -0
  148. data/test/unit/mock/cmd/find-etc-rc-d-name-S +12 -0
  149. data/test/unit/mock/cmd/find-net-interface +9 -0
  150. data/test/unit/mock/cmd/gem-list-local-a-q-rubocop +1 -0
  151. data/test/unit/mock/cmd/get-net-tcpconnection +24 -0
  152. data/test/unit/mock/cmd/get-netadapter-binding-bridge +4 -0
  153. data/test/unit/mock/cmd/get-package-firefox +30 -0
  154. data/test/unit/mock/cmd/get-package-ruby +18 -0
  155. data/test/unit/mock/cmd/get-service-dhcp +10 -0
  156. data/test/unit/mock/cmd/get-windows-feature +7 -0
  157. data/test/unit/mock/cmd/getent-hosts-example.com +1 -0
  158. data/test/unit/mock/cmd/getent-passwd-root +1 -0
  159. data/test/unit/mock/cmd/id-chartmann +1 -0
  160. data/test/unit/mock/cmd/id-root +1 -0
  161. data/test/unit/mock/cmd/initctl-show-config-ssh +3 -0
  162. data/test/unit/mock/cmd/initctl-status-ssh +1 -0
  163. data/test/unit/mock/cmd/iptables-s +6 -0
  164. data/test/unit/mock/cmd/launchctl-list +3 -0
  165. data/test/unit/mock/cmd/ls-1-etc-init.d +2 -0
  166. data/test/unit/mock/cmd/ls-sys-class-net-br +2 -0
  167. data/test/unit/mock/cmd/lsmod +2 -0
  168. data/test/unit/mock/cmd/lsof-np-itcp +4 -0
  169. data/test/unit/mock/cmd/netstat-tulpen +5 -0
  170. data/test/unit/mock/cmd/npm-ls-g--json-bower +9 -0
  171. data/test/unit/mock/cmd/pacman-qi-curl +21 -0
  172. data/test/unit/mock/cmd/ping-example.com +6 -0
  173. data/test/unit/mock/cmd/pip-show-jinja2 +11 -0
  174. data/test/unit/mock/cmd/ps-aux +3 -0
  175. data/test/unit/mock/cmd/pw-usershow-root-7 +1 -0
  176. data/test/unit/mock/cmd/reg_schedule +1 -0
  177. data/test/unit/mock/cmd/rpm-qia-curl +24 -0
  178. data/test/unit/mock/cmd/sbin_sysctl +1 -0
  179. data/test/unit/mock/cmd/secedit-export +7 -0
  180. data/test/unit/mock/cmd/service-e +2 -0
  181. data/test/unit/mock/cmd/service-sendmail-onestatus +3 -0
  182. data/test/unit/mock/cmd/service-sshd-status +1 -0
  183. data/test/unit/mock/cmd/sockstat +5 -0
  184. data/test/unit/mock/cmd/success +0 -0
  185. data/test/unit/mock/cmd/systemctl-show-all-sshd +6 -0
  186. data/test/unit/mock/cmd/win32_product +8 -0
  187. data/test/unit/mock/cmd/yum-repolist-all +52 -0
  188. data/test/unit/mock/files/auditd.conf +4 -0
  189. data/test/unit/mock/files/bond0 +37 -0
  190. data/test/unit/mock/files/etcgroup +3 -0
  191. data/test/unit/mock/files/example.csv +6 -0
  192. data/test/unit/mock/files/inetd.conf +2 -0
  193. data/test/unit/mock/files/kitchen.yml +7 -0
  194. data/test/unit/mock/files/limits.conf +5 -0
  195. data/test/unit/mock/files/login.defs +5 -0
  196. data/test/unit/mock/files/mysql.conf +8 -0
  197. data/test/unit/mock/files/mysql2.conf +2 -0
  198. data/test/unit/mock/files/ntp.conf +5 -0
  199. data/test/unit/mock/files/passwd +2 -0
  200. data/test/unit/mock/files/policyfile.lock.json +12 -0
  201. data/test/unit/mock/files/ssh_config +5 -0
  202. data/test/unit/mock/files/sshd_config +7 -0
  203. data/test/unit/mock/profiles/empty/metadata.rb +0 -0
  204. data/test/unit/mock/profiles/metadata/metadata.rb +1 -0
  205. data/test/unit/profile_context_test.rb +140 -0
  206. data/test/unit/profile_test.rb +49 -0
  207. data/test/unit/resources/apt_test.rb +46 -0
  208. data/test/unit/resources/audit_policy_test.rb +13 -0
  209. data/test/unit/resources/auditd_conf_test.rb +15 -0
  210. data/test/unit/resources/auditd_rules_test.rb +21 -0
  211. data/test/unit/resources/bond_test.rb +24 -0
  212. data/test/unit/resources/bridge_test.rb +56 -0
  213. data/test/unit/resources/csv_test.rb +35 -0
  214. data/test/unit/resources/etc_group_test.rb +37 -0
  215. data/test/unit/resources/gem_test.rb +20 -0
  216. data/test/unit/resources/group_test.rb +96 -0
  217. data/test/unit/resources/host_test.rb +38 -0
  218. data/test/unit/resources/inetd_conf_test.rb +15 -0
  219. data/test/unit/resources/interface_test.rb +54 -0
  220. data/test/unit/resources/iptables_test.rb +30 -0
  221. data/test/unit/resources/json_test.rb +36 -0
  222. data/test/unit/resources/kernel_module_test.rb +23 -0
  223. data/test/unit/resources/kernel_parameter_test.rb +13 -0
  224. data/test/unit/resources/limits_conf_test.rb +14 -0
  225. data/test/unit/resources/login_def_test.rb +16 -0
  226. data/test/unit/resources/mysql_conf_test.rb +14 -0
  227. data/test/unit/resources/npm_test.rb +20 -0
  228. data/test/unit/resources/ntp_conf_test.rb +16 -0
  229. data/test/unit/resources/oneget_test.rb +45 -0
  230. data/test/unit/resources/os_env_test.rb +13 -0
  231. data/test/unit/resources/package_test.rb +51 -0
  232. data/test/unit/resources/passwd_test.rb +24 -0
  233. data/test/unit/resources/pip_test.rb +15 -0
  234. data/test/unit/resources/port_test.rb +46 -0
  235. data/test/unit/resources/processes_test.rb +32 -0
  236. data/test/unit/resources/registry_key_test.rb +19 -0
  237. data/test/unit/resources/script_test.rb +19 -0
  238. data/test/unit/resources/security_policy_test.rb +16 -0
  239. data/test/unit/resources/service_test.rb +116 -0
  240. data/test/unit/resources/ssh_conf_test.rb +33 -0
  241. data/test/unit/resources/user_test.rb +93 -0
  242. data/test/unit/resources/windows_feature.rb +17 -0
  243. data/test/unit/resources/yaml_test.rb +34 -0
  244. data/test/unit/resources/yum_test.rb +68 -0
  245. data/test/unit/simpleconfig_test.rb +80 -0
  246. data/test/unit/utils/content_parser_test.rb +30 -0
  247. metadata +555 -0
@@ -0,0 +1,213 @@
1
+ # encoding: utf-8
2
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'rake'
19
+
20
+ SOURCE = File.join(File.dirname(__FILE__), '..', 'MAINTAINERS.toml')
21
+ TARGET = File.join(File.dirname(__FILE__), '..', 'MAINTAINERS.md')
22
+
23
+ # The list of repositories that teams should own
24
+ REPOSITORIES = ['chef/inspec']
25
+
26
+ begin
27
+ require 'tomlrb'
28
+ require 'octokit'
29
+ require 'pp'
30
+ task default: :generate
31
+
32
+ namespace :maintainers do
33
+ desc 'Generate MarkDown version of MAINTAINERS file'
34
+ task :generate do
35
+ maintainers = Tomlrb.load_file SOURCE
36
+ out = "<!-- This is a generated file. Please do not edit directly -->\n\n"
37
+ out << "<!-- Modify MAINTAINERS.toml and run `rake maintainers:generate` to regenerate. -->\n\n"
38
+ out << '# ' + maintainers['Preamble']['title'] + "\n\n"
39
+ out << maintainers['Preamble']['text'] + "\n"
40
+ out << components(maintainers['people'], maintainers['Org']['Components'])
41
+ File.open(TARGET, 'w') { |fn|
42
+ fn.write out
43
+ }
44
+ end
45
+
46
+ desc 'Synchronize GitHub teams'
47
+ task :synchronize do
48
+ Octokit.auto_paginate = true
49
+ github_teams
50
+ prepare_teams(source['Org']['Components'].dup)
51
+ sync_teams!
52
+ end
53
+ end
54
+
55
+ def github
56
+ @github ||= Octokit::Client.new(netrc: true)
57
+ end
58
+
59
+ def source
60
+ @source ||= Tomlrb.load_file SOURCE
61
+ end
62
+
63
+ def teams
64
+ @teams ||= { 'inspec-maintainers' => { 'title' => 'Maintainers of the InSpec toolset' } }
65
+ end
66
+
67
+ def add_members(team, name)
68
+ teams['inspec-maintainers']['members'] ||= []
69
+ teams['inspec-maintainers']['members'] << name
70
+ teams[team] ||= {}
71
+ teams[team]['members'] ||= []
72
+ teams[team]['members'] << name
73
+ end
74
+
75
+ def set_team_title(team, title)
76
+ teams[team] ||= {}
77
+ teams[team]['title'] = title
78
+ end
79
+
80
+ def gh_teams
81
+ @gh_teams ||= {}
82
+ end
83
+
84
+ # we have to resolve team names to ids. While we're at it, we can get the privacy
85
+ # setting, so we know whether we need to update it
86
+ def github_teams
87
+ github.org_teams('chef').each do |team|
88
+ gh_teams[team[:slug]] = { 'id' => team[:id], 'privacy' => team[:privacy] }
89
+ end
90
+ end
91
+
92
+ def get_github_team(team)
93
+ github.team_members(gh_teams[team]['id']).map do |member|
94
+ member[:login]
95
+ end.sort.uniq.map(&:downcase)
96
+ rescue
97
+ []
98
+ end
99
+
100
+ def create_team(team)
101
+ puts "creating new github team: #{team} with title: #{teams[team]['title']} "
102
+ t = github.create_team('chef', name: team, description: teams[team]['title'],
103
+ privacy: 'closed', repo_names: REPOSITORIES,
104
+ accept: 'application/vnd.github.ironman-preview+json')
105
+ gh_teams[team] = { 'id' => t[:id], 'privacy' => t[:privacy] }
106
+ end
107
+
108
+ def compare_teams(current, desired)
109
+ # additions are the subtraction of the current state from the desired state
110
+ # deletions are the subtraction of the desired state from the current state
111
+ [desired - current, current - desired]
112
+ end
113
+
114
+ def prepare_teams(cmp)
115
+ %w{text paths}.each { |k| cmp.delete(k) }
116
+ if cmp.key?('team')
117
+ team = cmp.delete('team')
118
+ add_members(team, cmp.delete('lieutenant')) if cmp.key?('lieutenant')
119
+ add_members(team, cmp.delete('maintainers')) if cmp.key?('maintainers')
120
+ set_team_title(team, cmp.delete('title'))
121
+ else
122
+ %w{maintainers lieutenant title}.each { |k| cmp.delete(k) }
123
+ end
124
+ cmp.each { |_k, v| prepare_teams(v) }
125
+ end
126
+
127
+ def update_team(team, additions, deletions)
128
+ create_team(team) unless gh_teams.key?(team)
129
+ update_team_privacy(team)
130
+ add_team_members(team, additions)
131
+ remove_team_members(team, deletions)
132
+ rescue
133
+ puts "failed for #{team}"
134
+ end
135
+
136
+ def update_team_privacy(_team)
137
+ # return
138
+ # return if gh_teams[team]['privacy'] == 'closed'
139
+ # puts "Setting #{team} privacy to closed from #{gh_teams[team]['privacy']}"
140
+ # github.update_team(gh_teams[team]['id'], privacy: 'closed',
141
+ # accept: 'application/vnd.github.ironman-preview+json')
142
+ end
143
+
144
+ def add_team_members(team, additions)
145
+ additions.each do |member|
146
+ puts "Adding #{member} to #{team}"
147
+ github.add_team_membership(gh_teams[team]['id'], member, role: 'member',
148
+ accept: 'application/vnd.github.ironman-preview+json')
149
+ end
150
+ end
151
+
152
+ def remove_team_members(team, deletions)
153
+ deletions.each do |member|
154
+ puts "Removing #{member} from #{team}"
155
+ github.remove_team_membership(gh_teams[team]['id'], member,
156
+ accept: 'application/vnd.github.ironman-preview+json')
157
+ end
158
+ end
159
+
160
+ def sync_teams!
161
+ teams.each do |name, details|
162
+ current = get_github_team(name)
163
+ desired = details['members'].flatten.sort.uniq.map(&:downcase)
164
+ additions, deletions = compare_teams(current, desired)
165
+ update_team(name, additions, deletions)
166
+ end
167
+ end
168
+
169
+ def get_person(person)
170
+ source['people'][person]
171
+ end
172
+
173
+ def components(list, cmp)
174
+ out = '## ' + cmp.delete('title') + "\n\n"
175
+ out << cmp.delete('text') + "\n" if cmp.key?('text')
176
+ out << "To mention the team, use @chef/#{cmp.delete('team')}\n\n" if cmp.key?('team')
177
+ if cmp.key?('lieutenant')
178
+ out << "### Lieutenant\n\n"
179
+ out << person(list, cmp.delete('lieutenant')) + "\n\n"
180
+ end
181
+ out << maintainers(list, cmp.delete('maintainers')) + "\n" if cmp.key?('maintainers')
182
+ cmp.delete('paths')
183
+ cmp.each { |_k, v| out << components(list, v) }
184
+ out
185
+ end
186
+
187
+ def maintainers(list, people)
188
+ o = "### Maintainers\n\n"
189
+ people.each do |p|
190
+ o << person(list, p) + "\n"
191
+ end
192
+ o
193
+ end
194
+
195
+ # rubocop:disable Metrics/AbcSize
196
+ def person(list, person)
197
+ if list[person].key?('GitHub')
198
+ out = "* [#{list[person]['Name']}](https://github.com/#{list[person]['GitHub']})"
199
+ else
200
+ out = "* #{list[person]['Name']}"
201
+ end
202
+ out << "\n * IRC - #{list[person]['IRC']}" if list[person].key?('IRC')
203
+ out << "\n * [@#{list[person]['Twitter']}](https://twitter.com/#{list[person]['Twitter']})" if list[person].key?('Twitter')
204
+ out << "\n * [#{list[person]['email']}](mailto:#{list[person]['email']})" if list[person].key?('email')
205
+ out << "\n * #{list[person]['phone']}" if list[person].key?('phone')
206
+ out << "\n * [ServerFault](#{list[person]['ServerFault']})" if list[person].key?('ServerFault')
207
+ out
208
+ end
209
+ # rubocop:enable all
210
+
211
+ rescue LoadError
212
+ STDERR.puts "\n*** TomlRb not available.\n\n"
213
+ end
@@ -0,0 +1,156 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+
4
+ require 'docker'
5
+ require 'yaml'
6
+ require 'concurrent'
7
+
8
+ class DockerRunner
9
+ def initialize(conf_path = nil)
10
+ @conf_path = conf_path ||
11
+ ENV['config']
12
+
13
+ if @conf_path.nil?
14
+ fail "You must provide a configuration file with docker boxes"
15
+ end
16
+
17
+ unless File.file?(@conf_path)
18
+ fail "Can't find configuration in #{@conf_path}"
19
+ end
20
+
21
+ @conf = YAML.load_file(@conf_path)
22
+ if @conf.nil? or @conf.empty?
23
+ fail "Can't read coniguration in #{@conf_path}"
24
+ end
25
+ if @conf['images'].nil?
26
+ fail "You must configure test images in your #{@conf_path}"
27
+ end
28
+
29
+ @images = docker_images_by_tag
30
+ @image_pull_tickets = Concurrent::Semaphore.new(2)
31
+ @docker_run_tickets = Concurrent::Semaphore.new(5)
32
+ end
33
+
34
+ def run_all(&block)
35
+ fail 'You must provide a block for run_all' unless block_given?
36
+
37
+ promises = @conf['images'].map do |id|
38
+ run_on_target(id, &block)
39
+ end
40
+
41
+ # wait for all tests to be finished
42
+ sleep(0.1) until promises.all?(&:fulfilled?)
43
+
44
+ # return resulting values
45
+ promises.map(&:value)
46
+ end
47
+
48
+ def run_on_target(name, &block)
49
+ pr = Concurrent::Promise.new {
50
+ begin
51
+ container = start_container(name)
52
+ res = block.call(name, container)
53
+ # special rescue block to handle not implemented error
54
+ rescue NotImplementedError => err
55
+ raise err.message
56
+ end
57
+ # always stop the container
58
+ stop_container(container)
59
+ res
60
+ }.execute
61
+
62
+ # failure handling
63
+ pr.rescue do |err|
64
+ msg = "\033[31;1m#{err.message}\033[0m"
65
+ puts msg
66
+ msg + "\n" + err.backtrace.join("\n")
67
+ end
68
+ end
69
+
70
+ def provision_image(image, prov, files)
71
+ return image if prov['script'].nil?
72
+ path = File.join(File.dirname(@conf_path), prov['script'])
73
+ unless File.file?(path)
74
+ puts "Can't find script file #{path}"
75
+ return image
76
+ end
77
+ puts " script #{path}"
78
+ dst = "/bootstrap#{files.length}.sh"
79
+ files.push(dst)
80
+ image.insert_local('localPath' => path, 'outputPath' => dst)
81
+ end
82
+
83
+ def bootstrap_image(name, image)
84
+ files = []
85
+ provisions = Array(@conf['provision'])
86
+ puts "--> provision docker #{name}" unless provisions.empty?
87
+ provisions.each do |prov|
88
+ image = provision_image(image, prov, files)
89
+ end
90
+ [image, files]
91
+ end
92
+
93
+ def start_container(name, version = nil)
94
+ unless name.include?(':')
95
+ version ||= 'latest'
96
+ name = "#{name}:#{version}"
97
+ end
98
+ puts "--> schedule docker #{name}"
99
+
100
+ image = @images[name]
101
+ if image.nil?
102
+ puts "\033[35;1m--> pull docker images #{name} "\
103
+ "(this may take a while)\033[0m"
104
+
105
+ @image_pull_tickets.acquire(1)
106
+ puts "... start pull image #{name}"
107
+ image = Docker::Image.create('fromImage' => name)
108
+ @image_pull_tickets.release(1)
109
+
110
+ unless image.nil?
111
+ puts "\033[35;1m--> pull docker images finished for #{name}\033[0m"
112
+ end
113
+ end
114
+
115
+ fail "Can't find nor pull docker image #{name}" if image.nil?
116
+
117
+ image, scripts = bootstrap_image(name, image)
118
+
119
+ @docker_run_tickets.acquire(1)
120
+
121
+ puts "--> start docker #{name}"
122
+ container = Docker::Container.create(
123
+ 'Cmd' => %w{sleep 3600},
124
+ 'Image' => image.id,
125
+ 'OpenStdin' => true,
126
+ )
127
+ container.start
128
+
129
+ scripts.each do |script|
130
+ container.exec(%w{chmod +x}.push(script))
131
+ container.exec(%w{sh -c}.push(script))
132
+ end
133
+
134
+ container
135
+ end
136
+
137
+ def stop_container(container)
138
+ @docker_run_tickets.release(1)
139
+ puts "--> killrm docker #{container.id}"
140
+ container.kill
141
+ container.delete(force: true)
142
+ end
143
+
144
+ private
145
+
146
+ # get all docker image tags
147
+ def docker_images_by_tag
148
+ images = {}
149
+ Docker::Image.all.map do |img|
150
+ Array(img.info['RepoTags']).each do |tag|
151
+ images[tag] = img
152
+ end
153
+ end
154
+ images
155
+ end
156
+ end
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+
4
+ require_relative 'docker_run'
5
+ require_relative '../lib/inspec'
6
+
7
+ tests = ARGV
8
+ if tests.empty?
9
+ puts 'Nothing to do.'
10
+ exit 0
11
+ end
12
+
13
+ class DockerTester
14
+ def initialize(tests)
15
+ @tests = tests
16
+ @docker = DockerRunner.new
17
+ end
18
+
19
+ def run
20
+ puts ['Running tests:', @tests].flatten.join("\n- ")
21
+ puts ''
22
+
23
+ conf = RSpec.configuration
24
+ reporter = conf.reporter
25
+ results = nil
26
+
27
+ # start reporting loop
28
+ reporter.report(0) do |report|
29
+ results = @docker.run_all do |name, container|
30
+ status = test_container(container, report)
31
+ status.all? ? nil : "Failed to run tests on #{name}"
32
+ end
33
+ end
34
+
35
+ # check if we were successful
36
+ failures = results.compact
37
+ failures.each { |f| puts "\033[31;1m#{f}\033[0m\n\n" }
38
+ failures.empty? or fail 'Test failures'
39
+ end
40
+
41
+ def test_container(container, report)
42
+ puts "--> run test on docker #{container.id}"
43
+ opts = { 'target' => "docker://#{container.id}" }
44
+ runner = Inspec::Runner.new(opts)
45
+ runner.add_tests(@tests)
46
+ tests = runner.tests.ordered_example_groups
47
+ tests.map { |g| g.run(report) }
48
+ end
49
+ end
50
+
51
+ DockerTester.new(tests).run
data/test/helper.rb ADDED
@@ -0,0 +1,200 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+
5
+ require 'minitest/autorun'
6
+ require 'minitest/spec'
7
+
8
+ require 'simplecov'
9
+ SimpleCov.start do
10
+ add_filter '/test/'
11
+ add_group 'Resources', 'lib/resources'
12
+ add_group 'Matchers', 'lib/matchers'
13
+ add_group 'Backends', 'lib/inspec/backend'
14
+ end
15
+
16
+ require 'inspec/resource'
17
+ require 'inspec/backend'
18
+ require 'inspec/profile'
19
+
20
+ class MockLoader
21
+ # pass the os identifier to emulate a specific operating system
22
+ def initialize(os = nil)
23
+ # collects emulation operating systems
24
+ @operating_systems = {
25
+ arch: { family: 'arch', release: nil, arch: nil },
26
+ centos5: { family: 'redhat', release: '5.11', arch: 'x86_64' },
27
+ centos6: { family: 'redhat', release: '6.6', arch: 'x86_64' },
28
+ centos7: { family: 'redhat', release: '7.1.1503', arch: 'x86_64' },
29
+ debian6: { family: 'debian', release: '6', arch: 'x86_64' },
30
+ debian7: { family: 'debian', release: '7', arch: 'x86_64' },
31
+ debian8: { family: 'debian', release: '8', arch: 'x86_64' },
32
+ freebsd9: { family: 'freebsd', release: '9', arch: 'amd64' },
33
+ freebsd10: { family: 'freebsd', release: '10', arch: 'amd64' },
34
+ osx104: { family: 'darwin', release: '10.10.4', arch: nil, name: 'mac_os_x' },
35
+ ubuntu1204: { family: 'ubuntu', release: '12.04', arch: 'x86_64' },
36
+ ubuntu1404: { family: 'ubuntu', release: '14.04', arch: 'x86_64' },
37
+ ubuntu1504: { family: 'ubuntu', release: '15.04', arch: 'x86_64' },
38
+ windows: { family: 'windows', release: nil, arch: nil },
39
+ undefined: { family: nil, release: nil, arch: nil },
40
+ }
41
+
42
+ # selects operating system
43
+ @os = @operating_systems[os || :ubuntu1404]
44
+ end
45
+
46
+ def backend
47
+ return @backend if defined?(@backend)
48
+ scriptpath = ::File.realpath(::File.dirname(__FILE__))
49
+
50
+ # create mock backend
51
+ @backend = Inspec::Backend.create({ backend: :mock })
52
+ mock = @backend.backend
53
+
54
+ # set os emulation
55
+ mock.mock_os(@os)
56
+
57
+ # create all mock files
58
+ local = Train.create('local').connection
59
+ mockfile = lambda { |x|
60
+ path = ::File.join(scriptpath, '/unit/mock/files', x)
61
+ local.file(path)
62
+ }
63
+ mockdir = lambda { |x|
64
+ md = Object.new
65
+
66
+ class << md
67
+ attr_accessor :isdir
68
+ end
69
+ md.isdir = x
70
+
71
+ def md.directory?
72
+ isdir
73
+ end
74
+ md
75
+ }
76
+
77
+ mock.files = {
78
+ '/proc/net/bonding/bond0' => mockfile.call('bond0'),
79
+ '/etc/ssh/ssh_config' => mockfile.call('ssh_config'),
80
+ '/etc/ssh/sshd_config' => mockfile.call('sshd_config'),
81
+ '/etc/passwd' => mockfile.call('passwd'),
82
+ '/etc/ntp.conf' => mockfile.call('ntp.conf'),
83
+ '/etc/login.defs' => mockfile.call('login.defs'),
84
+ '/etc/security/limits.conf' => mockfile.call('limits.conf'),
85
+ '/etc/inetd.conf' => mockfile.call('inetd.conf'),
86
+ '/etc/group' => mockfile.call('etcgroup'),
87
+ '/etc/audit/auditd.conf' => mockfile.call('auditd.conf'),
88
+ '/etc/mysql/my.cnf' => mockfile.call('mysql.conf'),
89
+ '/etc/mysql/mysql2.conf' => mockfile.call('mysql2.conf'),
90
+ 'kitchen.yml' => mockfile.call('kitchen.yml'),
91
+ 'example.csv' => mockfile.call('example.csv'),
92
+ 'policyfile.lock.json' => mockfile.call('policyfile.lock.json'),
93
+ '/sys/class/net/br0/bridge' => mockdir.call(true),
94
+ }
95
+
96
+ # create all mock commands
97
+ cmd = lambda {|x|
98
+ stdout = ::File.read(::File.join(scriptpath, '/unit/mock/cmd/'+x))
99
+ mock.mock_command('', stdout, '', 0)
100
+ }
101
+
102
+ empty = lambda {
103
+ mock.mock_command('', '', '', 0)
104
+ }
105
+
106
+ mock.commands = {
107
+ 'ps aux' => cmd.call('ps-aux'),
108
+ 'type win_secpol.cfg' => cmd.call('secedit-export'),
109
+ 'secedit /export /cfg win_secpol.cfg' => cmd.call('success'),
110
+ 'del win_secpol.cfg' => cmd.call('success'),
111
+ 'su - root -c \'echo $PATH\'' => cmd.call('PATH'),
112
+ '(Get-Item \'Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Schedule\').GetValue(\'Start\')' => cmd.call('reg_schedule'),
113
+ 'Auditpol /get /subcategory:\'User Account Management\' /r' => cmd.call('auditpol'),
114
+ '/sbin/auditctl -l' => cmd.call('auditctl'),
115
+ 'yum -v repolist all' => cmd.call('yum-repolist-all'),
116
+ 'dpkg -s curl' => cmd.call('dpkg-s-curl'),
117
+ 'rpm -qia curl' => cmd.call('rpm-qia-curl'),
118
+ 'pacman -Qi curl' => cmd.call('pacman-qi-curl'),
119
+ 'gem list --local -a -q ^rubocop$' => cmd.call('gem-list-local-a-q-rubocop'),
120
+ 'npm ls -g --json bower' => cmd.call('npm-ls-g--json-bower'),
121
+ 'pip show jinja2' => cmd.call('pip-show-jinja2'),
122
+ "Get-Package -Name 'Mozilla Firefox' | ConvertTo-Json" => cmd.call('get-package-firefox'),
123
+ "Get-Package -Name 'Ruby 2.1.6-p336-x64' | ConvertTo-Json" => cmd.call('get-package-ruby'),
124
+ "New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Service -Value (Get-Service -Name dhcp| Select-Object -Property Name, DisplayName, Status) -PassThru | Add-Member -MemberType NoteProperty -Name WMI -Value (Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq 'dhcp' -or $_.DisplayName -eq 'dhcp'} | Select-Object -Property StartMode) -PassThru | ConvertTo-Json" => cmd.call('get-service-dhcp'),
125
+ "Get-WindowsFeature | Where-Object {$_.Name -eq 'dhcp' -or $_.DisplayName -eq 'dhcp'} | Select-Object -Property Name,DisplayName,Description,Installed,InstallState | ConvertTo-Json" => cmd.call('get-windows-feature'),
126
+ 'lsmod' => cmd.call('lsmod'),
127
+ '/sbin/sysctl -q -n net.ipv4.conf.all.forwarding' => cmd.call('sbin_sysctl'),
128
+ # ports on windows
129
+ 'Get-NetTCPConnection | Select-Object -Property State, Caption, Description, LocalAddress, LocalPort, RemoteAddress, RemotePort, DisplayName, Status | ConvertTo-Json' => cmd.call('get-net-tcpconnection'),
130
+ # ports on mac
131
+ 'lsof -nP -iTCP -iUDP -sTCP:LISTEN' => cmd.call('lsof-np-itcp'),
132
+ # ports on linux
133
+ 'netstat -tulpen' => cmd.call('netstat-tulpen'),
134
+ # ports on freebsd
135
+ 'sockstat -46l' => cmd.call('sockstat'),
136
+ # packages on windows
137
+ "Get-WmiObject -Class Win32_Product | Where-Object {$_.Name -eq 'Microsoft Visual C++ 2008 Redistributable - x64 9.0.30729.6161'} | Select-Object -Property Name,Version,Vendor,PackageCode,Caption,Description | ConvertTo-Json" => cmd.call('win32_product'),
138
+ # service status upstart on ubuntu
139
+ 'initctl status ssh' => cmd.call('initctl-status-ssh'),
140
+ # service config for upstart on ubuntu
141
+ 'initctl show-config ssh' => cmd.call('initctl-show-config-ssh'),
142
+ # show ssh service Centos 7
143
+ 'systemctl show --all sshd' => cmd.call('systemctl-show-all-sshd'),
144
+ # services on macos
145
+ 'launchctl list' => cmd.call('launchctl-list'),
146
+ # services on freebsd 10
147
+ 'service -e' => cmd.call('service-e'),
148
+ 'service sendmail onestatus' => cmd.call('service-sendmail-onestatus'),
149
+ # services for system 5 e.g. centos6, debian 6
150
+ 'service sshd status' => cmd.call('service-sshd-status'),
151
+ 'find /etc/rc*.d -name S*' => cmd.call('find-etc-rc-d-name-S'),
152
+ 'ls -1 /etc/init.d/' => cmd.call('ls-1-etc-init.d'),
153
+ # user information for linux
154
+ 'id root' => cmd.call('id-root'),
155
+ 'getent passwd root' => cmd.call('getent-passwd-root'),
156
+ 'chage -l root' => cmd.call('chage-l-root'),
157
+ # user info for mac
158
+ 'id chartmann' => cmd.call('id-chartmann'),
159
+ 'dscl -q . -read /Users/chartmann NFSHomeDirectory PrimaryGroupID RecordName UniqueID UserShell' => cmd.call('dscl'),
160
+ # user info for freebsd
161
+ 'pw usershow root -7' => cmd.call('pw-usershow-root-7'),
162
+ # user info for windows
163
+ '650b6b72a66316418b25421a54afe21a230704558082914c54711904bb10e370' => cmd.call('GetUserAccount'),
164
+ # group info for windows
165
+ 'Get-WmiObject Win32_Group | Select-Object -Property Caption, Domain, Name, SID, LocalAccount | ConvertTo-Json' => cmd.call('GetWin32Group'),
166
+ # network interface
167
+ '9e80f048a1af5a0f6ab8a465e46ea5ed5ba6587e9b5e54a7a0c0a1a02bb6f663' => cmd.call('find-net-interface'),
168
+ 'c33821dece09c8b334e03a5bb9daefdf622007f73af4932605e758506584ec3f' => empty.call,
169
+ 'Get-NetAdapter | Select-Object -Property Name, InterfaceDescription, Status, State, MacAddress, LinkSpeed, ReceiveLinkSpeed, TransmitLinkSpeed, Virtual | ConvertTo-Json' => cmd.call('Get-NetAdapter'),
170
+ # bridge on linux
171
+ 'ls -1 /sys/class/net/br0/brif/' => cmd.call('ls-sys-class-net-br'),
172
+ # bridge on Windows
173
+ 'Get-NetAdapterBinding -ComponentID ms_bridge | Get-NetAdapter | Select-Object -Property Name, InterfaceDescription | ConvertTo-Json' => cmd.call('get-netadapter-binding-bridge'),
174
+ # host for Windows
175
+ 'Resolve-DnsName –Type A microsoft.com | ConvertTo-Json' => cmd.call('Resolve-DnsName'),
176
+ 'Test-NetConnection -ComputerName microsoft.com | Select-Object -Property ComputerName, PingSucceeded | ConvertTo-Json' => cmd.call('Test-NetConnection'),
177
+ # host for Linux
178
+ 'getent hosts example.com' => cmd.call('getent-hosts-example.com'),
179
+ 'ping -w 1 -c 1 example.com' => cmd.call('ping-example.com'),
180
+ # apt
181
+ "find /etc/apt/ -name *.list -exec sh -c 'cat {} || echo -n' \\;" => cmd.call('etc-apt'),
182
+ # iptables
183
+ 'iptables -S' => cmd.call('iptables-s'),
184
+ }
185
+
186
+ @backend
187
+ end
188
+
189
+ # loads a resource class and instantiates the class with the given arguments
190
+ def load_resource(resource, *args)
191
+ # initialize resource with backend and parameters
192
+ @resource_class = Inspec::Resource.registry[resource]
193
+ @resource = @resource_class.new(backend, resource, *args)
194
+ end
195
+ end
196
+
197
+ def load_resource(*args)
198
+ m = MockLoader.new(:ubuntu1404)
199
+ m.send('load_resource', *args)
200
+ end