terracop 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/ruby.yml +20 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.rubocop.yml +14 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +66 -0
- data/LICENSE.md +21 -0
- data/README.md +49 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/bin/terracop +52 -0
- data/default_config.yml +1 -0
- data/lib/terracop/cop/aws/describe_security_group_rules.rb +35 -0
- data/lib/terracop/cop/aws/ensure_tags.rb +51 -0
- data/lib/terracop/cop/aws/iam_role_policy.rb +47 -0
- data/lib/terracop/cop/aws/open_egress.rb +42 -0
- data/lib/terracop/cop/aws/open_ingress.rb +44 -0
- data/lib/terracop/cop/aws/open_ssh.rb +39 -0
- data/lib/terracop/cop/aws/security_group_rule_cop.rb +45 -0
- data/lib/terracop/cop/aws/unrestricted_egress_ports.rb +37 -0
- data/lib/terracop/cop/aws/unrestricted_ingress_ports.rb +38 -0
- data/lib/terracop/cop/aws/wide_egress.rb +53 -0
- data/lib/terracop/cop/aws/wide_ingress.rb +53 -0
- data/lib/terracop/cop/base.rb +105 -0
- data/lib/terracop/cop/style/dash_in_resource_name.rb +35 -0
- data/lib/terracop/cop/style/resource_type_in_name.rb +53 -0
- data/lib/terracop/cop/style/snake_case.rb +35 -0
- data/lib/terracop/formatters/default.rb +25 -0
- data/lib/terracop/formatters/html.rb +16 -0
- data/lib/terracop/formatters/json.rb +53 -0
- data/lib/terracop/formatters/report.html.erb +289 -0
- data/lib/terracop/plan_loader.rb +39 -0
- data/lib/terracop/runner.rb +50 -0
- data/lib/terracop/state_loader.rb +39 -0
- data/lib/terracop/version.rb +5 -0
- data/lib/terracop.rb +49 -0
- data/terracop.gemspec +45 -0
- metadata +200 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 74d2da000770e8368a6f10a0743761edde55a5da61828714725b7567462c13c0
|
4
|
+
data.tar.gz: 05b65c4774aa057bed14d6cc4760c73354ae8675897d3fd7921d2e44952d426d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 303ef1de659d6d6192c20e4ff171b4078e6df28b3c14a1cb2201ea1971c0e2d587c357d1f862b951a194ca60326fb898853bf1476bf487af045a2ac029537c3b
|
7
|
+
data.tar.gz: bb449b0e607af344ac09e5acec0b198e91d8fe4d2a7c1ac55d53e49a1c3dbd815eaa680675a53a211ce5e03a3c9d00bd8d1d5f8a55de3561d1f206a85ba1804f
|
@@ -0,0 +1,20 @@
|
|
1
|
+
name: Ruby
|
2
|
+
|
3
|
+
on: [push]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
|
8
|
+
runs-on: ubuntu-latest
|
9
|
+
|
10
|
+
steps:
|
11
|
+
- uses: actions/checkout@v1
|
12
|
+
- name: Set up Ruby 2.6
|
13
|
+
uses: actions/setup-ruby@v1
|
14
|
+
with:
|
15
|
+
ruby-version: 2.6.x
|
16
|
+
- name: Build and test with Rake
|
17
|
+
run: |
|
18
|
+
gem install bundler
|
19
|
+
bundle install --jobs 4 --retry 3
|
20
|
+
bundle exec rspec
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## 0.1.0
|
8
|
+
|
9
|
+
- Initial release.
|
10
|
+
- Can load Terraform 0.12 state and plan files.
|
11
|
+
- Performs a number of generic checks on all resources.
|
12
|
+
- Performs some AWS-specific security checks on some resources.
|
13
|
+
- Loads a default configuration file.
|
14
|
+
- Allows configuration overrides through `.terracop.yml`.
|
data/Gemfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in terracop.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
# Use latest unreleased simplecov to get Branch Coverage
|
9
|
+
# gem 'simplecov', git: 'https://github.com/colszowka/simplecov.git'
|
10
|
+
# gem 'simplecov-html', git: 'https://github.com/colszowka/simplecov-html.git'
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
terracop (0.1.0)
|
5
|
+
colorize (~> 0.8)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
ast (2.4.0)
|
11
|
+
byebug (11.0.1)
|
12
|
+
colorize (0.8.1)
|
13
|
+
diff-lcs (1.3)
|
14
|
+
docile (1.3.2)
|
15
|
+
jaro_winkler (1.5.4)
|
16
|
+
json (2.3.0)
|
17
|
+
parallel (1.19.1)
|
18
|
+
parser (2.6.5.0)
|
19
|
+
ast (~> 2.4.0)
|
20
|
+
rainbow (3.0.0)
|
21
|
+
rake (10.5.0)
|
22
|
+
rspec (3.9.0)
|
23
|
+
rspec-core (~> 3.9.0)
|
24
|
+
rspec-expectations (~> 3.9.0)
|
25
|
+
rspec-mocks (~> 3.9.0)
|
26
|
+
rspec-core (3.9.0)
|
27
|
+
rspec-support (~> 3.9.0)
|
28
|
+
rspec-expectations (3.9.0)
|
29
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
+
rspec-support (~> 3.9.0)
|
31
|
+
rspec-mocks (3.9.0)
|
32
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
33
|
+
rspec-support (~> 3.9.0)
|
34
|
+
rspec-support (3.9.0)
|
35
|
+
rubocop (0.78.0)
|
36
|
+
jaro_winkler (~> 1.5.1)
|
37
|
+
parallel (~> 1.10)
|
38
|
+
parser (>= 2.6)
|
39
|
+
rainbow (>= 2.2.2, < 4.0)
|
40
|
+
ruby-progressbar (~> 1.7)
|
41
|
+
unicode-display_width (>= 1.4.0, < 1.7)
|
42
|
+
rubocop-rspec (1.37.1)
|
43
|
+
rubocop (>= 0.68.1)
|
44
|
+
ruby-progressbar (1.10.1)
|
45
|
+
simplecov (0.17.1)
|
46
|
+
docile (~> 1.1)
|
47
|
+
json (>= 1.8, < 3)
|
48
|
+
simplecov-html (~> 0.10.0)
|
49
|
+
simplecov-html (0.10.2)
|
50
|
+
unicode-display_width (1.6.0)
|
51
|
+
|
52
|
+
PLATFORMS
|
53
|
+
ruby
|
54
|
+
|
55
|
+
DEPENDENCIES
|
56
|
+
bundler (~> 2.0)
|
57
|
+
byebug (~> 11.0)
|
58
|
+
rake (~> 10.0)
|
59
|
+
rspec (~> 3.0)
|
60
|
+
rubocop (~> 0.78)
|
61
|
+
rubocop-rspec (~> 1.37)
|
62
|
+
simplecov (~> 0.10)
|
63
|
+
terracop!
|
64
|
+
|
65
|
+
BUNDLED WITH
|
66
|
+
2.0.2
|
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
Copyright © 2019 Francesco Boffa
|
3
|
+
|
4
|
+
* * *
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
7
|
+
this software and associated documentation files (the “Software”), to deal in
|
8
|
+
the Software without restriction, including without limitation the rights to
|
9
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
10
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
11
|
+
subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
14
|
+
copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
18
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
19
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
20
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
21
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Terracop
|
2
|
+
|
3
|
+
Terracop is a HashiCorp [Terraform](https://www.terraform.io/) state / plan
|
4
|
+
parser and analyzer. Put it in a CI pipeline to analyze your Terraform plans
|
5
|
+
or run it on already applied states and see what could be improved.
|
6
|
+
|
7
|
+
The checks run by Terracop go anywhere from resource names guidelines to
|
8
|
+
identifying security holes in your configuration.
|
9
|
+
|
10
|
+
_Terracop is massively inspired by [Rubocop](https://github.com/rubocop-hq/rubocop)._
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
**Terracop** installation is pretty standard:
|
15
|
+
|
16
|
+
$ gem install terracop
|
17
|
+
|
18
|
+
If you'd rather install RuboCop using bundler, don't require it in your Gemfile:
|
19
|
+
|
20
|
+
gem 'terracop', require: false
|
21
|
+
|
22
|
+
## Compatibility
|
23
|
+
|
24
|
+
Terracop can work with state and plan files generated by Terraform 0.12.
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
You can run terracop from the same directory where you would run terraform and
|
29
|
+
it will automatically pull the state file and analyze it.
|
30
|
+
|
31
|
+
If you want to analyze a state file somewhere on your machine you can pass it
|
32
|
+
like this:
|
33
|
+
|
34
|
+
$ terracop --state path/to/state/file
|
35
|
+
|
36
|
+
Terracop can (will) parse also terraform plan files, in order to report
|
37
|
+
potential issues before you apply the plan and make the problem permanent. Eg:
|
38
|
+
|
39
|
+
$ terraform plan -out tfplan
|
40
|
+
$ terracop --plan tfplan
|
41
|
+
$ terraform apply tfplan
|
42
|
+
|
43
|
+
## Contributing
|
44
|
+
|
45
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/aomega08/terracop.
|
46
|
+
|
47
|
+
## Copyright
|
48
|
+
|
49
|
+
Copyright (c) 2019-2020 Francesco Boffa. See [LICENSE.md](LICENSE.md) for further details.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'terracop'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/bin/terracop
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
5
|
+
|
6
|
+
require 'bundler/setup'
|
7
|
+
require 'optparse'
|
8
|
+
require 'terracop'
|
9
|
+
|
10
|
+
formatter = :default
|
11
|
+
state = nil
|
12
|
+
plan = nil
|
13
|
+
type = :state
|
14
|
+
|
15
|
+
OptionParser.new do |opts|
|
16
|
+
opts.banner += ' <state_file.json>'
|
17
|
+
|
18
|
+
opts.on(
|
19
|
+
'-f', '--format FORMAT', /(default|html|json)/,
|
20
|
+
'Use a specific formatter for the output'
|
21
|
+
) do |arg|
|
22
|
+
formatter = arg[0].to_sym
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on('-s', '--state FILE', 'Specify the state file to analyze') do |arg|
|
26
|
+
state = arg
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on('-p', '--plan FILE', 'Specify the terraform plan to analyze') do |arg|
|
30
|
+
plan = arg
|
31
|
+
type = :plan
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on_tail('-h', '--help', 'Prints this help') do
|
35
|
+
puts opts
|
36
|
+
exit
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on_tail('-v', '--version', 'Show version') do
|
40
|
+
puts Terracop::VERSION
|
41
|
+
exit
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.parse!
|
45
|
+
end
|
46
|
+
|
47
|
+
if state && plan
|
48
|
+
puts 'You can analyze either a state or a plan, but not both.'
|
49
|
+
exit
|
50
|
+
end
|
51
|
+
|
52
|
+
Terracop::Runner.new(type, state || plan, formatter).run
|
data/default_config.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
---
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terracop/cop/base'
|
4
|
+
|
5
|
+
module Terracop
|
6
|
+
module Cop
|
7
|
+
module Aws
|
8
|
+
# This cop checks for AWS Security Group rules with no description.
|
9
|
+
# Reading terraform code can immediately tell why a rule is in place, but
|
10
|
+
# the AWS console is a bit more cryptic and a description can help.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # bad
|
14
|
+
# resource "aws_security_group_rule" "rule" {
|
15
|
+
# source_security_group_id = "sg-123456"
|
16
|
+
# }
|
17
|
+
#
|
18
|
+
# # good
|
19
|
+
# resource "aws_security_group_rule" "rule" {
|
20
|
+
# source_security_group_id = "sg-123456"
|
21
|
+
# description = "Traffic from the load balancer"
|
22
|
+
# }
|
23
|
+
class DescribeSecurityGroupRules < Base
|
24
|
+
register
|
25
|
+
applies_to :aws_security_group_rule
|
26
|
+
|
27
|
+
def check
|
28
|
+
return unless attributes['description'] == ''
|
29
|
+
|
30
|
+
offense('Add a description to security group rules.')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terracop/cop/base'
|
4
|
+
|
5
|
+
module Terracop
|
6
|
+
module Cop
|
7
|
+
module Aws
|
8
|
+
# This cop makes sure that AWS resources that can be tagged, are indeed
|
9
|
+
# tagged.
|
10
|
+
# By default it just checks that resources have at least one tag, any tag.
|
11
|
+
# It is configurable to enforce the existance of some specific tags.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# # bad
|
15
|
+
# resource "aws_alb" "lb" {
|
16
|
+
# name_prefix = "app"
|
17
|
+
# }
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# resource "aws_alb" "lb" {
|
21
|
+
# name_prefix = "app"
|
22
|
+
# tags = {
|
23
|
+
# environment = "staging"
|
24
|
+
# }
|
25
|
+
# }
|
26
|
+
class EnsureTags < Base
|
27
|
+
register
|
28
|
+
|
29
|
+
def check
|
30
|
+
return unless attributes['tags']
|
31
|
+
|
32
|
+
if self.class.config['Required']
|
33
|
+
check_required(self.class.config['Required'])
|
34
|
+
elsif attributes['tags'].empty?
|
35
|
+
offense 'Tag resources properly.'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def check_required(required_tags)
|
42
|
+
required_tags.each do |key|
|
43
|
+
unless attributes['tags'][key]
|
44
|
+
offense "Required tag \"#{key}\" is missing on this resource."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terracop/cop/base'
|
4
|
+
|
5
|
+
module Terracop
|
6
|
+
module Cop
|
7
|
+
module Aws
|
8
|
+
# This cop warns against the use of inline role policies.
|
9
|
+
# Inline policies tend to be copy/pasted, sometimes with minor changes
|
10
|
+
# and are not shown in the "Policies" tab of AWS IAM.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # bad
|
14
|
+
# resource "aws_role" "role" { }
|
15
|
+
#
|
16
|
+
# resource "aws_iam_role_policy" "policy" {
|
17
|
+
# role = aws_role.role.id
|
18
|
+
# name = "policy"
|
19
|
+
#
|
20
|
+
# policy = <some policy>
|
21
|
+
# }
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# resource "aws_role" "role" { }
|
25
|
+
#
|
26
|
+
# resource "aws_iam_policy" "policy" {
|
27
|
+
# name = "test-policy"
|
28
|
+
#
|
29
|
+
# policy = <some policy>
|
30
|
+
# }
|
31
|
+
#
|
32
|
+
# resource "aws_iam_role_policy_attachment" "attach" {
|
33
|
+
# role = aws_iam_role.role.name
|
34
|
+
# policy_arn = aws_iam_policy.policy.arn
|
35
|
+
# }
|
36
|
+
class IamRolePolicy < Base
|
37
|
+
register
|
38
|
+
applies_to :aws_iam_role_policy
|
39
|
+
|
40
|
+
def check
|
41
|
+
offense('Use aws_iam_role_policy_attachment instead of attaching ' \
|
42
|
+
'inline policies with aws_iam_role_policy.')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terracop/cop/aws/security_group_rule_cop'
|
4
|
+
|
5
|
+
module Terracop
|
6
|
+
module Cop
|
7
|
+
module Aws
|
8
|
+
# This cop warns against an egress rule to 0.0.0.0/0.
|
9
|
+
# While very common, and not necessarily an offense, you may want to lock
|
10
|
+
# the outbound traffic to some specific addresses (or even other security
|
11
|
+
# groups), especially in highly regulated environments.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# # bad
|
15
|
+
# resource "aws_security_group_rule" "egress" {
|
16
|
+
# type = "egress"
|
17
|
+
# cidr_blocks = ["0.0.0.0/0"]
|
18
|
+
# }
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# resource "aws_security_group_rule" "egress" {
|
22
|
+
# type = "egress"
|
23
|
+
# cidr_blocks = ["10.4.0.0/16"]
|
24
|
+
# }
|
25
|
+
#
|
26
|
+
# # better
|
27
|
+
# resource "aws_security_group_rule" "egress" {
|
28
|
+
# type = "egress"
|
29
|
+
# security_group_id = aws_security_group.destination.id
|
30
|
+
# }
|
31
|
+
class OpenEgress < SecurityGroupRuleCop
|
32
|
+
register
|
33
|
+
|
34
|
+
def check
|
35
|
+
return unless egress? && any_ip?
|
36
|
+
|
37
|
+
offense('Avoid allowing egress traffic to 0.0.0.0/0.', :security)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terracop/cop/aws/security_group_rule_cop'
|
4
|
+
|
5
|
+
module Terracop
|
6
|
+
module Cop
|
7
|
+
module Aws
|
8
|
+
# This cop warns against an ingress rule from 0.0.0.0/0.
|
9
|
+
# With a couple of specific exceptions, you don't want to allow traffic
|
10
|
+
# from anywhere in the world to most of your infrastructure.
|
11
|
+
# A common exception is the external Load Balancer receiving traffic
|
12
|
+
# for a website. Use the `Except` configuration to whitelist that specific
|
13
|
+
# rule.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# # bad
|
17
|
+
# resource "aws_security_group_rule" "ingress" {
|
18
|
+
# type = "ingress"
|
19
|
+
# cidr_blocks = ["0.0.0.0/0"]
|
20
|
+
# }
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# resource "aws_security_group_rule" "ingress" {
|
24
|
+
# type = "ingress"
|
25
|
+
# cidr_blocks = ["10.4.0.0/16"]
|
26
|
+
# }
|
27
|
+
#
|
28
|
+
# # better
|
29
|
+
# resource "aws_security_group_rule" "ingress" {
|
30
|
+
# type = "ingress"
|
31
|
+
# security_group_id = aws_security_group.source.id
|
32
|
+
# }
|
33
|
+
class OpenIngress < SecurityGroupRuleCop
|
34
|
+
register
|
35
|
+
|
36
|
+
def check
|
37
|
+
return unless ingress? && any_ip?
|
38
|
+
|
39
|
+
offense('Avoid allowing ingress traffic from 0.0.0.0/0.', :security)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terracop/cop/aws/security_group_rule_cop'
|
4
|
+
|
5
|
+
module Terracop
|
6
|
+
module Cop
|
7
|
+
module Aws
|
8
|
+
# This cop warns against an ingress rule from 0.0.0.0/0 on port 22 (SSH).
|
9
|
+
# That is a Very Bad Idea™.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# # bad
|
13
|
+
# resource "aws_security_group_rule" "ingress" {
|
14
|
+
# type = "ingress"
|
15
|
+
# cidr_blocks = ["0.0.0.0/0"]
|
16
|
+
# # Notice this port range includes 22
|
17
|
+
# from_port = 10
|
18
|
+
# to_port = 30
|
19
|
+
# }
|
20
|
+
#
|
21
|
+
# # good
|
22
|
+
# resource "aws_security_group_rule" "ingress" {
|
23
|
+
# type = "ingress"
|
24
|
+
# cidr_blocks = ["1.2.3.4/32"]
|
25
|
+
# from_port = 22
|
26
|
+
# to_port = 22
|
27
|
+
# }
|
28
|
+
class OpenSsh < SecurityGroupRuleCop
|
29
|
+
register
|
30
|
+
|
31
|
+
def check
|
32
|
+
return unless ingress? && any_ip? && tcp? && port?(22)
|
33
|
+
|
34
|
+
offense('Do not leave port 22 (SSH) open to the world.', :security)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terracop/cop/base'
|
4
|
+
|
5
|
+
module Terracop
|
6
|
+
module Cop
|
7
|
+
module Aws
|
8
|
+
# Base class that provides helper methods for other Cops checking
|
9
|
+
# Security Group rules.
|
10
|
+
class SecurityGroupRuleCop < Base
|
11
|
+
applies_to :aws_security_group_rule
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def ingress?
|
16
|
+
attributes['type'] == 'ingress'
|
17
|
+
end
|
18
|
+
|
19
|
+
def egress?
|
20
|
+
attributes['type'] == 'egress'
|
21
|
+
end
|
22
|
+
|
23
|
+
def any_ip?
|
24
|
+
attributes['cidr_blocks'].include?('0.0.0.0/0')
|
25
|
+
end
|
26
|
+
|
27
|
+
def tcp?
|
28
|
+
attributes['protocol'] == 'tcp'
|
29
|
+
end
|
30
|
+
|
31
|
+
def udp?
|
32
|
+
attributes['protocol'] == 'udp'
|
33
|
+
end
|
34
|
+
|
35
|
+
def port?(port)
|
36
|
+
(attributes['from_port']..attributes['to_port']).include?(port)
|
37
|
+
end
|
38
|
+
|
39
|
+
def any_port?
|
40
|
+
(attributes['from_port']..attributes['to_port']).count == 65_536
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terracop/cop/aws/security_group_rule_cop'
|
4
|
+
|
5
|
+
module Terracop
|
6
|
+
module Cop
|
7
|
+
module Aws
|
8
|
+
# This cop warns against egress security group rules that allow any port.
|
9
|
+
# This would, for example, allow an attacker to use your machine to send
|
10
|
+
# spam emails, since you left port 25 outbound open.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # bad
|
14
|
+
# resource "aws_security_group_rule" "egress" {
|
15
|
+
# type = "egress"
|
16
|
+
# from_port = 0
|
17
|
+
# to_port = 65535
|
18
|
+
# }
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# resource "aws_security_group_rule" "egress" {
|
22
|
+
# type = "egress"
|
23
|
+
# from_port = 443
|
24
|
+
# to_port = 443
|
25
|
+
# }
|
26
|
+
class UnrestrictedEgressPorts < SecurityGroupRuleCop
|
27
|
+
register
|
28
|
+
|
29
|
+
def check
|
30
|
+
return unless egress? && (tcp? || udp?) && any_port?
|
31
|
+
|
32
|
+
offense('Limit egress traffic to small port ranges.', :security)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|