aws_security_viz 0.1.4.pre.alpha.pre.81 → 0.1.4.pre.alpha.pre.82
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 +5 -13
- data/Rakefile +2 -0
- data/aws_security_viz.gemspec +5 -4
- data/exe/aws_security_viz +8 -1
- data/lib/aws_security_viz.rb +2 -0
- data/lib/debug_graph.rb +4 -0
- data/lib/graph.rb +21 -23
- data/lib/graph_filter.rb +38 -0
- data/spec/graph_filter_spec.rb +85 -0
- data/spec/integration/aws_expected.json +1 -0
- data/spec/integration/expected.json +1 -1
- data/spec/integration/visualize_aws_spec.rb +25 -2
- data/spec/visualize_aws_spec.rb +110 -93
- metadata +49 -30
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
OTgwZTlmZmE4NzhkZDg5OWUwYzYzYjAwOTA3MWM5MmEzY2I2YzU3NQ==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f1fed3837156ff2477390d1b411bbbd97a1d96fc
|
4
|
+
data.tar.gz: e737d124886db6d03301ae21d489aef70db7bec6
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
Yzk2ZTA5ODk2NTA3ZDQ2MmE3NzBmMTgyMDdmZWFlMmIyMzEyYTEzMDdmNDIy
|
11
|
-
MTMyNjdmZjU2YzVjNzJkNDYyYjUzMTY2ZWM0YzFjOTM0NmY0M2E=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
N2ExYzhiOWM2ZDM0MWFiNjMyNjljOGZjYjM2OWRhNjkxMDYyZmRiN2NmMTA5
|
14
|
-
ZTY5Y2JmMjZhM2YyOTI5ZGU4ZGQzZmQxNmZkZTQyNjQ0ZTNmMzcxMmJhYjlm
|
15
|
-
YjUyNmQxZjUyOTA2ZGJiODljMjAzMmY4NjAxZTk2YWRiMzc1ZmM=
|
6
|
+
metadata.gz: 6fd4637e47289e1c56f93d78268b1f379113db10f61fd65a5f1ad2e9909594662fbab47d7920bce17d0902a55d9318c34f6800366c7d2fdcaf54d9eac3450162
|
7
|
+
data.tar.gz: 09898633b2c77440f2b85a64f424db98658275f4fcd23300ecce2bbd357f06eef16b8aee177641b57c6f7f7009dc54cb3c73f5d6002cdb8e1637db36cd3e1865
|
data/Rakefile
CHANGED
data/aws_security_viz.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.name = 'aws_security_viz'
|
7
7
|
s.version = AwsSecurityViz::VERSION
|
8
8
|
s.version = "#{s.version}-alpha-#{ENV['TRAVIS_BUILD_NUMBER']}" if ENV['TRAVIS']
|
9
|
-
s.date = '
|
9
|
+
s.date = Time.now.strftime('%Y-%m-%d')
|
10
10
|
s.summary = "Visualize your aws security groups"
|
11
11
|
s.description = "Provides a quick mechanism to visualize your EC2 security groups in multiple formats"
|
12
12
|
s.authors = ["Anay Nayak"]
|
@@ -22,15 +22,16 @@ Gem::Specification.new do |s|
|
|
22
22
|
s.require_paths = ["lib"]
|
23
23
|
|
24
24
|
s.add_development_dependency "bundler", "~> 1.13"
|
25
|
-
s.add_development_dependency "rake", "~>
|
25
|
+
s.add_development_dependency "rake", "~> 12.0.0"
|
26
26
|
s.add_development_dependency "rspec", "~> 3.5.0"
|
27
27
|
|
28
28
|
s.add_runtime_dependency 'ruby-graphviz', "~> 1.2.2"
|
29
|
-
s.add_runtime_dependency 'fog-aws', "~>
|
29
|
+
s.add_runtime_dependency 'fog-aws', "~> 1.2.0"
|
30
30
|
s.add_runtime_dependency 'unf', "~> 0.1.4"
|
31
|
-
s.add_runtime_dependency 'json', "~> 2.0.
|
31
|
+
s.add_runtime_dependency 'json', "~> 2.0.3"
|
32
32
|
s.add_runtime_dependency 'trollop', "~> 2.1.2"
|
33
33
|
s.add_runtime_dependency 'organic_hash', "~> 1.0.2"
|
34
|
+
s.add_runtime_dependency 'rgl', "~> 0.5.2"
|
34
35
|
|
35
36
|
s.required_ruby_version = '>= 2.0.0'
|
36
37
|
end
|
data/exe/aws_security_viz
CHANGED
@@ -11,6 +11,8 @@ opts = Trollop::options do
|
|
11
11
|
opt :filename, 'Output file name', :type => :string, :default => 'aws-security-viz.png'
|
12
12
|
opt :config, 'Config file (opts.yml)', :type => :string, :default => 'opts.yml'
|
13
13
|
opt :color, 'Colored node edges', :default => false
|
14
|
+
opt :source_filter, 'Source filter', :default => nil, :type => :string
|
15
|
+
opt :target_filter, 'Target filter', :default => nil, :type => :string
|
14
16
|
end
|
15
17
|
|
16
18
|
cmd = ARGV.shift
|
@@ -21,5 +23,10 @@ if cmd=="setup"
|
|
21
23
|
end
|
22
24
|
|
23
25
|
config = AwsConfig.load(opts[:config]).merge(obfuscate: ENV['OBFUSCATE'], debug: ENV['DEBUG'])
|
24
|
-
|
26
|
+
begin
|
27
|
+
VisualizeAws.new(config, opts).unleash(opts[:filename])
|
28
|
+
rescue Exception => e
|
29
|
+
puts "[ERROR] #{e.message}"
|
30
|
+
exit 1
|
31
|
+
end
|
25
32
|
|
data/lib/aws_security_viz.rb
CHANGED
@@ -4,6 +4,7 @@ require_relative 'provider/ec2'
|
|
4
4
|
require_relative 'renderer/graphviz'
|
5
5
|
require_relative 'renderer/json'
|
6
6
|
require_relative 'graph'
|
7
|
+
require_relative 'graph_filter'
|
7
8
|
require_relative 'exclusions'
|
8
9
|
require_relative 'debug_graph'
|
9
10
|
require_relative 'color_picker'
|
@@ -19,6 +20,7 @@ class VisualizeAws
|
|
19
20
|
|
20
21
|
def unleash(output_file)
|
21
22
|
g = build
|
23
|
+
g.filter(@options[:source_filter], @options[:target_filter])
|
22
24
|
if output_file.end_with?('json')
|
23
25
|
g.output(Renderer::Json.new(output_file, @config))
|
24
26
|
FileUtils.copy(File.expand_path('../export/html/view.html', __FILE__),
|
data/lib/debug_graph.rb
CHANGED
data/lib/graph.rb
CHANGED
@@ -1,45 +1,43 @@
|
|
1
|
+
require 'rgl/adjacency'
|
2
|
+
|
1
3
|
class Graph
|
2
|
-
attr_reader :
|
4
|
+
attr_reader :underlying
|
3
5
|
|
4
|
-
def initialize(config)
|
6
|
+
def initialize(config, underlying=RGL::DirectedAdjacencyGraph.new)
|
5
7
|
@config = config
|
6
|
-
@
|
7
|
-
@
|
8
|
+
@underlying = underlying
|
9
|
+
@edge_properties = {}
|
8
10
|
end
|
9
11
|
|
10
12
|
def add_node(name)
|
11
13
|
log("node: #{name}")
|
12
|
-
|
13
|
-
[:node, name]
|
14
|
-
}
|
14
|
+
@underlying.add_vertex(name)
|
15
15
|
end
|
16
16
|
|
17
17
|
def add_edge(from, to, opts)
|
18
18
|
log("edge: #{from} -> #{to}")
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
@underlying.add_edge(from, to)
|
20
|
+
@edge_properties[[from, to]] = opts
|
21
|
+
end
|
22
|
+
|
23
|
+
def filter(source, destination)
|
24
|
+
@underlying = GraphFilter.new(underlying).filter(source, destination)
|
24
25
|
end
|
25
26
|
|
26
27
|
def output(renderer)
|
27
|
-
@
|
28
|
-
|
29
|
-
renderer.add_edge(
|
28
|
+
@underlying.each_vertex { |v| renderer.add_node(v) }
|
29
|
+
@underlying.each_edge { |u, v|
|
30
|
+
renderer.add_edge(u, v, opts(u, v))
|
30
31
|
}
|
31
32
|
renderer.output
|
32
33
|
end
|
33
34
|
|
34
|
-
def uniquely_add(target, type, *opts, &block)
|
35
|
-
return if opts.compact.empty?
|
36
|
-
return if @nodes.include?([type, opts])
|
37
|
-
@nodes.add([type, opts])
|
38
|
-
target << yield
|
39
|
-
end
|
40
|
-
|
41
|
-
|
42
35
|
def log(msg)
|
43
36
|
puts msg if @config.debug?
|
44
37
|
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def opts(u, v)
|
41
|
+
@edge_properties[[u, v]]
|
42
|
+
end
|
45
43
|
end
|
data/lib/graph_filter.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rgl/traversal'
|
2
|
+
require 'rgl/implicit'
|
3
|
+
|
4
|
+
class GraphFilter
|
5
|
+
def initialize(graph)
|
6
|
+
@graph = graph
|
7
|
+
end
|
8
|
+
|
9
|
+
def filter(source, destination)
|
10
|
+
return @graph if source.nil? && destination.nil?
|
11
|
+
if !source.nil? && destination.nil?
|
12
|
+
return reduce(@graph, source)
|
13
|
+
end
|
14
|
+
if !destination.nil? && source.nil?
|
15
|
+
return reduce(@graph.reverse, destination).reverse
|
16
|
+
end
|
17
|
+
search(@graph, source, destination)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def reduce(graph, source)
|
22
|
+
tree = graph.bfs_search_tree_from(source)
|
23
|
+
graph.vertices_filtered_by { |v| tree.has_vertex? v }
|
24
|
+
end
|
25
|
+
|
26
|
+
def search(graph, source, destination)
|
27
|
+
visitor = RGL::DFSVisitor.new(graph)
|
28
|
+
path = []
|
29
|
+
paths = []
|
30
|
+
visitor.set_examine_vertex_event_handler { |x| path << x }
|
31
|
+
visitor.set_finish_vertex_event_handler { |x| path.pop }
|
32
|
+
visitor.set_examine_edge_event_handler { |x, y| paths << path.dclone + [y] if y == destination }
|
33
|
+
graph.depth_first_visit(source, visitor) { |x|}
|
34
|
+
to_remove = graph.vertices - paths.flatten
|
35
|
+
graph.remove_vertices(*to_remove)
|
36
|
+
graph
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe GraphFilter do
|
5
|
+
it 'should include nodes reachable from source' do
|
6
|
+
graph = GraphFilter.new(RGL::DirectedAdjacencyGraph[1, 2, 2, 3, 2, 4, 4, 5])
|
7
|
+
|
8
|
+
expect(graph.filter(2, nil).to_s).to eq('(2-3)(2-4)(4-5)')
|
9
|
+
end
|
10
|
+
it 'should remove nodes not reachable from source' do
|
11
|
+
graph = GraphFilter.new(RGL::DirectedAdjacencyGraph[1, 2, 2, 3, 2, 4, 4, 5, 3, 5])
|
12
|
+
|
13
|
+
expect(graph.filter(3, nil).to_s).to eq('(3-5)')
|
14
|
+
end
|
15
|
+
it 'should remove nodes not reachable to destination' do
|
16
|
+
graph = GraphFilter.new(RGL::DirectedAdjacencyGraph[
|
17
|
+
1, 2,
|
18
|
+
1, 3,
|
19
|
+
1, 4,
|
20
|
+
2, 3,
|
21
|
+
2, 4,
|
22
|
+
3, 4,
|
23
|
+
])
|
24
|
+
|
25
|
+
expect(graph.filter(nil, 3).to_s).to eq('(1-2)(1-3)(2-3)')
|
26
|
+
end
|
27
|
+
it 'should remove nodes not reachable to destination from source' do
|
28
|
+
graph = GraphFilter.new(RGL::DirectedAdjacencyGraph[
|
29
|
+
1, 2,
|
30
|
+
1, 3,
|
31
|
+
1, 4,
|
32
|
+
2, 3,
|
33
|
+
2, 4,
|
34
|
+
])
|
35
|
+
|
36
|
+
expect(graph.filter(2, 4).to_s).to eq('(2-4)')
|
37
|
+
end
|
38
|
+
it 'should retain edges which pass through intermediate nodes' do
|
39
|
+
graph = GraphFilter.new(RGL::DirectedAdjacencyGraph[
|
40
|
+
1, 2,
|
41
|
+
1, 3,
|
42
|
+
1, 4,
|
43
|
+
2, 3,
|
44
|
+
2, 4,
|
45
|
+
3, 4,
|
46
|
+
])
|
47
|
+
expect(graph.filter(2, 4).to_s).to eq('(2-3)(2-4)(3-4)')
|
48
|
+
end
|
49
|
+
it 'should remove nodes not reachable to destination from source #1' do
|
50
|
+
graph = GraphFilter.new(RGL::DirectedAdjacencyGraph[
|
51
|
+
1, 2,
|
52
|
+
1, 3,
|
53
|
+
1, 4,
|
54
|
+
2, 3,
|
55
|
+
2, 4,
|
56
|
+
3, 5,
|
57
|
+
5, 4
|
58
|
+
])
|
59
|
+
expect(graph.filter(1, 5).to_s).to eq('(1-2)(1-3)(2-3)(3-5)')
|
60
|
+
end
|
61
|
+
it 'should remove nodes not reachable to destination from source #2' do
|
62
|
+
graph = GraphFilter.new(RGL::DirectedAdjacencyGraph[
|
63
|
+
1, 2,
|
64
|
+
1, 3,
|
65
|
+
1, 4,
|
66
|
+
2, 3,
|
67
|
+
2, 4,
|
68
|
+
3, 5,
|
69
|
+
5, 4
|
70
|
+
])
|
71
|
+
expect(graph.filter(1, 4).to_s).to eq('(1-2)(1-3)(1-4)(2-3)(2-4)(3-5)(5-4)')
|
72
|
+
end
|
73
|
+
it 'should remove nodes not reachable to destination from source #3' do
|
74
|
+
graph = GraphFilter.new(RGL::DirectedAdjacencyGraph[
|
75
|
+
1, 2,
|
76
|
+
1, 3,
|
77
|
+
1, 4,
|
78
|
+
2, 3,
|
79
|
+
2, 4,
|
80
|
+
3, 5,
|
81
|
+
5, 4
|
82
|
+
])
|
83
|
+
expect(graph.filter(2, 4).to_s).to eq('(2-3)(2-4)(3-5)(5-4)')
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
{"nodes":[{"id":"asv-app","label":"asv-app"},{"id":"asv-bastion","label":"asv-bastion"},{"id":"*","label":"*"},{"id":"asv-db","label":"asv-db"},{"id":"asv-solr","label":"asv-solr"},{"id":"default","label":"default"}],"edges":[{"id":"asv-bastion-asv-app","source":"asv-bastion","target":"asv-app","label":"22/tcp"},{"id":"*-asv-app","source":"*","target":"asv-app","label":"80/tcp"},{"id":"*-asv-bastion","source":"*","target":"asv-bastion","label":"22/tcp"},{"id":"asv-app-asv-db","source":"asv-app","target":"asv-db","label":"5432/tcp"},{"id":"asv-bastion-asv-db","source":"asv-bastion","target":"asv-db","label":"22/tcp"},{"id":"asv-app-asv-solr","source":"asv-app","target":"asv-solr","label":"8983/tcp"},{"id":"asv-bastion-asv-solr","source":"asv-bastion","target":"asv-solr","label":"22/tcp"},{"id":"default-default","source":"default","target":"default","label":"*"},{"id":"*-default","source":"*","target":"default","label":"22/tcp"},{"id":"default-*","source":"default","target":"*","label":"*"}]}
|
@@ -1 +1 @@
|
|
1
|
-
{"nodes":[{"id":"app","label":"app"},{"id":"8.8.8.8/32","label":"8.8.8.8/32"},{"id":"amazon-elb-sg","label":"amazon-elb-sg"},{"id":"*","label":"*"},{"id":"db","label":"db"}],"edges":[{"id":"8.8.8.8/32-app","source":"8.8.8.8/32","target":"app","label":"80/tcp"},{"id":"amazon-elb-sg-app","source":"amazon-elb-sg","target":"app","label":"80/tcp"},{"id":"*-app","source":"*","target":"app","label":"22/tcp"}
|
1
|
+
{"nodes":[{"id":"app","label":"app"},{"id":"8.8.8.8/32","label":"8.8.8.8/32"},{"id":"amazon-elb-sg","label":"amazon-elb-sg"},{"id":"*","label":"*"},{"id":"db","label":"db"}],"edges":[{"id":"app-db","source":"app","target":"db","label":"5984/tcp"},{"id":"8.8.8.8/32-app","source":"8.8.8.8/32","target":"app","label":"80/tcp"},{"id":"amazon-elb-sg-app","source":"amazon-elb-sg","target":"app","label":"80/tcp"},{"id":"*-app","source":"*","target":"app","label":"22/tcp"}]}
|
@@ -9,6 +9,8 @@ describe VisualizeAws do
|
|
9
9
|
}
|
10
10
|
let(:source_file) { File.join(File.dirname(__FILE__), 'dummy.json') }
|
11
11
|
let(:config) { AwsConfig.new({groups: {'0.0.0.0/0' => '*'}}) }
|
12
|
+
let(:expected_content) { File.read(expected_file) }
|
13
|
+
let(:actual_content) { temp_file.read }
|
12
14
|
|
13
15
|
context 'json to dot file' do
|
14
16
|
let(:expected_file) { File.join(File.dirname(__FILE__), 'dummy.dot') }
|
@@ -16,7 +18,7 @@ describe VisualizeAws do
|
|
16
18
|
|
17
19
|
it 'should parse json input', :integration => true do
|
18
20
|
VisualizeAws.new(config, opts).unleash(temp_file.path)
|
19
|
-
expect(
|
21
|
+
expect(expected_content).to eq(actual_content)
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
@@ -27,7 +29,28 @@ describe VisualizeAws do
|
|
27
29
|
it 'should parse json input', :integration => true do
|
28
30
|
expect(FileUtils).to receive(:copy)
|
29
31
|
VisualizeAws.new(config, opts).unleash(temp_file.path)
|
30
|
-
expect(
|
32
|
+
expect(JSON.parse(expected_content)).to eq(JSON.parse(actual_content))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
if ENV['TEST_ACCESS_KEY']
|
37
|
+
context 'ec2 to json file' do
|
38
|
+
let(:expected_file) { File.join(File.dirname(__FILE__), 'aws_expected.json') }
|
39
|
+
let(:temp_file) { Tempfile.new(%w(aws .json)) }
|
40
|
+
let(:opts) {
|
41
|
+
{
|
42
|
+
:filename => temp_file,
|
43
|
+
:secret_key => ENV['TEST_SECRET_KEY'],
|
44
|
+
:access_key => ENV['TEST_ACCESS_KEY']
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
it 'should read from ec2 account', :integration => true do
|
49
|
+
expect(FileUtils).to receive(:copy)
|
50
|
+
VisualizeAws.new(config, opts).unleash(temp_file.path)
|
51
|
+
expect(JSON.parse(expected_content)['edges']).to match_array(JSON.parse(actual_content)['edges'])
|
52
|
+
expect(JSON.parse(expected_content)['nodes']).to match_array(JSON.parse(actual_content)['nodes'])
|
53
|
+
end
|
31
54
|
end
|
32
55
|
end
|
33
56
|
end
|
data/spec/visualize_aws_spec.rb
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
class DummyRenderer
|
4
|
+
attr_reader :output
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@output = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_node(name)
|
11
|
+
@output << [:node, name]
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_edge(from, to, opts)
|
15
|
+
@output << [:edge, from, to, opts]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
3
19
|
describe VisualizeAws do
|
4
20
|
before do
|
5
21
|
@ec2 = double(Fog::Compute)
|
@@ -7,16 +23,17 @@ describe VisualizeAws do
|
|
7
23
|
end
|
8
24
|
|
9
25
|
let(:visualize_aws) { VisualizeAws.new(AwsConfig.new) }
|
26
|
+
let(:renderer) { DummyRenderer.new }
|
10
27
|
|
11
28
|
it 'should add nodes, edges for each security group' do
|
12
29
|
expect(@ec2).to receive(:security_groups).and_return([group('Remote ssh', group_ingress('22', 'My machine')), group('My machine')])
|
13
30
|
graph = visualize_aws.build
|
14
31
|
|
15
|
-
expect(graph.
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
32
|
+
expect(graph.output(renderer)).to contain_exactly(
|
33
|
+
[:node, 'Remote ssh'],
|
34
|
+
[:node, 'My machine'],
|
35
|
+
[:edge, 'My machine', 'Remote ssh', {:color => :blue, :label => '22/tcp'}],
|
36
|
+
)
|
20
37
|
end
|
21
38
|
|
22
39
|
context 'groups' do
|
@@ -24,33 +41,33 @@ describe VisualizeAws do
|
|
24
41
|
expect(@ec2).to receive(:security_groups).and_return([group('Web', group_ingress('80', 'ELB'))])
|
25
42
|
graph = visualize_aws.build
|
26
43
|
|
27
|
-
expect(graph.
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
44
|
+
expect(graph.output(renderer)).to contain_exactly(
|
45
|
+
[:node, 'Web'],
|
46
|
+
[:node, 'ELB'],
|
47
|
+
[:edge, 'ELB', 'Web', {:color => :blue, :label => '80/tcp'}]
|
48
|
+
)
|
32
49
|
end
|
33
50
|
|
34
51
|
it 'should add an edge for each security ingress' do
|
35
52
|
expect(@ec2).to receive(:security_groups).and_return(
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
53
|
+
[
|
54
|
+
group('App', group_ingress('80', 'Web'), group_ingress('8983', 'Internal')),
|
55
|
+
group('Web', group_ingress('80', 'External')),
|
56
|
+
group('Db', group_ingress('7474', 'App'))
|
57
|
+
])
|
41
58
|
graph = visualize_aws.build
|
42
59
|
|
43
|
-
expect(graph.
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
60
|
+
expect(graph.output(renderer)).to contain_exactly(
|
61
|
+
[:node, 'App'],
|
62
|
+
[:node, 'Web'],
|
63
|
+
[:edge, 'Web', 'App', {:color => :blue, :label => '80/tcp'}],
|
64
|
+
[:node, 'Internal'],
|
65
|
+
[:edge, 'Internal', 'App', {:color => :blue, :label => '8983/tcp'}],
|
66
|
+
[:node, 'External'],
|
67
|
+
[:edge, 'External', 'Web', {:color => :blue, :label => '80/tcp'}],
|
68
|
+
[:node, 'Db'],
|
69
|
+
[:edge, 'App', 'Db', {:color => :blue, :label => '7474/tcp'}]
|
70
|
+
)
|
54
71
|
|
55
72
|
end
|
56
73
|
end
|
@@ -59,123 +76,123 @@ describe VisualizeAws do
|
|
59
76
|
|
60
77
|
it 'should add an edge for each cidr ingress' do
|
61
78
|
expect(@ec2).to receive(:security_groups).and_return(
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
79
|
+
[
|
80
|
+
group('Web', group_ingress('80', 'External')),
|
81
|
+
group('Db', group_ingress('7474', 'App'), cidr_ingress('22', '127.0.0.1/32'))
|
82
|
+
])
|
66
83
|
graph = visualize_aws.build
|
67
84
|
|
68
|
-
expect(graph.
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
85
|
+
expect(graph.output(renderer)).to contain_exactly(
|
86
|
+
[:node, 'Web'],
|
87
|
+
[:node, 'External'],
|
88
|
+
[:edge, 'External', 'Web', {:color => :blue, :label => '80/tcp'}],
|
89
|
+
[:node, 'Db'],
|
90
|
+
[:node, 'App'],
|
91
|
+
[:edge, 'App', 'Db', {:color => :blue, :label => '7474/tcp'}],
|
92
|
+
[:node, '127.0.0.1/32'],
|
93
|
+
[:edge, '127.0.0.1/32', 'Db', {:color => :blue, :label => '22/tcp'}]
|
94
|
+
)
|
78
95
|
|
79
96
|
end
|
80
97
|
|
81
98
|
it 'should add map edges for cidr ingress' do
|
82
99
|
expect(@ec2).to receive(:security_groups).and_return(
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
100
|
+
[
|
101
|
+
group('Web', group_ingress('80', 'External')),
|
102
|
+
group('Db', group_ingress('7474', 'App'), cidr_ingress('22', '127.0.0.1/32'))
|
103
|
+
])
|
87
104
|
mapping = {'127.0.0.1/32' => 'Work'}
|
88
105
|
mapping = CidrGroupMapping.new([], mapping)
|
89
106
|
allow(CidrGroupMapping).to receive(:new).and_return(mapping)
|
90
107
|
|
91
108
|
graph = visualize_aws.build
|
92
109
|
|
93
|
-
expect(graph.
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
110
|
+
expect(graph.output(renderer)).to contain_exactly(
|
111
|
+
[:node, 'Web'],
|
112
|
+
[:node, 'External'],
|
113
|
+
[:edge, 'External', 'Web', {:color => :blue, :label => '80/tcp'}],
|
114
|
+
[:node, 'Db'],
|
115
|
+
[:node, 'App'],
|
116
|
+
[:edge, 'App', 'Db', {:color => :blue, :label => '7474/tcp'}],
|
117
|
+
[:node, 'Work'],
|
118
|
+
[:edge, 'Work', 'Db', {:color => :blue, :label => '22/tcp'}]
|
119
|
+
)
|
103
120
|
|
104
121
|
end
|
105
122
|
|
106
123
|
it 'should group mapped duplicate edges for cidr ingress' do
|
107
124
|
expect(@ec2).to receive(:security_groups).and_return(
|
108
|
-
|
109
|
-
|
110
|
-
|
125
|
+
[
|
126
|
+
group('ssh', cidr_ingress('22', '192.168.0.1/32'), cidr_ingress('22', '127.0.0.1/32'))
|
127
|
+
])
|
111
128
|
mapping = {'127.0.0.1/32' => 'Work', '192.168.0.1/32' => 'Work'}
|
112
129
|
mapping = CidrGroupMapping.new([], mapping)
|
113
130
|
allow(CidrGroupMapping).to receive(:new).and_return(mapping)
|
114
131
|
|
115
132
|
graph = visualize_aws.build
|
116
133
|
|
117
|
-
expect(graph.
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
134
|
+
expect(graph.output(renderer)).to contain_exactly(
|
135
|
+
[:node, 'ssh'],
|
136
|
+
[:node, 'Work'],
|
137
|
+
[:edge, 'Work', 'ssh', {:color => :blue, :label => '22/tcp'}]
|
138
|
+
)
|
122
139
|
end
|
123
140
|
end
|
124
141
|
|
125
142
|
context "filter" do
|
126
143
|
it 'include cidr which do not match the pattern' do
|
127
144
|
expect(@ec2).to receive(:security_groups).and_return(
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
145
|
+
[
|
146
|
+
group('Web', cidr_ingress('22', '127.0.0.1/32')),
|
147
|
+
group('Db', cidr_ingress('22', '192.0.1.1/32'))
|
148
|
+
])
|
132
149
|
|
133
150
|
opts = {:exclude => ['127.*']}
|
134
151
|
graph = VisualizeAws.new(AwsConfig.new(opts)).build
|
135
152
|
|
136
|
-
expect(graph.
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
153
|
+
expect(graph.output(renderer)).to contain_exactly(
|
154
|
+
[:node, 'Web'],
|
155
|
+
[:node, 'Db'],
|
156
|
+
[:node, '192.0.1.1/32'],
|
157
|
+
[:edge, '192.0.1.1/32', 'Db', {:color => :blue, :label => '22/tcp'}]
|
158
|
+
)
|
142
159
|
end
|
143
160
|
|
144
161
|
it 'include groups which do not match the pattern' do
|
145
162
|
expect(@ec2).to receive(:security_groups).and_return(
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
163
|
+
[
|
164
|
+
group('Web', group_ingress('80', 'External')),
|
165
|
+
group('Db', group_ingress('7474', 'App'), cidr_ingress('22', '127.0.0.1/32'))
|
166
|
+
])
|
150
167
|
|
151
168
|
opts = {:exclude => ['D.*b', 'App']}
|
152
169
|
graph = VisualizeAws.new(AwsConfig.new(opts)).build
|
153
170
|
|
154
|
-
expect(graph.
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
171
|
+
expect(graph.output(renderer)).to contain_exactly(
|
172
|
+
[:node, 'Web'],
|
173
|
+
[:node, 'External'],
|
174
|
+
[:edge, 'External', 'Web', {:color => :blue, :label => '80/tcp'}]
|
175
|
+
)
|
159
176
|
end
|
160
177
|
|
161
178
|
it 'include derived groups which do not match the pattern' do
|
162
179
|
expect(@ec2).to receive(:security_groups).and_return(
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
180
|
+
[
|
181
|
+
group('Web', group_ingress('80', 'External')),
|
182
|
+
group('Db', group_ingress('7474', 'App'), cidr_ingress('22', '127.0.0.1/32'))
|
183
|
+
])
|
167
184
|
|
168
185
|
opts = {:exclude => ['App']}
|
169
186
|
graph = VisualizeAws.new(AwsConfig.new(opts)).build
|
170
187
|
|
171
|
-
expect(graph.
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
188
|
+
expect(graph.output(renderer)).to contain_exactly(
|
189
|
+
[:node, 'Web'],
|
190
|
+
[:node, 'External'],
|
191
|
+
[:edge, 'External', 'Web', {:color => :blue, :label => '80/tcp'}],
|
192
|
+
[:node, 'Db'],
|
193
|
+
[:node, '127.0.0.1/32'],
|
194
|
+
[:edge, '127.0.0.1/32', 'Db', {:color => :blue, :label => '22/tcp'}]
|
195
|
+
)
|
179
196
|
|
180
197
|
end
|
181
198
|
end
|
metadata
CHANGED
@@ -1,141 +1,155 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aws_security_viz
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.4.pre.alpha.pre.
|
4
|
+
version: 0.1.4.pre.alpha.pre.82
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anay Nayak
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-02-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.13'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.13'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 12.0.0
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 12.0.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - ~>
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: 3.5.0
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - ~>
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 3.5.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: ruby-graphviz
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - ~>
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: 1.2.2
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - ~>
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 1.2.2
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: fog-aws
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - ~>
|
73
|
+
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 1.2.0
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - ~>
|
80
|
+
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 1.2.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: unf
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - ~>
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: 0.1.4
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - ~>
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 0.1.4
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: json
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - ~>
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: 2.0.
|
103
|
+
version: 2.0.3
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - ~>
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: 2.0.
|
110
|
+
version: 2.0.3
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: trollop
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - ~>
|
115
|
+
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
117
|
version: 2.1.2
|
118
118
|
type: :runtime
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - ~>
|
122
|
+
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: 2.1.2
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: organic_hash
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- - ~>
|
129
|
+
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
131
|
version: 1.0.2
|
132
132
|
type: :runtime
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- - ~>
|
136
|
+
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: 1.0.2
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rgl
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 0.5.2
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 0.5.2
|
139
153
|
description: Provides a quick mechanism to visualize your EC2 security groups in multiple
|
140
154
|
formats
|
141
155
|
email: anayak007+rubygems@gmail.com
|
@@ -144,8 +158,8 @@ executables:
|
|
144
158
|
extensions: []
|
145
159
|
extra_rdoc_files: []
|
146
160
|
files:
|
147
|
-
- .gitignore
|
148
|
-
- .travis.yml
|
161
|
+
- ".gitignore"
|
162
|
+
- ".travis.yml"
|
149
163
|
- CHANGELOG.md
|
150
164
|
- Gemfile
|
151
165
|
- LICENSE.md
|
@@ -167,12 +181,15 @@ files:
|
|
167
181
|
- lib/exclusions.rb
|
168
182
|
- lib/export/html/view.html
|
169
183
|
- lib/graph.rb
|
184
|
+
- lib/graph_filter.rb
|
170
185
|
- lib/opts.yml.sample
|
171
186
|
- lib/provider/ec2.rb
|
172
187
|
- lib/provider/json.rb
|
173
188
|
- lib/renderer/graphviz.rb
|
174
189
|
- lib/renderer/json.rb
|
175
190
|
- lib/version.rb
|
191
|
+
- spec/graph_filter_spec.rb
|
192
|
+
- spec/integration/aws_expected.json
|
176
193
|
- spec/integration/dummy.dot
|
177
194
|
- spec/integration/dummy.json
|
178
195
|
- spec/integration/expected.json
|
@@ -189,12 +206,12 @@ require_paths:
|
|
189
206
|
- lib
|
190
207
|
required_ruby_version: !ruby/object:Gem::Requirement
|
191
208
|
requirements:
|
192
|
-
- -
|
209
|
+
- - ">="
|
193
210
|
- !ruby/object:Gem::Version
|
194
211
|
version: 2.0.0
|
195
212
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
196
213
|
requirements:
|
197
|
-
- -
|
214
|
+
- - ">"
|
198
215
|
- !ruby/object:Gem::Version
|
199
216
|
version: 1.3.1
|
200
217
|
requirements: []
|
@@ -204,6 +221,8 @@ signing_key:
|
|
204
221
|
specification_version: 4
|
205
222
|
summary: Visualize your aws security groups
|
206
223
|
test_files:
|
224
|
+
- spec/graph_filter_spec.rb
|
225
|
+
- spec/integration/aws_expected.json
|
207
226
|
- spec/integration/dummy.dot
|
208
227
|
- spec/integration/dummy.json
|
209
228
|
- spec/integration/expected.json
|