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,181 +1,181 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
# author: Christoph Hartmann
|
3
|
-
# author: Dominik Richter
|
4
|
-
#
|
5
|
-
# Security Configuration and Analysis
|
6
|
-
#
|
7
|
-
# Export local security policy:
|
8
|
-
# secedit /export /cfg secpol.cfg
|
9
|
-
#
|
10
|
-
# @link http://www.microsoft.com/en-us/download/details.aspx?id=25250
|
11
|
-
#
|
12
|
-
# In Windows, some security options are managed differently that the local GPO
|
13
|
-
# All local GPO parameters can be examined via Registry, but not all security
|
14
|
-
# parameters. Therefore we need a combination of Registry and secedit output
|
15
|
-
|
16
|
-
require 'hashie'
|
17
|
-
|
18
|
-
module Inspec::Resources
|
19
|
-
# known and supported MS privilege rights
|
20
|
-
# @see https://technet.microsoft.com/en-us/library/dd277311.aspx
|
21
|
-
# @see https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx
|
22
|
-
MS_PRIVILEGES_RIGHTS = [
|
23
|
-
'SeNetworkLogonRight',
|
24
|
-
'SeBackupPrivilege',
|
25
|
-
'SeChangeNotifyPrivilege',
|
26
|
-
'SeSystemtimePrivilege',
|
27
|
-
'SeCreatePagefilePrivilege',
|
28
|
-
'SeDebugPrivilege',
|
29
|
-
'SeRemoteShutdownPrivilege',
|
30
|
-
'SeAuditPrivilege',
|
31
|
-
'SeIncreaseQuotaPrivilege',
|
32
|
-
'SeIncreaseBasePriorityPrivilege',
|
33
|
-
'SeLoadDriverPrivilege',
|
34
|
-
'SeBatchLogonRight',
|
35
|
-
'SeServiceLogonRight',
|
36
|
-
'SeInteractiveLogonRight',
|
37
|
-
'SeSecurityPrivilege',
|
38
|
-
'SeSystemEnvironmentPrivilege',
|
39
|
-
'SeProfileSingleProcessPrivilege',
|
40
|
-
'SeSystemProfilePrivilege',
|
41
|
-
'SeAssignPrimaryTokenPrivilege',
|
42
|
-
'SeRestorePrivilege',
|
43
|
-
'SeShutdownPrivilege',
|
44
|
-
'SeTakeOwnershipPrivilege',
|
45
|
-
'SeUndockPrivilege',
|
46
|
-
'SeManageVolumePrivilege',
|
47
|
-
'SeRemoteInteractiveLogonRight',
|
48
|
-
'SeImpersonatePrivilege',
|
49
|
-
'SeCreateGlobalPrivilege',
|
50
|
-
'SeIncreaseWorking',
|
51
|
-
'SeTimeZonePrivilege',
|
52
|
-
'SeCreateSymbolicLinkPrivilege',
|
53
|
-
'SeDenyNetworkLogonRight', # Deny access to this computer from the network
|
54
|
-
'SeDenyInteractiveLogonRight', # Deny logon locally
|
55
|
-
'SeDenyBatchLogonRight', # Deny logon as a batch job
|
56
|
-
'SeDenyServiceLogonRight', # Deny logon as a service
|
57
|
-
'SeTcbPrivilege',
|
58
|
-
'SeMachineAccountPrivilege',
|
59
|
-
'SeCreateTokenPrivilege',
|
60
|
-
'SeCreatePermanentPrivilege',
|
61
|
-
'SeEnableDelegationPrivilege',
|
62
|
-
'SeLockMemoryPrivilege',
|
63
|
-
'SeSyncAgentPrivilege',
|
64
|
-
'SeUnsolicitedInputPrivilege',
|
65
|
-
'SeTrustedCredManAccessPrivilege',
|
66
|
-
'SeRelabelPrivilege', # the privilege to change a Windows integrity label (new to Windows Vista)
|
67
|
-
'SeDenyRemoteInteractiveLogonRight', # Deny logon through Terminal Services
|
68
|
-
].freeze
|
69
|
-
|
70
|
-
class SecurityPolicy < Inspec.resource(1)
|
71
|
-
name 'security_policy'
|
72
|
-
desc 'Use the security_policy InSpec audit resource to test security policies on the Microsoft Windows platform.'
|
73
|
-
example "
|
74
|
-
describe security_policy do
|
75
|
-
its('SeNetworkLogonRight') { should include 'S-1-5-11' }
|
76
|
-
end
|
77
|
-
|
78
|
-
describe security_policy(translate_sid: true) do
|
79
|
-
its('SeNetworkLogonRight') { should include 'NT AUTHORITY\\Authenticated Users' }
|
80
|
-
end
|
81
|
-
"
|
82
|
-
|
83
|
-
def initialize(opts = {})
|
84
|
-
@translate_sid = opts[:translate_sid] || false
|
85
|
-
end
|
86
|
-
|
87
|
-
def content
|
88
|
-
read_content
|
89
|
-
end
|
90
|
-
|
91
|
-
def params(*opts)
|
92
|
-
opts.inject(read_params) do |res, nxt|
|
93
|
-
res.respond_to?(:key) ? res[nxt] : nil
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def method_missing(name)
|
98
|
-
params = read_params
|
99
|
-
return nil if params.nil?
|
100
|
-
|
101
|
-
# deep search for hash key
|
102
|
-
params.extend Hashie::Extensions::DeepFind
|
103
|
-
res = params.deep_find(name.to_s)
|
104
|
-
|
105
|
-
# return an empty array if configuration does not include rights configuration
|
106
|
-
return [] if res.nil? && MS_PRIVILEGES_RIGHTS.include?(name.to_s)
|
107
|
-
res
|
108
|
-
end
|
109
|
-
|
110
|
-
def to_s
|
111
|
-
'Security Policy'
|
112
|
-
end
|
113
|
-
|
114
|
-
private
|
115
|
-
|
116
|
-
def read_content
|
117
|
-
return @content if defined?(@content)
|
118
|
-
|
119
|
-
# using process pid to prevent any race conditions with multiple runners
|
120
|
-
export_file = "win_secpol-#{Process.pid}.cfg"
|
121
|
-
|
122
|
-
# export the security policy
|
123
|
-
cmd = inspec.command("secedit /export /cfg #{export_file}")
|
124
|
-
return nil if cmd.exit_status.to_i != 0
|
125
|
-
|
126
|
-
# store file content
|
127
|
-
cmd = inspec.command("Get-Content #{export_file}")
|
128
|
-
return skip_resource "Can't read security policy" if cmd.exit_status.to_i != 0
|
129
|
-
|
130
|
-
@content = cmd.stdout
|
131
|
-
ensure
|
132
|
-
# delete temp file
|
133
|
-
inspec.command("Remove-Item #{export_file}").exit_status.to_i
|
134
|
-
end
|
135
|
-
|
136
|
-
def read_params
|
137
|
-
return @params if defined?(@params)
|
138
|
-
return @params = {} if read_content.nil?
|
139
|
-
|
140
|
-
conf = SimpleConfig.new(
|
141
|
-
@content,
|
142
|
-
assignment_regex: /^\s*(.*)=\s*(\S*)\s*$/,
|
143
|
-
)
|
144
|
-
@params = convert_hash(conf.params)
|
145
|
-
end
|
146
|
-
|
147
|
-
# extracts the values, this methods detects:
|
148
|
-
# numbers and SIDs and optimizes them for further usage
|
149
|
-
def extract_value(val)
|
150
|
-
if val =~ /^\d+$/
|
151
|
-
val.to_i
|
152
|
-
# special handling for SID array
|
153
|
-
elsif val =~ /[,]{0,1}\*\S/
|
154
|
-
if @translate_sid
|
155
|
-
val.split(',').map { |v|
|
156
|
-
object_name = inspec.command("(New-Object System.Security.Principal.SecurityIdentifier(\"#{v.sub('*S', 'S')}\")).Translate( [System.Security.Principal.NTAccount]).Value").stdout.to_s.strip
|
157
|
-
object_name.empty? || object_name.nil? ? v.sub('*S', 'S') : object_name
|
158
|
-
}
|
159
|
-
else
|
160
|
-
val.split(',').map { |v|
|
161
|
-
v.sub('*S', 'S')
|
162
|
-
}
|
163
|
-
end
|
164
|
-
# special handling for string values with "
|
165
|
-
elsif !(m = /^\"(.*)\"$/.match(val)).nil?
|
166
|
-
m[1]
|
167
|
-
else
|
168
|
-
val
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
def convert_hash(hash)
|
173
|
-
new_hash = {}
|
174
|
-
hash.each do |k, v|
|
175
|
-
v.is_a?(Hash) ? value = convert_hash(v) : value = extract_value(v)
|
176
|
-
new_hash[k.strip] = value
|
177
|
-
end
|
178
|
-
new_hash
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
1
|
+
# encoding: utf-8
|
2
|
+
# author: Christoph Hartmann
|
3
|
+
# author: Dominik Richter
|
4
|
+
#
|
5
|
+
# Security Configuration and Analysis
|
6
|
+
#
|
7
|
+
# Export local security policy:
|
8
|
+
# secedit /export /cfg secpol.cfg
|
9
|
+
#
|
10
|
+
# @link http://www.microsoft.com/en-us/download/details.aspx?id=25250
|
11
|
+
#
|
12
|
+
# In Windows, some security options are managed differently that the local GPO
|
13
|
+
# All local GPO parameters can be examined via Registry, but not all security
|
14
|
+
# parameters. Therefore we need a combination of Registry and secedit output
|
15
|
+
|
16
|
+
require 'hashie'
|
17
|
+
|
18
|
+
module Inspec::Resources
|
19
|
+
# known and supported MS privilege rights
|
20
|
+
# @see https://technet.microsoft.com/en-us/library/dd277311.aspx
|
21
|
+
# @see https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx
|
22
|
+
MS_PRIVILEGES_RIGHTS = [
|
23
|
+
'SeNetworkLogonRight',
|
24
|
+
'SeBackupPrivilege',
|
25
|
+
'SeChangeNotifyPrivilege',
|
26
|
+
'SeSystemtimePrivilege',
|
27
|
+
'SeCreatePagefilePrivilege',
|
28
|
+
'SeDebugPrivilege',
|
29
|
+
'SeRemoteShutdownPrivilege',
|
30
|
+
'SeAuditPrivilege',
|
31
|
+
'SeIncreaseQuotaPrivilege',
|
32
|
+
'SeIncreaseBasePriorityPrivilege',
|
33
|
+
'SeLoadDriverPrivilege',
|
34
|
+
'SeBatchLogonRight',
|
35
|
+
'SeServiceLogonRight',
|
36
|
+
'SeInteractiveLogonRight',
|
37
|
+
'SeSecurityPrivilege',
|
38
|
+
'SeSystemEnvironmentPrivilege',
|
39
|
+
'SeProfileSingleProcessPrivilege',
|
40
|
+
'SeSystemProfilePrivilege',
|
41
|
+
'SeAssignPrimaryTokenPrivilege',
|
42
|
+
'SeRestorePrivilege',
|
43
|
+
'SeShutdownPrivilege',
|
44
|
+
'SeTakeOwnershipPrivilege',
|
45
|
+
'SeUndockPrivilege',
|
46
|
+
'SeManageVolumePrivilege',
|
47
|
+
'SeRemoteInteractiveLogonRight',
|
48
|
+
'SeImpersonatePrivilege',
|
49
|
+
'SeCreateGlobalPrivilege',
|
50
|
+
'SeIncreaseWorking',
|
51
|
+
'SeTimeZonePrivilege',
|
52
|
+
'SeCreateSymbolicLinkPrivilege',
|
53
|
+
'SeDenyNetworkLogonRight', # Deny access to this computer from the network
|
54
|
+
'SeDenyInteractiveLogonRight', # Deny logon locally
|
55
|
+
'SeDenyBatchLogonRight', # Deny logon as a batch job
|
56
|
+
'SeDenyServiceLogonRight', # Deny logon as a service
|
57
|
+
'SeTcbPrivilege',
|
58
|
+
'SeMachineAccountPrivilege',
|
59
|
+
'SeCreateTokenPrivilege',
|
60
|
+
'SeCreatePermanentPrivilege',
|
61
|
+
'SeEnableDelegationPrivilege',
|
62
|
+
'SeLockMemoryPrivilege',
|
63
|
+
'SeSyncAgentPrivilege',
|
64
|
+
'SeUnsolicitedInputPrivilege',
|
65
|
+
'SeTrustedCredManAccessPrivilege',
|
66
|
+
'SeRelabelPrivilege', # the privilege to change a Windows integrity label (new to Windows Vista)
|
67
|
+
'SeDenyRemoteInteractiveLogonRight', # Deny logon through Terminal Services
|
68
|
+
].freeze
|
69
|
+
|
70
|
+
class SecurityPolicy < Inspec.resource(1)
|
71
|
+
name 'security_policy'
|
72
|
+
desc 'Use the security_policy InSpec audit resource to test security policies on the Microsoft Windows platform.'
|
73
|
+
example "
|
74
|
+
describe security_policy do
|
75
|
+
its('SeNetworkLogonRight') { should include 'S-1-5-11' }
|
76
|
+
end
|
77
|
+
|
78
|
+
describe security_policy(translate_sid: true) do
|
79
|
+
its('SeNetworkLogonRight') { should include 'NT AUTHORITY\\Authenticated Users' }
|
80
|
+
end
|
81
|
+
"
|
82
|
+
|
83
|
+
def initialize(opts = {})
|
84
|
+
@translate_sid = opts[:translate_sid] || false
|
85
|
+
end
|
86
|
+
|
87
|
+
def content
|
88
|
+
read_content
|
89
|
+
end
|
90
|
+
|
91
|
+
def params(*opts)
|
92
|
+
opts.inject(read_params) do |res, nxt|
|
93
|
+
res.respond_to?(:key) ? res[nxt] : nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def method_missing(name)
|
98
|
+
params = read_params
|
99
|
+
return nil if params.nil?
|
100
|
+
|
101
|
+
# deep search for hash key
|
102
|
+
params.extend Hashie::Extensions::DeepFind
|
103
|
+
res = params.deep_find(name.to_s)
|
104
|
+
|
105
|
+
# return an empty array if configuration does not include rights configuration
|
106
|
+
return [] if res.nil? && MS_PRIVILEGES_RIGHTS.include?(name.to_s)
|
107
|
+
res
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_s
|
111
|
+
'Security Policy'
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def read_content
|
117
|
+
return @content if defined?(@content)
|
118
|
+
|
119
|
+
# using process pid to prevent any race conditions with multiple runners
|
120
|
+
export_file = "win_secpol-#{Process.pid}.cfg"
|
121
|
+
|
122
|
+
# export the security policy
|
123
|
+
cmd = inspec.command("secedit /export /cfg #{export_file}")
|
124
|
+
return nil if cmd.exit_status.to_i != 0
|
125
|
+
|
126
|
+
# store file content
|
127
|
+
cmd = inspec.command("Get-Content #{export_file}")
|
128
|
+
return skip_resource "Can't read security policy" if cmd.exit_status.to_i != 0
|
129
|
+
|
130
|
+
@content = cmd.stdout
|
131
|
+
ensure
|
132
|
+
# delete temp file
|
133
|
+
inspec.command("Remove-Item #{export_file}").exit_status.to_i
|
134
|
+
end
|
135
|
+
|
136
|
+
def read_params
|
137
|
+
return @params if defined?(@params)
|
138
|
+
return @params = {} if read_content.nil?
|
139
|
+
|
140
|
+
conf = SimpleConfig.new(
|
141
|
+
@content,
|
142
|
+
assignment_regex: /^\s*(.*)=\s*(\S*)\s*$/,
|
143
|
+
)
|
144
|
+
@params = convert_hash(conf.params)
|
145
|
+
end
|
146
|
+
|
147
|
+
# extracts the values, this methods detects:
|
148
|
+
# numbers and SIDs and optimizes them for further usage
|
149
|
+
def extract_value(val)
|
150
|
+
if val =~ /^\d+$/
|
151
|
+
val.to_i
|
152
|
+
# special handling for SID array
|
153
|
+
elsif val =~ /[,]{0,1}\*\S/
|
154
|
+
if @translate_sid
|
155
|
+
val.split(',').map { |v|
|
156
|
+
object_name = inspec.command("(New-Object System.Security.Principal.SecurityIdentifier(\"#{v.sub('*S', 'S')}\")).Translate( [System.Security.Principal.NTAccount]).Value").stdout.to_s.strip
|
157
|
+
object_name.empty? || object_name.nil? ? v.sub('*S', 'S') : object_name
|
158
|
+
}
|
159
|
+
else
|
160
|
+
val.split(',').map { |v|
|
161
|
+
v.sub('*S', 'S')
|
162
|
+
}
|
163
|
+
end
|
164
|
+
# special handling for string values with "
|
165
|
+
elsif !(m = /^\"(.*)\"$/.match(val)).nil?
|
166
|
+
m[1]
|
167
|
+
else
|
168
|
+
val
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def convert_hash(hash)
|
173
|
+
new_hash = {}
|
174
|
+
hash.each do |k, v|
|
175
|
+
v.is_a?(Hash) ? value = convert_hash(v) : value = extract_value(v)
|
176
|
+
new_hash[k.strip] = value
|
177
|
+
end
|
178
|
+
new_hash
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
data/lib/resources/service.rb
CHANGED
@@ -1,784 +1,784 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
# author: Christoph Hartmann
|
3
|
-
# author: Dominik Richter
|
4
|
-
# author: Stephan Renatus
|
5
|
-
|
6
|
-
require 'hashie'
|
7
|
-
|
8
|
-
module Inspec::Resources
|
9
|
-
class Runlevels < Hash
|
10
|
-
attr_accessor :owner
|
11
|
-
|
12
|
-
def self.from_hash(owner, hash = {}, filter = nil)
|
13
|
-
res = Runlevels.new(owner)
|
14
|
-
filter = filter.first if filter.is_a?(Array) && filter.length <= 1
|
15
|
-
|
16
|
-
ks = case filter
|
17
|
-
when nil
|
18
|
-
hash.keys
|
19
|
-
when Regexp
|
20
|
-
hash.keys.find_all { |x| x.to_s =~ filter }
|
21
|
-
when Array
|
22
|
-
f = filter.map(&:to_s)
|
23
|
-
hash.keys.find_all { |x| f.include?(x.to_s) }
|
24
|
-
when Numeric
|
25
|
-
hash.keys.include?(filter) ? [filter] : []
|
26
|
-
else
|
27
|
-
hash.keys.find_all { |x| x == filter }
|
28
|
-
end
|
29
|
-
|
30
|
-
ks.each { |k| res[k] = hash[k] }
|
31
|
-
res
|
32
|
-
end
|
33
|
-
|
34
|
-
def initialize(owner, default = false)
|
35
|
-
@owner = owner
|
36
|
-
super(default)
|
37
|
-
end
|
38
|
-
|
39
|
-
def filter(f)
|
40
|
-
Runlevels.from_hash(owner, self, f)
|
41
|
-
end
|
42
|
-
|
43
|
-
# Check if all runlevels are enabled
|
44
|
-
#
|
45
|
-
# @return [boolean] true if all runlevels are enabled
|
46
|
-
def enabled?
|
47
|
-
values.all?
|
48
|
-
end
|
49
|
-
|
50
|
-
# Check if all runlevels are disabled
|
51
|
-
#
|
52
|
-
# @return [boolean] true if all runlevels are disabled
|
53
|
-
def disabled?
|
54
|
-
values.none?
|
55
|
-
end
|
56
|
-
|
57
|
-
def to_s
|
58
|
-
"#{owner} runlevels #{keys.join(', ')}"
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# We detect the init system for each operating system, based on the operating
|
63
|
-
# system.
|
64
|
-
#
|
65
|
-
# Fedora 15 : systemd
|
66
|
-
# RedHat 7 : systemd
|
67
|
-
# Ubuntu 15.04 : systemd
|
68
|
-
# Ubuntu < 15.04 : upstart
|
69
|
-
#
|
70
|
-
# TODO: extend the logic to detect the running init system, independently of OS
|
71
|
-
class Service < Inspec.resource(1)
|
72
|
-
name 'service'
|
73
|
-
desc 'Use the service InSpec audit resource to test if the named service is installed, running and/or enabled.'
|
74
|
-
example "
|
75
|
-
describe service('service_name') do
|
76
|
-
it { should be_installed }
|
77
|
-
it { should be_enabled }
|
78
|
-
it { should be_running }
|
79
|
-
its('type') { should be 'systemd' }
|
80
|
-
its ('startmode') { should be 'Auto'}
|
81
|
-
end
|
82
|
-
|
83
|
-
describe service('service_name').runlevels(3, 5) do
|
84
|
-
it { should be_enabled }
|
85
|
-
end
|
86
|
-
|
87
|
-
describe service('service_name').params do
|
88
|
-
its('UnitFileState') { should eq 'enabled' }
|
89
|
-
end
|
90
|
-
"
|
91
|
-
|
92
|
-
attr_reader :service_ctl
|
93
|
-
|
94
|
-
def initialize(service_name, service_ctl = nil)
|
95
|
-
@service_name = service_name
|
96
|
-
@service_mgmt = nil
|
97
|
-
@service_ctl ||= service_ctl
|
98
|
-
@cache = nil
|
99
|
-
@service_mgmt = select_service_mgmt
|
100
|
-
|
101
|
-
return skip_resource 'The `service` resource is not supported on your OS yet.' if @service_mgmt.nil?
|
102
|
-
end
|
103
|
-
|
104
|
-
def select_service_mgmt # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
105
|
-
os = inspec.os
|
106
|
-
platform = os[:name]
|
107
|
-
|
108
|
-
# Ubuntu
|
109
|
-
# @see: https://wiki.ubuntu.com/SystemdForUpstartUsers
|
110
|
-
# Ubuntu 15.04 : Systemd
|
111
|
-
# Systemd runs with PID 1 as /sbin/init.
|
112
|
-
# Upstart runs with PID 1 as /sbin/upstart.
|
113
|
-
# Ubuntu < 15.04 : Upstart
|
114
|
-
# Upstart runs with PID 1 as /sbin/init.
|
115
|
-
# Systemd runs with PID 1 as /lib/systemd/systemd.
|
116
|
-
if %w{ubuntu}.include?(platform)
|
117
|
-
version = os[:release].to_f
|
118
|
-
if version < 15.04
|
119
|
-
Upstart.new(inspec, service_ctl)
|
120
|
-
else
|
121
|
-
Systemd.new(inspec, service_ctl)
|
122
|
-
end
|
123
|
-
elsif %w{linuxmint}.include?(platform)
|
124
|
-
version = os[:release].to_f
|
125
|
-
if version < 18
|
126
|
-
Upstart.new(inspec, service_ctl)
|
127
|
-
else
|
128
|
-
Systemd.new(inspec, service_ctl)
|
129
|
-
end
|
130
|
-
elsif %w{debian}.include?(platform)
|
131
|
-
version = os[:release].to_i
|
132
|
-
if version > 7
|
133
|
-
Systemd.new(inspec, service_ctl)
|
134
|
-
else
|
135
|
-
SysV.new(inspec, service_ctl || '/usr/sbin/service')
|
136
|
-
end
|
137
|
-
elsif %w{redhat fedora centos oracle}.include?(platform)
|
138
|
-
version = os[:release].to_i
|
139
|
-
if (%w{redhat centos oracle}.include?(platform) && version >= 7) || (platform == 'fedora' && version >= 15)
|
140
|
-
Systemd.new(inspec, service_ctl)
|
141
|
-
else
|
142
|
-
SysV.new(inspec, service_ctl || '/sbin/service')
|
143
|
-
end
|
144
|
-
elsif %w{wrlinux}.include?(platform)
|
145
|
-
SysV.new(inspec, service_ctl)
|
146
|
-
elsif %w{mac_os_x}.include?(platform)
|
147
|
-
LaunchCtl.new(inspec, service_ctl)
|
148
|
-
elsif os.windows?
|
149
|
-
WindowsSrv.new(inspec)
|
150
|
-
elsif %w{freebsd}.include?(platform)
|
151
|
-
BSDInit.new(inspec, service_ctl)
|
152
|
-
elsif %w{arch}.include?(platform)
|
153
|
-
Systemd.new(inspec, service_ctl)
|
154
|
-
elsif %w{coreos}.include?(platform)
|
155
|
-
Systemd.new(inspec, service_ctl)
|
156
|
-
elsif %w{suse opensuse}.include?(platform)
|
157
|
-
if os[:release].to_i >= 12
|
158
|
-
Systemd.new(inspec, service_ctl)
|
159
|
-
else
|
160
|
-
SysV.new(inspec, service_ctl || '/sbin/service')
|
161
|
-
end
|
162
|
-
elsif %w{aix}.include?(platform)
|
163
|
-
SrcMstr.new(inspec)
|
164
|
-
elsif %w{amazon}.include?(platform)
|
165
|
-
Upstart.new(inspec, service_ctl)
|
166
|
-
elsif os.solaris?
|
167
|
-
Svcs.new(inspec)
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
def info
|
172
|
-
return nil if @service_mgmt.nil?
|
173
|
-
@cache ||= @service_mgmt.info(@service_name)
|
174
|
-
end
|
175
|
-
|
176
|
-
# verifies if the service is enabled
|
177
|
-
def enabled?(_level = nil)
|
178
|
-
return false if info.nil?
|
179
|
-
info[:enabled]
|
180
|
-
end
|
181
|
-
|
182
|
-
def params
|
183
|
-
return {} if info.nil?
|
184
|
-
Hashie::Mash.new(info[:params] || {})
|
185
|
-
end
|
186
|
-
|
187
|
-
# verifies the service is registered
|
188
|
-
def installed?(_name = nil, _version = nil)
|
189
|
-
return false if info.nil?
|
190
|
-
info[:installed]
|
191
|
-
end
|
192
|
-
|
193
|
-
# verifies the service is currently running
|
194
|
-
def running?(_under = nil)
|
195
|
-
return false if info.nil?
|
196
|
-
info[:running]
|
197
|
-
end
|
198
|
-
|
199
|
-
# get all runlevels that are available and their configuration
|
200
|
-
def runlevels(*args)
|
201
|
-
return Runlevels.new(self) if info.nil? or info[:runlevels].nil?
|
202
|
-
Runlevels.from_hash(self, info[:runlevels], args)
|
203
|
-
end
|
204
|
-
|
205
|
-
# returns the service type from info
|
206
|
-
def type
|
207
|
-
return nil if info.nil?
|
208
|
-
info[:type]
|
209
|
-
end
|
210
|
-
|
211
|
-
# returns the service name from info
|
212
|
-
def name
|
213
|
-
return @service_name if info.nil?
|
214
|
-
info[:name]
|
215
|
-
end
|
216
|
-
|
217
|
-
# returns the service description from info
|
218
|
-
def description
|
219
|
-
return nil if info.nil?
|
220
|
-
info[:description]
|
221
|
-
end
|
222
|
-
|
223
|
-
# returns the service start up mode from info
|
224
|
-
def startmode
|
225
|
-
return nil if info.nil?
|
226
|
-
info[:startmode]
|
227
|
-
end
|
228
|
-
|
229
|
-
def to_s
|
230
|
-
"Service #{@service_name}"
|
231
|
-
end
|
232
|
-
|
233
|
-
private :info
|
234
|
-
end
|
235
|
-
|
236
|
-
class ServiceManager
|
237
|
-
attr_reader :inspec, :service_ctl
|
238
|
-
def initialize(inspec, service_ctl = nil)
|
239
|
-
@inspec = inspec
|
240
|
-
@service_ctl ||= service_ctl
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
# @see: http://www.freedesktop.org/software/systemd/man/systemctl.html
|
245
|
-
# @see: http://www.freedesktop.org/software/systemd/man/systemd-system.conf.html
|
246
|
-
class Systemd < ServiceManager
|
247
|
-
def initialize(inspec, service_ctl = nil)
|
248
|
-
@service_ctl = service_ctl || 'systemctl'
|
249
|
-
super
|
250
|
-
end
|
251
|
-
|
252
|
-
def is_enabled?(service_name)
|
253
|
-
result = inspec.command("#{service_ctl} is-enabled #{service_name} --quiet")
|
254
|
-
return true if result.exit_status == 0
|
255
|
-
|
256
|
-
# Some systems may not have a `.service` file for a particular service
|
257
|
-
# which causes the `systemctl is-enabled` check to fail despite the
|
258
|
-
# service being enabled. In that event we fallback to `sysv_service`.
|
259
|
-
if result.stderr =~ /Failed to get.*No such file or directory/
|
260
|
-
return inspec.sysv_service(service_name).enabled?
|
261
|
-
end
|
262
|
-
|
263
|
-
false
|
264
|
-
end
|
265
|
-
|
266
|
-
def is_active?(service_name)
|
267
|
-
inspec.command("#{service_ctl} is-active #{service_name} --quiet").exit_status == 0
|
268
|
-
end
|
269
|
-
|
270
|
-
def info(service_name)
|
271
|
-
cmd = inspec.command("#{service_ctl} show --all #{service_name}")
|
272
|
-
return nil if cmd.exit_status.to_i != 0
|
273
|
-
|
274
|
-
# parse data
|
275
|
-
params = SimpleConfig.new(
|
276
|
-
cmd.stdout.chomp,
|
277
|
-
assignment_regex: /^\s*([^=]*?)\s*=\s*(.*?)\s*$/,
|
278
|
-
multiple_values: false,
|
279
|
-
).params
|
280
|
-
|
281
|
-
# LoadState values eg. loaded, not-found
|
282
|
-
installed = params['LoadState'] == 'loaded'
|
283
|
-
|
284
|
-
{
|
285
|
-
name: params['Id'],
|
286
|
-
description: params['Description'],
|
287
|
-
installed: installed,
|
288
|
-
running: is_active?(service_name),
|
289
|
-
enabled: is_enabled?(service_name),
|
290
|
-
type: 'systemd',
|
291
|
-
params: params,
|
292
|
-
}
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
# AIX services
|
297
|
-
class SrcMstr < ServiceManager
|
298
|
-
attr_reader :name
|
299
|
-
|
300
|
-
def info(service_name)
|
301
|
-
@name = service_name
|
302
|
-
running = status?
|
303
|
-
return nil if running.nil?
|
304
|
-
|
305
|
-
{
|
306
|
-
name: service_name,
|
307
|
-
description: nil,
|
308
|
-
installed: true,
|
309
|
-
running: running,
|
310
|
-
enabled: enabled?,
|
311
|
-
type: 'srcmstr',
|
312
|
-
}
|
313
|
-
end
|
314
|
-
|
315
|
-
private
|
316
|
-
|
317
|
-
def status?
|
318
|
-
status_cmd = inspec.command("lssrc -s #{@name}")
|
319
|
-
return nil if status_cmd.exit_status.to_i != 0
|
320
|
-
status_cmd.stdout.split(/\n/).last.chomp =~ /active$/ ? true : false
|
321
|
-
end
|
322
|
-
|
323
|
-
def enabled?
|
324
|
-
enabled_rc_tcpip? || enabled_inittab?
|
325
|
-
end
|
326
|
-
|
327
|
-
def enabled_rc_tcpip?
|
328
|
-
inspec.command(
|
329
|
-
"grep -v ^# /etc/rc.tcpip | grep 'start ' | grep -Eq '(/{0,1}| )#{name} '",
|
330
|
-
).exit_status == 0
|
331
|
-
end
|
332
|
-
|
333
|
-
def enabled_inittab?
|
334
|
-
inspec.command("lsitab #{name}").exit_status == 0
|
335
|
-
end
|
336
|
-
end
|
337
|
-
|
338
|
-
# @see: http://upstart.ubuntu.com
|
339
|
-
class Upstart < ServiceManager
|
340
|
-
def initialize(service_name, service_ctl = nil)
|
341
|
-
@service_ctl = service_ctl || 'initctl'
|
342
|
-
super
|
343
|
-
end
|
344
|
-
|
345
|
-
def info(service_name)
|
346
|
-
# get the status of upstart service
|
347
|
-
status = inspec.command("#{service_ctl} status #{service_name}")
|
348
|
-
|
349
|
-
# fallback for systemv services, those are not handled via `initctl`
|
350
|
-
return SysV.new(inspec).info(service_name) if status.exit_status.to_i != 0 || status.stdout == ''
|
351
|
-
|
352
|
-
# @see: http://upstart.ubuntu.com/cookbook/#job-states
|
353
|
-
# grep for running to indicate the service is there
|
354
|
-
running = !status.stdout[%r{start/running}].nil?
|
355
|
-
|
356
|
-
{
|
357
|
-
name: service_name,
|
358
|
-
description: nil,
|
359
|
-
installed: true,
|
360
|
-
running: running,
|
361
|
-
enabled: info_enabled(service_name),
|
362
|
-
type: 'upstart',
|
363
|
-
}
|
364
|
-
end
|
365
|
-
|
366
|
-
private
|
367
|
-
|
368
|
-
def info_enabled(service_name)
|
369
|
-
# check if a service is enabled
|
370
|
-
config = inspec.file("/etc/init/#{service_name}.conf").content
|
371
|
-
|
372
|
-
# disregard if the config does not exist
|
373
|
-
return nil if config.nil?
|
374
|
-
|
375
|
-
!config.match(/^\s*start on/).nil?
|
376
|
-
end
|
377
|
-
|
378
|
-
def version
|
379
|
-
@version ||= begin
|
380
|
-
out = inspec.command("#{service_ctl} --version").stdout
|
381
|
-
Gem::Version.new(out[/\(upstart ([^\)]+)\)/, 1])
|
382
|
-
end
|
383
|
-
end
|
384
|
-
end
|
385
|
-
|
386
|
-
class SysV < ServiceManager
|
387
|
-
RUNLEVELS = { 0=>false, 1=>false, 2=>false, 3=>false, 4=>false, 5=>false, 6=>false }.freeze
|
388
|
-
|
389
|
-
def initialize(service_name, service_ctl = nil)
|
390
|
-
@service_ctl = service_ctl || 'service'
|
391
|
-
super
|
392
|
-
end
|
393
|
-
|
394
|
-
def info(service_name)
|
395
|
-
# check if service is installed
|
396
|
-
# read all available services via ls /etc/init.d/
|
397
|
-
srvlist = inspec.command('ls -1 /etc/init.d/')
|
398
|
-
return nil if srvlist.exit_status != 0
|
399
|
-
|
400
|
-
# check if the service is in list
|
401
|
-
service = srvlist.stdout.split("\n").select { |srv| srv == service_name }
|
402
|
-
|
403
|
-
# abort if we could not find any service
|
404
|
-
return nil if service.empty?
|
405
|
-
|
406
|
-
# read all enabled services from runlevel
|
407
|
-
# on rhel via: 'chkconfig --list', is not installed by default
|
408
|
-
# bash: for i in `find /etc/rc*.d -name S*`; do basename $i | sed -r 's/^S[0-9]+//'; done | sort | uniq
|
409
|
-
enabled_services_cmd = inspec.command('find /etc/rc*.d /etc/init.d/rc*.d -name "S*"').stdout
|
410
|
-
service_line = %r{rc(?<runlevel>[0-6])\.d/S[^/]*?#{Regexp.escape service_name}$}
|
411
|
-
all_services = enabled_services_cmd.split("\n").map { |line|
|
412
|
-
service_line.match(line)
|
413
|
-
}.compact
|
414
|
-
enabled = !all_services.empty?
|
415
|
-
|
416
|
-
# Determine a list of runlevels which this service is activated for
|
417
|
-
runlevels = RUNLEVELS.dup
|
418
|
-
all_services.each { |x| runlevels[x[:runlevel].to_i] = true }
|
419
|
-
|
420
|
-
# check if service is really running
|
421
|
-
# service throws an exit code if the service is not installed or
|
422
|
-
# not enabled
|
423
|
-
|
424
|
-
cmd = inspec.command("#{service_ctl} #{service_name} status")
|
425
|
-
running = cmd.exit_status == 0
|
426
|
-
{
|
427
|
-
name: service_name,
|
428
|
-
description: nil,
|
429
|
-
installed: true,
|
430
|
-
running: running,
|
431
|
-
enabled: enabled,
|
432
|
-
runlevels: runlevels,
|
433
|
-
type: 'sysv',
|
434
|
-
}
|
435
|
-
end
|
436
|
-
end
|
437
|
-
|
438
|
-
# @see: https://www.freebsd.org/doc/en/articles/linux-users/startup.html
|
439
|
-
# @see: https://www.freebsd.org/cgi/man.cgi?query=rc.conf&sektion=5
|
440
|
-
class BSDInit < ServiceManager
|
441
|
-
def initialize(service_name, service_ctl = nil)
|
442
|
-
@service_ctl = service_ctl || 'service'
|
443
|
-
super
|
444
|
-
end
|
445
|
-
|
446
|
-
def info(service_name)
|
447
|
-
# check if service is enabled
|
448
|
-
# services are enabled in /etc/rc.conf and /etc/defaults/rc.conf
|
449
|
-
# via #{service_name}_enable="YES"
|
450
|
-
# service SERVICE status returns the following result if not activated:
|
451
|
-
# Cannot 'status' sshd. Set sshd_enable to YES in /etc/rc.conf or use 'onestatus' instead of 'status'.
|
452
|
-
# gather all enabled services
|
453
|
-
cmd = inspec.command("#{service_ctl} -e")
|
454
|
-
return nil if cmd.exit_status != 0
|
455
|
-
|
456
|
-
# search for the service
|
457
|
-
srv = /(^.*#{service_name}$)/.match(cmd.stdout)
|
458
|
-
return nil if srv.nil? || srv[0].nil?
|
459
|
-
enabled = true
|
460
|
-
|
461
|
-
# check if the service is running
|
462
|
-
# if the service is not available or not running, we always get an error code
|
463
|
-
cmd = inspec.command("#{service_ctl} #{service_name} onestatus")
|
464
|
-
running = cmd.exit_status == 0
|
465
|
-
|
466
|
-
{
|
467
|
-
name: service_name,
|
468
|
-
description: nil,
|
469
|
-
installed: true,
|
470
|
-
running: running,
|
471
|
-
enabled: enabled,
|
472
|
-
type: 'bsd-init',
|
473
|
-
}
|
474
|
-
end
|
475
|
-
end
|
476
|
-
|
477
|
-
class Runit < ServiceManager
|
478
|
-
def initialize(service_name, service_ctl = nil)
|
479
|
-
@service_ctl = service_ctl || 'sv'
|
480
|
-
super
|
481
|
-
end
|
482
|
-
|
483
|
-
# rubocop:disable Style/DoubleNegation
|
484
|
-
def info(service_name)
|
485
|
-
# get the status of runit service
|
486
|
-
cmd = inspec.command("#{service_ctl} status #{service_name}")
|
487
|
-
# return nil unless cmd.exit_status == 0 # NOTE(sr) why do we do this?
|
488
|
-
|
489
|
-
installed = cmd.exit_status == 0
|
490
|
-
running = installed && !!(cmd.stdout =~ /^run:/)
|
491
|
-
enabled = installed && (running || !!(cmd.stdout =~ /normally up/) || !!(cmd.stdout =~ /want up/))
|
492
|
-
|
493
|
-
{
|
494
|
-
name: service_name,
|
495
|
-
description: nil,
|
496
|
-
installed: installed,
|
497
|
-
running: running,
|
498
|
-
enabled: enabled,
|
499
|
-
type: 'runit',
|
500
|
-
}
|
501
|
-
end
|
502
|
-
end
|
503
|
-
|
504
|
-
# MacOS / Darwin
|
505
|
-
# new launctl on macos 10.10
|
506
|
-
class LaunchCtl < ServiceManager
|
507
|
-
def initialize(service_name, service_ctl = nil)
|
508
|
-
@service_ctl = service_ctl || 'launchctl'
|
509
|
-
super
|
510
|
-
end
|
511
|
-
|
512
|
-
def info(service_name)
|
513
|
-
# get the status of upstart service
|
514
|
-
cmd = inspec.command("#{service_ctl} list")
|
515
|
-
return nil if cmd.exit_status != 0
|
516
|
-
|
517
|
-
# search for the service
|
518
|
-
srv = /(^.*#{service_name}.*)/.match(cmd.stdout)
|
519
|
-
return nil if srv.nil? || srv[0].nil?
|
520
|
-
|
521
|
-
# extract values from service
|
522
|
-
parsed_srv = /^(?<pid>[0-9-]+)\t(?<exit>[0-9]+)\t(?<name>\S*)$/.match(srv[0])
|
523
|
-
enabled = !parsed_srv['name'].nil? # it's in the list
|
524
|
-
|
525
|
-
# check if the service is running
|
526
|
-
pid = parsed_srv['pid']
|
527
|
-
running = pid != '-'
|
528
|
-
|
529
|
-
# extract service label
|
530
|
-
srv = parsed_srv['name'] || service_name
|
531
|
-
|
532
|
-
{
|
533
|
-
name: srv,
|
534
|
-
description: nil,
|
535
|
-
installed: true,
|
536
|
-
running: running,
|
537
|
-
enabled: enabled,
|
538
|
-
type: 'darwin',
|
539
|
-
}
|
540
|
-
end
|
541
|
-
end
|
542
|
-
|
543
|
-
# Determine the service state from Windows
|
544
|
-
# Uses Powershell to retrieve the information
|
545
|
-
class WindowsSrv < ServiceManager
|
546
|
-
# Determine service details
|
547
|
-
# PS: Get-Service -Name 'dhcp'| Select-Object -Property Name, DisplayName, Status | ConvertTo-Json
|
548
|
-
# {
|
549
|
-
# "Name": "dhcp",
|
550
|
-
# "DisplayName": "DHCP Client",
|
551
|
-
# "Status": 4
|
552
|
-
# }
|
553
|
-
#
|
554
|
-
# Until StartMode is not added to Get-Service, we need to do a workaround
|
555
|
-
# @see: https://connect.microsoft.com/PowerShell/feedback/details/424948/i-would-like-to-see-the-property-starttype-added-to-get-services
|
556
|
-
# Also see: https://msdn.microsoft.com/en-us/library/aa384896(v=vs.85).aspx
|
557
|
-
# Use the following powershell to determine the start mode
|
558
|
-
# PS: Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq $name -or $_.DisplayName -eq $name} | Select-Object -Prop
|
559
|
-
# erty Name, StartMode, State, Status | ConvertTo-Json
|
560
|
-
# {
|
561
|
-
# "Name": "Dhcp",
|
562
|
-
# "StartMode": "Auto",
|
563
|
-
# "State": "Running",
|
564
|
-
# "Status": "OK"
|
565
|
-
# }
|
566
|
-
#
|
567
|
-
# Windows Services have the following status code:
|
568
|
-
# @see: https://msdn.microsoft.com/en-us/library/windows/desktop/ms685996(v=vs.85).aspx
|
569
|
-
# - 1: Stopped
|
570
|
-
# - 2: Starting
|
571
|
-
# - 3: Stopping
|
572
|
-
# - 4: Running
|
573
|
-
# - 5: Continue Pending
|
574
|
-
# - 6: Pause Pending
|
575
|
-
# - 7: Paused
|
576
|
-
def info(service_name)
|
577
|
-
cmd = inspec.command("New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Service -Value (Get-Service -Name '#{service_name}'| Select-Object -Property Name, DisplayName, Status) -PassThru | Add-Member -MemberType NoteProperty -Name WMI -Value (Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq '#{service_name}' -or $_.DisplayName -eq '#{service_name}'} | Select-Object -Property StartMode) -PassThru | ConvertTo-Json")
|
578
|
-
|
579
|
-
# cannot rely on exit code for now, successful command returns exit code 1
|
580
|
-
# return nil if cmd.exit_status != 0
|
581
|
-
# try to parse json
|
582
|
-
begin
|
583
|
-
service = JSON.parse(cmd.stdout)
|
584
|
-
rescue JSON::ParserError => _e
|
585
|
-
return nil
|
586
|
-
end
|
587
|
-
|
588
|
-
# check that we got a response
|
589
|
-
return nil if service.nil? || service['Service'].nil?
|
590
|
-
|
591
|
-
{
|
592
|
-
name: service['Service']['Name'],
|
593
|
-
description: service['Service']['DisplayName'],
|
594
|
-
installed: true,
|
595
|
-
running: service_running?(service),
|
596
|
-
enabled: service_enabled?(service),
|
597
|
-
startmode: service['WMI']['StartMode'],
|
598
|
-
type: 'windows',
|
599
|
-
}
|
600
|
-
end
|
601
|
-
|
602
|
-
private
|
603
|
-
|
604
|
-
# detect if service is enabled
|
605
|
-
def service_enabled?(service)
|
606
|
-
!service['WMI'].nil? &&
|
607
|
-
!service['WMI']['StartMode'].nil? &&
|
608
|
-
(service['WMI']['StartMode'] == 'Auto' ||
|
609
|
-
service['WMI']['StartMode'] == 'Manual')
|
610
|
-
end
|
611
|
-
|
612
|
-
# detect if service is running
|
613
|
-
def service_running?(service)
|
614
|
-
!service['Service']['Status'].nil? && service['Service']['Status'] == 4
|
615
|
-
end
|
616
|
-
end
|
617
|
-
|
618
|
-
# Solaris services
|
619
|
-
class Svcs < ServiceManager
|
620
|
-
def initialize(service_name, service_ctl = nil)
|
621
|
-
@service_ctl = service_ctl || 'svcs'
|
622
|
-
super
|
623
|
-
end
|
624
|
-
|
625
|
-
def info(service_name)
|
626
|
-
# get the status of runit service
|
627
|
-
cmd = inspec.command("#{service_ctl} -l #{service_name}")
|
628
|
-
return nil if cmd.exit_status != 0
|
629
|
-
|
630
|
-
params = SimpleConfig.new(
|
631
|
-
cmd.stdout.chomp,
|
632
|
-
assignment_regex: /^(\w+)\s*(.*)$/,
|
633
|
-
multiple_values: false,
|
634
|
-
).params
|
635
|
-
|
636
|
-
installed = cmd.exit_status == 0
|
637
|
-
running = installed && (params['state'] == 'online')
|
638
|
-
enabled = installed && (params['enabled'] == 'true')
|
639
|
-
|
640
|
-
{
|
641
|
-
name: service_name,
|
642
|
-
description: params['name'],
|
643
|
-
installed: installed,
|
644
|
-
running: running,
|
645
|
-
enabled: enabled,
|
646
|
-
type: 'svcs',
|
647
|
-
}
|
648
|
-
end
|
649
|
-
end
|
650
|
-
|
651
|
-
# specific resources for specific service managers
|
652
|
-
|
653
|
-
class SystemdService < Service
|
654
|
-
name 'systemd_service'
|
655
|
-
desc 'Use the systemd_service InSpec audit resource to test if the named service (controlled by systemd) is installed, running and/or enabled.'
|
656
|
-
example "
|
657
|
-
# to override service mgmt auto-detection
|
658
|
-
describe systemd_service('service_name') do
|
659
|
-
it { should be_installed }
|
660
|
-
it { should be_enabled }
|
661
|
-
it { should be_running }
|
662
|
-
end
|
663
|
-
|
664
|
-
# to set a non-standard systemctl path
|
665
|
-
describe systemd_service('service_name', '/path/to/systemctl') do
|
666
|
-
it { should be_running }
|
667
|
-
end
|
668
|
-
"
|
669
|
-
|
670
|
-
def select_service_mgmt
|
671
|
-
Systemd.new(inspec, service_ctl)
|
672
|
-
end
|
673
|
-
end
|
674
|
-
|
675
|
-
class UpstartService < Service
|
676
|
-
name 'upstart_service'
|
677
|
-
desc 'Use the upstart_service InSpec audit resource to test if the named service (controlled by upstart) is installed, running and/or enabled.'
|
678
|
-
example "
|
679
|
-
# to override service mgmt auto-detection
|
680
|
-
describe upstart_service('service_name') do
|
681
|
-
it { should be_installed }
|
682
|
-
it { should be_enabled }
|
683
|
-
it { should be_running }
|
684
|
-
end
|
685
|
-
|
686
|
-
# to set a non-standard initctl path
|
687
|
-
describe upstart_service('service_name', '/path/to/initctl') do
|
688
|
-
it { should be_running }
|
689
|
-
end
|
690
|
-
"
|
691
|
-
|
692
|
-
def select_service_mgmt
|
693
|
-
Upstart.new(inspec, service_ctl)
|
694
|
-
end
|
695
|
-
end
|
696
|
-
|
697
|
-
class SysVService < Service
|
698
|
-
name 'sysv_service'
|
699
|
-
desc 'Use the sysv_service InSpec audit resource to test if the named service (controlled by SysV) is installed, running and/or enabled.'
|
700
|
-
example "
|
701
|
-
# to override service mgmt auto-detection
|
702
|
-
describe sysv_service('service_name') do
|
703
|
-
it { should be_installed }
|
704
|
-
it { should be_enabled }
|
705
|
-
it { should be_running }
|
706
|
-
end
|
707
|
-
|
708
|
-
# to set a non-standard service path
|
709
|
-
describe sysv_service('service_name', '/path/to/service') do
|
710
|
-
it { should be_running }
|
711
|
-
end
|
712
|
-
"
|
713
|
-
|
714
|
-
def select_service_mgmt
|
715
|
-
SysV.new(inspec, service_ctl)
|
716
|
-
end
|
717
|
-
end
|
718
|
-
|
719
|
-
class BSDService < Service
|
720
|
-
name 'bsd_service'
|
721
|
-
desc 'Use the bsd_service InSpec audit resource to test if the named service (controlled by BSD init) is installed, running and/or enabled.'
|
722
|
-
example "
|
723
|
-
# to override service mgmt auto-detection
|
724
|
-
describe bsd_service('service_name') do
|
725
|
-
it { should be_installed }
|
726
|
-
it { should be_enabled }
|
727
|
-
it { should be_running }
|
728
|
-
end
|
729
|
-
|
730
|
-
# to set a non-standard service path
|
731
|
-
describe bsd_service('service_name', '/path/to/service') do
|
732
|
-
it { should be_running }
|
733
|
-
end
|
734
|
-
"
|
735
|
-
|
736
|
-
def select_service_mgmt
|
737
|
-
BSDInit.new(inspec, service_ctl)
|
738
|
-
end
|
739
|
-
end
|
740
|
-
|
741
|
-
class LaunchdService < Service
|
742
|
-
name 'launchd_service'
|
743
|
-
desc 'Use the launchd_service InSpec audit resource to test if the named service (controlled by launchd) is installed, running and/or enabled.'
|
744
|
-
example "
|
745
|
-
# to override service mgmt auto-detection
|
746
|
-
describe launchd_service('service_name') do
|
747
|
-
it { should be_installed }
|
748
|
-
it { should be_enabled }
|
749
|
-
it { should be_running }
|
750
|
-
end
|
751
|
-
|
752
|
-
# to set a non-standard launchctl path
|
753
|
-
describe launchd_service('service_name', '/path/to/launchctl') do
|
754
|
-
it { should be_running }
|
755
|
-
end
|
756
|
-
"
|
757
|
-
|
758
|
-
def select_service_mgmt
|
759
|
-
LaunchCtl.new(inspec, service_ctl)
|
760
|
-
end
|
761
|
-
end
|
762
|
-
|
763
|
-
class RunitService < Service
|
764
|
-
name 'runit_service'
|
765
|
-
desc 'Use the runit_service InSpec audit resource to test if the named service (controlled by runit) is installed, running and/or enabled.'
|
766
|
-
example "
|
767
|
-
# to override service mgmt auto-detection
|
768
|
-
describe runit_service('service_name') do
|
769
|
-
it { should be_installed }
|
770
|
-
it { should be_enabled }
|
771
|
-
it { should be_running }
|
772
|
-
end
|
773
|
-
|
774
|
-
# to set a non-standard sv path
|
775
|
-
describe runit_service('service_name', '/path/to/sv') do
|
776
|
-
it { should be_running }
|
777
|
-
end
|
778
|
-
"
|
779
|
-
|
780
|
-
def select_service_mgmt
|
781
|
-
Runit.new(inspec, service_ctl)
|
782
|
-
end
|
783
|
-
end
|
784
|
-
end
|
1
|
+
# encoding: utf-8
|
2
|
+
# author: Christoph Hartmann
|
3
|
+
# author: Dominik Richter
|
4
|
+
# author: Stephan Renatus
|
5
|
+
|
6
|
+
require 'hashie'
|
7
|
+
|
8
|
+
module Inspec::Resources
|
9
|
+
class Runlevels < Hash
|
10
|
+
attr_accessor :owner
|
11
|
+
|
12
|
+
def self.from_hash(owner, hash = {}, filter = nil)
|
13
|
+
res = Runlevels.new(owner)
|
14
|
+
filter = filter.first if filter.is_a?(Array) && filter.length <= 1
|
15
|
+
|
16
|
+
ks = case filter
|
17
|
+
when nil
|
18
|
+
hash.keys
|
19
|
+
when Regexp
|
20
|
+
hash.keys.find_all { |x| x.to_s =~ filter }
|
21
|
+
when Array
|
22
|
+
f = filter.map(&:to_s)
|
23
|
+
hash.keys.find_all { |x| f.include?(x.to_s) }
|
24
|
+
when Numeric
|
25
|
+
hash.keys.include?(filter) ? [filter] : []
|
26
|
+
else
|
27
|
+
hash.keys.find_all { |x| x == filter }
|
28
|
+
end
|
29
|
+
|
30
|
+
ks.each { |k| res[k] = hash[k] }
|
31
|
+
res
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(owner, default = false)
|
35
|
+
@owner = owner
|
36
|
+
super(default)
|
37
|
+
end
|
38
|
+
|
39
|
+
def filter(f)
|
40
|
+
Runlevels.from_hash(owner, self, f)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Check if all runlevels are enabled
|
44
|
+
#
|
45
|
+
# @return [boolean] true if all runlevels are enabled
|
46
|
+
def enabled?
|
47
|
+
values.all?
|
48
|
+
end
|
49
|
+
|
50
|
+
# Check if all runlevels are disabled
|
51
|
+
#
|
52
|
+
# @return [boolean] true if all runlevels are disabled
|
53
|
+
def disabled?
|
54
|
+
values.none?
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_s
|
58
|
+
"#{owner} runlevels #{keys.join(', ')}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# We detect the init system for each operating system, based on the operating
|
63
|
+
# system.
|
64
|
+
#
|
65
|
+
# Fedora 15 : systemd
|
66
|
+
# RedHat 7 : systemd
|
67
|
+
# Ubuntu 15.04 : systemd
|
68
|
+
# Ubuntu < 15.04 : upstart
|
69
|
+
#
|
70
|
+
# TODO: extend the logic to detect the running init system, independently of OS
|
71
|
+
class Service < Inspec.resource(1)
|
72
|
+
name 'service'
|
73
|
+
desc 'Use the service InSpec audit resource to test if the named service is installed, running and/or enabled.'
|
74
|
+
example "
|
75
|
+
describe service('service_name') do
|
76
|
+
it { should be_installed }
|
77
|
+
it { should be_enabled }
|
78
|
+
it { should be_running }
|
79
|
+
its('type') { should be 'systemd' }
|
80
|
+
its ('startmode') { should be 'Auto'}
|
81
|
+
end
|
82
|
+
|
83
|
+
describe service('service_name').runlevels(3, 5) do
|
84
|
+
it { should be_enabled }
|
85
|
+
end
|
86
|
+
|
87
|
+
describe service('service_name').params do
|
88
|
+
its('UnitFileState') { should eq 'enabled' }
|
89
|
+
end
|
90
|
+
"
|
91
|
+
|
92
|
+
attr_reader :service_ctl
|
93
|
+
|
94
|
+
def initialize(service_name, service_ctl = nil)
|
95
|
+
@service_name = service_name
|
96
|
+
@service_mgmt = nil
|
97
|
+
@service_ctl ||= service_ctl
|
98
|
+
@cache = nil
|
99
|
+
@service_mgmt = select_service_mgmt
|
100
|
+
|
101
|
+
return skip_resource 'The `service` resource is not supported on your OS yet.' if @service_mgmt.nil?
|
102
|
+
end
|
103
|
+
|
104
|
+
def select_service_mgmt # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
105
|
+
os = inspec.os
|
106
|
+
platform = os[:name]
|
107
|
+
|
108
|
+
# Ubuntu
|
109
|
+
# @see: https://wiki.ubuntu.com/SystemdForUpstartUsers
|
110
|
+
# Ubuntu 15.04 : Systemd
|
111
|
+
# Systemd runs with PID 1 as /sbin/init.
|
112
|
+
# Upstart runs with PID 1 as /sbin/upstart.
|
113
|
+
# Ubuntu < 15.04 : Upstart
|
114
|
+
# Upstart runs with PID 1 as /sbin/init.
|
115
|
+
# Systemd runs with PID 1 as /lib/systemd/systemd.
|
116
|
+
if %w{ubuntu}.include?(platform)
|
117
|
+
version = os[:release].to_f
|
118
|
+
if version < 15.04
|
119
|
+
Upstart.new(inspec, service_ctl)
|
120
|
+
else
|
121
|
+
Systemd.new(inspec, service_ctl)
|
122
|
+
end
|
123
|
+
elsif %w{linuxmint}.include?(platform)
|
124
|
+
version = os[:release].to_f
|
125
|
+
if version < 18
|
126
|
+
Upstart.new(inspec, service_ctl)
|
127
|
+
else
|
128
|
+
Systemd.new(inspec, service_ctl)
|
129
|
+
end
|
130
|
+
elsif %w{debian}.include?(platform)
|
131
|
+
version = os[:release].to_i
|
132
|
+
if version > 7
|
133
|
+
Systemd.new(inspec, service_ctl)
|
134
|
+
else
|
135
|
+
SysV.new(inspec, service_ctl || '/usr/sbin/service')
|
136
|
+
end
|
137
|
+
elsif %w{redhat fedora centos oracle}.include?(platform)
|
138
|
+
version = os[:release].to_i
|
139
|
+
if (%w{redhat centos oracle}.include?(platform) && version >= 7) || (platform == 'fedora' && version >= 15)
|
140
|
+
Systemd.new(inspec, service_ctl)
|
141
|
+
else
|
142
|
+
SysV.new(inspec, service_ctl || '/sbin/service')
|
143
|
+
end
|
144
|
+
elsif %w{wrlinux}.include?(platform)
|
145
|
+
SysV.new(inspec, service_ctl)
|
146
|
+
elsif %w{mac_os_x}.include?(platform)
|
147
|
+
LaunchCtl.new(inspec, service_ctl)
|
148
|
+
elsif os.windows?
|
149
|
+
WindowsSrv.new(inspec)
|
150
|
+
elsif %w{freebsd}.include?(platform)
|
151
|
+
BSDInit.new(inspec, service_ctl)
|
152
|
+
elsif %w{arch}.include?(platform)
|
153
|
+
Systemd.new(inspec, service_ctl)
|
154
|
+
elsif %w{coreos}.include?(platform)
|
155
|
+
Systemd.new(inspec, service_ctl)
|
156
|
+
elsif %w{suse opensuse}.include?(platform)
|
157
|
+
if os[:release].to_i >= 12
|
158
|
+
Systemd.new(inspec, service_ctl)
|
159
|
+
else
|
160
|
+
SysV.new(inspec, service_ctl || '/sbin/service')
|
161
|
+
end
|
162
|
+
elsif %w{aix}.include?(platform)
|
163
|
+
SrcMstr.new(inspec)
|
164
|
+
elsif %w{amazon}.include?(platform)
|
165
|
+
Upstart.new(inspec, service_ctl)
|
166
|
+
elsif os.solaris?
|
167
|
+
Svcs.new(inspec)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def info
|
172
|
+
return nil if @service_mgmt.nil?
|
173
|
+
@cache ||= @service_mgmt.info(@service_name)
|
174
|
+
end
|
175
|
+
|
176
|
+
# verifies if the service is enabled
|
177
|
+
def enabled?(_level = nil)
|
178
|
+
return false if info.nil?
|
179
|
+
info[:enabled]
|
180
|
+
end
|
181
|
+
|
182
|
+
def params
|
183
|
+
return {} if info.nil?
|
184
|
+
Hashie::Mash.new(info[:params] || {})
|
185
|
+
end
|
186
|
+
|
187
|
+
# verifies the service is registered
|
188
|
+
def installed?(_name = nil, _version = nil)
|
189
|
+
return false if info.nil?
|
190
|
+
info[:installed]
|
191
|
+
end
|
192
|
+
|
193
|
+
# verifies the service is currently running
|
194
|
+
def running?(_under = nil)
|
195
|
+
return false if info.nil?
|
196
|
+
info[:running]
|
197
|
+
end
|
198
|
+
|
199
|
+
# get all runlevels that are available and their configuration
|
200
|
+
def runlevels(*args)
|
201
|
+
return Runlevels.new(self) if info.nil? or info[:runlevels].nil?
|
202
|
+
Runlevels.from_hash(self, info[:runlevels], args)
|
203
|
+
end
|
204
|
+
|
205
|
+
# returns the service type from info
|
206
|
+
def type
|
207
|
+
return nil if info.nil?
|
208
|
+
info[:type]
|
209
|
+
end
|
210
|
+
|
211
|
+
# returns the service name from info
|
212
|
+
def name
|
213
|
+
return @service_name if info.nil?
|
214
|
+
info[:name]
|
215
|
+
end
|
216
|
+
|
217
|
+
# returns the service description from info
|
218
|
+
def description
|
219
|
+
return nil if info.nil?
|
220
|
+
info[:description]
|
221
|
+
end
|
222
|
+
|
223
|
+
# returns the service start up mode from info
|
224
|
+
def startmode
|
225
|
+
return nil if info.nil?
|
226
|
+
info[:startmode]
|
227
|
+
end
|
228
|
+
|
229
|
+
def to_s
|
230
|
+
"Service #{@service_name}"
|
231
|
+
end
|
232
|
+
|
233
|
+
private :info
|
234
|
+
end
|
235
|
+
|
236
|
+
class ServiceManager
|
237
|
+
attr_reader :inspec, :service_ctl
|
238
|
+
def initialize(inspec, service_ctl = nil)
|
239
|
+
@inspec = inspec
|
240
|
+
@service_ctl ||= service_ctl
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# @see: http://www.freedesktop.org/software/systemd/man/systemctl.html
|
245
|
+
# @see: http://www.freedesktop.org/software/systemd/man/systemd-system.conf.html
|
246
|
+
class Systemd < ServiceManager
|
247
|
+
def initialize(inspec, service_ctl = nil)
|
248
|
+
@service_ctl = service_ctl || 'systemctl'
|
249
|
+
super
|
250
|
+
end
|
251
|
+
|
252
|
+
def is_enabled?(service_name)
|
253
|
+
result = inspec.command("#{service_ctl} is-enabled #{service_name} --quiet")
|
254
|
+
return true if result.exit_status == 0
|
255
|
+
|
256
|
+
# Some systems may not have a `.service` file for a particular service
|
257
|
+
# which causes the `systemctl is-enabled` check to fail despite the
|
258
|
+
# service being enabled. In that event we fallback to `sysv_service`.
|
259
|
+
if result.stderr =~ /Failed to get.*No such file or directory/
|
260
|
+
return inspec.sysv_service(service_name).enabled?
|
261
|
+
end
|
262
|
+
|
263
|
+
false
|
264
|
+
end
|
265
|
+
|
266
|
+
def is_active?(service_name)
|
267
|
+
inspec.command("#{service_ctl} is-active #{service_name} --quiet").exit_status == 0
|
268
|
+
end
|
269
|
+
|
270
|
+
def info(service_name)
|
271
|
+
cmd = inspec.command("#{service_ctl} show --all #{service_name}")
|
272
|
+
return nil if cmd.exit_status.to_i != 0
|
273
|
+
|
274
|
+
# parse data
|
275
|
+
params = SimpleConfig.new(
|
276
|
+
cmd.stdout.chomp,
|
277
|
+
assignment_regex: /^\s*([^=]*?)\s*=\s*(.*?)\s*$/,
|
278
|
+
multiple_values: false,
|
279
|
+
).params
|
280
|
+
|
281
|
+
# LoadState values eg. loaded, not-found
|
282
|
+
installed = params['LoadState'] == 'loaded'
|
283
|
+
|
284
|
+
{
|
285
|
+
name: params['Id'],
|
286
|
+
description: params['Description'],
|
287
|
+
installed: installed,
|
288
|
+
running: is_active?(service_name),
|
289
|
+
enabled: is_enabled?(service_name),
|
290
|
+
type: 'systemd',
|
291
|
+
params: params,
|
292
|
+
}
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# AIX services
|
297
|
+
class SrcMstr < ServiceManager
|
298
|
+
attr_reader :name
|
299
|
+
|
300
|
+
def info(service_name)
|
301
|
+
@name = service_name
|
302
|
+
running = status?
|
303
|
+
return nil if running.nil?
|
304
|
+
|
305
|
+
{
|
306
|
+
name: service_name,
|
307
|
+
description: nil,
|
308
|
+
installed: true,
|
309
|
+
running: running,
|
310
|
+
enabled: enabled?,
|
311
|
+
type: 'srcmstr',
|
312
|
+
}
|
313
|
+
end
|
314
|
+
|
315
|
+
private
|
316
|
+
|
317
|
+
def status?
|
318
|
+
status_cmd = inspec.command("lssrc -s #{@name}")
|
319
|
+
return nil if status_cmd.exit_status.to_i != 0
|
320
|
+
status_cmd.stdout.split(/\n/).last.chomp =~ /active$/ ? true : false
|
321
|
+
end
|
322
|
+
|
323
|
+
def enabled?
|
324
|
+
enabled_rc_tcpip? || enabled_inittab?
|
325
|
+
end
|
326
|
+
|
327
|
+
def enabled_rc_tcpip?
|
328
|
+
inspec.command(
|
329
|
+
"grep -v ^# /etc/rc.tcpip | grep 'start ' | grep -Eq '(/{0,1}| )#{name} '",
|
330
|
+
).exit_status == 0
|
331
|
+
end
|
332
|
+
|
333
|
+
def enabled_inittab?
|
334
|
+
inspec.command("lsitab #{name}").exit_status == 0
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
# @see: http://upstart.ubuntu.com
|
339
|
+
class Upstart < ServiceManager
|
340
|
+
def initialize(service_name, service_ctl = nil)
|
341
|
+
@service_ctl = service_ctl || 'initctl'
|
342
|
+
super
|
343
|
+
end
|
344
|
+
|
345
|
+
def info(service_name)
|
346
|
+
# get the status of upstart service
|
347
|
+
status = inspec.command("#{service_ctl} status #{service_name}")
|
348
|
+
|
349
|
+
# fallback for systemv services, those are not handled via `initctl`
|
350
|
+
return SysV.new(inspec).info(service_name) if status.exit_status.to_i != 0 || status.stdout == ''
|
351
|
+
|
352
|
+
# @see: http://upstart.ubuntu.com/cookbook/#job-states
|
353
|
+
# grep for running to indicate the service is there
|
354
|
+
running = !status.stdout[%r{start/running}].nil?
|
355
|
+
|
356
|
+
{
|
357
|
+
name: service_name,
|
358
|
+
description: nil,
|
359
|
+
installed: true,
|
360
|
+
running: running,
|
361
|
+
enabled: info_enabled(service_name),
|
362
|
+
type: 'upstart',
|
363
|
+
}
|
364
|
+
end
|
365
|
+
|
366
|
+
private
|
367
|
+
|
368
|
+
def info_enabled(service_name)
|
369
|
+
# check if a service is enabled
|
370
|
+
config = inspec.file("/etc/init/#{service_name}.conf").content
|
371
|
+
|
372
|
+
# disregard if the config does not exist
|
373
|
+
return nil if config.nil?
|
374
|
+
|
375
|
+
!config.match(/^\s*start on/).nil?
|
376
|
+
end
|
377
|
+
|
378
|
+
def version
|
379
|
+
@version ||= begin
|
380
|
+
out = inspec.command("#{service_ctl} --version").stdout
|
381
|
+
Gem::Version.new(out[/\(upstart ([^\)]+)\)/, 1])
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
class SysV < ServiceManager
|
387
|
+
RUNLEVELS = { 0=>false, 1=>false, 2=>false, 3=>false, 4=>false, 5=>false, 6=>false }.freeze
|
388
|
+
|
389
|
+
def initialize(service_name, service_ctl = nil)
|
390
|
+
@service_ctl = service_ctl || 'service'
|
391
|
+
super
|
392
|
+
end
|
393
|
+
|
394
|
+
def info(service_name)
|
395
|
+
# check if service is installed
|
396
|
+
# read all available services via ls /etc/init.d/
|
397
|
+
srvlist = inspec.command('ls -1 /etc/init.d/')
|
398
|
+
return nil if srvlist.exit_status != 0
|
399
|
+
|
400
|
+
# check if the service is in list
|
401
|
+
service = srvlist.stdout.split("\n").select { |srv| srv == service_name }
|
402
|
+
|
403
|
+
# abort if we could not find any service
|
404
|
+
return nil if service.empty?
|
405
|
+
|
406
|
+
# read all enabled services from runlevel
|
407
|
+
# on rhel via: 'chkconfig --list', is not installed by default
|
408
|
+
# bash: for i in `find /etc/rc*.d -name S*`; do basename $i | sed -r 's/^S[0-9]+//'; done | sort | uniq
|
409
|
+
enabled_services_cmd = inspec.command('find /etc/rc*.d /etc/init.d/rc*.d -name "S*"').stdout
|
410
|
+
service_line = %r{rc(?<runlevel>[0-6])\.d/S[^/]*?#{Regexp.escape service_name}$}
|
411
|
+
all_services = enabled_services_cmd.split("\n").map { |line|
|
412
|
+
service_line.match(line)
|
413
|
+
}.compact
|
414
|
+
enabled = !all_services.empty?
|
415
|
+
|
416
|
+
# Determine a list of runlevels which this service is activated for
|
417
|
+
runlevels = RUNLEVELS.dup
|
418
|
+
all_services.each { |x| runlevels[x[:runlevel].to_i] = true }
|
419
|
+
|
420
|
+
# check if service is really running
|
421
|
+
# service throws an exit code if the service is not installed or
|
422
|
+
# not enabled
|
423
|
+
|
424
|
+
cmd = inspec.command("#{service_ctl} #{service_name} status")
|
425
|
+
running = cmd.exit_status == 0
|
426
|
+
{
|
427
|
+
name: service_name,
|
428
|
+
description: nil,
|
429
|
+
installed: true,
|
430
|
+
running: running,
|
431
|
+
enabled: enabled,
|
432
|
+
runlevels: runlevels,
|
433
|
+
type: 'sysv',
|
434
|
+
}
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
# @see: https://www.freebsd.org/doc/en/articles/linux-users/startup.html
|
439
|
+
# @see: https://www.freebsd.org/cgi/man.cgi?query=rc.conf&sektion=5
|
440
|
+
class BSDInit < ServiceManager
|
441
|
+
def initialize(service_name, service_ctl = nil)
|
442
|
+
@service_ctl = service_ctl || 'service'
|
443
|
+
super
|
444
|
+
end
|
445
|
+
|
446
|
+
def info(service_name)
|
447
|
+
# check if service is enabled
|
448
|
+
# services are enabled in /etc/rc.conf and /etc/defaults/rc.conf
|
449
|
+
# via #{service_name}_enable="YES"
|
450
|
+
# service SERVICE status returns the following result if not activated:
|
451
|
+
# Cannot 'status' sshd. Set sshd_enable to YES in /etc/rc.conf or use 'onestatus' instead of 'status'.
|
452
|
+
# gather all enabled services
|
453
|
+
cmd = inspec.command("#{service_ctl} -e")
|
454
|
+
return nil if cmd.exit_status != 0
|
455
|
+
|
456
|
+
# search for the service
|
457
|
+
srv = /(^.*#{service_name}$)/.match(cmd.stdout)
|
458
|
+
return nil if srv.nil? || srv[0].nil?
|
459
|
+
enabled = true
|
460
|
+
|
461
|
+
# check if the service is running
|
462
|
+
# if the service is not available or not running, we always get an error code
|
463
|
+
cmd = inspec.command("#{service_ctl} #{service_name} onestatus")
|
464
|
+
running = cmd.exit_status == 0
|
465
|
+
|
466
|
+
{
|
467
|
+
name: service_name,
|
468
|
+
description: nil,
|
469
|
+
installed: true,
|
470
|
+
running: running,
|
471
|
+
enabled: enabled,
|
472
|
+
type: 'bsd-init',
|
473
|
+
}
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
class Runit < ServiceManager
|
478
|
+
def initialize(service_name, service_ctl = nil)
|
479
|
+
@service_ctl = service_ctl || 'sv'
|
480
|
+
super
|
481
|
+
end
|
482
|
+
|
483
|
+
# rubocop:disable Style/DoubleNegation
|
484
|
+
def info(service_name)
|
485
|
+
# get the status of runit service
|
486
|
+
cmd = inspec.command("#{service_ctl} status #{service_name}")
|
487
|
+
# return nil unless cmd.exit_status == 0 # NOTE(sr) why do we do this?
|
488
|
+
|
489
|
+
installed = cmd.exit_status == 0
|
490
|
+
running = installed && !!(cmd.stdout =~ /^run:/)
|
491
|
+
enabled = installed && (running || !!(cmd.stdout =~ /normally up/) || !!(cmd.stdout =~ /want up/))
|
492
|
+
|
493
|
+
{
|
494
|
+
name: service_name,
|
495
|
+
description: nil,
|
496
|
+
installed: installed,
|
497
|
+
running: running,
|
498
|
+
enabled: enabled,
|
499
|
+
type: 'runit',
|
500
|
+
}
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
# MacOS / Darwin
|
505
|
+
# new launctl on macos 10.10
|
506
|
+
class LaunchCtl < ServiceManager
|
507
|
+
def initialize(service_name, service_ctl = nil)
|
508
|
+
@service_ctl = service_ctl || 'launchctl'
|
509
|
+
super
|
510
|
+
end
|
511
|
+
|
512
|
+
def info(service_name)
|
513
|
+
# get the status of upstart service
|
514
|
+
cmd = inspec.command("#{service_ctl} list")
|
515
|
+
return nil if cmd.exit_status != 0
|
516
|
+
|
517
|
+
# search for the service
|
518
|
+
srv = /(^.*#{service_name}.*)/.match(cmd.stdout)
|
519
|
+
return nil if srv.nil? || srv[0].nil?
|
520
|
+
|
521
|
+
# extract values from service
|
522
|
+
parsed_srv = /^(?<pid>[0-9-]+)\t(?<exit>[0-9]+)\t(?<name>\S*)$/.match(srv[0])
|
523
|
+
enabled = !parsed_srv['name'].nil? # it's in the list
|
524
|
+
|
525
|
+
# check if the service is running
|
526
|
+
pid = parsed_srv['pid']
|
527
|
+
running = pid != '-'
|
528
|
+
|
529
|
+
# extract service label
|
530
|
+
srv = parsed_srv['name'] || service_name
|
531
|
+
|
532
|
+
{
|
533
|
+
name: srv,
|
534
|
+
description: nil,
|
535
|
+
installed: true,
|
536
|
+
running: running,
|
537
|
+
enabled: enabled,
|
538
|
+
type: 'darwin',
|
539
|
+
}
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
# Determine the service state from Windows
|
544
|
+
# Uses Powershell to retrieve the information
|
545
|
+
class WindowsSrv < ServiceManager
|
546
|
+
# Determine service details
|
547
|
+
# PS: Get-Service -Name 'dhcp'| Select-Object -Property Name, DisplayName, Status | ConvertTo-Json
|
548
|
+
# {
|
549
|
+
# "Name": "dhcp",
|
550
|
+
# "DisplayName": "DHCP Client",
|
551
|
+
# "Status": 4
|
552
|
+
# }
|
553
|
+
#
|
554
|
+
# Until StartMode is not added to Get-Service, we need to do a workaround
|
555
|
+
# @see: https://connect.microsoft.com/PowerShell/feedback/details/424948/i-would-like-to-see-the-property-starttype-added-to-get-services
|
556
|
+
# Also see: https://msdn.microsoft.com/en-us/library/aa384896(v=vs.85).aspx
|
557
|
+
# Use the following powershell to determine the start mode
|
558
|
+
# PS: Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq $name -or $_.DisplayName -eq $name} | Select-Object -Prop
|
559
|
+
# erty Name, StartMode, State, Status | ConvertTo-Json
|
560
|
+
# {
|
561
|
+
# "Name": "Dhcp",
|
562
|
+
# "StartMode": "Auto",
|
563
|
+
# "State": "Running",
|
564
|
+
# "Status": "OK"
|
565
|
+
# }
|
566
|
+
#
|
567
|
+
# Windows Services have the following status code:
|
568
|
+
# @see: https://msdn.microsoft.com/en-us/library/windows/desktop/ms685996(v=vs.85).aspx
|
569
|
+
# - 1: Stopped
|
570
|
+
# - 2: Starting
|
571
|
+
# - 3: Stopping
|
572
|
+
# - 4: Running
|
573
|
+
# - 5: Continue Pending
|
574
|
+
# - 6: Pause Pending
|
575
|
+
# - 7: Paused
|
576
|
+
def info(service_name)
|
577
|
+
cmd = inspec.command("New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Service -Value (Get-Service -Name '#{service_name}'| Select-Object -Property Name, DisplayName, Status) -PassThru | Add-Member -MemberType NoteProperty -Name WMI -Value (Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq '#{service_name}' -or $_.DisplayName -eq '#{service_name}'} | Select-Object -Property StartMode) -PassThru | ConvertTo-Json")
|
578
|
+
|
579
|
+
# cannot rely on exit code for now, successful command returns exit code 1
|
580
|
+
# return nil if cmd.exit_status != 0
|
581
|
+
# try to parse json
|
582
|
+
begin
|
583
|
+
service = JSON.parse(cmd.stdout)
|
584
|
+
rescue JSON::ParserError => _e
|
585
|
+
return nil
|
586
|
+
end
|
587
|
+
|
588
|
+
# check that we got a response
|
589
|
+
return nil if service.nil? || service['Service'].nil?
|
590
|
+
|
591
|
+
{
|
592
|
+
name: service['Service']['Name'],
|
593
|
+
description: service['Service']['DisplayName'],
|
594
|
+
installed: true,
|
595
|
+
running: service_running?(service),
|
596
|
+
enabled: service_enabled?(service),
|
597
|
+
startmode: service['WMI']['StartMode'],
|
598
|
+
type: 'windows',
|
599
|
+
}
|
600
|
+
end
|
601
|
+
|
602
|
+
private
|
603
|
+
|
604
|
+
# detect if service is enabled
|
605
|
+
def service_enabled?(service)
|
606
|
+
!service['WMI'].nil? &&
|
607
|
+
!service['WMI']['StartMode'].nil? &&
|
608
|
+
(service['WMI']['StartMode'] == 'Auto' ||
|
609
|
+
service['WMI']['StartMode'] == 'Manual')
|
610
|
+
end
|
611
|
+
|
612
|
+
# detect if service is running
|
613
|
+
def service_running?(service)
|
614
|
+
!service['Service']['Status'].nil? && service['Service']['Status'] == 4
|
615
|
+
end
|
616
|
+
end
|
617
|
+
|
618
|
+
# Solaris services
|
619
|
+
class Svcs < ServiceManager
|
620
|
+
def initialize(service_name, service_ctl = nil)
|
621
|
+
@service_ctl = service_ctl || 'svcs'
|
622
|
+
super
|
623
|
+
end
|
624
|
+
|
625
|
+
def info(service_name)
|
626
|
+
# get the status of runit service
|
627
|
+
cmd = inspec.command("#{service_ctl} -l #{service_name}")
|
628
|
+
return nil if cmd.exit_status != 0
|
629
|
+
|
630
|
+
params = SimpleConfig.new(
|
631
|
+
cmd.stdout.chomp,
|
632
|
+
assignment_regex: /^(\w+)\s*(.*)$/,
|
633
|
+
multiple_values: false,
|
634
|
+
).params
|
635
|
+
|
636
|
+
installed = cmd.exit_status == 0
|
637
|
+
running = installed && (params['state'] == 'online')
|
638
|
+
enabled = installed && (params['enabled'] == 'true')
|
639
|
+
|
640
|
+
{
|
641
|
+
name: service_name,
|
642
|
+
description: params['name'],
|
643
|
+
installed: installed,
|
644
|
+
running: running,
|
645
|
+
enabled: enabled,
|
646
|
+
type: 'svcs',
|
647
|
+
}
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
# specific resources for specific service managers
|
652
|
+
|
653
|
+
class SystemdService < Service
|
654
|
+
name 'systemd_service'
|
655
|
+
desc 'Use the systemd_service InSpec audit resource to test if the named service (controlled by systemd) is installed, running and/or enabled.'
|
656
|
+
example "
|
657
|
+
# to override service mgmt auto-detection
|
658
|
+
describe systemd_service('service_name') do
|
659
|
+
it { should be_installed }
|
660
|
+
it { should be_enabled }
|
661
|
+
it { should be_running }
|
662
|
+
end
|
663
|
+
|
664
|
+
# to set a non-standard systemctl path
|
665
|
+
describe systemd_service('service_name', '/path/to/systemctl') do
|
666
|
+
it { should be_running }
|
667
|
+
end
|
668
|
+
"
|
669
|
+
|
670
|
+
def select_service_mgmt
|
671
|
+
Systemd.new(inspec, service_ctl)
|
672
|
+
end
|
673
|
+
end
|
674
|
+
|
675
|
+
class UpstartService < Service
|
676
|
+
name 'upstart_service'
|
677
|
+
desc 'Use the upstart_service InSpec audit resource to test if the named service (controlled by upstart) is installed, running and/or enabled.'
|
678
|
+
example "
|
679
|
+
# to override service mgmt auto-detection
|
680
|
+
describe upstart_service('service_name') do
|
681
|
+
it { should be_installed }
|
682
|
+
it { should be_enabled }
|
683
|
+
it { should be_running }
|
684
|
+
end
|
685
|
+
|
686
|
+
# to set a non-standard initctl path
|
687
|
+
describe upstart_service('service_name', '/path/to/initctl') do
|
688
|
+
it { should be_running }
|
689
|
+
end
|
690
|
+
"
|
691
|
+
|
692
|
+
def select_service_mgmt
|
693
|
+
Upstart.new(inspec, service_ctl)
|
694
|
+
end
|
695
|
+
end
|
696
|
+
|
697
|
+
class SysVService < Service
|
698
|
+
name 'sysv_service'
|
699
|
+
desc 'Use the sysv_service InSpec audit resource to test if the named service (controlled by SysV) is installed, running and/or enabled.'
|
700
|
+
example "
|
701
|
+
# to override service mgmt auto-detection
|
702
|
+
describe sysv_service('service_name') do
|
703
|
+
it { should be_installed }
|
704
|
+
it { should be_enabled }
|
705
|
+
it { should be_running }
|
706
|
+
end
|
707
|
+
|
708
|
+
# to set a non-standard service path
|
709
|
+
describe sysv_service('service_name', '/path/to/service') do
|
710
|
+
it { should be_running }
|
711
|
+
end
|
712
|
+
"
|
713
|
+
|
714
|
+
def select_service_mgmt
|
715
|
+
SysV.new(inspec, service_ctl)
|
716
|
+
end
|
717
|
+
end
|
718
|
+
|
719
|
+
class BSDService < Service
|
720
|
+
name 'bsd_service'
|
721
|
+
desc 'Use the bsd_service InSpec audit resource to test if the named service (controlled by BSD init) is installed, running and/or enabled.'
|
722
|
+
example "
|
723
|
+
# to override service mgmt auto-detection
|
724
|
+
describe bsd_service('service_name') do
|
725
|
+
it { should be_installed }
|
726
|
+
it { should be_enabled }
|
727
|
+
it { should be_running }
|
728
|
+
end
|
729
|
+
|
730
|
+
# to set a non-standard service path
|
731
|
+
describe bsd_service('service_name', '/path/to/service') do
|
732
|
+
it { should be_running }
|
733
|
+
end
|
734
|
+
"
|
735
|
+
|
736
|
+
def select_service_mgmt
|
737
|
+
BSDInit.new(inspec, service_ctl)
|
738
|
+
end
|
739
|
+
end
|
740
|
+
|
741
|
+
class LaunchdService < Service
|
742
|
+
name 'launchd_service'
|
743
|
+
desc 'Use the launchd_service InSpec audit resource to test if the named service (controlled by launchd) is installed, running and/or enabled.'
|
744
|
+
example "
|
745
|
+
# to override service mgmt auto-detection
|
746
|
+
describe launchd_service('service_name') do
|
747
|
+
it { should be_installed }
|
748
|
+
it { should be_enabled }
|
749
|
+
it { should be_running }
|
750
|
+
end
|
751
|
+
|
752
|
+
# to set a non-standard launchctl path
|
753
|
+
describe launchd_service('service_name', '/path/to/launchctl') do
|
754
|
+
it { should be_running }
|
755
|
+
end
|
756
|
+
"
|
757
|
+
|
758
|
+
def select_service_mgmt
|
759
|
+
LaunchCtl.new(inspec, service_ctl)
|
760
|
+
end
|
761
|
+
end
|
762
|
+
|
763
|
+
class RunitService < Service
|
764
|
+
name 'runit_service'
|
765
|
+
desc 'Use the runit_service InSpec audit resource to test if the named service (controlled by runit) is installed, running and/or enabled.'
|
766
|
+
example "
|
767
|
+
# to override service mgmt auto-detection
|
768
|
+
describe runit_service('service_name') do
|
769
|
+
it { should be_installed }
|
770
|
+
it { should be_enabled }
|
771
|
+
it { should be_running }
|
772
|
+
end
|
773
|
+
|
774
|
+
# to set a non-standard sv path
|
775
|
+
describe runit_service('service_name', '/path/to/sv') do
|
776
|
+
it { should be_running }
|
777
|
+
end
|
778
|
+
"
|
779
|
+
|
780
|
+
def select_service_mgmt
|
781
|
+
Runit.new(inspec, service_ctl)
|
782
|
+
end
|
783
|
+
end
|
784
|
+
end
|