inspec-iggy 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3dc2db85eac33aa51e03baf7f0df30858a7f1815
4
- data.tar.gz: e44f8c04a3998030335b6a2dcf2ff225c7b18d4f
3
+ metadata.gz: 77411a21b6c3a1f74cdc23ab20f48d96af93a985
4
+ data.tar.gz: 0b32db4421f119d92343a7cd834e2953989ee3b7
5
5
  SHA512:
6
- metadata.gz: 9c8086684b141e0590b9a55f1b135922a4c9f4f7df3c3c32ec6422c890c7fe24a61443b16eac2e35bedb02f3a031a7efc7586a81d378addcd4c0d52d5d6de037
7
- data.tar.gz: cda93559f8d2c47aaa4447d51d6f2151a006e3249e1297f379d041402c0f168f209acfda6003e42e30859ad901063e1aa0c5973127306ef7fdfec29ef7c058de
6
+ metadata.gz: eac8bdf420cb4951742c5e66038bfda33c7776d7c75b78fab08f8cff81c18883c956670991ea62a8c4822a290bb24981db267b93ca68a5aa0487db57ee0462ce
7
+ data.tar.gz: b01d470916ca825f4a30cc3376a3979815160efd76fe2ee6d1ecae8b98d6881b937b6fa139a050719b6b38dcd9c056e725a6984b4e389ad862be2d16af179279
data/Gemfile CHANGED
@@ -1,15 +1,17 @@
1
1
  # encoding: utf-8
2
- source "http://rubygems.org"
2
+ source 'http://rubygems.org'
3
3
 
4
4
  gemspec
5
5
 
6
6
  group :test do
7
- gem "bundler"
8
- gem "rake"
9
- gem "chefstyle"
7
+ gem 'rake' # Build task manager
8
+ gem 'chefstyle'
9
+ gem 'byebug' # A debugger REPL
10
+ gem 'rubocop' # Needed for style linting
11
+ gem 'm'
10
12
  end
11
13
 
12
14
  group :tools do
13
- gem "github_changelog_generator"
14
- gem "rb-readline"
15
+ gem 'github_changelog_generator'
16
+ gem 'rb-readline'
15
17
  end
data/README.md CHANGED
@@ -1,53 +1,61 @@
1
1
  # Description #
2
2
 
