mirror of
https://github.com/inspec/inspec
synced 2024-11-10 07:04:15 +00:00
CHEF-6439 Mandatory Profile Signing (Preview) (#6705)
* Updated exec option to allow unsigned profiles run Signed-off-by: Nik08 <nikita.mathur@progress.com> * Added method to verify signed profile and to check for signed profile Signed-off-by: Nik08 <nikita.mathur@progress.com> * Invoked logic on each run to verify profiles if signed else raise sig req error Signed-off-by: Nik08 <nikita.mathur@progress.com> * Tests cases added to validate behaviour of inspec exec with signed and unsigned profiles with --chef-allow-unsigned flag Signed-off-by: Nik08 <nikita.mathur@progress.com> * Refactored and moved delete_signing_keys to common helper library for tests Signed-off-by: Nik08 <nikita.mathur@progress.com> * Updated code comments for more information and clarity on security update of signed profiles inspec exec Signed-off-by: Nik08 <nikita.mathur@progress.com> * Test cases to validate inspec run with combination of signed and unsigned profiles Signed-off-by: Nik08 <nikita.mathur@progress.com> * Documented usage of flag --chef-allow-unsigned Signed-off-by: Nik08 <nikita.mathur@progress.com> * Renamed the flag to run unsigned profiles to --allow-unsigned Signed-off-by: Nik08 <nikita.mathur@progress.com> * Refactored logic on profile level for profile signing verification Signed-off-by: Nik08 <nikita.mathur@progress.com> * Renaming the argument variable - from runner_call to silent Signed-off-by: Nik08 <nikita.mathur@progress.com> * Added profile mandate check for other inspec commands running profile evaluation Signed-off-by: Nik08 <nikita.mathur@progress.com> * Updated error message for profile sign requirement Signed-off-by: Nik08 <nikita.mathur@progress.com> * Updated test helper to fix inspec json test Signed-off-by: Nik08 <nikita.mathur@progress.com> * Fixed inspec json ability to use cli options successfully Signed-off-by: Nik08 <nikita.mathur@progress.com> * Documentation added for signed profiles mandatory usage with CLI commands Signed-off-by: Nik08 <nikita.mathur@progress.com> * Flow changes of raising exception when unsigned instead of direct exit Signed-off-by: Nik08 <nikita.mathur@progress.com> * Renamed unsigned profile flags Signed-off-by: Nik08 <nikita.mathur@progress.com> * Extracted out allow unsigned condition to config and modified comment info Signed-off-by: Nik08 <nikita.mathur@progress.com> * Doc update on consent of using signed and unsigned profiles Signed-off-by: Nik08 <nikita.mathur@progress.com> * Fix in signing mandatin check and added additional check on runner for better error UI for exec command Signed-off-by: Nik08 <nikita.mathur@progress.com> * Removed repeated allow-unsigned-profile defination from exec_options Signed-off-by: Nik08 <nikita.mathur@progress.com> * Test fixes Signed-off-by: Nik08 <nikita.mathur@progress.com> * Enabled feature preview flag for mandatory signing Signed-off-by: Nik08 <nikita.mathur@progress.com> * Test fixes after feature flag usage for mandatory signing Signed-off-by: Nik08 <nikita.mathur@progress.com> * Doc changes using feature preview flag for mandatory signing feature Signed-off-by: Nik08 <nikita.mathur@progress.com> * Inspec exec tests fixes for ENV values and parallel test fix using default option --allow-unsigned-profile false Signed-off-by: Nik08 <nikita.mathur@progress.com> * Kitchen fix while using signed profiles with inspec Signed-off-by: Nik08 <nikita.mathur@progress.com> * Unit test fix for profile resource exception Signed-off-by: Nik08 <nikita.mathur@progress.com> * Virtual profile detection improved Signed-off-by: Nik08 <nikita.mathur@progress.com> * Move mandatory profile sigining info to sigining page Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com> * Renamed flag from --allow-unsigned-profile to --allow-unsigned-profiles Signed-off-by: Nik08 <nikita.mathur@progress.com> * Typo fix in signing doc Signed-off-by: Nik08 <nikita.mathur@progress.com> * Trim note in cli.md about mandatory profile signing Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com> * Docs changes Signed-off-by: Ian Maddaus <ian.maddaus@progress.com> * Correct docs regarding exit code 5 Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com> --------- Signed-off-by: Nik08 <nikita.mathur@progress.com> Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com> Signed-off-by: Ian Maddaus <ian.maddaus@progress.com> Co-authored-by: Clinton Wolfe <clintoncwolfe@gmail.com> Co-authored-by: Ian Maddaus <ian.maddaus@progress.com>
This commit is contained in:
parent
1a87a2a588
commit
fd4e6d97a6
20 changed files with 289 additions and 32 deletions
|
@ -281,18 +281,22 @@ exit codes:
|
||||||
1 usage or general error
|
1 usage or general error
|
||||||
2 error in plugin system
|
2 error in plugin system
|
||||||
3 fatal deprecation encountered
|
3 fatal deprecation encountered
|
||||||
|
5 invalid profile signature
|
||||||
|
6 mandatory profile signing mode enabled and no signature found
|
||||||
100 normal exit, at least one test failed
|
100 normal exit, at least one test failed
|
||||||
101 normal exit, at least one test skipped but none failed
|
101 normal exit, at least one test skipped but none failed
|
||||||
172 chef license not accepted
|
172 chef license not accepted
|
||||||
```
|
```
|
||||||
|
|
||||||
Below are some examples of using `exec` with different test locations:
|
### Examples
|
||||||
|
|
||||||
|
Below are some examples of using `exec` with different test locations.
|
||||||
|
|
||||||
Chef Automate:
|
Chef Automate:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
inspec automate login
|
inspec automate login
|
||||||
inspec exec compliance://username/linux-baseline
|
inspec exec compliance://username/linux-baselinem
|
||||||
```
|
```
|
||||||
|
|
||||||
`inspec compliance` is a backwards compatible alias for `inspec automate` and works the same way:
|
`inspec compliance` is a backwards compatible alias for `inspec automate` and works the same way:
|
||||||
|
@ -358,6 +362,12 @@ Web-hosted file with basic authentication (supports .zip):
|
||||||
inspec exec https://username:password@webserver/linux-baseline.tar.gz
|
inspec exec https://username:password@webserver/linux-baseline.tar.gz
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Web-hosted signed profile:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
inspec exec https://username:password@webserver/linux-baseline.iaf
|
||||||
|
```
|
||||||
|
|
||||||
### Syntax
|
### Syntax
|
||||||
|
|
||||||
This subcommand has the following syntax:
|
This subcommand has the following syntax:
|
||||||
|
@ -370,6 +380,15 @@ inspec exec LOCATIONS
|
||||||
|
|
||||||
This subcommand has the following additional options:
|
This subcommand has the following additional options:
|
||||||
|
|
||||||
|
`--allow-unsigned-profiles`
|
||||||
|
: Allow InSpec to execute unsigned profiles if mandatory profile signing is enabled. Defaults to false.
|
||||||
|
|
||||||
|
**Chef InSpec 6** and greater has an optional setting that requires signed profiles.
|
||||||
|
If you try to execute an unsigned profile with this feature enabled, InSpec won't execute the profile and returns exit code 6.
|
||||||
|
Use `--allow-unsigned-profiles` to execute unsigned profiles if mandatory profile signing is enabled.
|
||||||
|
|
||||||
|
For more information, see [Signed InSpec Profiles](/inspec/signing/).
|
||||||
|
|
||||||
`--attrs=one two three`
|
`--attrs=one two three`
|
||||||
: Legacy name for --input-file - deprecated.
|
: Legacy name for --input-file - deprecated.
|
||||||
|
|
||||||
|
@ -655,6 +674,15 @@ inspec json PATH
|
||||||
|
|
||||||
This subcommand has the following additional options:
|
This subcommand has the following additional options:
|
||||||
|
|
||||||
|
`--allow-unsigned-profiles`
|
||||||
|
: Allow InSpec to read unsigned profiles if [mandatory profile signing](/inspec/signing/) is enabled. Defaults to false.
|
||||||
|
|
||||||
|
**Chef InSpec 6** and greater has an optional setting that requires signed profiles.
|
||||||
|
If you try to read an unsigned profile with this feature enabled, InSpec won't read the profile and returns exit code 6.
|
||||||
|
Use `--allow-unsigned-profiles` to read unsigned profiles if mandatory profile signing is enabled.
|
||||||
|
|
||||||
|
For more information, see [Signed InSpec Profiles](/inspec/signing/).
|
||||||
|
|
||||||
`--controls=one two three`
|
`--controls=one two three`
|
||||||
: A list of controls to include. Ignore all other tests.
|
: A list of controls to include. Ignore all other tests.
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ gh_repo = "inspec"
|
||||||
weight = 60
|
weight = 60
|
||||||
+++
|
+++
|
||||||
|
|
||||||
This page documents the `inspec sign` command introduced in InSpec 5 and details some methods to work with signed profiles.
|
This page documents the `inspec sign` command introduced in InSpec 5, the mandatory profile signing feature introduced in InSpec 6, and details some methods to work with signed profiles.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -19,6 +19,17 @@ This page documents the `inspec sign` command introduced in InSpec 5 and details
|
||||||
|
|
||||||
A signed profile, or `.iaf` file, is an InSpec profile with a digital signature that attests to its authenticity. Progress Chef authored profiles are available as signed profiles starting from 2022.
|
A signed profile, or `.iaf` file, is an InSpec profile with a digital signature that attests to its authenticity. Progress Chef authored profiles are available as signed profiles starting from 2022.
|
||||||
|
|
||||||
|
IAF files are not human-readable, but may be viewed using `inspec export`. Support for IAF v2.0 was added to InSpec 5.
|
||||||
|
|
||||||
|
### Mandatory profile signing
|
||||||
|
|
||||||
|
**Chef InSpec 6** and above has an optional setting that requires that all profiles are signed.
|
||||||
|
If mandatory profile signing is enabled, InSpec will not execute functions with an un-signed profile and exits with exit code 6.
|
||||||
|
|
||||||
|
To enable mandatory profile signing, set the environment variable `CHEF_PREVIEW_MANDATORY_PROFILE_SIGNING` to any non-empty value.
|
||||||
|
|
||||||
|
If you need to bypass mandatory profile signing, use the `--allow-unsigned-profiles` CLI option or set the `CHEF_ALLOW_UNSIGNED_PROFILES` environment variable.
|
||||||
|
|
||||||
### How does Profile Signing Work?
|
### How does Profile Signing Work?
|
||||||
|
|
||||||
Profile signing uses a matched pair of keys. The _signing key_ is secret and is used to sign the profile. The _validation key_ is widely distributed and verifies the signed profile signature.
|
Profile signing uses a matched pair of keys. The _signing key_ is secret and is used to sign the profile. The _validation key_ is widely distributed and verifies the signed profile signature.
|
||||||
|
|
|
@ -11,6 +11,22 @@ gh_repo = "inspec"
|
||||||
weight = 55
|
weight = 55
|
||||||
+++
|
+++
|
||||||
|
|
||||||
|
## Exit code 5
|
||||||
|
|
||||||
|
You tried to execute a function with a signed profile, but the signature is either bad or InSpec couldn't find the validation key.
|
||||||
|
For more information, see the [profile signing documentation](/inspec/signing/).
|
||||||
|
|
||||||
|
## Exit code 6
|
||||||
|
|
||||||
|
You enabled mandatory profile signing and tried to execute a function with an unsigned profile.
|
||||||
|
For more information, see the [profile signing documentation](/inspec/signing/).
|
||||||
|
|
||||||
|
## Exit code 174
|
||||||
|
|
||||||
|
Exit code 174 comes from running Chef InSpec 6 or greater without setting a Chef License key.
|
||||||
|
See the [InSpec install documentation](/inspec/install/) for setting a Chef License key.
|
||||||
|
See the [Chef License documentation](/licensing/) for more information about Chef licensing.
|
||||||
|
|
||||||
## Undefined Local Variable or Method Error for Cloud Resource
|
## Undefined Local Variable or Method Error for Cloud Resource
|
||||||
|
|
||||||
This error is a result of invoking a resource from one of the cloud resource packs without initializing an InSpec profile with that resource pack (AWS, Azure, or GCP) as a dependency.
|
This error is a result of invoking a resource from one of the cloud resource packs without initializing an InSpec profile with that resource pack (AWS, Azure, or GCP) as a dependency.
|
||||||
|
@ -23,12 +39,6 @@ See the relevant resource pack readme for instructions:
|
||||||
- [inspec-azure README](https://github.com/inspec/inspec-azure#use-the-resources)
|
- [inspec-azure README](https://github.com/inspec/inspec-azure#use-the-resources)
|
||||||
- [inspec-gcp README](https://github.com/inspec/inspec-gcp#use-the-resources)
|
- [inspec-gcp README](https://github.com/inspec/inspec-gcp#use-the-resources)
|
||||||
|
|
||||||
## Exit Code 174
|
|
||||||
|
|
||||||
Exit code 174 comes from running Chef InSpec 6 or greater without setting a Chef License key.
|
|
||||||
See the [InSpec install documentation](/inspec/install/) for setting a Chef License key.
|
|
||||||
See the [Chef License documentation](/licensing/) for more information about Chef licensing.
|
|
||||||
|
|
||||||
## License is not entitled to use InSpec
|
## License is not entitled to use InSpec
|
||||||
|
|
||||||
The license key set with Chef InSpec is not entitled to use Chef InSpec. Each license key is entitled to one or more Chef products. To view the products that your key is entitled to, run the `inspec license list` command, which will list your license entitlements.
|
The license key set with Chef InSpec is not entitled to use Chef InSpec. Each license key is entitled to one or more Chef products. To view the products that your key is entitled to, run the `inspec license list` command, which will list your license entitlements.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
wNEzKHmtSf1pIdciEC6DOs5SlOs3IbW1psVFLlmZc0NbnHe6MEahAnKWmHUP
|
ouQqvzkBxmM7J1cDE6/gsw8bE01+o2XFfDQoobG+n91yyCw0zjRzOUZ/NH+j
|
||||||
9YrDv2JMQo1I8MM/cez8XDxpK4O5y4HT66RqoAlfBkg82LmYC7f1Cy34ByCj
|
xZwB07D8+NNZQ4kYXMCkGXRQqZGq9nasIFfgwMjA4tWTWaE9i3jcL2Q3HOUF
|
||||||
LBZg5o/IVBGnY+Ksbhtp0mQYEyU048FnXIfh9uOfbKahU8HkPJssTkIw3fjL
|
aZ55wGtr5gpQc+pzQktFGRnC6ChmUgCzO2xTpl/h7TlNWIFU3INF8e2QnGqW
|
||||||
Vrd5GQ4ssfW1XXFaxx7DjxWlPmWBVhd8c1Y2RlACZyI+w1DQNYimrWvgiFym
|
+2Xe7KDhY+4cTYKu9PSWfo6cBv71N/t2fbRTYyeCBoukav3fOioitO0aGiWv
|
||||||
0VbnndiSX+2x84AZHE9AmsebcAYk9QlqO1N0VeYqBZj45FXLtpsNwYo0amDa
|
RrR8HSomvEQE+9yQanxkyiSjPW93/SCI2rjNq9ouIGXYzTchRkgxH+hZXV1V
|
||||||
D/wyKGxRQLUYXyd2tDVJMWbeHPHy8UIK17RoSctrEg==
|
CJw+H5I+k9sx3I8on5eXK3Oa0Bd3zGetrXFfQLu83A==
|
||||||
|
|
|
@ -85,4 +85,7 @@
|
||||||
inspec-reporter-progress-bar:
|
inspec-reporter-progress-bar:
|
||||||
description: Use progress bar streaming reporter
|
description: Use progress bar streaming reporter
|
||||||
inspec-reporter-child-status:
|
inspec-reporter-child-status:
|
||||||
description: Child status reporter used in inspec parallel reporting.
|
description: Child status reporter used in inspec parallel reporting.
|
||||||
|
inspec-mandatory-profile-signing:
|
||||||
|
description: Required to use a signed Inspec profile by default with inspec commands
|
||||||
|
env_preview: true
|
|
@ -171,6 +171,8 @@ module Inspec
|
||||||
desc: "Use the given path for caching dependencies, (default: ~/.inspec/cache)."
|
desc: "Use the given path for caching dependencies, (default: ~/.inspec/cache)."
|
||||||
option :auto_install_gems, type: :boolean, default: false,
|
option :auto_install_gems, type: :boolean, default: false,
|
||||||
desc: "Auto installs gem dependencies of the profile or resource pack."
|
desc: "Auto installs gem dependencies of the profile or resource pack."
|
||||||
|
option :allow_unsigned_profiles, type: :boolean, default: false,
|
||||||
|
desc: "Allow unsigned profiles to be used in InSpec command."
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.supermarket_options
|
def self.supermarket_options
|
||||||
|
@ -373,6 +375,9 @@ module Inspec
|
||||||
|
|
||||||
def pretty_handle_exception(exception)
|
def pretty_handle_exception(exception)
|
||||||
case exception
|
case exception
|
||||||
|
when Inspec::ProfileSignatureRequired
|
||||||
|
$stderr.puts exception.message
|
||||||
|
Inspec::UI.new.exit(:signature_required)
|
||||||
when Inspec::InvalidProfileSignature
|
when Inspec::InvalidProfileSignature
|
||||||
$stderr.puts exception.message
|
$stderr.puts exception.message
|
||||||
Inspec::UI.new.exit(:bad_signature)
|
Inspec::UI.new.exit(:bad_signature)
|
||||||
|
|
|
@ -80,6 +80,10 @@ module Inspec
|
||||||
puts
|
puts
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def allow_unsigned_profiles?
|
||||||
|
self["allow_unsigned_profiles"] || ENV["CHEF_ALLOW_UNSIGNED_PROFILES"]
|
||||||
|
end
|
||||||
|
|
||||||
# return all telemetry options from config
|
# return all telemetry options from config
|
||||||
# @return [Hash]
|
# @return [Hash]
|
||||||
def telemetry_options
|
def telemetry_options
|
||||||
|
|
|
@ -27,4 +27,6 @@ module Inspec
|
||||||
|
|
||||||
class FeatureConfigMissingError < Error; end
|
class FeatureConfigMissingError < Error; end
|
||||||
class FeatureConfigTamperedError < Error; end
|
class FeatureConfigTamperedError < Error; end
|
||||||
|
|
||||||
|
class ProfileSignatureRequired < Error; end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,7 @@ require "inspec/utils/json_profile_summary"
|
||||||
require "inspec/dependency_loader"
|
require "inspec/dependency_loader"
|
||||||
require "inspec/dependency_installer"
|
require "inspec/dependency_installer"
|
||||||
require "inspec/utils/profile_ast_helpers"
|
require "inspec/utils/profile_ast_helpers"
|
||||||
|
require "plugins/inspec-sign/lib/inspec-sign/base"
|
||||||
|
|
||||||
module Inspec
|
module Inspec
|
||||||
class Profile
|
class Profile
|
||||||
|
@ -82,7 +83,7 @@ module Inspec
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :source_reader, :backend, :runner_context, :check_mode
|
attr_reader :source_reader, :backend, :runner_context, :check_mode
|
||||||
attr_accessor :parent_profile, :profile_id, :profile_name
|
attr_accessor :parent_profile, :profile_id, :profile_name, :target
|
||||||
def_delegator :@source_reader, :tests
|
def_delegator :@source_reader, :tests
|
||||||
def_delegator :@source_reader, :libraries
|
def_delegator :@source_reader, :libraries
|
||||||
def_delegator :@source_reader, :metadata
|
def_delegator :@source_reader, :metadata
|
||||||
|
@ -181,6 +182,26 @@ module Inspec
|
||||||
@state == :failed
|
@state == :failed
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def verify_if_signed
|
||||||
|
if signed?
|
||||||
|
verify_signed_profile
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def signed?
|
||||||
|
# Signed profiles have .iaf extension
|
||||||
|
(@source_reader&.target&.parent&.class == Inspec::IafProvider)
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_signed_profile
|
||||||
|
# Kitchen inspec send target profile in Hash format in some scenarios. For example: While using local profile with kitchen, {:path => "path/to/kitchen/lcoal-profile"}
|
||||||
|
target_profile = target.is_a?(Hash) ? target.values[0] : target
|
||||||
|
InspecPlugins::Sign::Base.profile_verify(target_profile, true) if target_profile
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Is this profile is supported on the current platform of the
|
# Is this profile is supported on the current platform of the
|
||||||
# backend machine and the current inspec version.
|
# backend machine and the current inspec version.
|
||||||
|
@ -215,8 +236,30 @@ module Inspec
|
||||||
@params ||= load_params
|
@params ||= load_params
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def virtual_profile?
|
||||||
|
# A virtual profile is for virtual profile evaluation
|
||||||
|
# Used by shell & inspec detect command.
|
||||||
|
(name == "inspec-shell") && (files&.length == 1) && (files[0] == "inspec.yml")
|
||||||
|
end
|
||||||
|
|
||||||
def collect_tests
|
def collect_tests
|
||||||
unless @tests_collected || failed?
|
unless @tests_collected || failed?
|
||||||
|
|
||||||
|
# This is that one common place in InSpec engine which is used to collect tests of InSpec profile
|
||||||
|
# One common place used by most of the CLI commands using profile, like exec, export etc
|
||||||
|
# Checking for profile signature in parent profile only
|
||||||
|
# Child profiles of a signed profile are extracted to cache dir
|
||||||
|
# Hence they are not in .iaf format
|
||||||
|
# Only runs this block when preview flag CHEF_PREVIEW_MANDATORY_PROFILE_SIGNING is set
|
||||||
|
Inspec.with_feature("inspec-mandatory-profile-signing") {
|
||||||
|
if !parent_profile && !virtual_profile?
|
||||||
|
cfg = Inspec::Config.cached
|
||||||
|
if cfg.is_a?(Inspec::Config) && !cfg.allow_unsigned_profiles?
|
||||||
|
raise Inspec::ProfileSignatureRequired, "Signature required for profile: #{name}. Please provide a signed profile. Or set CHEF_ALLOW_UNSIGNED_PROFILES in the environment. Or use `--allow-unsigned-profiles` flag with InSpec CLI." unless verify_if_signed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
return unless supports_platform?
|
return unless supports_platform?
|
||||||
|
|
||||||
locked_dependencies.each(&:collect_tests)
|
locked_dependencies.each(&:collect_tests)
|
||||||
|
|
|
@ -163,6 +163,16 @@ module Inspec
|
||||||
|
|
||||||
def run(with = nil)
|
def run(with = nil)
|
||||||
ChefLicensing.check_software_entitlement! if Inspec::Dist::EXEC_NAME == "inspec"
|
ChefLicensing.check_software_entitlement! if Inspec::Dist::EXEC_NAME == "inspec"
|
||||||
|
|
||||||
|
# Validate if profiles are signed and verified
|
||||||
|
# Additional check is required to provide error message in case of inspec exec command (exec command can use multiple profiles as well)
|
||||||
|
# Only runs this block when preview flag CHEF_PREVIEW_MANDATORY_PROFILE_SIGNING is set
|
||||||
|
Inspec.with_feature("inspec-mandatory-profile-signing") {
|
||||||
|
unless @conf.allow_unsigned_profiles?
|
||||||
|
verify_target_profiles_if_signed(@target_profiles)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
Inspec::Log.debug "Starting run with targets: #{@target_profiles.map(&:to_s)}"
|
Inspec::Log.debug "Starting run with targets: #{@target_profiles.map(&:to_s)}"
|
||||||
load
|
load
|
||||||
run_tests(with)
|
run_tests(with)
|
||||||
|
@ -174,6 +184,14 @@ module Inspec
|
||||||
Inspec::UI.new.exit(:usage_error)
|
Inspec::UI.new.exit(:usage_error)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def verify_target_profiles_if_signed(target_profiles)
|
||||||
|
unsigned_profiles = []
|
||||||
|
target_profiles.each do |profile|
|
||||||
|
unsigned_profiles << profile.name unless profile.verify_if_signed
|
||||||
|
end
|
||||||
|
raise Inspec::ProfileSignatureRequired, "Signature required for profile/s: #{unsigned_profiles.join(", ")}. Please provide a signed profile. Or set CHEF_ALLOW_UNSIGNED_PROFILES in the environment. Or use `--allow-unsigned-profiles` flag with InSpec CLI. " unless unsigned_profiles.empty?
|
||||||
|
end
|
||||||
|
|
||||||
def render_output(run_data)
|
def render_output(run_data)
|
||||||
return if @conf["reporter"].nil?
|
return if @conf["reporter"].nil?
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ module Inspec
|
||||||
EXIT_FATAL_DEPRECATION = 3
|
EXIT_FATAL_DEPRECATION = 3
|
||||||
EXIT_GEM_DEPENDENCY_LOAD_ERROR = 4
|
EXIT_GEM_DEPENDENCY_LOAD_ERROR = 4
|
||||||
EXIT_BAD_SIGNATURE = 5
|
EXIT_BAD_SIGNATURE = 5
|
||||||
|
EXIT_SIGNATURE_REQUIRED = 6
|
||||||
EXIT_LICENSE_NOT_ACCEPTED = 172
|
EXIT_LICENSE_NOT_ACCEPTED = 172
|
||||||
EXIT_LICENSE_NOT_ENTITLED = 173
|
EXIT_LICENSE_NOT_ENTITLED = 173
|
||||||
EXIT_LICENSE_NOT_SET = 174
|
EXIT_LICENSE_NOT_SET = 174
|
||||||
|
|
|
@ -3,6 +3,7 @@ require "fileutils" unless defined?(FileUtils)
|
||||||
require "minitest/autorun"
|
require "minitest/autorun"
|
||||||
require "inspec/backend"
|
require "inspec/backend"
|
||||||
require_relative "../../lib/inspec-habitat/profile"
|
require_relative "../../lib/inspec-habitat/profile"
|
||||||
|
require "inspec/feature"
|
||||||
|
|
||||||
class InspecPlugins::Habitat::ProfileTest < Minitest::Test
|
class InspecPlugins::Habitat::ProfileTest < Minitest::Test
|
||||||
def setup
|
def setup
|
||||||
|
|
|
@ -84,8 +84,8 @@ class ParallelCli < Minitest::Test
|
||||||
def test_parallel_dry_run_with_verbose_option
|
def test_parallel_dry_run_with_verbose_option
|
||||||
out = run_inspec_process("parallel exec #{complete_profile} -o #{options_file_1} --dry-run --verbose")
|
out = run_inspec_process("parallel exec #{complete_profile} -o #{options_file_1} --dry-run --verbose")
|
||||||
stdout = out.stdout
|
stdout = out.stdout
|
||||||
assert_includes stdout, "complete-profile -t ssh://vagrant@127.0.0.1:2201 --reporter child-status cli:myfile.out --no-create-lockfile --no-sudo --winrm-transport negotiate --insecure false --winrm-shell-type powershell --auto-install-gems false --distinct-exit true --diff true --sort-results-by file --filter-empty-profiles false --reporter-include-source false"
|
assert_includes stdout, "complete-profile -t ssh://vagrant@127.0.0.1:2201 --reporter child-status cli:myfile.out --no-create-lockfile --no-sudo --winrm-transport negotiate --insecure false --winrm-shell-type powershell --auto-install-gems false --allow-unsigned-profiles false --distinct-exit true --diff true --sort-results-by file --filter-empty-profiles false --reporter-include-source false"
|
||||||
assert_includes stdout, "control-tags -t ssh://vagrant@127.0.0.1:2201 --reporter child-status cli:myfile.out --winrm-transport negotiate --insecure false --winrm-shell-type powershell --auto-install-gems false --distinct-exit true --diff true --sort-results-by file --filter-empty-profiles false --reporter-include-source false"
|
assert_includes stdout, "control-tags -t ssh://vagrant@127.0.0.1:2201 --reporter child-status cli:myfile.out --winrm-transport negotiate --insecure false --winrm-shell-type powershell --auto-install-gems false --allow-unsigned-profiles false --distinct-exit true --diff true --sort-results-by file --filter-empty-profiles false --reporter-include-source false"
|
||||||
assert_equal stdout.split("\n").count, 6
|
assert_equal stdout.split("\n").count, 6
|
||||||
assert_exit_code 0, out
|
assert_exit_code 0, out
|
||||||
end
|
end
|
||||||
|
|
|
@ -92,12 +92,16 @@ module InspecPlugins
|
||||||
Inspec::UI.new.exit(:usage_error)
|
Inspec::UI.new.exit(:usage_error)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.profile_verify(signed_profile_path)
|
def self.profile_verify(signed_profile_path, silent = false)
|
||||||
file_to_verify = signed_profile_path
|
file_to_verify = signed_profile_path
|
||||||
puts "Verifying #{file_to_verify}"
|
puts "Verifying #{file_to_verify}" unless silent
|
||||||
|
|
||||||
iaf_file = Inspec::IafFile.new(file_to_verify)
|
iaf_file = Inspec::IafFile.new(file_to_verify)
|
||||||
if iaf_file.valid?
|
if iaf_file.valid?
|
||||||
|
# Signed profile verification is called from runner and not from CLI
|
||||||
|
# Do not exit and do not print logs
|
||||||
|
return if silent
|
||||||
|
|
||||||
puts "Detected format version '#{iaf_file.version}'"
|
puts "Detected format version '#{iaf_file.version}'"
|
||||||
puts "Attempting to verify using key '#{iaf_file.key_name}'"
|
puts "Attempting to verify using key '#{iaf_file.key_name}'"
|
||||||
puts "Profile is valid."
|
puts "Profile is valid."
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
require "fileutils" unless defined?(FileUtils)
|
require "fileutils" unless defined?(FileUtils)
|
||||||
require "plugins/shared/core_plugin_test_helper"
|
require "plugins/shared/core_plugin_test_helper"
|
||||||
require "securerandom" unless defined?(SecureRandom)
|
require "securerandom" unless defined?(SecureRandom)
|
||||||
|
require "functional/helper"
|
||||||
|
|
||||||
class SignCli < Minitest::Test
|
class SignCli < Minitest::Test
|
||||||
include CorePluginFunctionalHelper
|
include CorePluginFunctionalHelper
|
||||||
|
@ -17,7 +18,7 @@ class SignCli < Minitest::Test
|
||||||
assert_includes stdout, "Generating validation key"
|
assert_includes stdout, "Generating validation key"
|
||||||
|
|
||||||
assert_exit_code 0, out
|
assert_exit_code 0, out
|
||||||
delete_keys(unique_key_name)
|
delete_signing_keys(unique_key_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -42,7 +43,7 @@ class SignCli < Minitest::Test
|
||||||
|
|
||||||
assert_includes out.stdout.force_encoding(Encoding::UTF_8), "Verifying artifact-profile-0.1.0.iaf"
|
assert_includes out.stdout.force_encoding(Encoding::UTF_8), "Verifying artifact-profile-0.1.0.iaf"
|
||||||
assert_exit_code 0, out
|
assert_exit_code 0, out
|
||||||
delete_keys(unique_key_name)
|
delete_signing_keys(unique_key_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -67,12 +68,7 @@ class SignCli < Minitest::Test
|
||||||
|
|
||||||
assert_includes out.stdout.force_encoding(Encoding::UTF_8), "Verifying artifact-profile-5_3-0.1.0.iaf"
|
assert_includes out.stdout.force_encoding(Encoding::UTF_8), "Verifying artifact-profile-5_3-0.1.0.iaf"
|
||||||
assert_exit_code 0, out
|
assert_exit_code 0, out
|
||||||
delete_keys(unique_key_name)
|
delete_signing_keys(unique_key_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_keys(unique_key_name)
|
|
||||||
File.delete("#{Inspec.config_dir}/keys/#{unique_key_name}.pem.key") if File.exist?("#{Inspec.config_dir}/keys/#{unique_key_name}.pem.key")
|
|
||||||
File.delete("#{Inspec.config_dir}/keys/#{unique_key_name}.pem.pub") if File.exist?("#{Inspec.config_dir}/keys/#{unique_key_name}.pem.pub")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -186,8 +186,9 @@ module FunctionalHelper
|
||||||
prefix += opts[:prefix] || ""
|
prefix += opts[:prefix] || ""
|
||||||
prefix += assemble_env_prefix(opts[:env])
|
prefix += assemble_env_prefix(opts[:env])
|
||||||
command_line += " --reporter json " if opts[:json] && command_line =~ /\bexec\b/
|
command_line += " --reporter json " if opts[:json] && command_line =~ /\bexec\b/
|
||||||
command_line += " --no-create-lockfile " if (!opts[:lock]) && command_line =~ /\bexec\b/
|
|
||||||
command_line += " --enhanced_outcomes " if opts[:enhanced_outcomes] && command_line =~ /\bexec\b/
|
command_line += " --enhanced_outcomes " if opts[:enhanced_outcomes] && command_line =~ /\bexec\b/
|
||||||
|
command_line += " --allow-unsigned-profiles #{opts[:allow_unsigned_profiles]}" if opts[:allow_unsigned_profiles]
|
||||||
|
command_line += " --no-create-lockfile" if (!opts[:lock]) && command_line =~ /\bexec\b/
|
||||||
|
|
||||||
run_result = nil
|
run_result = nil
|
||||||
if opts[:tmpdir]
|
if opts[:tmpdir]
|
||||||
|
@ -256,6 +257,11 @@ module FunctionalHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def delete_signing_keys(unique_key_name)
|
||||||
|
File.delete("#{Inspec.config_dir}/keys/#{unique_key_name}.pem.key") if File.exist?("#{Inspec.config_dir}/keys/#{unique_key_name}.pem.key")
|
||||||
|
File.delete("#{Inspec.config_dir}/keys/#{unique_key_name}.pem.pub") if File.exist?("#{Inspec.config_dir}/keys/#{unique_key_name}.pem.pub")
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def assemble_env_prefix(env = {})
|
def assemble_env_prefix(env = {})
|
||||||
|
|
|
@ -1479,4 +1479,127 @@ EOT
|
||||||
_(run_result.stdout).must_include ":status: passed"
|
_(run_result.stdout).must_include ":status: passed"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "Signed profile mandatory feature" do
|
||||||
|
|
||||||
|
it "should be able to run a single signed profile successfully" do
|
||||||
|
prepare_examples do |dir|
|
||||||
|
skip_windows! # Breakage confirmed, only on CI: https://buildkite.com/chef-oss/inspec-inspec-master-verify/builds/2355#2c9d032e-4a24-4e7c-aef2-1c9e2317d9e2
|
||||||
|
|
||||||
|
unique_key_name = SecureRandom.uuid
|
||||||
|
|
||||||
|
# Create profile
|
||||||
|
profile = File.join(dir, "artifact-profile")
|
||||||
|
run_inspec_process("init profile artifact-profile", prefix: "cd #{dir};")
|
||||||
|
|
||||||
|
# Generate key to sign profile
|
||||||
|
run_inspec_process("sign generate-keys --keyname #{unique_key_name}", prefix: "cd #{dir};")
|
||||||
|
|
||||||
|
# Sign profile
|
||||||
|
run_inspec_process("sign profile #{profile} --keyname #{unique_key_name}", prefix: "cd #{dir};")
|
||||||
|
|
||||||
|
# Run inspec exec on signed profiles with allow_unsigned_profiles false (default behaviour)
|
||||||
|
run_result = run_inspec_process("exec artifact-profile-0.1.0.iaf", prefix: "cd #{dir};", allow_unsigned_profiles: false, env: { CHEF_PREVIEW_MANDATORY_PROFILE_SIGNING: "1" })
|
||||||
|
_(run_result.stdout).must_include "1 successful control"
|
||||||
|
_(run_result.exit_status).must_equal 0
|
||||||
|
|
||||||
|
delete_signing_keys(unique_key_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be able to run multiple signed profiles successfully" do
|
||||||
|
prepare_examples do |dir|
|
||||||
|
skip_windows! # Breakage confirmed, only on CI: https://buildkite.com/chef-oss/inspec-inspec-master-verify/builds/2355#2c9d032e-4a24-4e7c-aef2-1c9e2317d9e2
|
||||||
|
|
||||||
|
unique_key_name = SecureRandom.uuid
|
||||||
|
|
||||||
|
# Create 2 profiles
|
||||||
|
profile_1 = File.join(dir, "artifact-profile-1")
|
||||||
|
run_inspec_process("init profile artifact-profile-1", prefix: "cd #{dir};")
|
||||||
|
|
||||||
|
profile_2 = File.join(dir, "artifact-profile-2")
|
||||||
|
run_inspec_process("init profile artifact-profile-2", prefix: "cd #{dir};")
|
||||||
|
|
||||||
|
# Generate key to sign profile
|
||||||
|
run_inspec_process("sign generate-keys --keyname #{unique_key_name}", prefix: "cd #{dir};")
|
||||||
|
|
||||||
|
# Sign both the profiles
|
||||||
|
run_inspec_process("sign profile #{profile_1} --keyname #{unique_key_name}", prefix: "cd #{dir};")
|
||||||
|
run_inspec_process("sign profile #{profile_2} --keyname #{unique_key_name}", prefix: "cd #{dir};")
|
||||||
|
|
||||||
|
# Run inspec exec on both the signed profiles with allow_unsigned_profiles false (default behaviour)
|
||||||
|
run_result = run_inspec_process("exec artifact-profile-1-0.1.0.iaf artifact-profile-2-0.1.0.iaf", prefix: "cd #{dir};", allow_unsigned_profiles: false, env: { CHEF_PREVIEW_MANDATORY_PROFILE_SIGNING: "1" })
|
||||||
|
_(run_result.stdout).must_include "2 successful controls"
|
||||||
|
_(run_result.exit_status).must_equal 0
|
||||||
|
|
||||||
|
delete_signing_keys(unique_key_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should raise signature required error for single unsigned profile without flag --allow-unsigned-profiles" do
|
||||||
|
run_result = run_inspec_process("exec #{complete_profile} --no-create-lockfile", allow_unsigned_profiles: false, env: { CHEF_PREVIEW_MANDATORY_PROFILE_SIGNING: "1" })
|
||||||
|
_(run_result.stderr).must_include "Signature required"
|
||||||
|
_(run_result.stderr).must_include "profile/s: complete"
|
||||||
|
_(run_result.exit_status).must_equal 6
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should raise signature required error for multiple unsigned profiles without flag --allow-unsigned-profiles" do
|
||||||
|
run_result = run_inspec_process("exec #{complete_profile} #{inheritance_profile} --no-create-lockfile", allow_unsigned_profiles: false, env: { CHEF_PREVIEW_MANDATORY_PROFILE_SIGNING: "1" })
|
||||||
|
_(run_result.stderr).must_include "Signature required"
|
||||||
|
_(run_result.stderr).must_include "profile/s: complete, inheritance"
|
||||||
|
_(run_result.exit_status).must_equal 6
|
||||||
|
end
|
||||||
|
|
||||||
|
it "when running combination of signed and unsigned profile without flag --allow-unsigned-profiles should raise signature required error and exit" do
|
||||||
|
prepare_examples do |dir|
|
||||||
|
skip_windows! # Breakage confirmed, only on CI: https://buildkite.com/chef-oss/inspec-inspec-master-verify/builds/2355#2c9d032e-4a24-4e7c-aef2-1c9e2317d9e2
|
||||||
|
|
||||||
|
unique_key_name = SecureRandom.uuid
|
||||||
|
|
||||||
|
# Create profile
|
||||||
|
profile = File.join(dir, "artifact-profile")
|
||||||
|
run_inspec_process("init profile artifact-profile", prefix: "cd #{dir};")
|
||||||
|
|
||||||
|
# Generate key to sign profile
|
||||||
|
run_inspec_process("sign generate-keys --keyname #{unique_key_name}", prefix: "cd #{dir};")
|
||||||
|
|
||||||
|
# Sign profile
|
||||||
|
run_inspec_process("sign profile #{profile} --keyname #{unique_key_name}", prefix: "cd #{dir};")
|
||||||
|
|
||||||
|
# Run inspec exec on combination of a signed profile and an unsigned profile with allow_unsigned_profiles false (default behaviour)
|
||||||
|
run_result = run_inspec_process("exec #{complete_profile} artifact-profile-0.1.0.iaf", prefix: "cd #{dir};", allow_unsigned_profiles: false, env: { CHEF_PREVIEW_MANDATORY_PROFILE_SIGNING: "1" })
|
||||||
|
_(run_result.stderr).must_include "Signature required"
|
||||||
|
_(run_result.stderr).must_include "profile/s: complete"
|
||||||
|
_(run_result.exit_status).must_equal 6
|
||||||
|
|
||||||
|
delete_signing_keys(unique_key_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "when running combination of signed and unsigned profile with flag --allow-unsigned-profiles should run successfully without raising signature required error" do
|
||||||
|
prepare_examples do |dir|
|
||||||
|
skip_windows! # Breakage confirmed, only on CI: https://buildkite.com/chef-oss/inspec-inspec-master-verify/builds/2355#2c9d032e-4a24-4e7c-aef2-1c9e2317d9e2
|
||||||
|
|
||||||
|
unique_key_name = SecureRandom.uuid
|
||||||
|
|
||||||
|
# Create profile
|
||||||
|
profile = File.join(dir, "artifact-profile")
|
||||||
|
run_inspec_process("init profile artifact-profile", prefix: "cd #{dir};")
|
||||||
|
|
||||||
|
# Generate key to sign profile
|
||||||
|
run_inspec_process("sign generate-keys --keyname #{unique_key_name}", prefix: "cd #{dir};")
|
||||||
|
|
||||||
|
# Sign profile
|
||||||
|
run_inspec_process("sign profile #{profile} --keyname #{unique_key_name}", prefix: "cd #{dir};")
|
||||||
|
|
||||||
|
# Run inspec exec on combination of a signed profile and an unsigned profile with allow_unsigned_profiles true
|
||||||
|
|
||||||
|
run_result = run_inspec_process("exec #{complete_profile} artifact-profile-0.1.0.iaf", prefix: "cd #{dir};", allow_unsigned_profiles: true, env: { CHEF_PREVIEW_MANDATORY_PROFILE_SIGNING: "1" })
|
||||||
|
_(run_result.stdout).must_include "2 successful controls"
|
||||||
|
_(run_result.exit_status).must_equal 0
|
||||||
|
|
||||||
|
delete_signing_keys(unique_key_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,7 @@ require "inspec/config"
|
||||||
require "inspec/profile"
|
require "inspec/profile"
|
||||||
require "inspec/runner_mock"
|
require "inspec/runner_mock"
|
||||||
require "inspec/fetcher/mock"
|
require "inspec/fetcher/mock"
|
||||||
|
require "inspec/feature"
|
||||||
|
|
||||||
describe "controls" do
|
describe "controls" do
|
||||||
def load(content)
|
def load(content)
|
||||||
|
@ -15,8 +16,7 @@ describe "controls" do
|
||||||
backend: Inspec::Backend.create(Inspec::Config.mock),
|
backend: Inspec::Backend.create(Inspec::Config.mock),
|
||||||
}
|
}
|
||||||
|
|
||||||
Inspec::Profile.for_target(data, opts)
|
Inspec::Profile.for_target(data, opts).params[:controls]["1"]
|
||||||
.params[:controls]["1"]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:rand_string) { rand.to_s }
|
let(:rand_string) { rand.to_s }
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
require "helper"
|
require "helper"
|
||||||
require "inspec/profile_context"
|
require "inspec/profile_context"
|
||||||
require "inspec/runner_mock"
|
require "inspec/runner_mock"
|
||||||
|
require "inspec/feature"
|
||||||
|
|
||||||
describe "resource exception" do
|
describe "resource exception" do
|
||||||
let(:profile) do
|
let(:profile) do
|
||||||
|
|
|
@ -4,6 +4,7 @@ require "inspec/runner_mock"
|
||||||
require "inspec/resource"
|
require "inspec/resource"
|
||||||
require "inspec/resources/command"
|
require "inspec/resources/command"
|
||||||
require "inspec/profile"
|
require "inspec/profile"
|
||||||
|
require "inspec/feature"
|
||||||
|
|
||||||
describe Inspec::Profile do
|
describe Inspec::Profile do
|
||||||
let(:logger) { Minitest::Mock.new }
|
let(:logger) { Minitest::Mock.new }
|
||||||
|
|
Loading…
Reference in a new issue