Inspec automate command extended for compliances

Signed-off-by: Nikita Mathur <nikita.mathur@chef.io>
This commit is contained in:
Nikita Mathur 2021-04-30 17:19:33 +05:30
parent 8a93f08a13
commit a4d822ae43
12 changed files with 220 additions and 26 deletions

View file

@ -179,7 +179,7 @@ $ inspec --help
Commands:
inspec archive PATH # archive a profile to tar.gz (default) ...
inspec check PATH # verify all tests at the specified PATH
inspec compliance SUBCOMMAND ... # Chef Compliance commands
inspec compliance SUBCOMMAND or automate SUBCOMMAND ... # Chef Compliance commands
inspec detect # detect the target OS
inspec exec PATH(S) # run all test files at the specified PATH.
inspec help [COMMAND] # Describe available commands or one spe...

View file

@ -8,6 +8,8 @@ The `compliance` set of subcommands handle user-initiated communication with Che
When Automate initiates scans, the `compliance` subcommand is not used.
An alternate subcommand to `compliance` is `automate`. And it works similarly using `inspec automate`.
## Operational Notes
### Obtaining an a test Automate server
@ -65,9 +67,10 @@ There are several other minor commands not listed here - see `lib/cli.rb` for a
### login
Saves a credentials file locally. Future invocations of `inspec compliance` use the credentials file to authenticate.
Saves a credentials file locally. Future invocations of `inspec compliance` or `inspec automate` use the credentials file to authenticate.
`be inspec compliance login --user=admin --token='1234567890asdfghjkl' --insecure https://chef-automate.test`
`be inspec compliance login --user=admin --token='1234567890asdfghjkl' --insecure https://chef-automate.test` or
`be inspec automate login --user=admin --token='1234567890asdfghjkl' --insecure https://chef-automate.test`
Here are the results of running login, from `.inspec/compliance/config.json`:

View file

@ -187,6 +187,10 @@ Chef Automate:
inspec compliance login
inspec exec compliance://username/linux-baseline
```
An alternate command for login:
```
inspec automate login
```
Chef Supermarket:
```

View file