3
- InSpec-Iggy (InSpec Generate -> "IG" -> "Iggy") is an [InSpec](https://inspec.io) plugin for generating compliance controls and profiles from [Terraform](https://terraform.io) ```tfstate``` files. Iggy generates InSpec AWS controls by mapping Terraform resources to InSpec resources. You may also use tags to annotate your Terraform scripts to specify which compliance profiles to be used and Iggy will create a profile including those dependencies.
3
+ [![Build Status Master](https://travis-ci.org/inspec/inspec-iggy.svg?branch=master)](https://travis-ci.org/inspec/inspec-iggy)
4
4
 
5
- Iggy was originally a stand-alone CLI inspired by Christoph Hartmann's [inspec-verify-provision](https://github.com/chris-rock/inspec-verify-provision) and the blog post on testing [Terraform with InSpec](http://lollyrock.com/articles/inspec-terraform/).
5
+ InSpec-Iggy (InSpec Generate -> "IG" -> "Iggy") is an [InSpec](https://inspec.io) plugin for generating compliance controls and profiles from [Terraform](https://terraform.io) `tfstate` files and [AWS CloudFormation](https://aws.amazon.com/cloudformation/) templates. Iggy generates InSpec controls by mapping Terraform and CloudFormation resources to InSpec resources and exports a profile that may be used from the `inspec` CLI or uploaded to [Chef Automate](https://automate.chef.io/).
6
6
 
7
- The [CHANGELOG.md](https://github.com/mattray/iggy/blob/master/CHANGELOG.md) covers current, previous and future development milestones and contains the features backlog.
7
+ inspec terraform generate -n myprofile
8
+ inspec exec myprofile -t aws://us-west-2
9
+ inspec compliance upload myprofile
8
10
 
9
- # Requirements #
11
+ Iggy was originally a stand-alone CLI inspired by Christoph Hartmann's [inspec-verify-provision](https://github.com/chris-rock/inspec-verify-provision) and the blog post on testing [InSpec for provisioning testing: Verify Terraform setups with InSpec](http://lollyrock.com/articles/inspec-terraform/).
10
12
 
11
- Iggy generates compliance profiles for InSpec 2, which includes the AWS and Azure resources. Because resources are continuing to be added to InSpec, you may want the latest version to support as many resource coverage as possible.
13
+ The [CHANGELOG.md](https://github.com/inspec/iggy/blob/master/CHANGELOG.md) covers current, previous and future development milestones and contains the features backlog.
12
14
 
13
- Written and tested with Ruby 2.4.4 (or whatever InSpec 2.0 supports).
15
+ 1. [Requirements](#requirements)
16
+ 2. [Installation](#installation)
17
+ 3. [InSpec Terraform Generate](#itg)
18
+ 4. [InSpec Terraform Extract](#ite)
19
+ 5. [InSpec Cloudformation Generate](#icg)
20
+ 6. [Testing](#testing)
14
21
 
15
- # Installation #
22
+ # Requirements <a name="requirements"></a>
16
23
 
17
- `inspec-iggy` is a plugin for InSpec and may be installed as follows
24
+ Iggy generates compliance profiles for InSpec 2.3 and later, which includes the AWS and Azure resources. Because resources are continuing to be added to InSpec, you may want the latest version to support as many resource coverage as possible. It has currently been tested primarily with AWS but other InSpec-supported platforms should work as well.
18
25
 
19
- ```bash
20
- # install InSpec
21
- gem install inspec
22
- gem install inspec-iggy
23
- inspec terraform version
24
- ```
26
+ Written and tested with Ruby 2.5.1.
27
+
28
+ # Installation <a name="installation"></a>
25
29
 
26
- ## * for development: ##
30
+ `inspec-iggy` is a plugin for InSpec. InSpec 2.3 or later is required. To install, use:
27
31
 
28
- ```bash
29
- # Install `inspec-iggy` via a symlink:
30
- git clone git@github.com:inspec/inspec-iggy ~/inspec-iggy
31
- mkdir -p ~/.inspec/plugins
32
- ln -s ~/inspec-iggy/ ~/.inspec/plugins/inspec-iggy
33
- inspec terraform version
32
+ ```
33
+ $ inspec plugin install inspec-iggy
34
34
  ```
35
35
 
36
- ## * or build a gem: ##
36
+ # InSpec Terraform Generate<a name="itg"></a>
37
37
 
38
- ```bash
39
- # Build the `inspec-iggy` then install:
40
- git clone https://github.com/inspec/inspec-iggy && cd inspec-iggy && gem build *gemspec && gem install *gem
41
- inspec terraform version
42
- ```
38
+ inspec terraform generate --tfstate terraform.tfstate --name myprofile
43
39
 
44
- # InSpec Terraform Generate #
40
+ Iggy dynamically pulls the available AWS resources from InSpec and attempts to map them to Terraform resources, producing an InSpec profile. ```inspec terraform generate --help``` will show all available options.
45
41
 
46
- inspec terraform generate --tfstate terraform.tfstate
42
+ ## Usage
47
43
 
48
- Iggy dynamically pulls the available AWS resources from InSpec and attempts to map them to the Terraform resources. Newer versions of InSpec may provide additional coverage.
44
+ inspec terraform generate [options] -n, --name=NAME
49
45
 
50
- # InSpec Terraform Extract (EXPERIMENTAL)#
46
+ -n, --name=NAME Name of profile to be generated (required)
47
+ -t, [--tfstate=TFSTATE] Specify path to the input terraform.tfstate (default: .)
48
+ [--debug], [--no-debug] Verbose debugging messages
49
+ [--copyright=COPYRIGHT] Name of the copyright holder (default: The Authors)
50
+ [--email=EMAIL] Email address of the author (default: you@example.com)
51
+ [--license=LICENSE] License for the profile (default: Apache-2.0)
52
+ [--maintainer=MAINTAINER] Name of the copyright holder (default: The Authors)
53
+ [--summary=SUMMARY] One line summary for the profile (default: An InSpec Compliance Profile)
54
+ [--title=TITLE] Human-readable name for the profile (default: InSpec Profile)
55
+ [--version=VERSION] Specify the profile version (default: 0.1.0)
56
+ [--overwrite], [--no-overwrite] Overwrites existing profile directory
57
+
58
+ # InSpec Terraform Extract (EXPERIMENTAL)<a name="ite"></a>
51
59
 
52
60
  inspec terraform extract --tfstate terraform.tfstate
53
61
 
@@ -78,27 +86,70 @@ Currently it only supports URL-based compliance profiles. InSpec supports other
78
86
 
79
87
  inspec exec https://github.com/dev-sec/linux-baseline -t ssh://clckwrk@52.33.203.34 -i ~/.ssh/mattray-apac
80
88
 
81
- # CloudFormation Support #
89
+ # InSpec CloudFormation Generate<a name="icg"></a>
90
+
91
+ inspec cloudformation generate --template mytemplate.json --stack mystack-20180909T052147Z --profile myprofile
92
+
93
+ Iggy supports AWS CloudFormation templates by mapping the AWS resources to InSpec resources and using the stack name or unique stack ID associated with the CloudFormation template as an entry point to check those resources in the generated profile. ```inspec cloudformation generate --help``` will show all available options.
94
+
95
+ ## Usage
96
+
97
+ inspec cloudformation generate [options] -n, --name=NAME -s, --stack=STACK -t, --template=TEMPLATE
98
+
99
+ -n, --name=NAME Name of profile to be generated (required)
100
+ -s, --stack=STACK Specify stack name or unique stack ID associated with the CloudFormation template
101
+ -t, --template=TEMPLATE Specify path to the input CloudFormation template
102
+ [--debug], [--no-debug] Verbose debugging messages
103
+ [--copyright=COPYRIGHT] Name of the copyright holder (default: The Authors)
104
+ [--email=EMAIL] Email address of the author (default: you@example.com)
105
+ [--license=LICENSE] License for the profile (default: Apache-2.0)
106
+ [--maintainer=MAINTAINER] Name of the copyright holder (default: The Authors)
107
+ [--summary=SUMMARY] One line summary for the profile (default: An InSpec Compliance Profile)
108
+ [--title=TITLE] Human-readable name for the profile (default: InSpec Profile)
109
+ [--version=VERSION] Specify the profile version (default: 0.1.0)
110
+ [--overwrite], [--no-overwrite] Overwrites existing profile directory
111
+
112
+ # Development
113
+
114
+ ## Installation
115
+
116
+ To point `inspec` at a local copy of `inspec-iggy` for development, use:
117
+
118
+ ```
119
+ $ inspec plugin install path/to/your/inspec-iggy/lib/inspec-iggy.rb
120
+ ```
121
+
122
+ # Testing Iggy<a name="testing"></a>
82
123
 
83
- **CloudFormation support has been started, but it is incomplete while focusing on Terraform.** Here is an example of the current output, note that it's not tied to an actual deployed CloudFormation Stack, so that will need to be provided for the entry point of testing.
84
- https://gist.github.com/c4d6eda82dfb25502ef381cc631a1edd
85
124
 
86
- # Testing #
125
+ Unit, Functional, and Integration tests are provided, though more are welcome. Iggy uses the Minitest library for testing, using the classic `def test...` syntax. Because Iggy loads InSpec into memory, and InSpec uses RSpec internally, Spec-style testing breaks.
87
126
 
88
- Iggy uses [RSpec](http://rspec.info/) for testing. You should run the following before committing.
127
+ To run all tests, run
128
+
129
+ ```
130
+ $ bundle exec rake test
131
+ ```
89
132
 
90
- $ rspec
133
+ Linting is also provided via Rubocop.
91
134
 
92
- For style
135
+ To check for code style issues, run:
93
136
 
94
- $ chefstyle .
137
+ ```
138
+ $ bundle exec rake lint
139
+ ```
140
+
141
+ You can auto-correct many issues:
142
+
143
+ ```
144
+ $ bundle exec rubocop -a
145
+ ```
95
146
 
96
147
  # License and Author #
97
148
 
98
149
  | | |
99
150
  |:---------------|:------------------------------------------|
100
151
  | **Author** | Matt Ray (<matt@chef.io>) |
101
- | **Copyright:** | Copyright (c) 2017 Chef Software Inc. |
152
+ | **Copyright:** | Copyright (c) 2017-2018 Chef Software Inc.|
102
153
  | **License:** | Apache License, Version 2.0 |
103
154
 
104
155
  Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,26 +1,26 @@
1
1
  # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
2
+ lib = File.expand_path('lib', __dir__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
- require "inspec-iggy/version"
5
+ require 'inspec-iggy/version'
6
6
 
7
7
  Gem::Specification.new do |spec|
8
- spec.name = "inspec-iggy"
9
- spec.version = Iggy::VERSION
10
- spec.authors = ["Matt Ray"]
11
- spec.email = ["matt@chef.io"]
12
- spec.summary = "InSpec plugin to generate InSpec compliance profiles from Terraform."
13
- spec.description = "Generate InSpec compliance profiles from Terraform by tagging instances and mapping Terraform to InSpec."
14
- spec.homepage = "https://github.com/inspec/inspec-iggy"
15
- spec.license = "Apache-2.0"
8
+ spec.name = 'inspec-iggy'
9
+ spec.version = InspecPlugins::Iggy::VERSION
10
+ spec.authors = ['Matt Ray']
11
+ spec.email = ['matt@chef.io']
12
+ spec.summary = 'InSpec plugin to generate InSpec compliance profiles from Terraform.'
13
+ spec.description = 'Generate InSpec compliance profiles from Terraform by tagging instances and mapping Terraform to InSpec.'
14
+ spec.homepage = 'https://github.com/inspec/inspec-iggy'
15
+ spec.license = 'Apache-2.0'
16
16
 
17
17
  spec.files = %w{
18
18
  README.md inspec-iggy.gemspec Gemfile
19
19
  } + Dir.glob(
20
- "{bin,docs,examples,lib,tasks,test}/**/*", File::FNM_DOTMATCH
20
+ '{bin,docs,examples,lib,tasks}/**/*', File::FNM_DOTMATCH
21
21
  ).reject { |f| File.directory?(f) }
22
22
 
23
- spec.require_paths = ["lib"]
23
+ spec.require_paths = ['lib']
24
24
 
25
- spec.add_dependency "inspec", ">=2.0", "<3.0.0"
25
+ spec.add_dependency 'inspec', '>=2.3', '<4.0.0'
26
26
  end
@@ -1,7 +1,16 @@
1
1
  # encoding: utf-8
2
2
 
3
+ # This file is known as the "entry point."
4
+ # This is the file InSpec will try to load if it
5
+ # thinks your plugin is installed.
6
+
7
+ # The *only* thing this file should do is setup the
8
+ # load path, then load the plugin definition file.
9
+
10
+ # Next two lines simply add the path of the gem to the load path.
11
+ # This is not needed when being loaded as a gem; but when doing
12
+ # plugin development, you may need it. Either way, it's harmless.
3
13
  libdir = File.dirname(__FILE__)
4
14
  $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
5
15
 
6
- require "inspec-iggy/cli"
7
- require "inspec-iggy/version"
16
+ require 'inspec-iggy/plugin'
@@ -0,0 +1,92 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Author:: Matt Ray (<matt@chef.io>)
4
+ #
5
+ # Copyright:: 2018, Chef Software, Inc <legal@chef.io>
6
+ #
7
+
8
+ require 'inspec/plugin/v2'
9
+
10
+ require 'inspec-iggy/version'
11
+ require 'inspec-iggy/profile'
12
+ require 'inspec-iggy/cloudformation/parser'
13
+
14
+ module InspecPlugins::Iggy
15
+ module CloudFormation
16
+ class CliCommand < Inspec.plugin(2, :cli_command)
17
+ subcommand_desc 'cloudformation SUBCOMMAND ...', 'Generate InSpec from CloudFormation'
18
+
19
+ # Thor.map(Hash) allows you to make aliases for commands.
20
+ map('-v' => 'version') # Treat `inspec terraform -v`` as `inspec terraform version`
21
+ map('--version' => 'version') # Treat `inspec terraform -version`` as `inspec terraform version`
22
+
23
+ desc 'version', 'Display version information', hide: true
24
+ def version
25
+ say("Iggy v#{InspecPlugins::Iggy::VERSION}")
26
+ end
27
+
28
+ option :debug,
29
+ desc: 'Verbose debugging messages',
30
+ type: :boolean,
31
+ default: false
32
+
33
+ option :copyright,
34
+ desc: 'Name of the copyright holder',
35
+ default: 'The Authors'
36
+
37
+ option :email,
38
+ desc: 'Email address of the author',
39
+ default: 'you@example.com'
40
+
41
+ option :license,
42
+ desc: 'License for the profile',
43
+ default: 'Apache-2.0'
44
+
45
+ option :maintainer,
46
+ desc: 'Name of the copyright holder',
47
+ default: 'The Authors'
48
+
49
+ option :summary,
50
+ desc: 'One line summary for the profile',
51
+ default: 'An InSpec Compliance Profile'
52
+
53
+ option :title,
54
+ desc: 'Human-readable name for the profile',
55
+ default: 'InSpec Profile'
56
+
57
+ option :version,
58
+ desc: 'Specify the profile version',
59
+ default: '0.1.0'
60
+
61
+ option :overwrite,
62
+ desc: 'Overwrites existing profile directory',
63
+ type: :boolean,
64
+ default: false
65
+
66
+ option :name,
67
+ aliases: '-n',
68
+ required: true,
69
+ desc: 'Name of profile to be generated'
70
+
71
+ option :stack,
72
+ aliases: '-s',
73
+ required: true,
74
+ desc: 'Specify stack name or unique stack ID associated with the CloudFormation template'
75
+
76
+ option :template,
77
+ aliases: '-t',
78
+ required: true,
79
+ desc: 'Specify path to the input CloudFormation template'
80
+
81
+ desc 'generate [options]', 'Generate InSpec compliance controls from CloudFormation template'
82
+ def generate
83
+ Inspec::Log.level = :debug if options[:debug]
84
+ # hash of generated controls
85
+ generated_controls = InspecPlugins::Iggy::CloudFormation::Parser.parse_generate(options[:template])
86
+ printable_controls = InspecPlugins::Iggy::InspecHelper.cfn_controls(options[:title], generated_controls, options[:stack])
87
+ InspecPlugins::Iggy::Profile.render_profile(self, options, options[:template], printable_controls)
88
+ exit 0
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,106 @@
1
+ #
2
+ # Author:: Matt Ray (<matt@chef.io>)
3
+ #
4
+ # Copyright:: 2018, Chef Software, Inc <legal@chef.io>
5
+ #
6
+
7
+ require 'json'
8
+ require 'inspec/objects/control'
9
+ require 'inspec/objects/ruby_helper'
10
+ require 'inspec/objects/describe'
11
+
12
+ require 'inspec-iggy/inspec_helper'
13
+
14
+ module InspecPlugins::Iggy::CloudFormation
15
+ class Parser
16
+ def self.parse_generate(file) # rubocop:disable all
17
+ Inspec::Log.debug "CloudFormation.parse_generate file = #{file}"
18
+ begin
19
+ unless File.file?(file)
20
+ STDERR.puts "ERROR: #{file} is an invalid file, please check your path."
21
+ exit(-1)
22
+ end
23
+ template = JSON.parse(File.read(file))
24
+ rescue JSON::ParserError => e
25
+ STDERR.puts e.message
26
+ STDERR.puts "ERROR: Parsing error in #{file}."
27
+ exit(-1)
28
+ end
29
+ absolutename = File.absolute_path(file)
30
+
31
+ # InSpec controls generated
32
+ generated_controls = []
33
+ # iterate over the resources
34
+ cfn_resources = template['Resources']
35
+ cfn_resources.keys.each do |cfn_res|
36
+ # split out the last ::, these are all AWS
37
+ cfn_resource = cfn_resources[cfn_res]['Type'].split('::').last
38
+ # split camelcase and join with underscores
39
+ cfn_res_type = 'aws_' + cfn_resource.split(/(?=[A-Z])/).join('_').downcase
40
+
41
+ # add translation layer
42
+ if InspecPlugins::Iggy::InspecHelper::TRANSLATED_RESOURCES.key?(cfn_res_type)
43
+ Inspec::Log.debug "CloudFormation.parse_generate cfn_res_type = #{cfn_res_type} #{InspecPlugins::Iggy::InspecHelper::TRANSLATED_RESOURCES[cfn_res_type]} TRANSLATED"
44
+ cfn_res_type = InspecPlugins::Iggy::InspecHelper::TRANSLATED_RESOURCES[cfn_res_type]
45
+ end
46
+
47
+ # does this match an InSpec resource?
48
+ if InspecPlugins::Iggy::InspecHelper::RESOURCES.include?(cfn_res_type)
49
+ Inspec::Log.debug "CloudFormation.parse_generate cfn_res_type = #{cfn_res_type} MATCH"
50
+
51
+ # insert new control based off the resource's ID
52
+ ctrl = Inspec::Control.new
53
+ ctrl.id = "#{cfn_res_type}::#{cfn_res}"
54
+ ctrl.title = "InSpec-Iggy #{cfn_res_type}::#{cfn_res}"
55
+ ctrl.descriptions['default'] = "#{cfn_res_type}::#{cfn_res} from the source file #{absolutename}\nGenerated by InSpec-Iggy v#{InspecPlugins::Iggy::VERSION}"
56
+ ctrl.impact = '1.0'
57
+
58
+ describe = Inspec::Describe.new
59
+ # describes the resource with the logical_resource_id as argument, replaced at inspec exec
60
+ describe.qualifier.push([cfn_res_type, "resources[#{cfn_res}]"])
61
+
62
+ # ensure the resource exists
63
+ describe.add_test(nil, 'exist', nil)
64
+
65
+ # EC2 instances should be running
66
+ describe.add_test(nil, 'be_running', nil) if cfn_res_type.eql?('aws_ec2_instance')
67
+
68
+ # if there's a match, see if there are matching InSpec properties
69
+ inspec_properties = InspecPlugins::Iggy::InspecHelper.resource_properties(cfn_res_type)
70
+ cfn_resources[cfn_res]['Properties'].keys.each do |attr|
71
+ # insert '_' on the CamelCase to get camel_case
72
+ attr_split = attr.split(/(?=[A-Z])/)
73
+ property = attr_split.join('_').downcase
74
+ if inspec_properties.member?(property)
75
+ Inspec::Log.debug "CloudFormation.parse_generate #{cfn_res_type} inspec_property = #{property} MATCH"
76
+ value = cfn_resources[cfn_res]['Properties'][attr]
77
+ if (value.is_a? Hash) || (value.is_a? Array)
78
+ # these get replaced at inspec exec
79
+ if property.eql?('vpc_id') # rubocop:disable Metrics/BlockNesting
80
+ vpc = cfn_resources[cfn_res]['Properties'][attr].values.first
81
+ # https://github.com/inspec/inspec/issues/3173
82
+ describe.add_test(property, 'eq', "resources[#{vpc}]") unless cfn_res_type.eql?('aws_route_table') # rubocop:disable Metrics/BlockNesting
83
+ # AMI is a Ref into Parameters
84
+ elsif property.eql?('image_id') # rubocop:disable Metrics/BlockNesting
85
+ amiref = cfn_resources[cfn_res]['Properties'][attr].values.first
86
+ ami = template['Parameters'][amiref]['Default']
87
+ describe.add_test(property, 'eq', ami)
88
+ end
89
+ else
90
+ describe.add_test(property, 'eq', value)
91
+ end
92
+ else
93
+ Inspec::Log.debug "CloudFormation.parse_generate #{cfn_res_type} inspec_property = #{property} SKIP"
94
+ end
95
+ end
96
+ ctrl.add_test(describe)
97
+ generated_controls.push(ctrl)
98
+ else
99
+ Inspec::Log.debug "CloudFormation.parse_generate cfn_res_type = #{cfn_res_type} SKIP"
100
+ end
101
+ end
102
+ Inspec::Log.debug "CloudFormation.parse_generate generated_controls = #{generated_controls}"
103
+ generated_controls
104
+ end
105
+ end
106
+ end