inspec 2.1.81 → 2.1.83
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 +5 -5
- data/.rubocop.yml +101 -101
- data/CHANGELOG.md +3183 -3177
- data/Gemfile +56 -56
- data/LICENSE +14 -14
- data/MAINTAINERS.md +33 -33
- data/MAINTAINERS.toml +52 -52
- data/README.md +453 -453
- data/Rakefile +349 -349
- data/bin/inspec +12 -12
- data/docs/.gitignore +2 -2
- data/docs/README.md +41 -40
- data/docs/dev/control-eval.md +61 -61
- data/docs/dsl_inspec.md +258 -258
- data/docs/dsl_resource.md +100 -100
- data/docs/glossary.md +99 -99
- data/docs/habitat.md +191 -191
- data/docs/inspec_and_friends.md +114 -114
- data/docs/matchers.md +169 -169
- data/docs/migration.md +293 -293
- data/docs/platforms.md +118 -118
- data/docs/plugin_kitchen_inspec.md +50 -50
- data/docs/profiles.md +378 -378
- data/docs/reporters.md +105 -105
- data/docs/resources/aide_conf.md.erb +75 -75
- data/docs/resources/apache.md.erb +67 -67
- data/docs/resources/apache_conf.md.erb +68 -68
- data/docs/resources/apt.md.erb +71 -71
- data/docs/resources/audit_policy.md.erb +47 -47
- data/docs/resources/auditd.md.erb +79 -79
- data/docs/resources/auditd_conf.md.erb +68 -68
- data/docs/resources/aws_cloudtrail_trail.md.erb +155 -155
- data/docs/resources/aws_cloudtrail_trails.md.erb +86 -86
- data/docs/resources/aws_cloudwatch_alarm.md.erb +91 -91
- data/docs/resources/aws_cloudwatch_log_metric_filter.md.erb +154 -154
- data/docs/resources/aws_config_delivery_channel.md.erb +101 -101
- data/docs/resources/aws_config_recorder.md.erb +86 -86
- data/docs/resources/aws_ec2_instance.md.erb +112 -112
- data/docs/resources/aws_ec2_instances.md.erb +79 -79
- data/docs/resources/aws_iam_access_key.md.erb +129 -129
- data/docs/resources/aws_iam_access_keys.md.erb +204 -204
- data/docs/resources/aws_iam_group.md.erb +64 -64
- data/docs/resources/aws_iam_groups.md.erb +49 -49
- data/docs/resources/aws_iam_password_policy.md.erb +82 -82
- data/docs/resources/aws_iam_policies.md.erb +87 -87
- data/docs/resources/aws_iam_policy.md.erb +245 -245
- data/docs/resources/aws_iam_role.md.erb +69 -69
- data/docs/resources/aws_iam_root_user.md.erb +76 -76
- data/docs/resources/aws_iam_user.md.erb +120 -120
- data/docs/resources/aws_iam_users.md.erb +279 -279
- data/docs/resources/aws_kms_key.md.erb +177 -177
- data/docs/resources/aws_kms_keys.md.erb +89 -89
- data/docs/resources/aws_rds_instance.md.erb +66 -66
- data/docs/resources/aws_route_table.md.erb +53 -53
- data/docs/resources/aws_route_tables.md.erb +55 -55
- data/docs/resources/aws_s3_bucket.md.erb +146 -146
- data/docs/resources/aws_s3_bucket_object.md.erb +89 -89
- data/docs/resources/aws_s3_buckets.md.erb +59 -59
- data/docs/resources/aws_security_group.md.erb +296 -296
- data/docs/resources/aws_security_groups.md.erb +97 -97
- data/docs/resources/aws_sns_subscription.md.erb +130 -130
- data/docs/resources/aws_sns_topic.md.erb +69 -69
- data/docs/resources/aws_sns_topics.md.erb +58 -58
- data/docs/resources/aws_subnet.md.erb +140 -140
- data/docs/resources/aws_subnets.md.erb +132 -132
- data/docs/resources/aws_vpc.md.erb +125 -125
- data/docs/resources/aws_vpcs.md.erb +125 -125
- data/docs/resources/azure_generic_resource.md.erb +171 -171
- data/docs/resources/azure_resource_group.md.erb +284 -284
- data/docs/resources/azure_virtual_machine.md.erb +347 -347
- data/docs/resources/azure_virtual_machine_data_disk.md.erb +224 -224
- data/docs/resources/bash.md.erb +75 -75
- data/docs/resources/bond.md.erb +90 -90
- data/docs/resources/bridge.md.erb +57 -57
- data/docs/resources/bsd_service.md.erb +67 -67
- data/docs/resources/chocolatey_package.md.erb +58 -58
- data/docs/resources/command.md.erb +138 -138
- data/docs/resources/cpan.md.erb +79 -79
- data/docs/resources/cran.md.erb +64 -64
- data/docs/resources/crontab.md.erb +89 -89
- data/docs/resources/csv.md.erb +54 -54
- data/docs/resources/dh_params.md.erb +205 -205
- data/docs/resources/directory.md.erb +30 -30
- data/docs/resources/docker.md.erb +219 -219
- data/docs/resources/docker_container.md.erb +103 -103
- data/docs/resources/docker_image.md.erb +94 -94
- data/docs/resources/docker_service.md.erb +114 -114
- data/docs/resources/elasticsearch.md.erb +242 -242
- data/docs/resources/etc_fstab.md.erb +125 -125
- data/docs/resources/etc_group.md.erb +75 -75
- data/docs/resources/etc_hosts.md.erb +78 -78
- data/docs/resources/etc_hosts_allow.md.erb +74 -74
- data/docs/resources/etc_hosts_deny.md.erb +74 -74
- data/docs/resources/file.md.erb +526 -526
- data/docs/resources/filesystem.md.erb +41 -41
- data/docs/resources/firewalld.md.erb +107 -107
- data/docs/resources/gem.md.erb +79 -79
- data/docs/resources/group.md.erb +61 -61
- data/docs/resources/grub_conf.md.erb +101 -101
- data/docs/resources/host.md.erb +86 -86
- data/docs/resources/http.md.erb +197 -197
- data/docs/resources/iis_app.md.erb +122 -122
- data/docs/resources/iis_site.md.erb +135 -135
- data/docs/resources/inetd_conf.md.erb +94 -94
- data/docs/resources/ini.md.erb +76 -76
- data/docs/resources/interface.md.erb +58 -58
- data/docs/resources/iptables.md.erb +64 -64
- data/docs/resources/json.md.erb +63 -63
- data/docs/resources/kernel_module.md.erb +120 -120
- data/docs/resources/kernel_parameter.md.erb +53 -53
- data/docs/resources/key_rsa.md.erb +85 -85
- data/docs/resources/launchd_service.md.erb +57 -57
- data/docs/resources/limits_conf.md.erb +75 -75
- data/docs/resources/login_defs.md.erb +71 -71
- data/docs/resources/mount.md.erb +69 -69
- data/docs/resources/mssql_session.md.erb +60 -60
- data/docs/resources/mysql_conf.md.erb +99 -99
- data/docs/resources/mysql_session.md.erb +74 -74
- data/docs/resources/nginx.md.erb +79 -79
- data/docs/resources/nginx_conf.md.erb +138 -138
- data/docs/resources/npm.md.erb +60 -60
- data/docs/resources/ntp_conf.md.erb +60 -60
- data/docs/resources/oneget.md.erb +53 -53
- data/docs/resources/oracledb_session.md.erb +52 -52
- data/docs/resources/os.md.erb +141 -141
- data/docs/resources/os_env.md.erb +91 -91
- data/docs/resources/package.md.erb +120 -120
- data/docs/resources/packages.md.erb +67 -67
- data/docs/resources/parse_config.md.erb +103 -103
- data/docs/resources/parse_config_file.md.erb +138 -138
- data/docs/resources/passwd.md.erb +141 -141
- data/docs/resources/pip.md.erb +67 -67
- data/docs/resources/port.md.erb +137 -137
- data/docs/resources/postgres_conf.md.erb +79 -79
- data/docs/resources/postgres_hba_conf.md.erb +93 -93
- data/docs/resources/postgres_ident_conf.md.erb +76 -76
- data/docs/resources/postgres_session.md.erb +69 -69
- data/docs/resources/powershell.md.erb +102 -102
- data/docs/resources/processes.md.erb +109 -109
- data/docs/resources/rabbitmq_config.md.erb +41 -41
- data/docs/resources/registry_key.md.erb +158 -158
- data/docs/resources/runit_service.md.erb +57 -57
- data/docs/resources/security_policy.md.erb +47 -47
- data/docs/resources/service.md.erb +121 -121
- data/docs/resources/shadow.md.erb +146 -146
- data/docs/resources/ssh_config.md.erb +73 -73
- data/docs/resources/sshd_config.md.erb +83 -83
- data/docs/resources/ssl.md.erb +119 -119
- data/docs/resources/sys_info.md.erb +42 -42
- data/docs/resources/systemd_service.md.erb +57 -57
- data/docs/resources/sysv_service.md.erb +57 -57
- data/docs/resources/upstart_service.md.erb +57 -57
- data/docs/resources/user.md.erb +140 -140
- data/docs/resources/users.md.erb +127 -127
- data/docs/resources/vbscript.md.erb +55 -55
- data/docs/resources/virtualization.md.erb +57 -57
- data/docs/resources/windows_feature.md.erb +47 -47
- data/docs/resources/windows_hotfix.md.erb +53 -53
- data/docs/resources/windows_task.md.erb +95 -95
- data/docs/resources/wmi.md.erb +81 -81
- data/docs/resources/x509_certificate.md.erb +151 -151
- data/docs/resources/xinetd_conf.md.erb +156 -156
- data/docs/resources/xml.md.erb +85 -85
- data/docs/resources/yaml.md.erb +69 -69
- data/docs/resources/yum.md.erb +98 -98
- data/docs/resources/zfs_dataset.md.erb +53 -53
- data/docs/resources/zfs_pool.md.erb +47 -47
- 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 +217 -217
- 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 +23 -23
- 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-aws/controls/iam_password_policy_expiration.rb +8 -8
- data/examples/profile-aws/controls/iam_password_policy_max_age.rb +8 -8
- data/examples/profile-aws/controls/iam_root_user_mfa.rb +8 -8
- data/examples/profile-aws/controls/iam_users_access_key_age.rb +8 -8
- data/examples/profile-aws/controls/iam_users_console_users_mfa.rb +8 -8
- data/examples/profile-aws/inspec.yml +11 -11
- data/examples/profile-azure/controls/azure_resource_group_example.rb +24 -24
- data/examples/profile-azure/controls/azure_vm_example.rb +29 -29
- data/examples/profile-azure/inspec.yml +11 -11
- 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 +59 -59
- data/inspec.gemspec +49 -49
- 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 +193 -193
- data/lib/bundles/inspec-compliance/api.rb +360 -360
- data/lib/bundles/inspec-compliance/api/login.rb +193 -193
- data/lib/bundles/inspec-compliance/bootstrap.sh +41 -41
- data/lib/bundles/inspec-compliance/cli.rb +260 -260
- data/lib/bundles/inspec-compliance/configuration.rb +103 -103
- data/lib/bundles/inspec-compliance/http.rb +125 -125
- data/lib/bundles/inspec-compliance/support.rb +36 -36
- data/lib/bundles/inspec-compliance/target.rb +112 -112
- 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 +391 -391
- 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 +73 -73
- 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 +247 -247
- 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 +93 -93
- data/lib/inspec/base_cli.rb +368 -368
- data/lib/inspec/cached_fetcher.rb +66 -66
- data/lib/inspec/cli.rb +292 -292
- 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 +14 -14
- 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 +259 -259
- 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 +247 -247
- data/lib/inspec/method_source.rb +24 -24
- data/lib/inspec/objects.rb +14 -14
- data/lib/inspec/objects/attribute.rb +75 -75
- 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 +135 -135
- 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 +513 -513
- data/lib/inspec/profile_context.rb +208 -208
- data/lib/inspec/profile_vendor.rb +66 -66
- data/lib/inspec/reporters.rb +60 -60
- data/lib/inspec/reporters/automate.rb +76 -76
- data/lib/inspec/reporters/base.rb +25 -25
- data/lib/inspec/reporters/cli.rb +356 -356
- data/lib/inspec/reporters/json.rb +117 -117
- data/lib/inspec/reporters/json_min.rb +48 -48
- data/lib/inspec/reporters/junit.rb +78 -78
- data/lib/inspec/require_loader.rb +33 -33
- data/lib/inspec/resource.rb +190 -190
- data/lib/inspec/rule.rb +280 -280
- data/lib/inspec/runner.rb +345 -345
- data/lib/inspec/runner_mock.rb +41 -41
- data/lib/inspec/runner_rspec.rb +175 -175
- data/lib/inspec/runtime_profile.rb +26 -26
- data/lib/inspec/schema.rb +213 -213
- data/lib/inspec/secrets.rb +19 -19
- data/lib/inspec/secrets/yaml.rb +30 -30
- data/lib/inspec/shell.rb +220 -220
- 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 +339 -339
- data/lib/resource_support/aws.rb +50 -50
- data/lib/resource_support/aws/aws_backend_base.rb +12 -12
- data/lib/resource_support/aws/aws_backend_factory_mixin.rb +12 -12
- data/lib/resource_support/aws/aws_plural_resource_mixin.rb +21 -21
- data/lib/resource_support/aws/aws_resource_mixin.rb +66 -66
- data/lib/resource_support/aws/aws_singular_resource_mixin.rb +24 -24
- data/lib/resources/aide_conf.rb +151 -151
- data/lib/resources/apache.rb +48 -48
- data/lib/resources/apache_conf.rb +149 -149
- data/lib/resources/apt.rb +149 -149
- data/lib/resources/audit_policy.rb +63 -63
- data/lib/resources/auditd.rb +231 -231
- data/lib/resources/auditd_conf.rb +46 -46
- data/lib/resources/aws/aws_cloudtrail_trail.rb +93 -93
- data/lib/resources/aws/aws_cloudtrail_trails.rb +47 -47
- data/lib/resources/aws/aws_cloudwatch_alarm.rb +62 -62
- data/lib/resources/aws/aws_cloudwatch_log_metric_filter.rb +100 -100
- data/lib/resources/aws/aws_config_delivery_channel.rb +70 -70
- data/lib/resources/aws/aws_config_recorder.rb +93 -93
- data/lib/resources/aws/aws_ec2_instance.rb +157 -157
- data/lib/resources/aws/aws_ec2_instances.rb +64 -64
- data/lib/resources/aws/aws_iam_access_key.rb +106 -106
- data/lib/resources/aws/aws_iam_access_keys.rb +149 -149
- data/lib/resources/aws/aws_iam_group.rb +58 -58
- data/lib/resources/aws/aws_iam_groups.rb +52 -52
- data/lib/resources/aws/aws_iam_password_policy.rb +116 -116
- data/lib/resources/aws/aws_iam_policies.rb +53 -53
- data/lib/resources/aws/aws_iam_policy.rb +291 -291
- data/lib/resources/aws/aws_iam_role.rb +55 -55
- data/lib/resources/aws/aws_iam_root_user.rb +78 -78
- data/lib/resources/aws/aws_iam_user.rb +142 -142
- data/lib/resources/aws/aws_iam_users.rb +146 -146
- data/lib/resources/aws/aws_kms_key.rb +96 -96
- data/lib/resources/aws/aws_kms_keys.rb +53 -53
- data/lib/resources/aws/aws_rds_instance.rb +71 -71
- data/lib/resources/aws/aws_route_table.rb +63 -63
- data/lib/resources/aws/aws_route_tables.rb +60 -60
- data/lib/resources/aws/aws_s3_bucket.rb +137 -137
- data/lib/resources/aws/aws_s3_bucket_object.rb +82 -82
- data/lib/resources/aws/aws_s3_buckets.rb +51 -51
- data/lib/resources/aws/aws_security_group.rb +249 -249
- data/lib/resources/aws/aws_security_groups.rb +68 -68
- data/lib/resources/aws/aws_sns_subscription.rb +78 -78
- data/lib/resources/aws/aws_sns_topic.rb +53 -53
- data/lib/resources/aws/aws_sns_topics.rb +56 -56
- data/lib/resources/aws/aws_subnet.rb +88 -88
- data/lib/resources/aws/aws_subnets.rb +53 -53
- data/lib/resources/aws/aws_vpc.rb +73 -73
- data/lib/resources/aws/aws_vpcs.rb +52 -52
- data/lib/resources/azure/azure_backend.rb +377 -377
- data/lib/resources/azure/azure_generic_resource.rb +59 -59
- data/lib/resources/azure/azure_resource_group.rb +152 -152
- data/lib/resources/azure/azure_virtual_machine.rb +264 -264
- data/lib/resources/azure/azure_virtual_machine_data_disk.rb +134 -134
- data/lib/resources/bash.rb +35 -35
- data/lib/resources/bond.rb +69 -69
- data/lib/resources/bridge.rb +122 -122
- data/lib/resources/chocolatey_package.rb +78 -78
- data/lib/resources/command.rb +73 -73
- data/lib/resources/cpan.rb +58 -58
- data/lib/resources/cran.rb +64 -64
- data/lib/resources/crontab.rb +169 -169
- data/lib/resources/csv.rb +56 -56
- data/lib/resources/dh_params.rb +77 -77
- data/lib/resources/directory.rb +25 -25
- data/lib/resources/docker.rb +236 -236
- data/lib/resources/docker_container.rb +89 -89
- data/lib/resources/docker_image.rb +83 -83
- data/lib/resources/docker_object.rb +57 -57
- data/lib/resources/docker_service.rb +90 -90
- data/lib/resources/elasticsearch.rb +169 -169
- data/lib/resources/etc_fstab.rb +94 -94
- data/lib/resources/etc_group.rb +154 -154
- data/lib/resources/etc_hosts.rb +66 -66
- data/lib/resources/etc_hosts_allow_deny.rb +112 -112
- data/lib/resources/file.rb +298 -298
- data/lib/resources/filesystem.rb +31 -31
- data/lib/resources/firewalld.rb +143 -143
- data/lib/resources/gem.rb +70 -70
- data/lib/resources/groups.rb +215 -215
- data/lib/resources/grub_conf.rb +227 -227
- data/lib/resources/host.rb +306 -306
- data/lib/resources/http.rb +253 -253
- data/lib/resources/iis_app.rb +101 -101
- data/lib/resources/iis_site.rb +148 -148
- data/lib/resources/inetd_conf.rb +54 -54
- data/lib/resources/ini.rb +29 -29
- data/lib/resources/interface.rb +129 -129
- data/lib/resources/iptables.rb +80 -80
- data/lib/resources/json.rb +111 -111
- data/lib/resources/kernel_module.rb +107 -107
- data/lib/resources/kernel_parameter.rb +58 -58
- data/lib/resources/key_rsa.rb +63 -63
- data/lib/resources/limits_conf.rb +46 -46
- data/lib/resources/login_def.rb +57 -57
- data/lib/resources/mount.rb +88 -88
- data/lib/resources/mssql_session.rb +101 -101
- data/lib/resources/mysql.rb +82 -82
- data/lib/resources/mysql_conf.rb +127 -127
- data/lib/resources/mysql_session.rb +85 -85
- data/lib/resources/nginx.rb +96 -96
- data/lib/resources/nginx_conf.rb +226 -226
- data/lib/resources/npm.rb +48 -48
- data/lib/resources/ntp_conf.rb +51 -51
- data/lib/resources/oneget.rb +71 -71
- data/lib/resources/oracledb_session.rb +139 -139
- data/lib/resources/os.rb +36 -36
- data/lib/resources/os_env.rb +86 -86
- data/lib/resources/package.rb +370 -370
- data/lib/resources/packages.rb +111 -111
- data/lib/resources/parse_config.rb +112 -112
- data/lib/resources/passwd.rb +76 -76
- data/lib/resources/pip.rb +130 -130
- data/lib/resources/platform.rb +109 -109
- data/lib/resources/port.rb +771 -771
- data/lib/resources/postgres.rb +131 -131
- data/lib/resources/postgres_conf.rb +114 -114
- data/lib/resources/postgres_hba_conf.rb +90 -90
- data/lib/resources/postgres_ident_conf.rb +79 -79
- data/lib/resources/postgres_session.rb +71 -71
- data/lib/resources/powershell.rb +67 -67
- data/lib/resources/processes.rb +204 -204
- data/lib/resources/rabbitmq_conf.rb +51 -51
- data/lib/resources/registry_key.rb +297 -297
- data/lib/resources/security_policy.rb +180 -180
- data/lib/resources/service.rb +794 -794
- data/lib/resources/shadow.rb +159 -159
- data/lib/resources/ssh_conf.rb +97 -97
- data/lib/resources/ssl.rb +99 -99
- data/lib/resources/sys_info.rb +28 -28
- data/lib/resources/toml.rb +32 -32
- data/lib/resources/users.rb +654 -654
- data/lib/resources/vbscript.rb +68 -68
- data/lib/resources/virtualization.rb +247 -247
- data/lib/resources/windows_feature.rb +84 -84
- data/lib/resources/windows_hotfix.rb +35 -35
- data/lib/resources/windows_task.rb +102 -102
- data/lib/resources/wmi.rb +110 -110
- data/lib/resources/x509_certificate.rb +137 -137
- data/lib/resources/xinetd.rb +106 -106
- data/lib/resources/xml.rb +46 -46
- data/lib/resources/yaml.rb +43 -43
- data/lib/resources/yum.rb +180 -180
- 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/enumerable_delegation.rb +9 -9
- data/lib/utils/erlang_parser.rb +192 -192
- data/lib/utils/file_reader.rb +25 -25
- data/lib/utils/filter.rb +273 -273
- data/lib/utils/filter_array.rb +27 -27
- data/lib/utils/find_files.rb +47 -47
- 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 +105 -105
- data/lib/utils/object_traversal.rb +49 -49
- data/lib/utils/parser.rb +274 -274
- data/lib/utils/pkey_reader.rb +15 -15
- data/lib/utils/plugin_registry.rb +93 -93
- data/lib/utils/simpleconfig.rb +120 -120
- data/lib/utils/spdx.rb +13 -13
- data/lib/utils/spdx.txt +343 -343
- metadata +3 -3
|
@@ -1,360 +1,360 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
# author: Christoph Hartmann
|
|
3
|
-
# author: Dominik Richter
|
|
4
|
-
|
|
5
|
-
require 'net/http'
|
|
6
|
-
require 'uri'
|
|
7
|
-
require 'json'
|
|
8
|
-
|
|
9
|
-
require_relative 'api/login'
|
|
10
|
-
|
|
11
|
-
module Compliance
|
|
12
|
-
class ServerConfigurationMissing < StandardError; end
|
|
13
|
-
|
|
14
|
-
# API Implementation does not hold any state by itself,
|
|
15
|
-
# everything will be stored in local Configuration store
|
|
16
|
-
class API
|
|
17
|
-
extend Compliance::API::Login
|
|
18
|
-
|
|
19
|
-
# return all compliance profiles available for the user
|
|
20
|
-
# the user is either specified in the options hash or by default
|
|
21
|
-
# the username of the account is used that is logged in
|
|
22
|
-
def self.profiles(config) # rubocop:disable PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength
|
|
23
|
-
owner = config['owner'] || config['user']
|
|
24
|
-
|
|
25
|
-
# Chef Compliance
|
|
26
|
-
if is_compliance_server?(config)
|
|
27
|
-
url = "#{config['server']}/user/compliance"
|
|
28
|
-
# Chef Automate2
|
|
29
|
-
elsif is_automate2_server?(config)
|
|
30
|
-
url = "#{config['server']}/compliance/profiles/search"
|
|
31
|
-
# Chef Automate
|
|
32
|
-
elsif is_automate_server?(config)
|
|
33
|
-
url = "#{config['server']}/profiles/#{owner}"
|
|
34
|
-
else
|
|
35
|
-
raise ServerConfigurationMissing
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
headers = get_headers(config)
|
|
39
|
-
|
|
40
|
-
if is_automate2_server?(config)
|
|
41
|
-
body = { owner: owner }.to_json
|
|
42
|
-
response = Compliance::HTTP.post_with_headers(url, headers, body, config['insecure'])
|
|
43
|
-
else
|
|
44
|
-
response = Compliance::HTTP.get(url, headers, config['insecure'])
|
|
45
|
-
end
|
|
46
|
-
data = response.body
|
|
47
|
-
response_code = response.code
|
|
48
|
-
case response_code
|
|
49
|
-
when '200'
|
|
50
|
-
msg = 'success'
|
|
51
|
-
profiles = JSON.parse(data)
|
|
52
|
-
# iterate over profiles
|
|
53
|
-
if is_compliance_server?(config)
|
|
54
|
-
mapped_profiles = []
|
|
55
|
-
profiles.values.each { |org|
|
|
56
|
-
mapped_profiles += org.values
|
|
57
|
-
}
|
|
58
|
-
# Chef Automate pre 0.8.0
|
|
59
|
-
elsif is_automate_server_pre_080?(config)
|
|
60
|
-
mapped_profiles = profiles.values.flatten
|
|
61
|
-
elsif is_automate2_server?(config)
|
|
62
|
-
mapped_profiles = []
|
|
63
|
-
profiles['profiles'].each { |p|
|
|
64
|
-
mapped_profiles << p
|
|
65
|
-
}
|
|
66
|
-
else
|
|
67
|
-
mapped_profiles = profiles.map { |e|
|
|
68
|
-
e['owner_id'] = owner
|
|
69
|
-
e
|
|
70
|
-
}
|
|
71
|
-
end
|
|
72
|
-
return msg, mapped_profiles
|
|
73
|
-
when '401'
|
|
74
|
-
msg = '401 Unauthorized. Please check your token.'
|
|
75
|
-
return msg, []
|
|
76
|
-
else
|
|
77
|
-
msg = "An unexpected error occurred (HTTP #{response_code}): #{response.message}"
|
|
78
|
-
return msg, []
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# return the server api version
|
|
83
|
-
# NB this method does not use Compliance::Configuration to allow for using
|
|
84
|
-
# it before we know the version (e.g. oidc or not)
|
|
85
|
-
def self.version(config)
|
|
86
|
-
url = config['server']
|
|
87
|
-
insecure = config['insecure']
|
|
88
|
-
|
|
89
|
-
raise ServerConfigurationMissing if url.nil?
|
|
90
|
-
|
|
91
|
-
headers = get_headers(config)
|
|
92
|
-
response = Compliance::HTTP.get(url+'/version', headers, insecure)
|
|
93
|
-
return {} if response.code == '404'
|
|
94
|
-
|
|
95
|
-
data = response.body
|
|
96
|
-
return {} if data.nil? || data.empty?
|
|
97
|
-
|
|
98
|
-
parsed = JSON.parse(data)
|
|
99
|
-
return {} unless parsed.key?('version') && !parsed['version'].empty?
|
|
100
|
-
|
|
101
|
-
parsed
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# verifies that a profile
|
|
105
|
-
def self.exist?(config, profile)
|
|
106
|
-
owner, id, ver = profile_split(profile)
|
|
107
|
-
|
|
108
|
-
# ensure that we do not manipulate the configuration object
|
|
109
|
-
user_config = config.dup
|
|
110
|
-
user_config['owner'] = owner
|
|
111
|
-
_msg, profiles = Compliance::API.profiles(user_config)
|
|
112
|
-
|
|
113
|
-
if !profiles.empty?
|
|
114
|
-
profiles.any? do |p|
|
|
115
|
-
profile_owner = p['owner_id'] || p['owner']
|
|
116
|
-
profile_owner == owner &&
|
|
117
|
-
p['name'] == id &&
|
|
118
|
-
(ver.nil? || p['version'] == ver)
|
|
119
|
-
end
|
|
120
|
-
else
|
|
121
|
-
false
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
def self.upload(config, owner, profile_name, archive_path)
|
|
126
|
-
# Chef Compliance
|
|
127
|
-
if is_compliance_server?(config)
|
|
128
|
-
url = "#{config['server']}/owners/#{owner}/compliance/#{profile_name}/tar"
|
|
129
|
-
# Chef Automate pre 0.8.0
|
|
130
|
-
elsif is_automate_server_pre_080?(config)
|
|
131
|
-
url = "#{config['server']}/#{owner}"
|
|
132
|
-
elsif is_automate2_server?(config)
|
|
133
|
-
url = "#{config['server']}/compliance/profiles?owner=#{owner}"
|
|
134
|
-
# Chef Automate
|
|
135
|
-
else
|
|
136
|
-
url = "#{config['server']}/profiles/#{owner}"
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
headers = get_headers(config)
|
|
140
|
-
if is_automate2_server?(config)
|
|
141
|
-
res = Compliance::HTTP.post_multipart_file(url, headers, archive_path, config['insecure'])
|
|
142
|
-
else
|
|
143
|
-
res = Compliance::HTTP.post_file(url, headers, archive_path, config['insecure'])
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
[res.is_a?(Net::HTTPSuccess), res.body]
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
# Use username and refresh_token to get an API access token
|
|
150
|
-
def self.get_token_via_refresh_token(url, refresh_token, insecure)
|
|
151
|
-
uri = URI.parse("#{url}/login")
|
|
152
|
-
req = Net::HTTP::Post.new(uri.path)
|
|
153
|
-
req.body = { token: refresh_token }.to_json
|
|
154
|
-
access_token = nil
|
|
155
|
-
response = Compliance::HTTP.send_request(uri, req, insecure)
|
|
156
|
-
data = response.body
|
|
157
|
-
if response.code == '200'
|
|
158
|
-
begin
|
|
159
|
-
tokendata = JSON.parse(data)
|
|
160
|
-
access_token = tokendata['access_token']
|
|
161
|
-
msg = 'Successfully fetched API access token'
|
|
162
|
-
success = true
|
|
163
|
-
rescue JSON::ParserError => e
|
|
164
|
-
success = false
|
|
165
|
-
msg = e.message
|
|
166
|
-
end
|
|
167
|
-
else
|
|
168
|
-
success = false
|
|
169
|
-
msg = "Failed to authenticate to #{url} \n\
|
|
170
|
-
Response code: #{response.code}\n Body: #{response.body}"
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
[success, msg, access_token]
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
# Use username and password to get an API access token
|
|
177
|
-
def self.get_token_via_password(url, username, password, insecure)
|
|
178
|
-
uri = URI.parse("#{url}/login")
|
|
179
|
-
req = Net::HTTP::Post.new(uri.path)
|
|
180
|
-
req.body = { userid: username, password: password }.to_json
|
|
181
|
-
access_token = nil
|
|
182
|
-
response = Compliance::HTTP.send_request(uri, req, insecure)
|
|
183
|
-
data = response.body
|
|
184
|
-
if response.code == '200'
|
|
185
|
-
access_token = data
|
|
186
|
-
msg = 'Successfully fetched an API access token valid for 12 hours'
|
|
187
|
-
success = true
|
|
188
|
-
else
|
|
189
|
-
success = false
|
|
190
|
-
msg = "Failed to authenticate to #{url} \n\
|
|
191
|
-
Response code: #{response.code}\n Body: #{response.body}"
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
[success, msg, access_token]
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
def self.get_headers(config)
|
|
198
|
-
token = get_token(config)
|
|
199
|
-
if is_automate_server?(config) || is_automate2_server?(config)
|
|
200
|
-
headers = { 'chef-delivery-enterprise' => config['automate']['ent'] }
|
|
201
|
-
if config['automate']['token_type'] == 'dctoken'
|
|
202
|
-
headers['x-data-collector-token'] = token
|
|
203
|
-
else
|
|
204
|
-
headers['chef-delivery-user'] = config['user']
|
|
205
|
-
headers['chef-delivery-token'] = token
|
|
206
|
-
end
|
|
207
|
-
else
|
|
208
|
-
headers = { 'Authorization' => "Bearer #{token}" }
|
|
209
|
-
end
|
|
210
|
-
headers
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
def self.get_token(config)
|
|
214
|
-
return config['token'] unless config['refresh_token']
|
|
215
|
-
_success, _msg, token = get_token_via_refresh_token(config['server'], config['refresh_token'], config['insecure'])
|
|
216
|
-
token
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
def self.target_url(config, profile)
|
|
220
|
-
owner, id, ver = profile_split(profile)
|
|
221
|
-
|
|
222
|
-
return "#{config['server']}/compliance/profiles/tar" if is_automate2_server?(config)
|
|
223
|
-
return "#{config['server']}/owners/#{owner}/compliance/#{id}/tar" unless is_automate_server?(config)
|
|
224
|
-
|
|
225
|
-
if ver.nil?
|
|
226
|
-
"#{config['server']}/profiles/#{owner}/#{id}/tar"
|
|
227
|
-
else
|
|
228
|
-
"#{config['server']}/profiles/#{owner}/#{id}/version/#{ver}/tar"
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
def self.profile_split(profile)
|
|
233
|
-
owner, id = profile.split('/')
|
|
234
|
-
id, version = id.split('#')
|
|
235
|
-
[owner, id, version]
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
# returns a parsed url for `admin/profile` or `compliance://admin/profile`
|
|
239
|
-
def self.sanitize_profile_name(profile)
|
|
240
|
-
if URI(profile).scheme == 'compliance'
|
|
241
|
-
uri = URI(profile)
|
|
242
|
-
else
|
|
243
|
-
uri = URI("compliance://#{profile}")
|
|
244
|
-
end
|
|
245
|
-
uri.to_s.sub(%r{^compliance:\/\/}, '')
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
def self.is_compliance_server?(config)
|
|
249
|
-
config['server_type'] == 'compliance'
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
def self.is_automate_server_pre_080?(config)
|
|
253
|
-
# Automate versions before 0.8.x do not have a valid version in the config
|
|
254
|
-
return false unless config['server_type'] == 'automate'
|
|
255
|
-
server_version_from_config(config).nil?
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
def self.is_automate_server_080_and_later?(config)
|
|
259
|
-
# Automate versions 0.8.x and later will have a "version" key in the config
|
|
260
|
-
# that is properly parsed out via server_version_from_config below
|
|
261
|
-
return false unless config['server_type'] == 'automate'
|
|
262
|
-
!server_version_from_config(config).nil?
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
def self.is_automate2_server?(config)
|
|
266
|
-
config['server_type'] == 'automate2'
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
def self.is_automate_server?(config)
|
|
270
|
-
config['server_type'] == 'automate'
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
def self.server_version_from_config(config)
|
|
274
|
-
# Automate versions 0.8.x and later will have a "version" key in the config
|
|
275
|
-
# that looks like: "version":{"api":"compliance","version":"0.8.24"}
|
|
276
|
-
return nil unless config.key?('version')
|
|
277
|
-
return nil unless config['version'].is_a?(Hash)
|
|
278
|
-
config['version']['version']
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
def self.determine_server_type(url, insecure)
|
|
282
|
-
if target_is_automate2_server?(url, insecure)
|
|
283
|
-
:automate2
|
|
284
|
-
elsif target_is_automate_server?(url, insecure)
|
|
285
|
-
:automate
|
|
286
|
-
elsif target_is_compliance_server?(url, insecure)
|
|
287
|
-
:compliance
|
|
288
|
-
else
|
|
289
|
-
Inspec::Log.debug('Could not determine server type using known endpoints')
|
|
290
|
-
nil
|
|
291
|
-
end
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
def self.target_is_automate2_server?(url, insecure)
|
|
295
|
-
automate_endpoint = '/dex/auth'
|
|
296
|
-
response = Compliance::HTTP.get(url + automate_endpoint, nil, insecure)
|
|
297
|
-
if response.code == '400'
|
|
298
|
-
Inspec::Log.debug(
|
|
299
|
-
"Received 400 from #{url}#{automate_endpoint} - " \
|
|
300
|
-
'assuming target is a Chef Automate2 instance',
|
|
301
|
-
)
|
|
302
|
-
true
|
|
303
|
-
else
|
|
304
|
-
false
|
|
305
|
-
end
|
|
306
|
-
end
|
|
307
|
-
|
|
308
|
-
def self.target_is_automate_server?(url, insecure)
|
|
309
|
-
automate_endpoint = '/compliance/version'
|
|
310
|
-
response = Compliance::HTTP.get(url + automate_endpoint, nil, insecure)
|
|
311
|
-
case response.code
|
|
312
|
-
when '401'
|
|
313
|
-
Inspec::Log.debug(
|
|
314
|
-
"Received 401 from #{url}#{automate_endpoint} - " \
|
|
315
|
-
'assuming target is a Chef Automate instance',
|
|
316
|
-
)
|
|
317
|
-
true
|
|
318
|
-
when '200'
|
|
319
|
-
# Chef Automate currently returns 401 for `/compliance/version` but some
|
|
320
|
-
# versions of OpsWorks Chef Automate return 200 and a Chef Manage page
|
|
321
|
-
# when unauthenticated requests are received.
|
|
322
|
-
if response.body.include?('Are You Looking For the Chef Server?')
|
|
323
|
-
Inspec::Log.debug(
|
|
324
|
-
"Received 200 from #{url}#{automate_endpoint} - " \
|
|
325
|
-
'assuming target is an OpsWorks Chef Automate instance',
|
|
326
|
-
)
|
|
327
|
-
true
|
|
328
|
-
else
|
|
329
|
-
Inspec::Log.debug(
|
|
330
|
-
"Received 200 from #{url}#{automate_endpoint} " \
|
|
331
|
-
'but did not receive the Chef Manage page - ' \
|
|
332
|
-
'assuming target is not a Chef Automate instance',
|
|
333
|
-
)
|
|
334
|
-
false
|
|
335
|
-
end
|
|
336
|
-
else
|
|
337
|
-
Inspec::Log.debug(
|
|
338
|
-
"Received unexpected status code #{response.code} " \
|
|
339
|
-
"from #{url}#{automate_endpoint} - " \
|
|
340
|
-
'assuming target is not a Chef Automate instance',
|
|
341
|
-
)
|
|
342
|
-
false
|
|
343
|
-
end
|
|
344
|
-
end
|
|
345
|
-
|
|
346
|
-
def self.target_is_compliance_server?(url, insecure)
|
|
347
|
-
# All versions of Chef Compliance return 200 for `/api/version`
|
|
348
|
-
compliance_endpoint = '/api/version'
|
|
349
|
-
|
|
350
|
-
response = Compliance::HTTP.get(url + compliance_endpoint, nil, insecure)
|
|
351
|
-
return false unless response.code == '200'
|
|
352
|
-
|
|
353
|
-
Inspec::Log.debug(
|
|
354
|
-
"Received 200 from #{url}#{compliance_endpoint} - " \
|
|
355
|
-
'assuming target is a Compliance server',
|
|
356
|
-
)
|
|
357
|
-
true
|
|
358
|
-
end
|
|
359
|
-
end
|
|
360
|
-
end
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# author: Christoph Hartmann
|
|
3
|
+
# author: Dominik Richter
|
|
4
|
+
|
|
5
|
+
require 'net/http'
|
|
6
|
+
require 'uri'
|
|
7
|
+
require 'json'
|
|
8
|
+
|
|
9
|
+
require_relative 'api/login'
|
|
10
|
+
|
|
11
|
+
module Compliance
|
|
12
|
+
class ServerConfigurationMissing < StandardError; end
|
|
13
|
+
|
|
14
|
+
# API Implementation does not hold any state by itself,
|
|
15
|
+
# everything will be stored in local Configuration store
|
|
16
|
+
class API
|
|
17
|
+
extend Compliance::API::Login
|
|
18
|
+
|
|
19
|
+
# return all compliance profiles available for the user
|
|
20
|
+
# the user is either specified in the options hash or by default
|
|
21
|
+
# the username of the account is used that is logged in
|
|
22
|
+
def self.profiles(config) # rubocop:disable PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength
|
|
23
|
+
owner = config['owner'] || config['user']
|
|
24
|
+
|
|
25
|
+
# Chef Compliance
|
|
26
|
+
if is_compliance_server?(config)
|
|
27
|
+
url = "#{config['server']}/user/compliance"
|
|
28
|
+
# Chef Automate2
|
|
29
|
+
elsif is_automate2_server?(config)
|
|
30
|
+
url = "#{config['server']}/compliance/profiles/search"
|
|
31
|
+
# Chef Automate
|
|
32
|
+
elsif is_automate_server?(config)
|
|
33
|
+
url = "#{config['server']}/profiles/#{owner}"
|
|
34
|
+
else
|
|
35
|
+
raise ServerConfigurationMissing
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
headers = get_headers(config)
|
|
39
|
+
|
|
40
|
+
if is_automate2_server?(config)
|
|
41
|
+
body = { owner: owner }.to_json
|
|
42
|
+
response = Compliance::HTTP.post_with_headers(url, headers, body, config['insecure'])
|
|
43
|
+
else
|
|
44
|
+
response = Compliance::HTTP.get(url, headers, config['insecure'])
|
|
45
|
+
end
|
|
46
|
+
data = response.body
|
|
47
|
+
response_code = response.code
|
|
48
|
+
case response_code
|
|
49
|
+
when '200'
|
|
50
|
+
msg = 'success'
|
|
51
|
+
profiles = JSON.parse(data)
|
|
52
|
+
# iterate over profiles
|
|
53
|
+
if is_compliance_server?(config)
|
|
54
|
+
mapped_profiles = []
|
|
55
|
+
profiles.values.each { |org|
|
|
56
|
+
mapped_profiles += org.values
|
|
57
|
+
}
|
|
58
|
+
# Chef Automate pre 0.8.0
|
|
59
|
+
elsif is_automate_server_pre_080?(config)
|
|
60
|
+
mapped_profiles = profiles.values.flatten
|
|
61
|
+
elsif is_automate2_server?(config)
|
|
62
|
+
mapped_profiles = []
|
|
63
|
+
profiles['profiles'].each { |p|
|
|
64
|
+
mapped_profiles << p
|
|
65
|
+
}
|
|
66
|
+
else
|
|
67
|
+
mapped_profiles = profiles.map { |e|
|
|
68
|
+
e['owner_id'] = owner
|
|
69
|
+
e
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
return msg, mapped_profiles
|
|
73
|
+
when '401'
|
|
74
|
+
msg = '401 Unauthorized. Please check your token.'
|
|
75
|
+
return msg, []
|
|
76
|
+
else
|
|
77
|
+
msg = "An unexpected error occurred (HTTP #{response_code}): #{response.message}"
|
|
78
|
+
return msg, []
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# return the server api version
|
|
83
|
+
# NB this method does not use Compliance::Configuration to allow for using
|
|
84
|
+
# it before we know the version (e.g. oidc or not)
|
|
85
|
+
def self.version(config)
|
|
86
|
+
url = config['server']
|
|
87
|
+
insecure = config['insecure']
|
|
88
|
+
|
|
89
|
+
raise ServerConfigurationMissing if url.nil?
|
|
90
|
+
|
|
91
|
+
headers = get_headers(config)
|
|
92
|
+
response = Compliance::HTTP.get(url+'/version', headers, insecure)
|
|
93
|
+
return {} if response.code == '404'
|
|
94
|
+
|
|
95
|
+
data = response.body
|
|
96
|
+
return {} if data.nil? || data.empty?
|
|
97
|
+
|
|
98
|
+
parsed = JSON.parse(data)
|
|
99
|
+
return {} unless parsed.key?('version') && !parsed['version'].empty?
|
|
100
|
+
|
|
101
|
+
parsed
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# verifies that a profile
|
|
105
|
+
def self.exist?(config, profile)
|
|
106
|
+
owner, id, ver = profile_split(profile)
|
|
107
|
+
|
|
108
|
+
# ensure that we do not manipulate the configuration object
|
|
109
|
+
user_config = config.dup
|
|
110
|
+
user_config['owner'] = owner
|
|
111
|
+
_msg, profiles = Compliance::API.profiles(user_config)
|
|
112
|
+
|
|
113
|
+
if !profiles.empty?
|
|
114
|
+
profiles.any? do |p|
|
|
115
|
+
profile_owner = p['owner_id'] || p['owner']
|
|
116
|
+
profile_owner == owner &&
|
|
117
|
+
p['name'] == id &&
|
|
118
|
+
(ver.nil? || p['version'] == ver)
|
|
119
|
+
end
|
|
120
|
+
else
|
|
121
|
+
false
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def self.upload(config, owner, profile_name, archive_path)
|
|
126
|
+
# Chef Compliance
|
|
127
|
+
if is_compliance_server?(config)
|
|
128
|
+
url = "#{config['server']}/owners/#{owner}/compliance/#{profile_name}/tar"
|
|
129
|
+
# Chef Automate pre 0.8.0
|
|
130
|
+
elsif is_automate_server_pre_080?(config)
|
|
131
|
+
url = "#{config['server']}/#{owner}"
|
|
132
|
+
elsif is_automate2_server?(config)
|
|
133
|
+
url = "#{config['server']}/compliance/profiles?owner=#{owner}"
|
|
134
|
+
# Chef Automate
|
|
135
|
+
else
|
|
136
|
+
url = "#{config['server']}/profiles/#{owner}"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
headers = get_headers(config)
|
|
140
|
+
if is_automate2_server?(config)
|
|
141
|
+
res = Compliance::HTTP.post_multipart_file(url, headers, archive_path, config['insecure'])
|
|
142
|
+
else
|
|
143
|
+
res = Compliance::HTTP.post_file(url, headers, archive_path, config['insecure'])
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
[res.is_a?(Net::HTTPSuccess), res.body]
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Use username and refresh_token to get an API access token
|
|
150
|
+
def self.get_token_via_refresh_token(url, refresh_token, insecure)
|
|
151
|
+
uri = URI.parse("#{url}/login")
|
|
152
|
+
req = Net::HTTP::Post.new(uri.path)
|
|
153
|
+
req.body = { token: refresh_token }.to_json
|
|
154
|
+
access_token = nil
|
|
155
|
+
response = Compliance::HTTP.send_request(uri, req, insecure)
|
|
156
|
+
data = response.body
|
|
157
|
+
if response.code == '200'
|
|
158
|
+
begin
|
|
159
|
+
tokendata = JSON.parse(data)
|
|
160
|
+
access_token = tokendata['access_token']
|
|
161
|
+
msg = 'Successfully fetched API access token'
|
|
162
|
+
success = true
|
|
163
|
+
rescue JSON::ParserError => e
|
|
164
|
+
success = false
|
|
165
|
+
msg = e.message
|
|
166
|
+
end
|
|
167
|
+
else
|
|
168
|
+
success = false
|
|
169
|
+
msg = "Failed to authenticate to #{url} \n\
|
|
170
|
+
Response code: #{response.code}\n Body: #{response.body}"
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
[success, msg, access_token]
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Use username and password to get an API access token
|
|
177
|
+
def self.get_token_via_password(url, username, password, insecure)
|
|
178
|
+
uri = URI.parse("#{url}/login")
|
|
179
|
+
req = Net::HTTP::Post.new(uri.path)
|
|
180
|
+
req.body = { userid: username, password: password }.to_json
|
|
181
|
+
access_token = nil
|
|
182
|
+
response = Compliance::HTTP.send_request(uri, req, insecure)
|
|
183
|
+
data = response.body
|
|
184
|
+
if response.code == '200'
|
|
185
|
+
access_token = data
|
|
186
|
+
msg = 'Successfully fetched an API access token valid for 12 hours'
|
|
187
|
+
success = true
|
|
188
|
+
else
|
|
189
|
+
success = false
|
|
190
|
+
msg = "Failed to authenticate to #{url} \n\
|
|
191
|
+
Response code: #{response.code}\n Body: #{response.body}"
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
[success, msg, access_token]
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def self.get_headers(config)
|
|
198
|
+
token = get_token(config)
|
|
199
|
+
if is_automate_server?(config) || is_automate2_server?(config)
|
|
200
|
+
headers = { 'chef-delivery-enterprise' => config['automate']['ent'] }
|
|
201
|
+
if config['automate']['token_type'] == 'dctoken'
|
|
202
|
+
headers['x-data-collector-token'] = token
|
|
203
|
+
else
|
|
204
|
+
headers['chef-delivery-user'] = config['user']
|
|
205
|
+
headers['chef-delivery-token'] = token
|
|
206
|
+
end
|
|
207
|
+
else
|
|
208
|
+
headers = { 'Authorization' => "Bearer #{token}" }
|
|
209
|
+
end
|
|
210
|
+
headers
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def self.get_token(config)
|
|
214
|
+
return config['token'] unless config['refresh_token']
|
|
215
|
+
_success, _msg, token = get_token_via_refresh_token(config['server'], config['refresh_token'], config['insecure'])
|
|
216
|
+
token
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def self.target_url(config, profile)
|
|
220
|
+
owner, id, ver = profile_split(profile)
|
|
221
|
+
|
|
222
|
+
return "#{config['server']}/compliance/profiles/tar" if is_automate2_server?(config)
|
|
223
|
+
return "#{config['server']}/owners/#{owner}/compliance/#{id}/tar" unless is_automate_server?(config)
|
|
224
|
+
|
|
225
|
+
if ver.nil?
|
|
226
|
+
"#{config['server']}/profiles/#{owner}/#{id}/tar"
|
|
227
|
+
else
|
|
228
|
+
"#{config['server']}/profiles/#{owner}/#{id}/version/#{ver}/tar"
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def self.profile_split(profile)
|
|
233
|
+
owner, id = profile.split('/')
|
|
234
|
+
id, version = id.split('#')
|
|
235
|
+
[owner, id, version]
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# returns a parsed url for `admin/profile` or `compliance://admin/profile`
|
|
239
|
+
def self.sanitize_profile_name(profile)
|
|
240
|
+
if URI(profile).scheme == 'compliance'
|
|
241
|
+
uri = URI(profile)
|
|
242
|
+
else
|
|
243
|
+
uri = URI("compliance://#{profile}")
|
|
244
|
+
end
|
|
245
|
+
uri.to_s.sub(%r{^compliance:\/\/}, '')
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def self.is_compliance_server?(config)
|
|
249
|
+
config['server_type'] == 'compliance'
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def self.is_automate_server_pre_080?(config)
|
|
253
|
+
# Automate versions before 0.8.x do not have a valid version in the config
|
|
254
|
+
return false unless config['server_type'] == 'automate'
|
|
255
|
+
server_version_from_config(config).nil?
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def self.is_automate_server_080_and_later?(config)
|
|
259
|
+
# Automate versions 0.8.x and later will have a "version" key in the config
|
|
260
|
+
# that is properly parsed out via server_version_from_config below
|
|
261
|
+
return false unless config['server_type'] == 'automate'
|
|
262
|
+
!server_version_from_config(config).nil?
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def self.is_automate2_server?(config)
|
|
266
|
+
config['server_type'] == 'automate2'
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def self.is_automate_server?(config)
|
|
270
|
+
config['server_type'] == 'automate'
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def self.server_version_from_config(config)
|
|
274
|
+
# Automate versions 0.8.x and later will have a "version" key in the config
|
|
275
|
+
# that looks like: "version":{"api":"compliance","version":"0.8.24"}
|
|
276
|
+
return nil unless config.key?('version')
|
|
277
|
+
return nil unless config['version'].is_a?(Hash)
|
|
278
|
+
config['version']['version']
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def self.determine_server_type(url, insecure)
|
|
282
|
+
if target_is_automate2_server?(url, insecure)
|
|
283
|
+
:automate2
|
|
284
|
+
elsif target_is_automate_server?(url, insecure)
|
|
285
|
+
:automate
|
|
286
|
+
elsif target_is_compliance_server?(url, insecure)
|
|
287
|
+
:compliance
|
|
288
|
+
else
|
|
289
|
+
Inspec::Log.debug('Could not determine server type using known endpoints')
|
|
290
|
+
nil
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def self.target_is_automate2_server?(url, insecure)
|
|
295
|
+
automate_endpoint = '/dex/auth'
|
|
296
|
+
response = Compliance::HTTP.get(url + automate_endpoint, nil, insecure)
|
|
297
|
+
if response.code == '400'
|
|
298
|
+
Inspec::Log.debug(
|
|
299
|
+
"Received 400 from #{url}#{automate_endpoint} - " \
|
|
300
|
+
'assuming target is a Chef Automate2 instance',
|
|
301
|
+
)
|
|
302
|
+
true
|
|
303
|
+
else
|
|
304
|
+
false
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def self.target_is_automate_server?(url, insecure)
|
|
309
|
+
automate_endpoint = '/compliance/version'
|
|
310
|
+
response = Compliance::HTTP.get(url + automate_endpoint, nil, insecure)
|
|
311
|
+
case response.code
|
|
312
|
+
when '401'
|
|
313
|
+
Inspec::Log.debug(
|
|
314
|
+
"Received 401 from #{url}#{automate_endpoint} - " \
|
|
315
|
+
'assuming target is a Chef Automate instance',
|
|
316
|
+
)
|
|
317
|
+
true
|
|
318
|
+
when '200'
|
|
319
|
+
# Chef Automate currently returns 401 for `/compliance/version` but some
|
|
320
|
+
# versions of OpsWorks Chef Automate return 200 and a Chef Manage page
|
|
321
|
+
# when unauthenticated requests are received.
|
|
322
|
+
if response.body.include?('Are You Looking For the Chef Server?')
|
|
323
|
+
Inspec::Log.debug(
|
|
324
|
+
"Received 200 from #{url}#{automate_endpoint} - " \
|
|
325
|
+
'assuming target is an OpsWorks Chef Automate instance',
|
|
326
|
+
)
|
|
327
|
+
true
|
|
328
|
+
else
|
|
329
|
+
Inspec::Log.debug(
|
|
330
|
+
"Received 200 from #{url}#{automate_endpoint} " \
|
|
331
|
+
'but did not receive the Chef Manage page - ' \
|
|
332
|
+
'assuming target is not a Chef Automate instance',
|
|
333
|
+
)
|
|
334
|
+
false
|
|
335
|
+
end
|
|
336
|
+
else
|
|
337
|
+
Inspec::Log.debug(
|
|
338
|
+
"Received unexpected status code #{response.code} " \
|
|
339
|
+
"from #{url}#{automate_endpoint} - " \
|
|
340
|
+
'assuming target is not a Chef Automate instance',
|
|
341
|
+
)
|
|
342
|
+
false
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def self.target_is_compliance_server?(url, insecure)
|
|
347
|
+
# All versions of Chef Compliance return 200 for `/api/version`
|
|
348
|
+
compliance_endpoint = '/api/version'
|
|
349
|
+
|
|
350
|
+
response = Compliance::HTTP.get(url + compliance_endpoint, nil, insecure)
|
|
351
|
+
return false unless response.code == '200'
|
|
352
|
+
|
|
353
|
+
Inspec::Log.debug(
|
|
354
|
+
"Received 200 from #{url}#{compliance_endpoint} - " \
|
|
355
|
+
'assuming target is a Compliance server',
|
|
356
|
+
)
|
|
357
|
+
true
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
end
|