aws_security_viz 0.2.5.pre.alpha.pre.35 → 1.0.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 +4 -4
- data/CHANGELOG.md +59 -1
- data/README.md +120 -79
- data/exe/aws_security_viz +4 -40
- data/lib/aws_security_viz/aws_config.rb +80 -0
- data/lib/aws_security_viz/cli.rb +151 -0
- data/lib/aws_security_viz/cli_guard.rb +39 -0
- data/lib/aws_security_viz/directed_graph.rb +72 -0
- data/lib/aws_security_viz/ec2/security_groups.rb +74 -0
- data/lib/aws_security_viz/exclusions.rb +18 -0
- data/lib/aws_security_viz/export/html/viewer.html +222 -0
- data/lib/aws_security_viz/graph.rb +93 -0
- data/lib/aws_security_viz/graph_filter.rb +20 -0
- data/lib/aws_security_viz/logging.rb +18 -0
- data/lib/aws_security_viz/model.rb +87 -0
- data/lib/aws_security_viz/obfuscation.rb +45 -0
- data/lib/{opts.yml.sample → aws_security_viz/opts.yml.sample} +2 -0
- data/lib/aws_security_viz/port_label.rb +44 -0
- data/lib/aws_security_viz/provider/ec2.rb +84 -0
- data/lib/aws_security_viz/provider/json.rb +17 -0
- data/lib/aws_security_viz/renderer/all.rb +49 -0
- data/lib/aws_security_viz/renderer/graphviz.rb +119 -0
- data/lib/aws_security_viz/renderer/html.rb +46 -0
- data/lib/aws_security_viz/renderer/json.rb +29 -0
- data/lib/aws_security_viz/renderer/mermaid.rb +105 -0
- data/lib/aws_security_viz/risk.rb +18 -0
- data/lib/aws_security_viz/vendor/cytoscape/LICENSE +19 -0
- data/lib/aws_security_viz/vendor/cytoscape/README.md +15 -0
- data/lib/aws_security_viz/vendor/cytoscape/cytoscape.min.js +31 -0
- data/lib/aws_security_viz/version.rb +5 -0
- data/lib/aws_security_viz.rb +44 -37
- metadata +39 -232
- data/.dockerignore +0 -7
- data/.editorconfig +0 -17
- data/.github/dependabot.yml +0 -15
- data/.github/workflows/ruby.yml +0 -34
- data/.github/workflows/rubygem.yml +0 -29
- data/.github/workflows/rubygem_release.yml +0 -27
- data/.gitignore +0 -43
- data/.tool-versions +0 -1
- data/CODE_OF_CONDUCT.md +0 -46
- data/Dockerfile +0 -9
- data/Gemfile +0 -3
- data/Rakefile +0 -15
- data/Vagrantfile +0 -18
- data/aws_security_viz.gemspec +0 -40
- data/config/boot.rb +0 -4
- data/images/sample.png +0 -0
- data/lib/aws_config.rb +0 -44
- data/lib/color_picker.rb +0 -245
- data/lib/debug/parse_log.rb +0 -26
- data/lib/debug_graph.rb +0 -29
- data/lib/ec2/ip_permission.rb +0 -35
- data/lib/ec2/security_groups.rb +0 -78
- data/lib/ec2/traffic.rb +0 -32
- data/lib/exclusions.rb +0 -14
- data/lib/export/html/navigator.html +0 -84
- data/lib/export/html/view.html +0 -163
- data/lib/graph.rb +0 -45
- data/lib/graph_filter.rb +0 -38
- data/lib/provider/ec2.rb +0 -105
- data/lib/provider/json.rb +0 -104
- data/lib/renderer/all.rb +0 -18
- data/lib/renderer/graphviz.rb +0 -37
- data/lib/renderer/json.rb +0 -23
- data/lib/renderer/navigator.rb +0 -33
- data/lib/version.rb +0 -3
- data/spec/color_picker_spec.rb +0 -20
- data/spec/graph_filter_spec.rb +0 -85
- data/spec/integration/aws_expected.json +0 -1
- data/spec/integration/dummy.dot +0 -50
- data/spec/integration/dummy.json +0 -64
- data/spec/integration/expected.json +0 -1
- data/spec/integration/navigator.json +0 -1
- data/spec/integration/visualize_aws_spec.rb +0 -96
- data/spec/spec_helper.rb +0 -36
- data/spec/visualize_aws_spec.rb +0 -194
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 57eea04dcfb8bce81f204b046fbb01525194362bf046a7754fb2c26704ad2e4f
|
|
4
|
+
data.tar.gz: b73a6510229a8fdb6f0697518b4790fe4cf1400b6ac1dcc44f50f4fb4899267f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c85a2f7002c23a0d3676fd7116646c5846e9497b9a10a59fe49c98a1d4b848971627e4ab10a3b009984c5ea4d9e192a08d1f27bfe3d5faee1966cca94a425536
|
|
7
|
+
data.tar.gz: 2ff0af0aeeaa6fa4e35f760ce6a8f489666e9d2a21bf63cc42812608e08b19de80a43900b1a641f187d8c4170e92ba6c1816c345055ae8cb545caf8af716260f
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,63 @@
|
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
|
3
3
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
4
|
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [1.0.0] - 2026-10-03
|
|
8
|
+
This release also covers the 0.3.0 work, which was never tagged or published.
|
|
9
|
+
|
|
10
|
+
### Breaking changes
|
|
11
|
+
- Ruby 3.3 or newer is required (it was 3.0 or newer in 0.2.4)
|
|
12
|
+
- The default output is now `aws-security-viz.html`, a self-contained page, instead of `aws-security-viz.png` (`.json` for json and navigator output). The output format is inferred from the -f/--output extension (.html, .json, .mmd, .dot/.gv, or an image such as .png/.svg). Image output still needs the Graphviz `dot` binary
|
|
13
|
+
- The old html viewers and the navigator renderer are removed, along with `--serve`
|
|
14
|
+
- There is no default region (it used to be us-east-1); the region comes from the AWS SDK credential/config chain (AWS_REGION, profile, ...) or -r, and a missing region fails with an error
|
|
15
|
+
- --profile no longer defaults to AWS_PROFILE, so explicit access keys are kept
|
|
16
|
+
- Node ids in JSON output are now security group ids; names are labels, and groups are clustered by VPC (and by region when several are queried)
|
|
17
|
+
- Edges are blue for ingress and red for egress; the per-group colour palette is gone and `--color` is ignored
|
|
18
|
+
- All library code is under the `AwsSecurityViz` namespace and moved to `lib/aws_security_viz/`; the top-level `VisualizeAws`, `Renderer`, `Ec2Provider` and similar constants no longer exist. Use the `aws_security_viz` executable, or `AwsSecurityViz::CLI`
|
|
19
|
+
- Obfuscation hashes are shorter (10 hex characters)
|
|
20
|
+
- Runtime dependencies are now only `aws-sdk-ec2` (>= 1.400, no upper cap) and `rexml`. `graphviz`, `rgl`, `optimist`, `webrick` and `organic_hash` are gone
|
|
21
|
+
|
|
22
|
+
### Deprecated
|
|
23
|
+
- --renderer: still works but prints a warning; use the -f extension instead. `--renderer navigator` now writes the html viewer
|
|
24
|
+
- --color: ignored, prints a warning
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- Self-contained HTML viewer (`-f out.html`, the default): one file with Cytoscape.js 3.34.3 vendored and the graph data inlined, a Content-Security-Policy, no CDN or server, works from `file://`
|
|
28
|
+
- Mermaid flowchart output (`-f out.mmd`), with a warning when the diagram exceeds Mermaid's default edge or text limits
|
|
29
|
+
- Multi-region support: `--region us-east-1,eu-west-1` or `--all-regions` (needs `ec2:DescribeRegions`); regions that fail with an authorisation error are skipped with a warning
|
|
30
|
+
- Risky ingress highlighting: 0.0.0.0/0 or ::/0 reaching a sensitive port (22, 3389, 3306, 5432, 1433, 6379, 9200, 27017, configurable with `risky_ports` in opts.yml) or allowing all traffic is drawn distinctly and flagged `risky` in JSON and HTML output. --fail-on-risk exits 2 when any is found
|
|
31
|
+
- --show-unused marks security groups with no attached network interface (dashed grey in DOT, `unused` class in Mermaid, `unused: true` in JSON and HTML output); it needs `ec2:DescribeNetworkInterfaces` and is ignored with --source-file
|
|
32
|
+
- Rule descriptions are kept: JSON edges carry a `descriptions` list and DOT edges a `tooltip` (hashed under --obfuscate)
|
|
33
|
+
- --input and --output aliases for -o/--source-file and -f/--filename; `init` alias for `setup`
|
|
34
|
+
- --layout picks the Graphviz layout engine (overrides `format` in opts.yml; unknown engines are rejected)
|
|
35
|
+
- --debug (or DEBUG=true) for verbose output and stack traces, --obfuscate (or OBFUSCATE=true) to hash group names, ports and ids
|
|
36
|
+
- IPv6 ranges and prefix lists are drawn as rule peers
|
|
37
|
+
- --vpc-id is honoured for --source-file input
|
|
38
|
+
- Tag-triggered release workflow using RubyGems trusted publishing, which also pushes a multi-arch (amd64 and arm64) image to ghcr.io/anaynayak/aws-security-viz tagged `<version>`, `<major>.<minor>` and `latest`. The image is built from this source and runs as a non-root user. Not yet verified locally (task-17: the Docker build and the release workflow have not been run)
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
- CLI moved into `AwsSecurityViz::CLI` on stdlib OptionParser
|
|
42
|
+
- Warnings, errors and debug output go to stderr through a Logger, keeping stdout clean
|
|
43
|
+
- Exit codes: 0 on success, 1 on error, 2 for --fail-on-risk, 130 on Ctrl-C
|
|
44
|
+
- Graph building uses a small adjacency-list graph and DOT is generated directly; Graphviz is only needed for image formats
|
|
45
|
+
- Rules that allow all traffic, ICMP, or a numeric protocol get readable labels, and all-traffic is labelled "all"
|
|
46
|
+
- Boolean environment variables (DEBUG, OBFUSCATE) are parsed as booleans
|
|
47
|
+
- The html asset is written next to the output file, not the current directory
|
|
48
|
+
- Gemfile.lock is committed, the gemspec has no upper version caps, and CI tests Ruby 3.3, 3.4 and 4.0
|
|
49
|
+
|
|
50
|
+
### Removed
|
|
51
|
+
- The navigator renderer, the old view.html and navigator.html viewers, --serve and the webrick dependency
|
|
52
|
+
- The `rake docker:push` task and the per-push alpha gem workflow, replaced by the tag-triggered release workflow
|
|
53
|
+
- Support for Ruby older than 3.3
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
- Security groups sharing a name in different VPCs no longer collapse into one node
|
|
57
|
+
- Several rules between the same two groups are merged into a single edge with merged port labels
|
|
58
|
+
- DescribeSecurityGroups is paginated and the VPC filter is applied server-side
|
|
59
|
+
- Unknown renderers and layout engines are rejected, and errors are reported cleanly (exit 1, Ctrl-C exits 130)
|
|
60
|
+
- The test suite loads on Ruby 4.0
|
|
61
|
+
|
|
5
62
|
## [0.2.4] - 2023-06-10
|
|
6
63
|
- Matrix builds for Ruby v3.0 onwards only
|
|
7
64
|
- Remove support for Ruby v2.x
|
|
@@ -95,7 +152,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
95
152
|
- Begin life as the gem [aws_security_viz](https://rubygems.org/gems/aws_security_viz)
|
|
96
153
|
|
|
97
154
|
|
|
98
|
-
[Unreleased]: https://github.com/anaynayak/aws-security-viz/compare/
|
|
155
|
+
[Unreleased]: https://github.com/anaynayak/aws-security-viz/compare/v1.0.0...HEAD
|
|
156
|
+
[1.0.0]: https://github.com/anaynayak/aws-security-viz/compare/v0.2.4...v1.0.0
|
|
99
157
|
[0.1.3]: https://github.com/anaynayak/aws-security-viz/compare/v0.1.2...v0.1.3
|
|
100
158
|
[0.1.2]: https://github.com/anaynayak/aws-security-viz/compare/v0.1.1...v0.1.2
|
|
101
159
|
[0.1.1]: https://github.com/anaynayak/aws-security-viz/compare/v0.1.0...v0.1.1
|
data/README.md
CHANGED
|
@@ -2,105 +2,150 @@ aws-security-viz -- A tool to visualize aws security groups
|
|
|
2
2
|
============================================================
|
|
3
3
|
[](https://github.com/anaynayak/aws-security-viz/actions?query=workflow%3ARuby)
|
|
4
4
|
[]()
|
|
5
|
-
[](https://libraries.io/github/anaynayak/aws-security-viz)
|
|
5
|
+
[](https://github.com/anaynayak/aws-security-viz/pkgs/container/aws-security-viz)
|
|
7
6
|

|
|
8
7
|
|
|
9
|
-
|
|
10
8
|
## DESCRIPTION
|
|
11
9
|
Need a quick way to visualize your current aws/amazon ec2 security group configuration? aws-security-viz does just that based on the EC2 security group ingress configuration.
|
|
12
10
|
|
|
11
|
+

|
|
12
|
+
|
|
13
13
|
## FEATURES
|
|
14
14
|
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* Reads the live AWS API, or the JSON from `aws ec2 describe-security-groups`.
|
|
16
|
+
* Output formats: a self-contained HTML viewer, JSON, Mermaid, DOT, and any image format Graphviz supports (png, svg, pdf and others).
|
|
17
|
+
* One region, several regions, or all regions in a single graph; groups are clustered by region and VPC.
|
|
18
|
+
* Risky public ingress (`0.0.0.0/0` or `::/0` on a sensitive port) is drawn as a dashed crimson edge, and `--fail-on-risk` turns it into a failing exit code for CI.
|
|
19
|
+
* `--show-unused` marks security groups that no network interface uses.
|
|
17
20
|
|
|
18
21
|
## INSTALLATION
|
|
22
|
+
|
|
23
|
+
From RubyGems:
|
|
24
|
+
|
|
19
25
|
```
|
|
20
26
|
$ gem install aws_security_viz
|
|
21
27
|
$ aws_security_viz --help
|
|
22
28
|
```
|
|
23
29
|
|
|
30
|
+
Or run the published image, which has Ruby and Graphviz built in (see [Docker usage](#docker-usage)):
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
$ docker pull ghcr.io/anaynayak/aws-security-viz:<version>
|
|
34
|
+
```
|
|
35
|
+
|
|
24
36
|
## DEPENDENCIES
|
|
25
37
|
|
|
26
|
-
*
|
|
38
|
+
* Ruby 3.3 or newer.
|
|
39
|
+
* Graphviz (`brew install graphviz`), only for image formats (png, svg, pdf and so on). The HTML, JSON, Mermaid and DOT outputs do not need it.
|
|
40
|
+
|
|
41
|
+
## USAGE
|
|
27
42
|
|
|
28
|
-
|
|
43
|
+
The output format follows the extension of `-f/--output`: `.html` (the default is `aws-security-viz.html`), `.json`, `.mmd`, `.dot` or `.gv`, or an image such as `.png` or `.svg`. The older `--renderer` flag is deprecated; it still works and prints a warning.
|
|
29
44
|
|
|
30
|
-
To generate
|
|
45
|
+
To generate a graph from an existing security_groups.json (created using aws-cli, see [Debugging](#debugging))
|
|
31
46
|
|
|
32
47
|
```
|
|
33
|
-
$ aws_security_viz -
|
|
48
|
+
$ aws_security_viz -o security_groups.json -f viz.svg
|
|
34
49
|
```
|
|
35
50
|
|
|
36
|
-
To
|
|
51
|
+
To query AWS directly with a shared-config profile (`--profile` also works with SSO profiles after `aws sso login --profile <profile_name>`)
|
|
37
52
|
|
|
38
53
|
```
|
|
39
|
-
$ aws_security_viz
|
|
54
|
+
$ aws_security_viz --profile <profile_name> --region us-west-1 -f viz.html
|
|
40
55
|
```
|
|
41
56
|
|
|
42
|
-
|
|
57
|
+
The credentials and region otherwise come from the standard AWS SDK chain (`AWS_PROFILE`, `AWS_REGION`, environment variables, SSO, instance roles). There is no built-in default region, so a missing region fails with `MissingRegionError`.
|
|
58
|
+
|
|
59
|
+
With [aws-vault](https://github.com/99designs/aws-vault/) and short lived temporary credentials
|
|
43
60
|
|
|
44
61
|
```
|
|
45
|
-
$
|
|
62
|
+
$ aws-vault exec <profile_name> -- aws_security_viz --region us-west-1 -f viz.html
|
|
46
63
|
```
|
|
47
64
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
With explicit keys (`-a/--access-key`, `-s/--secret-key`, `-e/--session-token`). Prefer a profile, aws-vault or SSO, because keys passed on the command line end up in your shell history.
|
|
66
|
+
|
|
67
|
+
### Regions
|
|
68
|
+
|
|
69
|
+
With several regions (`--region us-east-1,eu-west-1` or `--all-regions`) nodes are grouped by region, then VPC. With `--all-regions`, `-r` (first name if a list) is only the region used to call `DescribeRegions`. A region that fails with `UnauthorizedOperation`, `AuthFailure` or `OptInRequired` is skipped with a warning; the run fails only if every region fails. The JSON and HTML outputs carry the region on each node. `--region` and `--all-regions` are ignored (with a warning) when `--source-file` is used.
|
|
70
|
+
|
|
71
|
+
### Risky ingress
|
|
72
|
+
|
|
73
|
+
Ingress from `0.0.0.0/0` or `::/0` on a sensitive port (22, 3389, 3306, 5432, 1433, 6379, 9200, 27017) or on all traffic is flagged on stderr and drawn as a dashed crimson edge. Change the ports with `risky_ports` in opts.yml. `--fail-on-risk` exits with status 2 when any such edge exists; the output file is still written.
|
|
51
74
|
|
|
52
75
|
## DOCKER USAGE
|
|
53
76
|
|
|
54
|
-
|
|
77
|
+
Run aws-security-viz from the published image instead of installing Ruby and Graphviz. Releases push a multi-arch (amd64 and arm64) image to `ghcr.io/anaynayak/aws-security-viz`, tagged `<version>` (for example `1.0.0`), `<major>.<minor>` (for example `1.0`) and `latest` (stable releases only). Pin a version tag for reproducible runs. The image's entrypoint is `bundle exec aws_security_viz`, so everything after the image name is a normal CLI argument. It runs as a non-root user with `/work` as the working directory; mount a local directory there to get the output files.
|
|
55
78
|
|
|
56
|
-
1.
|
|
57
|
-
2. Build the docker container: `docker build -t sec-viz .`
|
|
79
|
+
1. With aws-vault (recommended):
|
|
58
80
|
|
|
59
|
-
|
|
81
|
+
```
|
|
82
|
+
aws-vault exec <profile_name> -- docker run --rm --user $(id -u):$(id -g) \
|
|
83
|
+
-e AWS_REGION -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_SESSION_TOKEN -e AWS_SECURITY_TOKEN \
|
|
84
|
+
-v "$(pwd)/aws-viz:/work" ghcr.io/anaynayak/aws-security-viz:<version> -f /work/aws.svg
|
|
85
|
+
```
|
|
60
86
|
|
|
61
|
-
|
|
87
|
+
2. With an SSO profile, mounting your AWS config (run `aws sso login --profile <profile_name>` first):
|
|
62
88
|
|
|
63
|
-
|
|
89
|
+
```
|
|
90
|
+
docker run --rm --user $(id -u):$(id -g) -e HOME=/home/viz -e AWS_PROFILE=<profile_name> -e AWS_REGION \
|
|
91
|
+
-v "$HOME/.aws:/home/viz/.aws" -v "$(pwd)/aws-viz:/work" \
|
|
92
|
+
ghcr.io/anaynayak/aws-security-viz:<version> -f /work/aws.svg
|
|
93
|
+
```
|
|
64
94
|
|
|
65
|
-
3.
|
|
95
|
+
3. With AWS credentials passed as parameters:
|
|
66
96
|
|
|
67
|
-
```
|
|
97
|
+
```
|
|
98
|
+
docker run --rm --user $(id -u):$(id -g) -v "$(pwd)/aws-viz:/work" ghcr.io/anaynayak/aws-security-viz:<version> \
|
|
99
|
+
-a REPLACE_AWS_ACCESS_KEY_ID -s REPLACE_SECRET -r REPLACE_REGION -f /work/aws.svg
|
|
100
|
+
```
|
|
68
101
|
|
|
69
|
-
|
|
102
|
+
4. To build the image from a checkout instead: `docker build -t sec-viz .` and use `sec-viz` in place of the ghcr.io name.
|
|
70
103
|
|
|
71
|
-
|
|
72
|
-
* `-v $(pwd)/aws-viz
|
|
73
|
-
*
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
*
|
|
77
|
-
* `-name sec-viz` the container will have the same name as the image we will start
|
|
104
|
+
Notes:
|
|
105
|
+
* `-v "$(pwd)/aws-viz:/work"` is the local directory where output is written. Create it first (`mkdir aws-viz`).
|
|
106
|
+
* `--user $(id -u):$(id -g)` matters on Linux: bind mounts keep the host's ownership, so the container's non-root
|
|
107
|
+
user cannot write to a directory owned by you and the run fails with "Permission denied". Running as your own
|
|
108
|
+
uid/gid fixes that and leaves the output files owned by you. Docker Desktop on macOS does not need it.
|
|
109
|
+
* `--rm` removes the container after the run.
|
|
78
110
|
|
|
79
|
-
You can also use other
|
|
111
|
+
You can also use any other option from the [help](#help) below.
|
|
80
112
|
|
|
81
113
|
### Help
|
|
82
114
|
|
|
83
115
|
```
|
|
84
116
|
$ aws_security_viz --help
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
117
|
+
Usage: aws_security_viz [options] [setup|init]
|
|
118
|
+
-a, --access-key=KEY AWS access key
|
|
119
|
+
-s, --secret-key=KEY AWS secret key
|
|
120
|
+
-e, --session-token=TOKEN AWS session token
|
|
121
|
+
-r, --region=REGION AWS region(s) to query, comma-separated (default: SDK chain, e.g. AWS_REGION or profile)
|
|
122
|
+
--all-regions Query every region returned by DescribeRegions
|
|
123
|
+
-p, --profile=NAME AWS shared-config profile to use (AWS_PROFILE is read by the SDK)
|
|
124
|
+
-v, --vpc-id=ID AWS VPC id to show
|
|
125
|
+
-o, --source-file, --input=FILE JSON source file containing security groups
|
|
126
|
+
-f, --filename, --output=FILE Output file; the format follows the extension: .html, .json, .mmd, .dot/.gv or an image such as .png/.svg (default: aws-security-viz.html)
|
|
127
|
+
-c, --config=FILE Config file (opts.yml)
|
|
128
|
+
-l, --[no-]color Deprecated, ignored: edges are blue for ingress and red for egress, risky public ingress is dashed crimson
|
|
129
|
+
-n, --renderer=NAME Deprecated, use the -f extension instead: renderer (graphviz|json|html|mermaid)
|
|
130
|
+
-y, --layout=ENGINE Graphviz layout engine (dot|neato|sfdp|fdp|twopi|circo); overrides opts.yml format
|
|
131
|
+
-d, --[no-]debug Verbose output and stack traces (or DEBUG=true)
|
|
132
|
+
-b, --[no-]obfuscate Hash group names and ports (or OBFUSCATE=true)
|
|
133
|
+
-u, --source-filter=FILTER Source filter
|
|
134
|
+
-t, --target-filter=FILTER Target filter
|
|
135
|
+
--show-unused Mark groups with no attached network interfaces (needs ec2:DescribeNetworkInterfaces; ignored with --source-file)
|
|
136
|
+
--fail-on-risk Exit 2 when 0.0.0.0/0 or ::/0 can reach a sensitive port (the output is still written)
|
|
137
|
+
-i, --version Print version and exit
|
|
138
|
+
-h, --help Show this message
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### Configuration
|
|
142
|
+
|
|
143
|
+
aws-security-viz only uses the `ec2:DescribeSecurityGroups` api, so a minimal IAM policy which grants only that action should be enough. Two flags call more:
|
|
144
|
+
|
|
145
|
+
1. `--all-regions` calls `ec2:DescribeRegions`.
|
|
146
|
+
2. `--show-unused` calls `ec2:DescribeNetworkInterfaces` (paginated, per region). Without the permission the run still succeeds and logs a warning, but no groups are marked unused.
|
|
147
|
+
|
|
148
|
+
Add an action to the policy only if you use its flag.
|
|
104
149
|
|
|
105
150
|
```json
|
|
106
151
|
{
|
|
@@ -108,20 +153,20 @@ aws-security-viz only uses the `ec2:DescribeSecurityGroups` api so a minimal IAM
|
|
|
108
153
|
"Statement": [
|
|
109
154
|
{
|
|
110
155
|
"Effect": "Allow",
|
|
111
|
-
"Action":
|
|
156
|
+
"Action": [
|
|
157
|
+
"ec2:DescribeSecurityGroups",
|
|
158
|
+
"ec2:DescribeRegions",
|
|
159
|
+
"ec2:DescribeNetworkInterfaces"
|
|
160
|
+
],
|
|
112
161
|
"Resource": "*"
|
|
113
162
|
}
|
|
114
163
|
]
|
|
115
164
|
}
|
|
116
165
|
```
|
|
117
166
|
|
|
118
|
-
Alternatively you can use [aws-vault](https://github.com/99designs/aws-vault/) and run it using short lived temporary credentials.
|
|
119
|
-
|
|
120
|
-
`$ aws-vault exec <profile> -- aws_security_viz -f aws.json --renderer navigator --serve 9091`
|
|
121
|
-
|
|
122
167
|
#### Advanced configuration
|
|
123
168
|
|
|
124
|
-
You can generate a configuration file using the following command:
|
|
169
|
+
You can generate a configuration file using the following command (`init` is an alias for `setup`):
|
|
125
170
|
```
|
|
126
171
|
$ aws_security_viz setup [-c opts.yml]
|
|
127
172
|
```
|
|
@@ -131,26 +176,25 @@ The opts.yml file lets you define the following options:
|
|
|
131
176
|
* Grouping of CIDR ips
|
|
132
177
|
* Define exclusion patterns
|
|
133
178
|
* Change graphviz format (neato, dot, sfdp etc)
|
|
179
|
+
* Change the ports flagged as risky (`risky_ports`)
|
|
134
180
|
|
|
135
181
|
## DEBUGGING
|
|
136
182
|
|
|
137
|
-
To generate the graph with debug statements,
|
|
183
|
+
To generate the graph with debug statements, pass `--debug` (or set `DEBUG=true`). Debug output goes to stderr.
|
|
138
184
|
|
|
139
185
|
```
|
|
140
|
-
$
|
|
186
|
+
$ aws_security_viz --debug --profile <profile_name> --region us-west-1 -f viz.svg
|
|
141
187
|
```
|
|
142
188
|
|
|
143
|
-
If it doesn't indicate the problem, please
|
|
144
|
-
|
|
145
|
-
You can send me an obfuscated version using the following command:
|
|
189
|
+
If it doesn't indicate the problem, please open an issue on [GitHub](https://github.com/anaynayak/aws-security-viz/issues) and attach the security group JSON. Obfuscate it first with `--obfuscate` (or `OBFUSCATE=true`), which hashes group names and ports in the output:
|
|
146
190
|
|
|
147
191
|
```
|
|
148
|
-
$
|
|
192
|
+
$ aws_security_viz --debug --obfuscate --profile <profile_name> --region us-west-1 -f viz.json
|
|
149
193
|
```
|
|
150
194
|
|
|
151
|
-
Execute the following command to generate the
|
|
195
|
+
Execute the following command to generate the JSON input for `--source-file`. You will need [aws-cli](https://github.com/aws/aws-cli) to execute the command
|
|
152
196
|
|
|
153
|
-
`aws ec2 describe-security-groups`
|
|
197
|
+
`aws ec2 describe-security-groups > security_groups.json`
|
|
154
198
|
|
|
155
199
|
|
|
156
200
|
## EXAMPLES
|
|
@@ -159,13 +203,11 @@ Execute the following command to generate the json. You will need [aws-cli](http
|
|
|
159
203
|
|
|
160
204
|

|
|
161
205
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
206
|
+
Generated from `spec/integration/dummy.json` with `aws_security_viz -o spec/integration/dummy.json -f images/sample.png`.
|
|
207
|
+
|
|
208
|
+
#### HTML viewer (useful with very large number of nodes)
|
|
165
209
|
|
|
166
|
-
|
|
167
|
-
Via json renderer `aws_security_viz -a your_aws_key -s your_aws_secret_key -f aws.json --renderer json`
|
|
168
|
-

|
|
210
|
+
`aws_security_viz --profile <profile_name> --region us-west-1 -f aws.html` writes one self-contained file with the graph data inlined. Open it from disk in a browser; no web server is needed.
|
|
169
211
|
|
|
170
212
|
## Additional examples
|
|
171
213
|
|
|
@@ -178,21 +220,20 @@ Via json renderer `aws_security_viz -a your_aws_key -s your_aws_secret_key -f aw
|
|
|
178
220
|
#### Generate visualization for `us-west-1` with target filter as `sec-group-1`. This will display all routes through which we can arrive at `sec-group-1`
|
|
179
221
|
|
|
180
222
|
```
|
|
181
|
-
$ aws_security_viz --region us-west-1 --target-filter=sec-group-1
|
|
223
|
+
$ aws_security_viz --region us-west-1 -f aws.html --target-filter=sec-group-1
|
|
182
224
|
```
|
|
183
225
|
|
|
184
226
|
#### Generate visualization for `us-west-1` restricted to vpc-id `vpc-12345`
|
|
185
227
|
```
|
|
186
|
-
$ aws_security_viz --region us-west-1 --vpc-id=vpc-12345
|
|
228
|
+
$ aws_security_viz --region us-west-1 -f aws.html --vpc-id=vpc-12345
|
|
187
229
|
```
|
|
188
230
|
|
|
189
|
-
####
|
|
231
|
+
#### Fail a CI job when a sensitive port is open to the internet
|
|
190
232
|
```
|
|
191
|
-
$ aws_security_viz --
|
|
233
|
+
$ aws_security_viz --all-regions --fail-on-risk -f aws.json
|
|
192
234
|
```
|
|
193
235
|
|
|
194
|
-
####
|
|
236
|
+
#### Generate a Mermaid diagram
|
|
195
237
|
```
|
|
196
|
-
$ aws_security_viz
|
|
238
|
+
$ aws_security_viz --region us-west-1 -f aws.mmd
|
|
197
239
|
```
|
|
198
|
-
The browser link to the view is printed on the CLI
|
data/exe/aws_security_viz
CHANGED
|
@@ -1,43 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require 'webrick'
|
|
6
|
-
require 'version'
|
|
4
|
+
require "aws_security_viz"
|
|
5
|
+
require "aws_security_viz/cli"
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
version "aws_security_viz v#{AwsSecurityViz::VERSION}"
|
|
10
|
-
opt :access_key, 'AWS access key', :default => ENV['AWS_ACCESS_KEY'] || ENV['AWS_ACCESS_KEY_ID'], :type => :string
|
|
11
|
-
opt :secret_key, 'AWS secret key', :default => ENV['AWS_SECRET_KEY'] || ENV['AWS_SECRET_ACCESS_KEY'], :type => :string
|
|
12
|
-
opt :session_token, 'AWS session token', :default => ENV['AWS_SESSION_TOKEN'] || nil, :type => :string
|
|
13
|
-
opt :region, 'AWS region to query', :default => 'us-east-1', :type => :string
|
|
14
|
-
opt :vpc_id, 'AWS VPC id to show', :type => :string
|
|
15
|
-
opt :source_file, 'JSON source file containing security groups', :type => :string
|
|
16
|
-
opt :filename, 'Output file name', :type => :string, :default => 'aws-security-viz.png'
|
|
17
|
-
opt :config, 'Config file (opts.yml)', :type => :string, :default => 'opts.yml'
|
|
18
|
-
opt :color, 'Colored node edges', :default => false
|
|
19
|
-
opt :renderer, "Renderer (#{Renderer.all.join('|')})", :default => 'graphviz'
|
|
20
|
-
opt :source_filter, 'Source filter', :default => nil, :type => :string
|
|
21
|
-
opt :target_filter, 'Target filter', :default => nil, :type => :string
|
|
22
|
-
opt :serve, 'Serve a HTTP server', :default => nil, :type => :integer
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
cmd = ARGV.shift
|
|
26
|
-
if cmd=="setup"
|
|
27
|
-
AwsConfig.write(opts[:config])
|
|
28
|
-
puts "#{opts[:config]} created in current directory."
|
|
29
|
-
exit
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
config = AwsConfig.load(opts[:config]).merge(obfuscate: ENV['OBFUSCATE'], debug: ENV['DEBUG'])
|
|
33
|
-
begin
|
|
34
|
-
VisualizeAws.new(config, opts).unleash(opts[:filename])
|
|
35
|
-
if opts[:serve]
|
|
36
|
-
puts "Navigate to http://localhost:#{opts[:serve]}/navigator.html##{opts[:filename]}"
|
|
37
|
-
WEBrick::HTTPServer.new({Port: opts[:serve], DocumentRoot: '.'}).start
|
|
38
|
-
end
|
|
39
|
-
rescue Exception => e
|
|
40
|
-
puts "[ERROR] #{e.message}"
|
|
41
|
-
raise e if config.debug?
|
|
42
|
-
exit 1
|
|
43
|
-
end
|
|
7
|
+
exit AwsSecurityViz::CLI.new(ARGV).run
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
require_relative "risk"
|
|
5
|
+
|
|
6
|
+
module AwsSecurityViz
|
|
7
|
+
class AwsConfig
|
|
8
|
+
def initialize(opts = {})
|
|
9
|
+
@opts = opts
|
|
10
|
+
@risky_ports = parse_risky_ports(opts[:risky_ports])
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def exclusions
|
|
14
|
+
@exclusions ||= Exclusions.new(@opts[:exclude])
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def egress?
|
|
18
|
+
@opts.key?(:egress) ? @opts[:egress] : true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def groups
|
|
22
|
+
@opts[:groups] || {}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Ports whose exposure to 0.0.0.0/0 or ::/0 is flagged (opts.yml :risky_ports); all traffic always is.
|
|
26
|
+
# Validated once when the config is built.
|
|
27
|
+
attr_reader :risky_ports
|
|
28
|
+
|
|
29
|
+
LAYOUTS = %w[dot neato sfdp fdp twopi circo].freeze
|
|
30
|
+
|
|
31
|
+
# Graphviz layout engine: --layout, else `format` in opts.yml, else dot.
|
|
32
|
+
def layout
|
|
33
|
+
engine = (@opts[:layout] || @opts[:format] || "dot").to_s
|
|
34
|
+
return engine if LAYOUTS.include?(engine)
|
|
35
|
+
raise ArgumentError, "unknown layout engine '#{engine}' (choose from: #{LAYOUTS.join(", ")})"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def debug?
|
|
39
|
+
@opts[:debug] || false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def obfuscate?
|
|
43
|
+
@opts[:obfuscate] || false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Parses an env-style flag: nil/empty is unset, true/1/yes/on and false/0/no/off are booleans.
|
|
47
|
+
def self.boolean(value, name = "value")
|
|
48
|
+
text = value.to_s.strip.downcase
|
|
49
|
+
return nil if text.empty?
|
|
50
|
+
return true if %w[true 1 yes on].include?(text)
|
|
51
|
+
return false if %w[false 0 no off].include?(text)
|
|
52
|
+
raise ArgumentError, "#{name} must be true, false, 1 or 0 (got '#{value}')"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.load(file)
|
|
56
|
+
config_opts = File.exist?(file) ? YAML.load_file(file) : {}
|
|
57
|
+
AwsConfig.new(config_opts)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def merge(opts)
|
|
61
|
+
AwsConfig.new(@opts.merge!(opts))
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.write(file)
|
|
65
|
+
FileUtils.cp(File.expand_path("../opts.yml.sample", __FILE__), file)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
# Decimal integers 1-65535 only (an Integer, or a string of digits); anything else is an error.
|
|
71
|
+
def parse_risky_ports(entries)
|
|
72
|
+
return Risk::DEFAULT_PORTS if entries.nil?
|
|
73
|
+
Array(entries).map do |entry|
|
|
74
|
+
port = Integer(entry.to_s, 10) if entry.to_s.match?(/\A\d+\z/)
|
|
75
|
+
raise ArgumentError, "risky_ports: invalid entry '#{entry}' (expected a port number 1-65535)" unless port&.between?(1, 65535)
|
|
76
|
+
port
|
|
77
|
+
end.freeze
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|