inspec 1.51.6 → 1.51.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +101 -101
- data/CHANGELOG.md +2915 -2902
- data/Gemfile +53 -53
- data/LICENSE +14 -14
- data/MAINTAINERS.md +31 -31
- data/MAINTAINERS.toml +47 -47
- data/README.md +419 -419
- data/Rakefile +167 -167
- data/bin/inspec +12 -12
- data/docs/.gitignore +2 -2
- data/docs/README.md +40 -40
- data/docs/dsl_inspec.md +258 -258
- data/docs/dsl_resource.md +93 -93
- data/docs/glossary.md +99 -99
- data/docs/habitat.md +191 -191
- data/docs/inspec_and_friends.md +107 -107
- data/docs/matchers.md +165 -165
- data/docs/migration.md +293 -293
- data/docs/plugin_kitchen_inspec.md +49 -49
- data/docs/profiles.md +370 -370
- data/docs/resources/aide_conf.md.erb +78 -78
- data/docs/resources/apache.md.erb +66 -66
- data/docs/resources/apache_conf.md.erb +67 -67
- data/docs/resources/apt.md.erb +70 -70
- data/docs/resources/audit_policy.md.erb +46 -46
- data/docs/resources/auditd.md.erb +78 -78
- data/docs/resources/auditd_conf.md.erb +68 -68
- data/docs/resources/auditd_rules.md.erb +116 -116
- data/docs/resources/bash.md.erb +74 -74
- data/docs/resources/bond.md.erb +89 -89
- data/docs/resources/bridge.md.erb +54 -54
- data/docs/resources/bsd_service.md.erb +65 -65
- data/docs/resources/command.md.erb +137 -137
- data/docs/resources/cpan.md.erb +77 -77
- data/docs/resources/cran.md.erb +63 -63
- data/docs/resources/crontab.md.erb +87 -87
- data/docs/resources/csv.md.erb +53 -53
- data/docs/resources/dh_params.md.erb +216 -216
- data/docs/resources/directory.md.erb +28 -28
- data/docs/resources/docker.md.erb +163 -163
- data/docs/resources/docker_container.md.erb +99 -99
- data/docs/resources/docker_image.md.erb +93 -93
- data/docs/resources/docker_service.md.erb +113 -113
- data/docs/resources/elasticsearch.md.erb +230 -230
- data/docs/resources/etc_fstab.md.erb +124 -124
- data/docs/resources/etc_group.md.erb +74 -74
- data/docs/resources/etc_hosts.md.erb +75 -75
- data/docs/resources/etc_hosts_allow.md.erb +73 -73
- data/docs/resources/etc_hosts_deny.md.erb +73 -73
- data/docs/resources/file.md.erb +512 -512
- data/docs/resources/filesystem.md.erb +40 -40
- data/docs/resources/firewalld.md.erb +105 -105
- data/docs/resources/gem.md.erb +78 -78
- data/docs/resources/group.md.erb +60 -60
- data/docs/resources/grub_conf.md.erb +101 -100
- data/docs/resources/host.md.erb +77 -77
- data/docs/resources/http.md.erb +104 -98
- data/docs/resources/iis_app.md.erb +120 -116
- data/docs/resources/iis_site.md.erb +132 -128
- data/docs/resources/inetd_conf.md.erb +95 -84
- data/docs/resources/ini.md.erb +72 -69
- data/docs/resources/interface.md.erb +55 -46
- data/docs/resources/iptables.md.erb +63 -63
- data/docs/resources/json.md.erb +61 -61
- data/docs/resources/kernel_module.md.erb +106 -106
- data/docs/resources/kernel_parameter.md.erb +58 -58
- data/docs/resources/key_rsa.md.erb +73 -73
- data/docs/resources/launchd_service.md.erb +56 -56
- data/docs/resources/limits_conf.md.erb +66 -66
- data/docs/resources/login_def.md.erb +62 -62
- data/docs/resources/mount.md.erb +68 -68
- data/docs/resources/mssql_session.md.erb +59 -59
- data/docs/resources/mysql_conf.md.erb +98 -98
- data/docs/resources/mysql_session.md.erb +73 -73
- data/docs/resources/nginx.md.erb +78 -78
- data/docs/resources/nginx_conf.md.erb +127 -127
- data/docs/resources/npm.md.erb +59 -59
- data/docs/resources/ntp_conf.md.erb +59 -59
- data/docs/resources/oneget.md.erb +52 -52
- data/docs/resources/oracledb_session.md.erb +51 -51
- data/docs/resources/os.md.erb +140 -140
- data/docs/resources/os_env.md.erb +77 -77
- data/docs/resources/package.md.erb +119 -119
- data/docs/resources/packages.md.erb +66 -66
- data/docs/resources/parse_config.md.erb +102 -102
- data/docs/resources/parse_config_file.md.erb +137 -137
- data/docs/resources/passwd.md.erb +140 -140
- data/docs/resources/pip.md.erb +66 -66
- data/docs/resources/port.md.erb +136 -136
- data/docs/resources/postgres_conf.md.erb +78 -78
- data/docs/resources/postgres_hba_conf.md.erb +92 -92
- data/docs/resources/postgres_ident_conf.md.erb +75 -75
- data/docs/resources/postgres_session.md.erb +68 -68
- data/docs/resources/powershell.md.erb +101 -101
- data/docs/resources/processes.md.erb +107 -107
- data/docs/resources/rabbitmq_config.md.erb +40 -40
- data/docs/resources/registry_key.md.erb +157 -157
- data/docs/resources/runit_service.md.erb +56 -56
- data/docs/resources/security_policy.md.erb +46 -46
- data/docs/resources/service.md.erb +120 -120
- data/docs/resources/shadow.md.erb +143 -143
- data/docs/resources/ssh_config.md.erb +79 -79
- data/docs/resources/sshd_config.md.erb +82 -82
- data/docs/resources/ssl.md.erb +118 -118
- data/docs/resources/sys_info.md.erb +41 -41
- data/docs/resources/systemd_service.md.erb +56 -56
- data/docs/resources/sysv_service.md.erb +56 -56
- data/docs/resources/upstart_service.md.erb +56 -56
- data/docs/resources/user.md.erb +139 -139
- data/docs/resources/users.md.erb +126 -126
- data/docs/resources/vbscript.md.erb +54 -54
- data/docs/resources/virtualization.md.erb +56 -56
- data/docs/resources/windows_feature.md.erb +46 -46
- data/docs/resources/windows_hotfix.md.erb +52 -52
- data/docs/resources/windows_task.md.erb +89 -89
- data/docs/resources/wmi.md.erb +80 -80
- data/docs/resources/x509_certificate.md.erb +150 -150
- data/docs/resources/xinetd_conf.md.erb +155 -155
- data/docs/resources/xml.md.erb +84 -84
- data/docs/resources/yaml.md.erb +68 -68
- data/docs/resources/yum.md.erb +97 -97
- data/docs/resources/zfs_dataset.md.erb +52 -52
- data/docs/resources/zfs_pool.md.erb +46 -46
- data/docs/ruby_usage.md +203 -203
- data/docs/shared/matcher_be.md.erb +1 -1
- data/docs/shared/matcher_cmp.md.erb +43 -43
- data/docs/shared/matcher_eq.md.erb +3 -3
- data/docs/shared/matcher_include.md.erb +1 -1
- data/docs/shared/matcher_match.md.erb +1 -1
- data/docs/shell.md +172 -172
- data/examples/README.md +8 -8
- data/examples/inheritance/README.md +65 -65
- data/examples/inheritance/controls/example.rb +14 -14
- data/examples/inheritance/inspec.yml +15 -15
- data/examples/kitchen-ansible/.kitchen.yml +25 -25
- data/examples/kitchen-ansible/Gemfile +19 -19
- data/examples/kitchen-ansible/README.md +53 -53
- data/examples/kitchen-ansible/files/nginx.repo +6 -6
- data/examples/kitchen-ansible/tasks/main.yml +16 -16
- data/examples/kitchen-ansible/test/integration/default/default.yml +5 -5
- data/examples/kitchen-ansible/test/integration/default/web_spec.rb +28 -28
- data/examples/kitchen-chef/.kitchen.yml +20 -20
- data/examples/kitchen-chef/Berksfile +3 -3
- data/examples/kitchen-chef/Gemfile +19 -19
- data/examples/kitchen-chef/README.md +27 -27
- data/examples/kitchen-chef/metadata.rb +7 -7
- data/examples/kitchen-chef/recipes/default.rb +6 -6
- data/examples/kitchen-chef/recipes/nginx.rb +30 -30
- data/examples/kitchen-chef/test/integration/default/web_spec.rb +28 -28
- data/examples/kitchen-puppet/.kitchen.yml +22 -22
- data/examples/kitchen-puppet/Gemfile +20 -20
- data/examples/kitchen-puppet/Puppetfile +25 -25
- data/examples/kitchen-puppet/README.md +53 -53
- data/examples/kitchen-puppet/manifests/site.pp +33 -33
- data/examples/kitchen-puppet/metadata.json +11 -11
- data/examples/kitchen-puppet/test/integration/default/web_spec.rb +28 -28
- data/examples/meta-profile/README.md +37 -37
- data/examples/meta-profile/controls/example.rb +13 -13
- data/examples/meta-profile/inspec.yml +13 -13
- data/examples/profile-attribute.yml +2 -2
- data/examples/profile-attribute/README.md +14 -14
- data/examples/profile-attribute/controls/example.rb +11 -11
- data/examples/profile-attribute/inspec.yml +8 -8
- data/examples/profile-sensitive/README.md +29 -29
- data/examples/profile-sensitive/controls/sensitive-failures.rb +9 -9
- data/examples/profile-sensitive/controls/sensitive.rb +9 -9
- data/examples/profile-sensitive/inspec.yml +8 -8
- data/examples/profile/README.md +48 -48
- data/examples/profile/controls/example.rb +23 -23
- data/examples/profile/controls/gordon.rb +36 -36
- data/examples/profile/controls/meta.rb +34 -34
- data/examples/profile/inspec.yml +10 -10
- data/examples/profile/libraries/gordon_config.rb +53 -53
- data/inspec.gemspec +47 -47
- data/lib/bundles/README.md +3 -3
- data/lib/bundles/inspec-artifact.rb +7 -7
- data/lib/bundles/inspec-artifact/README.md +1 -1
- data/lib/bundles/inspec-artifact/cli.rb +277 -277
- data/lib/bundles/inspec-compliance.rb +16 -16
- data/lib/bundles/inspec-compliance/.kitchen.yml +20 -20
- data/lib/bundles/inspec-compliance/README.md +185 -185
- data/lib/bundles/inspec-compliance/api.rb +316 -316
- data/lib/bundles/inspec-compliance/api/login.rb +152 -152
- data/lib/bundles/inspec-compliance/bootstrap.sh +41 -41
- data/lib/bundles/inspec-compliance/cli.rb +277 -277
- data/lib/bundles/inspec-compliance/configuration.rb +103 -103
- data/lib/bundles/inspec-compliance/http.rb +86 -86
- data/lib/bundles/inspec-compliance/support.rb +36 -36
- data/lib/bundles/inspec-compliance/target.rb +98 -98
- data/lib/bundles/inspec-compliance/test/integration/default/cli.rb +93 -93
- data/lib/bundles/inspec-habitat.rb +12 -12
- data/lib/bundles/inspec-habitat/cli.rb +36 -36
- data/lib/bundles/inspec-habitat/log.rb +10 -10
- data/lib/bundles/inspec-habitat/profile.rb +390 -390
- data/lib/bundles/inspec-init.rb +8 -8
- data/lib/bundles/inspec-init/README.md +31 -31
- data/lib/bundles/inspec-init/cli.rb +97 -97
- data/lib/bundles/inspec-init/templates/profile/README.md +3 -3
- data/lib/bundles/inspec-init/templates/profile/controls/example.rb +19 -19
- data/lib/bundles/inspec-init/templates/profile/inspec.yml +8 -8
- data/lib/bundles/inspec-supermarket.rb +13 -13
- data/lib/bundles/inspec-supermarket/README.md +45 -45
- data/lib/bundles/inspec-supermarket/api.rb +84 -84
- data/lib/bundles/inspec-supermarket/cli.rb +65 -65
- data/lib/bundles/inspec-supermarket/target.rb +34 -34
- data/lib/fetchers/git.rb +163 -163
- data/lib/fetchers/local.rb +74 -74
- data/lib/fetchers/mock.rb +35 -35
- data/lib/fetchers/url.rb +204 -204
- data/lib/inspec.rb +24 -24
- data/lib/inspec/archive/tar.rb +29 -29
- data/lib/inspec/archive/zip.rb +19 -19
- data/lib/inspec/backend.rb +92 -92
- data/lib/inspec/base_cli.rb +324 -322
- data/lib/inspec/cached_fetcher.rb +66 -66
- data/lib/inspec/cli.rb +298 -298
- data/lib/inspec/completions/bash.sh.erb +45 -45
- data/lib/inspec/completions/fish.sh.erb +34 -34
- data/lib/inspec/completions/zsh.sh.erb +61 -61
- data/lib/inspec/control_eval_context.rb +179 -179
- data/lib/inspec/dependencies/cache.rb +72 -72
- data/lib/inspec/dependencies/dependency_set.rb +92 -92
- data/lib/inspec/dependencies/lockfile.rb +115 -115
- data/lib/inspec/dependencies/requirement.rb +123 -123
- data/lib/inspec/dependencies/resolver.rb +86 -86
- data/lib/inspec/describe.rb +27 -27
- data/lib/inspec/dsl.rb +66 -66
- data/lib/inspec/dsl_shared.rb +33 -33
- data/lib/inspec/env_printer.rb +157 -157
- data/lib/inspec/errors.rb +13 -13
- data/lib/inspec/exceptions.rb +12 -12
- data/lib/inspec/expect.rb +45 -45
- data/lib/inspec/fetcher.rb +45 -45
- data/lib/inspec/file_provider.rb +275 -275
- data/lib/inspec/formatters.rb +3 -3
- data/lib/inspec/formatters/base.rb +208 -208
- data/lib/inspec/formatters/json_rspec.rb +20 -20
- data/lib/inspec/formatters/show_progress.rb +12 -12
- data/lib/inspec/library_eval_context.rb +58 -58
- data/lib/inspec/log.rb +11 -11
- data/lib/inspec/metadata.rb +253 -253
- data/lib/inspec/method_source.rb +24 -24
- data/lib/inspec/objects.rb +14 -14
- data/lib/inspec/objects/attribute.rb +65 -65
- data/lib/inspec/objects/control.rb +61 -61
- data/lib/inspec/objects/describe.rb +92 -92
- data/lib/inspec/objects/each_loop.rb +36 -36
- data/lib/inspec/objects/list.rb +15 -15
- data/lib/inspec/objects/or_test.rb +40 -40
- data/lib/inspec/objects/ruby_helper.rb +15 -15
- data/lib/inspec/objects/tag.rb +27 -27
- data/lib/inspec/objects/test.rb +87 -87
- data/lib/inspec/objects/value.rb +27 -27
- data/lib/inspec/plugins.rb +60 -60
- data/lib/inspec/plugins/cli.rb +24 -24
- data/lib/inspec/plugins/fetcher.rb +86 -86
- data/lib/inspec/plugins/resource.rb +132 -132
- data/lib/inspec/plugins/secret.rb +15 -15
- data/lib/inspec/plugins/source_reader.rb +40 -40
- data/lib/inspec/polyfill.rb +12 -12
- data/lib/inspec/profile.rb +510 -510
- data/lib/inspec/profile_context.rb +207 -207
- data/lib/inspec/profile_vendor.rb +66 -66
- data/lib/inspec/reporters.rb +50 -33
- data/lib/inspec/reporters/base.rb +24 -23
- data/lib/inspec/reporters/cli.rb +395 -395
- data/lib/inspec/reporters/json.rb +134 -132
- data/lib/inspec/reporters/json_min.rb +48 -44
- data/lib/inspec/reporters/junit.rb +77 -77
- data/lib/inspec/require_loader.rb +33 -33
- data/lib/inspec/resource.rb +176 -176
- data/lib/inspec/rule.rb +266 -266
- data/lib/inspec/runner.rb +340 -337
- data/lib/inspec/runner_mock.rb +41 -41
- data/lib/inspec/runner_rspec.rb +163 -185
- data/lib/inspec/runtime_profile.rb +26 -26
- data/lib/inspec/schema.rb +186 -186
- data/lib/inspec/secrets.rb +19 -19
- data/lib/inspec/secrets/yaml.rb +30 -30
- data/lib/inspec/shell.rb +223 -223
- data/lib/inspec/shell_detector.rb +90 -90
- data/lib/inspec/source_reader.rb +29 -29
- data/lib/inspec/version.rb +8 -8
- data/lib/matchers/matchers.rb +397 -397
- data/lib/resources/aide_conf.rb +160 -160
- data/lib/resources/apache.rb +49 -49
- data/lib/resources/apache_conf.rb +158 -158
- data/lib/resources/apt.rb +150 -150
- data/lib/resources/audit_policy.rb +64 -64
- data/lib/resources/auditd.rb +233 -233
- data/lib/resources/auditd_conf.rb +56 -56
- data/lib/resources/auditd_rules.rb +205 -205
- data/lib/resources/bash.rb +36 -36
- data/lib/resources/bond.rb +69 -69
- data/lib/resources/bridge.rb +123 -123
- data/lib/resources/command.rb +69 -69
- data/lib/resources/cpan.rb +60 -60
- data/lib/resources/cran.rb +66 -66
- data/lib/resources/crontab.rb +169 -169
- data/lib/resources/csv.rb +58 -58
- data/lib/resources/dh_params.rb +83 -83
- data/lib/resources/directory.rb +25 -25
- data/lib/resources/docker.rb +239 -239
- data/lib/resources/docker_container.rb +92 -92
- data/lib/resources/docker_image.rb +86 -86
- data/lib/resources/docker_object.rb +57 -57
- data/lib/resources/docker_service.rb +94 -94
- data/lib/resources/elasticsearch.rb +168 -168
- data/lib/resources/etc_fstab.rb +102 -102
- data/lib/resources/etc_group.rb +157 -157
- data/lib/resources/etc_hosts.rb +81 -81
- data/lib/resources/etc_hosts_allow_deny.rb +122 -122
- data/lib/resources/file.rb +298 -298
- data/lib/resources/filesystem.rb +31 -31
- data/lib/resources/firewalld.rb +144 -144
- data/lib/resources/gem.rb +71 -71
- data/lib/resources/groups.rb +213 -213
- data/lib/resources/grub_conf.rb +237 -237
- data/lib/resources/host.rb +300 -300
- data/lib/resources/http.rb +252 -252
- data/lib/resources/iis_app.rb +103 -103
- data/lib/resources/iis_site.rb +147 -147
- data/lib/resources/inetd_conf.rb +63 -63
- data/lib/resources/ini.rb +29 -29
- data/lib/resources/interface.rb +130 -130
- data/lib/resources/iptables.rb +70 -70
- data/lib/resources/json.rb +115 -115
- data/lib/resources/kernel_module.rb +110 -110
- data/lib/resources/kernel_parameter.rb +58 -58
- data/lib/resources/key_rsa.rb +67 -67
- data/lib/resources/limits_conf.rb +56 -56
- data/lib/resources/login_def.rb +67 -67
- data/lib/resources/mount.rb +90 -90
- data/lib/resources/mssql_session.rb +103 -103
- data/lib/resources/mysql.rb +82 -82
- data/lib/resources/mysql_conf.rb +133 -133
- data/lib/resources/mysql_session.rb +72 -72
- data/lib/resources/nginx.rb +97 -97
- data/lib/resources/nginx_conf.rb +228 -228
- data/lib/resources/npm.rb +48 -48
- data/lib/resources/ntp_conf.rb +59 -59
- data/lib/resources/oneget.rb +72 -72
- data/lib/resources/oracledb_session.rb +140 -140
- data/lib/resources/os.rb +46 -46
- data/lib/resources/os_env.rb +76 -76
- data/lib/resources/package.rb +357 -357
- data/lib/resources/packages.rb +112 -112
- data/lib/resources/parse_config.rb +116 -116
- data/lib/resources/passwd.rb +96 -96
- data/lib/resources/pip.rb +89 -89
- data/lib/resources/platform.rb +112 -112
- data/lib/resources/port.rb +771 -771
- data/lib/resources/postgres.rb +132 -132
- data/lib/resources/postgres_conf.rb +122 -122
- data/lib/resources/postgres_hba_conf.rb +101 -101
- data/lib/resources/postgres_ident_conf.rb +79 -79
- data/lib/resources/postgres_session.rb +72 -72
- data/lib/resources/powershell.rb +58 -58
- data/lib/resources/processes.rb +204 -204
- data/lib/resources/rabbitmq_conf.rb +53 -53
- data/lib/resources/registry_key.rb +296 -296
- data/lib/resources/security_policy.rb +181 -181
- data/lib/resources/service.rb +784 -784
- data/lib/resources/shadow.rb +141 -141
- data/lib/resources/ssh_conf.rb +102 -102
- data/lib/resources/ssl.rb +99 -99
- data/lib/resources/sys_info.rb +26 -26
- data/lib/resources/toml.rb +32 -32
- data/lib/resources/users.rb +652 -652
- data/lib/resources/vbscript.rb +70 -70
- data/lib/resources/virtualization.rb +251 -251
- data/lib/resources/windows_feature.rb +85 -85
- data/lib/resources/windows_hotfix.rb +35 -35
- data/lib/resources/windows_task.rb +106 -106
- data/lib/resources/wmi.rb +114 -114
- data/lib/resources/x509_certificate.rb +143 -143
- data/lib/resources/xinetd.rb +112 -112
- data/lib/resources/xml.rb +45 -45
- data/lib/resources/yaml.rb +45 -45
- data/lib/resources/yum.rb +181 -181
- data/lib/resources/zfs_dataset.rb +60 -60
- data/lib/resources/zfs_pool.rb +49 -49
- data/lib/source_readers/flat.rb +39 -39
- data/lib/source_readers/inspec.rb +75 -75
- data/lib/utils/command_wrapper.rb +27 -27
- data/lib/utils/convert.rb +12 -12
- data/lib/utils/database_helpers.rb +77 -77
- data/lib/utils/erlang_parser.rb +192 -192
- data/lib/utils/filter.rb +272 -272
- data/lib/utils/filter_array.rb +27 -27
- data/lib/utils/find_files.rb +44 -44
- data/lib/utils/hash.rb +41 -41
- data/lib/utils/json_log.rb +18 -18
- data/lib/utils/latest_version.rb +22 -22
- data/lib/utils/modulator.rb +12 -12
- data/lib/utils/nginx_parser.rb +85 -85
- data/lib/utils/object_traversal.rb +49 -49
- data/lib/utils/parser.rb +274 -274
- data/lib/utils/plugin_registry.rb +93 -93
- data/lib/utils/simpleconfig.rb +132 -132
- data/lib/utils/spdx.rb +13 -13
- data/lib/utils/spdx.txt +343 -343
- metadata +2 -2
@@ -1,53 +1,53 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
|
3
|
-
# Custom resource based on the InSpec resource DSL
|
4
|
-
class GordonConfig < Inspec.resource(1)
|
5
|
-
name 'gordon_config'
|
6
|
-
|
7
|
-
desc "
|
8
|
-
Gordon's resource description ...
|
9
|
-
"
|
10
|
-
|
11
|
-
example "
|
12
|
-
describe gordon_config do
|
13
|
-
its('version') { should eq('1.0') }
|
14
|
-
its('file_size') { should > 1 }
|
15
|
-
end
|
16
|
-
"
|
17
|
-
|
18
|
-
# Load the configuration file on initialization
|
19
|
-
def initialize
|
20
|
-
@params = {}
|
21
|
-
@path = '/tmp/gordon/config.yaml'
|
22
|
-
@file = inspec.file(@path)
|
23
|
-
return skip_resource "Can't find file \"#{@path}\"" if !@file.file?
|
24
|
-
|
25
|
-
# Protect from invalid YAML content
|
26
|
-
begin
|
27
|
-
@params = YAML.load(@file.content)
|
28
|
-
# Add two extra matchers
|
29
|
-
@params['file_size'] = @file.size
|
30
|
-
@params['file_path'] = @path
|
31
|
-
@params['ruby'] = 'RUBY IS HERE TO HELP ME!'
|
32
|
-
rescue Exception
|
33
|
-
return skip_resource "#{@file}: #{$!}"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# Example method called by 'it { should exist }'
|
38
|
-
# Returns true or false from the 'File.exists?' method
|
39
|
-
def exists?
|
40
|
-
return File.exists?(@path)
|
41
|
-
end
|
42
|
-
|
43
|
-
# Example matcher for the number of commas in the file
|
44
|
-
def comma_count
|
45
|
-
text = @file.content
|
46
|
-
return text.count(',')
|
47
|
-
end
|
48
|
-
|
49
|
-
# Expose all parameters
|
50
|
-
def method_missing(name)
|
51
|
-
return @params[name.to_s]
|
52
|
-
end
|
53
|
-
end
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
# Custom resource based on the InSpec resource DSL
|
4
|
+
class GordonConfig < Inspec.resource(1)
|
5
|
+
name 'gordon_config'
|
6
|
+
|
7
|
+
desc "
|
8
|
+
Gordon's resource description ...
|
9
|
+
"
|
10
|
+
|
11
|
+
example "
|
12
|
+
describe gordon_config do
|
13
|
+
its('version') { should eq('1.0') }
|
14
|
+
its('file_size') { should > 1 }
|
15
|
+
end
|
16
|
+
"
|
17
|
+
|
18
|
+
# Load the configuration file on initialization
|
19
|
+
def initialize
|
20
|
+
@params = {}
|
21
|
+
@path = '/tmp/gordon/config.yaml'
|
22
|
+
@file = inspec.file(@path)
|
23
|
+
return skip_resource "Can't find file \"#{@path}\"" if !@file.file?
|
24
|
+
|
25
|
+
# Protect from invalid YAML content
|
26
|
+
begin
|
27
|
+
@params = YAML.load(@file.content)
|
28
|
+
# Add two extra matchers
|
29
|
+
@params['file_size'] = @file.size
|
30
|
+
@params['file_path'] = @path
|
31
|
+
@params['ruby'] = 'RUBY IS HERE TO HELP ME!'
|
32
|
+
rescue Exception
|
33
|
+
return skip_resource "#{@file}: #{$!}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Example method called by 'it { should exist }'
|
38
|
+
# Returns true or false from the 'File.exists?' method
|
39
|
+
def exists?
|
40
|
+
return File.exists?(@path)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Example matcher for the number of commas in the file
|
44
|
+
def comma_count
|
45
|
+
text = @file.content
|
46
|
+
return text.count(',')
|
47
|
+
end
|
48
|
+
|
49
|
+
# Expose all parameters
|
50
|
+
def method_missing(name)
|
51
|
+
return @params[name.to_s]
|
52
|
+
end
|
53
|
+
end
|
data/inspec.gemspec
CHANGED
@@ -1,47 +1,47 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'inspec/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = 'inspec'
|
8
|
-
spec.version = Inspec::VERSION
|
9
|
-
spec.authors = ['Dominik Richter']
|
10
|
-
spec.email = ['dominik.richter@gmail.com']
|
11
|
-
spec.summary = 'Infrastructure and compliance testing.'
|
12
|
-
spec.description = 'InSpec provides a framework for creating end-to-end infrastructure tests. You can use it for integration or even compliance testing. Create fully portable test profiles and use them in your workflow to ensure stability and security. Integrate InSpec in your change lifecycle for local testing, CI/CD, and deployment verification.'
|
13
|
-
spec.homepage = 'https://github.com/chef/inspec'
|
14
|
-
spec.license = 'Apache-2.0'
|
15
|
-
|
16
|
-
spec.files = %w{
|
17
|
-
README.md Rakefile MAINTAINERS.toml MAINTAINERS.md LICENSE inspec.gemspec
|
18
|
-
Gemfile CHANGELOG.md .rubocop.yml
|
19
|
-
} + Dir.glob(
|
20
|
-
'{bin,docs,examples,lib}/**/*', File::FNM_DOTMATCH
|
21
|
-
).reject { |f| File.directory?(f) }
|
22
|
-
|
23
|
-
spec.executables = %w{inspec}
|
24
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
25
|
-
spec.require_paths = ['lib']
|
26
|
-
|
27
|
-
spec.required_ruby_version = '>= 2.3'
|
28
|
-
|
29
|
-
spec.add_dependency 'train', '~> 0.32'
|
30
|
-
spec.add_dependency 'thor', '~> 0.19'
|
31
|
-
spec.add_dependency 'json', '>= 1.8', '< 3.0'
|
32
|
-
spec.add_dependency 'method_source', '~> 0.8'
|
33
|
-
spec.add_dependency 'rubyzip', '~> 1.1'
|
34
|
-
spec.add_dependency 'rspec', '~> 3'
|
35
|
-
spec.add_dependency 'rspec-its', '~> 1.2'
|
36
|
-
spec.add_dependency 'pry', '~> 0'
|
37
|
-
spec.add_dependency 'hashie', '~> 3.4'
|
38
|
-
spec.add_dependency 'mixlib-log'
|
39
|
-
spec.add_dependency 'sslshake', '~> 1.2'
|
40
|
-
spec.add_dependency 'parallel', '~> 1.9'
|
41
|
-
spec.add_dependency 'faraday', '>=0.9.0'
|
42
|
-
spec.add_dependency 'tomlrb', '~> 1.2'
|
43
|
-
spec.add_dependency 'addressable', '~> 2.4'
|
44
|
-
spec.add_dependency 'parslet', '~> 1.5'
|
45
|
-
spec.add_dependency 'semverse'
|
46
|
-
spec.add_dependency 'htmlentities'
|
47
|
-
end
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'inspec/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'inspec'
|
8
|
+
spec.version = Inspec::VERSION
|
9
|
+
spec.authors = ['Dominik Richter']
|
10
|
+
spec.email = ['dominik.richter@gmail.com']
|
11
|
+
spec.summary = 'Infrastructure and compliance testing.'
|
12
|
+
spec.description = 'InSpec provides a framework for creating end-to-end infrastructure tests. You can use it for integration or even compliance testing. Create fully portable test profiles and use them in your workflow to ensure stability and security. Integrate InSpec in your change lifecycle for local testing, CI/CD, and deployment verification.'
|
13
|
+
spec.homepage = 'https://github.com/chef/inspec'
|
14
|
+
spec.license = 'Apache-2.0'
|
15
|
+
|
16
|
+
spec.files = %w{
|
17
|
+
README.md Rakefile MAINTAINERS.toml MAINTAINERS.md LICENSE inspec.gemspec
|
18
|
+
Gemfile CHANGELOG.md .rubocop.yml
|
19
|
+
} + Dir.glob(
|
20
|
+
'{bin,docs,examples,lib}/**/*', File::FNM_DOTMATCH
|
21
|
+
).reject { |f| File.directory?(f) }
|
22
|
+
|
23
|
+
spec.executables = %w{inspec}
|
24
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
25
|
+
spec.require_paths = ['lib']
|
26
|
+
|
27
|
+
spec.required_ruby_version = '>= 2.3'
|
28
|
+
|
29
|
+
spec.add_dependency 'train', '~> 0.32'
|
30
|
+
spec.add_dependency 'thor', '~> 0.19'
|
31
|
+
spec.add_dependency 'json', '>= 1.8', '< 3.0'
|
32
|
+
spec.add_dependency 'method_source', '~> 0.8'
|
33
|
+
spec.add_dependency 'rubyzip', '~> 1.1'
|
34
|
+
spec.add_dependency 'rspec', '~> 3'
|
35
|
+
spec.add_dependency 'rspec-its', '~> 1.2'
|
36
|
+
spec.add_dependency 'pry', '~> 0'
|
37
|
+
spec.add_dependency 'hashie', '~> 3.4'
|
38
|
+
spec.add_dependency 'mixlib-log'
|
39
|
+
spec.add_dependency 'sslshake', '~> 1.2'
|
40
|
+
spec.add_dependency 'parallel', '~> 1.9'
|
41
|
+
spec.add_dependency 'faraday', '>=0.9.0'
|
42
|
+
spec.add_dependency 'tomlrb', '~> 1.2'
|
43
|
+
spec.add_dependency 'addressable', '~> 2.4'
|
44
|
+
spec.add_dependency 'parslet', '~> 1.5'
|
45
|
+
spec.add_dependency 'semverse'
|
46
|
+
spec.add_dependency 'htmlentities'
|
47
|
+
end
|
data/lib/bundles/README.md
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
# InSpec Bundled Plugins
|
2
|
-
|
3
|
-
This directory contains bundled InSpec plugins. Those plugins are shipped with InSpec temporarily only. Over the next months we are going to stabilize the InSpec Plugin API. Once this API reached stability, all bundled plugins will be externalized.
|
1
|
+
# InSpec Bundled Plugins
|
2
|
+
|
3
|
+
This directory contains bundled InSpec plugins. Those plugins are shipped with InSpec temporarily only. Over the next months we are going to stabilize the InSpec Plugin API. Once this API reached stability, all bundled plugins will be externalized.
|
@@ -1,7 +1,7 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
# author: Dave Parfitt
|
3
|
-
|
4
|
-
libdir = File.dirname(__FILE__)
|
5
|
-
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
6
|
-
|
7
|
-
require 'inspec-artifact/cli'
|
1
|
+
# encoding: utf-8
|
2
|
+
# author: Dave Parfitt
|
3
|
+
|
4
|
+
libdir = File.dirname(__FILE__)
|
5
|
+
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
6
|
+
|
7
|
+
require 'inspec-artifact/cli'
|
@@ -1 +1 @@
|
|
1
|
-
# TODO
|
1
|
+
# TODO
|
@@ -1,277 +1,277 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
# frozen_string_literal: true
|
3
|
-
require 'base64'
|
4
|
-
require 'openssl'
|
5
|
-
require 'pathname'
|
6
|
-
require 'set'
|
7
|
-
require 'tempfile'
|
8
|
-
require 'yaml'
|
9
|
-
|
10
|
-
# Notes:
|
11
|
-
#
|
12
|
-
# Generate keys
|
13
|
-
# The initial implementation uses 2048 bit RSA key pairs (public + private).
|
14
|
-
# Public keys must be available for a customer to install and verify an artifact.
|
15
|
-
# Private keys should be stored in a secure location and NOT be distributed.
|
16
|
-
# (They're only for creating artifacts).
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# .IAF file format
|
20
|
-
# .iaf = "Inspec Artifact File", easy to rename if you'd like something more appropriate.
|
21
|
-
# The iaf file wraps a binary artifact with some metadata. The first implementation
|
22
|
-
# looks like this:
|
23
|
-
#
|
24
|
-
# INSPEC-PROFILE-1
|
25
|
-
# name_of_signing_key
|
26
|
-
# algorithm
|
27
|
-
# signature
|
28
|
-
# <empty line>
|
29
|
-
# binary-blob
|
30
|
-
# <eof>
|
31
|
-
#
|
32
|
-
# Let's look at each line:
|
33
|
-
# INSPEC-PROFILE-1:
|
34
|
-
# This is the artifact version descriptor. It should't change unless the
|
35
|
-
# format of the archive changes.
|
36
|
-
#
|
37
|
-
# name_of_signing_key
|
38
|
-
# The name of the public key that can be used to verify an artifact
|
39
|
-
#
|
40
|
-
# algorithm
|
41
|
-
# The digest used to sign, I picked SHA512 to start with.
|
42
|
-
# If we support multiple digests, we'll need to have the verify() method
|
43
|
-
# support each digest.
|
44
|
-
#
|
45
|
-
# signature
|
46
|
-
# The result of passing the binary artifact through the digest algorithm above.
|
47
|
-
# Result is base64 encoded.
|
48
|
-
#
|
49
|
-
# <empty line>
|
50
|
-
# We use an empty line to separate artifact header from artifact body (binary blob).
|
51
|
-
# The artifact body can be anything you like.
|
52
|
-
#
|
53
|
-
# binary-blob
|
54
|
-
# A binary blob, most likely a .tar.gz or tar.xz file. We'll need to pick one and
|
55
|
-
# stick with it as part of the "INSPEC-PROFILE-1" artifact version. If we change block
|
56
|
-
# format, the artifact version descriptor must be incremented, and the sign()
|
57
|
-
# and verify() methods must be updated to support a newer version.
|
58
|
-
#
|
59
|
-
#
|
60
|
-
# Key revocation
|
61
|
-
# This implementation doesn't support key revocation. However, a customer
|
62
|
-
# can remove the public cert file before installation, and artifacts will then
|
63
|
-
# fail verification.
|
64
|
-
#
|
65
|
-
# Key locations
|
66
|
-
# This implementation uses the current working directory to find public and
|
67
|
-
# private keys. We should establish a common key directory (similar to /hab/cache/keys
|
68
|
-
# or ~/.hab/cache/keys in Habitat).
|
69
|
-
#
|
70
|
-
# Extracting artifacts outside of Inspec
|
71
|
-
# As in Habitat, the artifact format for Inspec allows the use of common
|
72
|
-
# Unix tools to read the header and body of an artifact.
|
73
|
-
# To extract the header from a .iaf:
|
74
|
-
# sed '/^$/q' foo.iaf
|
75
|
-
# To extract the raw content from a .iaf:
|
76
|
-
# sed '1,/^$/d' foo.iaf
|
77
|
-
|
78
|
-
module Artifact
|
79
|
-
KEY_BITS=2048
|
80
|
-
KEY_ALG=OpenSSL::PKey::RSA
|
81
|
-
|
82
|
-
INSPEC_PROFILE_VERSION_1='INSPEC-PROFILE-1'
|
83
|
-
INSPEC_REPORT_VERSION_1='INSPEC-REPORT-1'
|
84
|
-
|
85
|
-
ARTIFACT_DIGEST=OpenSSL::Digest::SHA512
|
86
|
-
ARTIFACT_DIGEST_NAME='SHA512'
|
87
|
-
|
88
|
-
VALID_PROFILE_VERSIONS=Set.new [INSPEC_PROFILE_VERSION_1]
|
89
|
-
VALID_PROFILE_DIGESTS=Set.new [ARTIFACT_DIGEST_NAME]
|
90
|
-
|
91
|
-
SIGNED_PROFILE_SUFFIX='iaf'
|
92
|
-
SIGNED_REPORT_SUFFIX='iar'
|
93
|
-
class CLI < Inspec::BaseCLI
|
94
|
-
namespace 'artifact'
|
95
|
-
|
96
|
-
# TODO: find another solution, once https://github.com/erikhuda/thor/issues/261 is fixed
|
97
|
-
def self.banner(command, _namespace = nil, _subcommand = false)
|
98
|
-
"#{basename} #{subcommand_prefix} #{command.usage}"
|
99
|
-
end
|
100
|
-
|
101
|
-
def self.subcommand_prefix
|
102
|
-
namespace
|
103
|
-
end
|
104
|
-
|
105
|
-
desc 'generate', 'Generate a RSA key pair for signing and verification'
|
106
|
-
option :keyname, type: :string, required: true,
|
107
|
-
desc: 'Desriptive name of key'
|
108
|
-
option :keydir, type: :string, default: './',
|
109
|
-
desc: 'Directory to search for keys'
|
110
|
-
def generate_keys
|
111
|
-
puts 'Generating keys'
|
112
|
-
keygen
|
113
|
-
end
|
114
|
-
|
115
|
-
desc 'sign-profile', 'Create a signed .iaf artifact'
|
116
|
-
option :profile, type: :string, required: true,
|
117
|
-
desc: 'Path to profile directory'
|
118
|
-
option :keyname, type: :string, required: true,
|
119
|
-
desc: 'Desriptive name of key'
|
120
|
-
def sign_profile
|
121
|
-
profile_sign
|
122
|
-
end
|
123
|
-
|
124
|
-
desc 'verify-profile', 'Verify a signed .iaf artifact'
|
125
|
-
option :infile, type: :string, required: true,
|
126
|
-
desc: '.iaf file to verify'
|
127
|
-
def verify_profile
|
128
|
-
profile_verify
|
129
|
-
end
|
130
|
-
|
131
|
-
desc 'install-profile', 'Verify and install a signed .iaf artifact'
|
132
|
-
option :infile, type: :string, required: true,
|
133
|
-
desc: '.iaf file to install'
|
134
|
-
option :destdir, type: :string, required: true,
|
135
|
-
desc: 'Installation directory'
|
136
|
-
def install_profile
|
137
|
-
profile_install
|
138
|
-
end
|
139
|
-
|
140
|
-
private
|
141
|
-
|
142
|
-
def keygen
|
143
|
-
key = KEY_ALG.new KEY_BITS
|
144
|
-
puts 'Generating private key'
|
145
|
-
open "#{options['keyname']}.pem.key", 'w' do |io| io.write key.to_pem end
|
146
|
-
puts 'Generating public key'
|
147
|
-
open "#{options['keyname']}.pem.pub", 'w' do |io| io.write key.public_key.to_pem end
|
148
|
-
end
|
149
|
-
|
150
|
-
def read_profile_metadata(path_to_profile)
|
151
|
-
begin
|
152
|
-
p = Pathname.new(path_to_profile)
|
153
|
-
p = p.join('inspec.yml')
|
154
|
-
if not p.exist?
|
155
|
-
raise "#{path_to_profile} doesn't appear to be a valid Inspec profile"
|
156
|
-
end
|
157
|
-
yaml = YAML.load_file(p.to_s)
|
158
|
-
yaml = yaml.to_hash
|
159
|
-
|
160
|
-
if not yaml.key? 'name'
|
161
|
-
raise 'Profile is invalid, name is not defined'
|
162
|
-
end
|
163
|
-
|
164
|
-
if not yaml.key? 'version'
|
165
|
-
raise 'Profile is invalid, version is not defined'
|
166
|
-
end
|
167
|
-
rescue => e
|
168
|
-
# rewrap it and pass it up to the CLI
|
169
|
-
raise "Error reading Inspec profile metadata: #{e}"
|
170
|
-
end
|
171
|
-
|
172
|
-
yaml
|
173
|
-
end
|
174
|
-
|
175
|
-
def profile_compress(path_to_profile, profile_md, workdir)
|
176
|
-
profile_name = profile_md['name']
|
177
|
-
profile_version = profile_md['version']
|
178
|
-
outfile_name = "#{workdir}/#{profile_name}-#{profile_version}.tar.gz"
|
179
|
-
`tar czf #{outfile_name} -C #{path_to_profile} .`
|
180
|
-
outfile_name
|
181
|
-
end
|
182
|
-
|
183
|
-
def profile_sign
|
184
|
-
Dir.mktmpdir do |workdir|
|
185
|
-
puts "Signing #{options['profile']} with key #{options['keyname']}"
|
186
|
-
path_to_profile = options['profile']
|
187
|
-
profile_md = read_profile_metadata(path_to_profile)
|
188
|
-
artifact_filename = "#{profile_md['name']}-#{profile_md['version']}.#{SIGNED_PROFILE_SUFFIX}"
|
189
|
-
tarfile = profile_compress(path_to_profile, profile_md, workdir)
|
190
|
-
content = IO.binread(tarfile)
|
191
|
-
signing_key = KEY_ALG.new File.read "#{options['keyname']}.pem.key"
|
192
|
-
sha = ARTIFACT_DIGEST.new
|
193
|
-
signature = signing_key.sign sha, content
|
194
|
-
# convert the signature to Base64
|
195
|
-
signature_base64 = Base64.encode64(signature)
|
196
|
-
tar_content = IO.binread(tarfile)
|
197
|
-
File.open(artifact_filename, 'wb') do |f|
|
198
|
-
f.puts(INSPEC_PROFILE_VERSION_1)
|
199
|
-
f.puts(options['keyname'])
|
200
|
-
f.puts(ARTIFACT_DIGEST_NAME)
|
201
|
-
f.puts(signature_base64)
|
202
|
-
f.puts('') # newline separates artifact header with body
|
203
|
-
f.write(tar_content)
|
204
|
-
end
|
205
|
-
puts "Successfully generated #{artifact_filename}"
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
def valid_header?(file_alg, file_version, file_keyname)
|
210
|
-
public_keyfile = "#{file_keyname}.pem.pub"
|
211
|
-
puts "Looking for #{public_keyfile} to verify artifact"
|
212
|
-
if !File.exist? public_keyfile
|
213
|
-
raise "Can't find #{public_keyfile}"
|
214
|
-
end
|
215
|
-
|
216
|
-
raise 'Invalid artifact digest algorithm detected' if !VALID_PROFILE_DIGESTS.member?(file_alg)
|
217
|
-
raise 'Invalid artifact version detected' if !VALID_PROFILE_VERSIONS.member?(file_version)
|
218
|
-
end
|
219
|
-
|
220
|
-
def verify(file_to_verifiy, &content_block)
|
221
|
-
f = File.open(file_to_verifiy, 'r')
|
222
|
-
file_version = f.readline.strip!
|
223
|
-
file_keyname = f.readline.strip!
|
224
|
-
file_alg = f.readline.strip!
|
225
|
-
|
226
|
-
file_sig = ''
|
227
|
-
# the signature is multi-line
|
228
|
-
while (line = f.readline) != "\n"
|
229
|
-
file_sig += line
|
230
|
-
end
|
231
|
-
file_sig.strip!
|
232
|
-
f.close
|
233
|
-
|
234
|
-
valid_header?(file_alg, file_version, file_keyname)
|
235
|
-
|
236
|
-
public_keyfile = "#{file_keyname}.pem.pub"
|
237
|
-
verification_key = KEY_ALG.new File.read public_keyfile
|
238
|
-
|
239
|
-
f = File.open(file_to_verifiy, 'r')
|
240
|
-
while f.readline != "\n" do end
|
241
|
-
content = f.read
|
242
|
-
|
243
|
-
signature = Base64.decode64(file_sig)
|
244
|
-
digest = ARTIFACT_DIGEST.new
|
245
|
-
if verification_key.verify digest, signature, content
|
246
|
-
content_block.yield(content)
|
247
|
-
else
|
248
|
-
puts 'Artifact is invalid'
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
def profile_verify
|
253
|
-
file_to_verifiy = options['infile']
|
254
|
-
puts "Verifying #{file_to_verifiy}"
|
255
|
-
verify(file_to_verifiy) do ||
|
256
|
-
puts 'Artifact is valid'
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
def profile_install
|
261
|
-
puts 'Installing profile'
|
262
|
-
file_to_verifiy = options['infile']
|
263
|
-
dest_dir = options['destdir']
|
264
|
-
verify(file_to_verifiy) do |content|
|
265
|
-
Dir.mktmpdir do |workdir|
|
266
|
-
tmpfile = Pathname.new(workdir).join('artifact_to_install.tar.gz')
|
267
|
-
File.write(tmpfile, content)
|
268
|
-
puts "Installing to #{dest_dir}"
|
269
|
-
`tar xzf #{tmpfile} -C #{dest_dir}`
|
270
|
-
end
|
271
|
-
end
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
# register the subcommand to Inspec CLI registry
|
276
|
-
Inspec::Plugins::CLI.add_subcommand(Artifact::CLI, 'artifact', 'artifact SUBCOMMAND ...', 'Sign, verify and install artifacts', {})
|
277
|
-
end
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'base64'
|
4
|
+
require 'openssl'
|
5
|
+
require 'pathname'
|
6
|
+
require 'set'
|
7
|
+
require 'tempfile'
|
8
|
+
require 'yaml'
|
9
|
+
|
10
|
+
# Notes:
|
11
|
+
#
|
12
|
+
# Generate keys
|
13
|
+
# The initial implementation uses 2048 bit RSA key pairs (public + private).
|
14
|
+
# Public keys must be available for a customer to install and verify an artifact.
|
15
|
+
# Private keys should be stored in a secure location and NOT be distributed.
|
16
|
+
# (They're only for creating artifacts).
|
17
|
+
#
|
18
|
+
#
|
19
|
+
# .IAF file format
|
20
|
+
# .iaf = "Inspec Artifact File", easy to rename if you'd like something more appropriate.
|
21
|
+
# The iaf file wraps a binary artifact with some metadata. The first implementation
|
22
|
+
# looks like this:
|
23
|
+
#
|
24
|
+
# INSPEC-PROFILE-1
|
25
|
+
# name_of_signing_key
|
26
|
+
# algorithm
|
27
|
+
# signature
|
28
|
+
# <empty line>
|
29
|
+
# binary-blob
|
30
|
+
# <eof>
|
31
|
+
#
|
32
|
+
# Let's look at each line:
|
33
|
+
# INSPEC-PROFILE-1:
|
34
|
+
# This is the artifact version descriptor. It should't change unless the
|
35
|
+
# format of the archive changes.
|
36
|
+
#
|
37
|
+
# name_of_signing_key
|
38
|
+
# The name of the public key that can be used to verify an artifact
|
39
|
+
#
|
40
|
+
# algorithm
|
41
|
+
# The digest used to sign, I picked SHA512 to start with.
|
42
|
+
# If we support multiple digests, we'll need to have the verify() method
|
43
|
+
# support each digest.
|
44
|
+
#
|
45
|
+
# signature
|
46
|
+
# The result of passing the binary artifact through the digest algorithm above.
|
47
|
+
# Result is base64 encoded.
|
48
|
+
#
|
49
|
+
# <empty line>
|
50
|
+
# We use an empty line to separate artifact header from artifact body (binary blob).
|
51
|
+
# The artifact body can be anything you like.
|
52
|
+
#
|
53
|
+
# binary-blob
|
54
|
+
# A binary blob, most likely a .tar.gz or tar.xz file. We'll need to pick one and
|
55
|
+
# stick with it as part of the "INSPEC-PROFILE-1" artifact version. If we change block
|
56
|
+
# format, the artifact version descriptor must be incremented, and the sign()
|
57
|
+
# and verify() methods must be updated to support a newer version.
|
58
|
+
#
|
59
|
+
#
|
60
|
+
# Key revocation
|
61
|
+
# This implementation doesn't support key revocation. However, a customer
|
62
|
+
# can remove the public cert file before installation, and artifacts will then
|
63
|
+
# fail verification.
|
64
|
+
#
|
65
|
+
# Key locations
|
66
|
+
# This implementation uses the current working directory to find public and
|
67
|
+
# private keys. We should establish a common key directory (similar to /hab/cache/keys
|
68
|
+
# or ~/.hab/cache/keys in Habitat).
|
69
|
+
#
|
70
|
+
# Extracting artifacts outside of Inspec
|
71
|
+
# As in Habitat, the artifact format for Inspec allows the use of common
|
72
|
+
# Unix tools to read the header and body of an artifact.
|
73
|
+
# To extract the header from a .iaf:
|
74
|
+
# sed '/^$/q' foo.iaf
|
75
|
+
# To extract the raw content from a .iaf:
|
76
|
+
# sed '1,/^$/d' foo.iaf
|
77
|
+
|
78
|
+
module Artifact
|
79
|
+
KEY_BITS=2048
|
80
|
+
KEY_ALG=OpenSSL::PKey::RSA
|
81
|
+
|
82
|
+
INSPEC_PROFILE_VERSION_1='INSPEC-PROFILE-1'
|
83
|
+
INSPEC_REPORT_VERSION_1='INSPEC-REPORT-1'
|
84
|
+
|
85
|
+
ARTIFACT_DIGEST=OpenSSL::Digest::SHA512
|
86
|
+
ARTIFACT_DIGEST_NAME='SHA512'
|
87
|
+
|
88
|
+
VALID_PROFILE_VERSIONS=Set.new [INSPEC_PROFILE_VERSION_1]
|
89
|
+
VALID_PROFILE_DIGESTS=Set.new [ARTIFACT_DIGEST_NAME]
|
90
|
+
|
91
|
+
SIGNED_PROFILE_SUFFIX='iaf'
|
92
|
+
SIGNED_REPORT_SUFFIX='iar'
|
93
|
+
class CLI < Inspec::BaseCLI
|
94
|
+
namespace 'artifact'
|
95
|
+
|
96
|
+
# TODO: find another solution, once https://github.com/erikhuda/thor/issues/261 is fixed
|
97
|
+
def self.banner(command, _namespace = nil, _subcommand = false)
|
98
|
+
"#{basename} #{subcommand_prefix} #{command.usage}"
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.subcommand_prefix
|
102
|
+
namespace
|
103
|
+
end
|
104
|
+
|
105
|
+
desc 'generate', 'Generate a RSA key pair for signing and verification'
|
106
|
+
option :keyname, type: :string, required: true,
|
107
|
+
desc: 'Desriptive name of key'
|
108
|
+
option :keydir, type: :string, default: './',
|
109
|
+
desc: 'Directory to search for keys'
|
110
|
+
def generate_keys
|
111
|
+
puts 'Generating keys'
|
112
|
+
keygen
|
113
|
+
end
|
114
|
+
|
115
|
+
desc 'sign-profile', 'Create a signed .iaf artifact'
|
116
|
+
option :profile, type: :string, required: true,
|
117
|
+
desc: 'Path to profile directory'
|
118
|
+
option :keyname, type: :string, required: true,
|
119
|
+
desc: 'Desriptive name of key'
|
120
|
+
def sign_profile
|
121
|
+
profile_sign
|
122
|
+
end
|
123
|
+
|
124
|
+
desc 'verify-profile', 'Verify a signed .iaf artifact'
|
125
|
+
option :infile, type: :string, required: true,
|
126
|
+
desc: '.iaf file to verify'
|
127
|
+
def verify_profile
|
128
|
+
profile_verify
|
129
|
+
end
|
130
|
+
|
131
|
+
desc 'install-profile', 'Verify and install a signed .iaf artifact'
|
132
|
+
option :infile, type: :string, required: true,
|
133
|
+
desc: '.iaf file to install'
|
134
|
+
option :destdir, type: :string, required: true,
|
135
|
+
desc: 'Installation directory'
|
136
|
+
def install_profile
|
137
|
+
profile_install
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def keygen
|
143
|
+
key = KEY_ALG.new KEY_BITS
|
144
|
+
puts 'Generating private key'
|
145
|
+
open "#{options['keyname']}.pem.key", 'w' do |io| io.write key.to_pem end
|
146
|
+
puts 'Generating public key'
|
147
|
+
open "#{options['keyname']}.pem.pub", 'w' do |io| io.write key.public_key.to_pem end
|
148
|
+
end
|
149
|
+
|
150
|
+
def read_profile_metadata(path_to_profile)
|
151
|
+
begin
|
152
|
+
p = Pathname.new(path_to_profile)
|
153
|
+
p = p.join('inspec.yml')
|
154
|
+
if not p.exist?
|
155
|
+
raise "#{path_to_profile} doesn't appear to be a valid Inspec profile"
|
156
|
+
end
|
157
|
+
yaml = YAML.load_file(p.to_s)
|
158
|
+
yaml = yaml.to_hash
|
159
|
+
|
160
|
+
if not yaml.key? 'name'
|
161
|
+
raise 'Profile is invalid, name is not defined'
|
162
|
+
end
|
163
|
+
|
164
|
+
if not yaml.key? 'version'
|
165
|
+
raise 'Profile is invalid, version is not defined'
|
166
|
+
end
|
167
|
+
rescue => e
|
168
|
+
# rewrap it and pass it up to the CLI
|
169
|
+
raise "Error reading Inspec profile metadata: #{e}"
|
170
|
+
end
|
171
|
+
|
172
|
+
yaml
|
173
|
+
end
|
174
|
+
|
175
|
+
def profile_compress(path_to_profile, profile_md, workdir)
|
176
|
+
profile_name = profile_md['name']
|
177
|
+
profile_version = profile_md['version']
|
178
|
+
outfile_name = "#{workdir}/#{profile_name}-#{profile_version}.tar.gz"
|
179
|
+
`tar czf #{outfile_name} -C #{path_to_profile} .`
|
180
|
+
outfile_name
|
181
|
+
end
|
182
|
+
|
183
|
+
def profile_sign
|
184
|
+
Dir.mktmpdir do |workdir|
|
185
|
+
puts "Signing #{options['profile']} with key #{options['keyname']}"
|
186
|
+
path_to_profile = options['profile']
|
187
|
+
profile_md = read_profile_metadata(path_to_profile)
|
188
|
+
artifact_filename = "#{profile_md['name']}-#{profile_md['version']}.#{SIGNED_PROFILE_SUFFIX}"
|
189
|
+
tarfile = profile_compress(path_to_profile, profile_md, workdir)
|
190
|
+
content = IO.binread(tarfile)
|
191
|
+
signing_key = KEY_ALG.new File.read "#{options['keyname']}.pem.key"
|
192
|
+
sha = ARTIFACT_DIGEST.new
|
193
|
+
signature = signing_key.sign sha, content
|
194
|
+
# convert the signature to Base64
|
195
|
+
signature_base64 = Base64.encode64(signature)
|
196
|
+
tar_content = IO.binread(tarfile)
|
197
|
+
File.open(artifact_filename, 'wb') do |f|
|
198
|
+
f.puts(INSPEC_PROFILE_VERSION_1)
|
199
|
+
f.puts(options['keyname'])
|
200
|
+
f.puts(ARTIFACT_DIGEST_NAME)
|
201
|
+
f.puts(signature_base64)
|
202
|
+
f.puts('') # newline separates artifact header with body
|
203
|
+
f.write(tar_content)
|
204
|
+
end
|
205
|
+
puts "Successfully generated #{artifact_filename}"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def valid_header?(file_alg, file_version, file_keyname)
|
210
|
+
public_keyfile = "#{file_keyname}.pem.pub"
|
211
|
+
puts "Looking for #{public_keyfile} to verify artifact"
|
212
|
+
if !File.exist? public_keyfile
|
213
|
+
raise "Can't find #{public_keyfile}"
|
214
|
+
end
|
215
|
+
|
216
|
+
raise 'Invalid artifact digest algorithm detected' if !VALID_PROFILE_DIGESTS.member?(file_alg)
|
217
|
+
raise 'Invalid artifact version detected' if !VALID_PROFILE_VERSIONS.member?(file_version)
|
218
|
+
end
|
219
|
+
|
220
|
+
def verify(file_to_verifiy, &content_block)
|
221
|
+
f = File.open(file_to_verifiy, 'r')
|
222
|
+
file_version = f.readline.strip!
|
223
|
+
file_keyname = f.readline.strip!
|
224
|
+
file_alg = f.readline.strip!
|
225
|
+
|
226
|
+
file_sig = ''
|
227
|
+
# the signature is multi-line
|
228
|
+
while (line = f.readline) != "\n"
|
229
|
+
file_sig += line
|
230
|
+
end
|
231
|
+
file_sig.strip!
|
232
|
+
f.close
|
233
|
+
|
234
|
+
valid_header?(file_alg, file_version, file_keyname)
|
235
|
+
|
236
|
+
public_keyfile = "#{file_keyname}.pem.pub"
|
237
|
+
verification_key = KEY_ALG.new File.read public_keyfile
|
238
|
+
|
239
|
+
f = File.open(file_to_verifiy, 'r')
|
240
|
+
while f.readline != "\n" do end
|
241
|
+
content = f.read
|
242
|
+
|
243
|
+
signature = Base64.decode64(file_sig)
|
244
|
+
digest = ARTIFACT_DIGEST.new
|
245
|
+
if verification_key.verify digest, signature, content
|
246
|
+
content_block.yield(content)
|
247
|
+
else
|
248
|
+
puts 'Artifact is invalid'
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def profile_verify
|
253
|
+
file_to_verifiy = options['infile']
|
254
|
+
puts "Verifying #{file_to_verifiy}"
|
255
|
+
verify(file_to_verifiy) do ||
|
256
|
+
puts 'Artifact is valid'
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def profile_install
|
261
|
+
puts 'Installing profile'
|
262
|
+
file_to_verifiy = options['infile']
|
263
|
+
dest_dir = options['destdir']
|
264
|
+
verify(file_to_verifiy) do |content|
|
265
|
+
Dir.mktmpdir do |workdir|
|
266
|
+
tmpfile = Pathname.new(workdir).join('artifact_to_install.tar.gz')
|
267
|
+
File.write(tmpfile, content)
|
268
|
+
puts "Installing to #{dest_dir}"
|
269
|
+
`tar xzf #{tmpfile} -C #{dest_dir}`
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
# register the subcommand to Inspec CLI registry
|
276
|
+
Inspec::Plugins::CLI.add_subcommand(Artifact::CLI, 'artifact', 'artifact SUBCOMMAND ...', 'Sign, verify and install artifacts', {})
|
277
|
+
end
|