@ -40,6 +40,11 @@ inspec compliance login https://compliance.test --user admin --insecure --token
where `--insecure` is required when using self-signed certificates.
An alternate command for login:
```bash
inspec automate login https://compliance.test --user admin --insecure --token ''
```
Use a compliance profile from the Chef Supermarket:
```YML

View file

@ -221,6 +221,10 @@ class Inspec::InspecCLI < Inspec::BaseCLI
#{Inspec::Dist::EXEC_NAME} compliance login
#{Inspec::Dist::EXEC_NAME} exec compliance://username/linux-baseline
```
An alternate command for login:
```
#{Inspec::Dist::EXEC_NAME} automate login
```
Supermarket:
```

View file

@ -117,6 +117,15 @@ module Inspec::Plugin::V2
# `inspec dosomething` => activate the :dosomething hook
activate_me ||= cli_args.include?(act.activator_name.to_s)
# Only one compliance command to be activated at one time.
# Since both commands are defined in the same class,
# activators were not getting fetched uniquely.
if cli_args.include?("automate") && act.activator_name.to_s.eql?("compliance")
activate_me = false
elsif cli_args.include?("compliance") && act.activator_name.to_s.eql?("automate")
activate_me = false
end
# OK, activate.
if activate_me
act.activate

View file

@ -6,6 +6,7 @@ This extensions offers the following features:
- execute profiles directly from Chef Automate/Chef Compliance locally
- upload a local profile to Chef Automate/Chef Compliance
The subcommand `compliance` has an alternate `automate`. And it works similarly using `inspec automate`.
To use the CLI, this InSpec add-on adds the following commands:
* `$ inspec compliance login` - authentication of the API token against Chef Automate/Chef Compliance
@ -14,12 +15,21 @@ To use the CLI, this InSpec add-on adds the following commands:
* `$ inspec compliance upload path/to/local/profile` - uploads a local profile to Chef Automate/Chef Compliance
* `$ inspec compliance logout` - logout of Chef Automate/Chef Compliance
Similar to these CLI commands are:
* `$ inspec automate login` - authentication of the API token against Chef Automate/Chef Compliance
* `$ inspec automate profiles` - list all available Compliance profiles
* `$ inspec automate upload path/to/local/profile` - uploads a local profile to Chef Automate/Chef Compliance
* `$ inspec automate logout` - logout of Chef Automate/Chef Compliance
Compliance profiles can be executed in two ways:
- via compliance exec: `inspec compliance exec profile`
- via compliance exec: `inspec compliance exec profile` or `inspec automate exec profile`
- via compliance scheme: `inspec exec compliance://profile`
## Usage
### Command options
@ -37,6 +47,21 @@ Commands:
inspec compliance version # displays the version of the Chef Compliance server
```
or
```
$ inspec automate
Commands:
inspec automate download PROFILE # downloads a profile from Chef Compliance
inspec automate exec PROFILE # executes a Chef Compliance profile
inspec automate help [COMMAND] # Describe subcommands or one specific subcommand
inspec automate login SERVER # Log in to a Chef Automate/Chef Compliance SERVER
inspec automate logout # user logout from Chef Compliance
inspec automate profiles # list all available profiles in Chef Compliance
inspec automate upload PATH # uploads a local profile to Chef Compliance
inspec automate version # displays the version of the Chef Compliance server
```
### Login with Chef Automate 2
You will need an API token for authentication. You can retrieve one via the admin section of your A2 web gui.
@ -67,6 +92,12 @@ You will need an access token for authentication. You can retrieve one via [UI](
$ inspec compliance login https://automate.compliance.test --insecure --user 'admin' --ent 'brewinc' --token 'zuop..._KzE'
```
or
```
$ inspec automate login https://automate.compliance.test --insecure --user 'admin' --ent 'brewinc' --token 'zuop..._KzE'
```
### Login with Chef Compliance
You will need an access token for authentication. You can retrieve one via:
@ -79,6 +110,12 @@ You can choose the access token (`--token`) or the refresh token (`--refresh_tok
$ inspec compliance login https://compliance.test --user admin --insecure --token '...'
```
or
```
$ inspec automate login https://compliance.test --user admin --insecure --token '...'
```
### List available profiles via Chef Compliance / Automate
```
@ -103,6 +140,30 @@ Available profiles:
* cis/cis-ubuntu14.04lts-level2
```
or
```
$ inspec automate profiles
Available profiles:
-------------------
* base/apache
* base/linux
* base/mysql
* base/postgres
* base/ssh
* base/windows
* cis/cis-centos6-level1
* cis/cis-centos6-level2
* cis/cis-centos7-level1
* cis/cis-centos7-level2
* cis/cis-rhel7-level1
* cis/cis-rhel7-level2
* cis/cis-ubuntu12.04lts-level1
* cis/cis-ubuntu12.04lts-level2
* cis/cis-ubuntu14.04lts-level1
* cis/cis-ubuntu14.04lts-level2
```
### Upload a profile to Chef Compliance / Automate
```
@ -144,6 +205,47 @@ Available profiles:
* cis/cis-ubuntu14.04lts-level2
```
or
```
$ inspec automate version
Chef Compliance version: 1.0.11
➜ inspec git:(chris-rock/cc-error-not-loggedin) ✗ b inspec automate upload examples/profile
I, [2016-05-06T14:27:20.907547 #37592] INFO -- : Checking profile in examples/profile
I, [2016-05-06T14:27:20.907668 #37592] INFO -- : Metadata OK.
I, [2016-05-06T14:27:20.968584 #37592] INFO -- : Found 4 controls.
I, [2016-05-06T14:27:20.968638 #37592] INFO -- : Control definitions OK.
Profile is valid
Generate temporary profile archive at /var/folders/jy/2bnrfb4s36jbjtzllvhhyqhw0000gn/T/profile20160506-37592-1tf326f.tar.gz
I, [2016-05-06T14:27:21.020017 #37592] INFO -- : Generate archive /var/folders/jy/2bnrfb4s36jbjtzllvhhyqhw0000gn/T/profile20160506-37592-1tf326f.tar.gz.
I, [2016-05-06T14:27:21.024837 #37592] INFO -- : Finished archive generation.
Start upload to admin/profile
Uploading to Chef Compliance
Successfully uploaded profile
# display all profiles
$ inspec automate profiles
Available profiles:
-------------------
* admin/profile
* base/apache
* base/linux
* base/mysql
* base/postgres
* base/ssh
* base/windows
* cis/cis-centos6-level1
* cis/cis-centos6-level2
* cis/cis-centos7-level1
* cis/cis-centos7-level2
* cis/cis-rhel7-level1
* cis/cis-rhel7-level2
* cis/cis-ubuntu12.04lts-level1
* cis/cis-ubuntu12.04lts-level2
* cis/cis-ubuntu14.04lts-level1
* cis/cis-ubuntu14.04lts-level2
```
### Run a profile from Chef Compliance / Chef Automate on Workstation
```
@ -179,6 +281,14 @@ $ inspec compliance logout
Successfully logged out
```
or
```
$ inspec automate logout
Successfully logged out
```
## Integration Tests
At this point of time, InSpec is not able to pick up the token directly, therefore the integration test is semi-automatic at this point of time:

View file

@ -7,6 +7,11 @@ module InspecPlugins
require_relative "inspec-compliance/cli"
InspecPlugins::Compliance::CLI
end
cli_command :automate do
require_relative "inspec-compliance/cli"
InspecPlugins::Compliance::CLI
end
end
autoload :Configuration, "plugins/inspec-compliance/lib/inspec-compliance/configuration"

View file

@ -9,7 +9,7 @@ module InspecPlugins
class CannotDetermineServerType < StandardError; end
def login(options)
raise ArgumentError, "Please specify a server using `#{EXEC_NAME} compliance login https://SERVER`" unless options["server"]
raise ArgumentError, "Please specify a server using `#{EXEC_NAME} compliance login https://SERVER` or `#{EXEC_NAME} automate login https://SERVER`" unless options["server"]
options["server"] = URI("https://#{options["server"]}").to_s if URI(options["server"]).scheme.nil?
@ -179,7 +179,7 @@ module InspecPlugins
def self.compliance_verify_thor_options(o)
error_msg = []
error_msg.push("Please specify a server using `#{EXEC_NAME} compliance login https://SERVER`") if o["server"].nil?
error_msg.push("Please specify a server using `#{EXEC_NAME} compliance login https://SERVER` or `#{EXEC_NAME} automate login https://SERVER`") if o["server"].nil?
if o["user"].nil? && o["refresh_token"].nil?
error_msg.push("Please specify a `--user='USER'` or a `--refresh-token='TOKEN'`")

View file

@ -6,8 +6,7 @@ module InspecPlugins
module Compliance
class CLI < Inspec.plugin(2, :cli_command)
include Inspec::Dist
subcommand_desc "compliance SUBCOMMAND", "#{COMPLIANCE_PRODUCT_NAME} commands"
subcommand_desc "compliance SUBCOMMAND or automate SUBCOMMAND", "#{COMPLIANCE_PRODUCT_NAME} commands"
# desc "login https://SERVER --insecure --user='USER' --ent='ENTERPRISE' --token='TOKEN'", 'Log in to a Chef Compliance/Chef Automate SERVER'
desc "login", "Log in to a #{COMPLIANCE_PRODUCT_NAME}/#{AUTOMATE_PRODUCT_NAME} SERVER"
@ -65,7 +64,7 @@ module InspecPlugins
exit 1
end
rescue InspecPlugins::Compliance::ServerConfigurationMissing
$stderr.puts "\nServer configuration information is missing. Please login using `#{EXEC_NAME} compliance login`"
$stderr.puts "\nServer configuration information is missing. Please login using `#{EXEC_NAME} #{subcommand_name} login`"
exit 1
end
@ -167,7 +166,7 @@ module InspecPlugins
# determine user information
if (config["token"].nil? && config["refresh_token"].nil?) || config["user"].nil?
error.call("Please login via `#{EXEC_NAME} compliance login`")
error.call("Please login via `#{EXEC_NAME} #{subcommand_name} login`")
end
# read profile name from inspec.yml
@ -233,7 +232,7 @@ module InspecPlugins
exit 1
end
rescue InspecPlugins::Compliance::ServerConfigurationMissing
puts "\nServer configuration information is missing. Please login using `#{EXEC_NAME} compliance login`"
puts "\nServer configuration information is missing. Please login using `#{EXEC_NAME} #{subcommand_name} login`"
exit 1
end
@ -258,9 +257,13 @@ module InspecPlugins
def loggedin(config)
serverknown = !config["server"].nil?
puts "You need to login first with `#{EXEC_NAME} compliance login`" unless serverknown
puts "You need to login first with `#{EXEC_NAME} #{subcommand_name} login`" unless serverknown
serverknown
end
def subcommand_name
@_invocations[Inspec::InspecCLI]&.first || "automate"
end
end
# register the subcommand to InSpec CLI registry

View file

@ -34,13 +34,13 @@ module InspecPlugins
if config["token"].nil? && config["refresh_token"].nil?
if config["server_type"] == "automate"
server = "automate"
msg = "#{EXEC_NAME} compliance login https://your_automate_server --user USER --ent ENT --dctoken DCTOKEN or --token USERTOKEN"
msg = "#{EXEC_NAME} compliance or automate login https://your_automate_server --user USER --ent ENT --dctoken DCTOKEN or --token USERTOKEN"
elsif config["server_type"] == "automate2"
server = "automate2"
msg = "#{EXEC_NAME} compliance login https://your_automate2_server --user USER --token APITOKEN"
msg = "#{EXEC_NAME} compliance or automate login https://your_automate2_server --user USER --token APITOKEN"
else
server = "compliance"
msg = "#{EXEC_NAME} compliance login https://your_compliance_server --user admin --insecure --token 'PASTE TOKEN HERE' "
msg = "#{EXEC_NAME} compliance or automate login https://your_compliance_server --user admin --insecure --token 'PASTE TOKEN HERE' "
end
raise Inspec::FetcherFailure, <<~EOF
@ -136,7 +136,8 @@ module InspecPlugins
if m.nil?
raise "Unable to determine compliance profile name. This can be caused by " \
"an incorrect server in your configuration. Try to login to compliance " \
"via the `#{EXEC_NAME} compliance login` command."
"via the `#{EXEC_NAME} compliance login` command or " \
"via the `#{EXEC_NAME} automate login` command."
end
"#{m[:owner]}/#{m[:id]}"

View file

@ -50,4 +50,54 @@ class ComplianceCli < Minitest::Test
assert_exit_code 0, out # TODO: make this error
end
## testing automate command for compliances
def test_help_output_using_automate_cmd
out = run_inspec_process("automate help")
assert_includes out.stdout, "inspec automate exec PROFILE"
assert_exit_code 0, out
end
def test_logout_command_using_automate_cmd
out = run_inspec_process("automate logout")
assert_includes out.stdout, ""
assert_exit_code 0, out
end
def test_error_login_with_invalid_url_using_automate_cmd
out = run_inspec_process("automate login")
assert_includes out.stderr, 'ERROR: "inspec automate login" was called with no arguments'
assert_exit_code 1, out
end
def test_profile_list_without_auth_using_automate_cmd
out = run_inspec_process("automate profiles")
assert_includes out.stdout, "You need to login first with `inspec automate login`"
assert_exit_code 0, out # TODO: make this error
end
def test_error_upload_without_args_using_automate_cmd
out = run_inspec_process("automate upload")
assert_includes out.stderr, 'ERROR: "inspec automate upload" was called with no arguments'
assert_exit_code 1, out
end
def test_error_upload_with_fake_path_using_automate_cmd
out = run_inspec_process("automate upload /path/to/dir")
assert_includes out.stdout, "You need to login first with `inspec automate login`"
assert_exit_code 0, out # TODO: make this error
end
end