mirror of
https://github.com/BishopFox/cloudfox.git
synced 2026-11-07 17:05:53 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f65be085cd | ||
|
|
16320591ea | ||
|
|
5267f430b0 | ||
|
|
4a63b18fd3 | ||
|
|
0642b82438 | ||
|
|
e688d79ed2 | ||
|
|
2f0cfd7b1f | ||
|
|
e3a1803cce | ||
|
|
6257da9391 | ||
|
|
d44899f213 | ||
|
|
29ab782b31 | ||
|
|
ba4ff4701a | ||
|
|
81be56e006 | ||
|
|
494443d7ce | ||
|
|
3d3cfc6c0c | ||
|
|
1b0925877d | ||
|
|
7727d1d08c | ||
|
|
c7f81dc5cc | ||
|
|
008fb84d23 | ||
|
|
142757b5c2 | ||
|
|
146034d619 | ||
|
|
9e056b29d7 | ||
|
|
871491738b | ||
|
|
033c953d57 | ||
|
|
a7e45b9ae5 | ||
|
|
ce513ba04d | ||
|
|
83f9af249d | ||
|
|
11652a9a6e | ||
|
|
6ac7a0bf6b | ||
|
|
c6fcdba665 | ||
|
|
4f2c2cb4c9 | ||
|
|
db8a6895c3 | ||
|
|
11763f04f6 | ||
|
|
0ba9e2d37f | ||
|
|
a3d1c09558 | ||
|
|
a3b7ab4269 | ||
|
|
60c6b8d61c | ||
|
|
8db014b136 | ||
|
|
72cd367086 | ||
|
|
f2d4b7c535 | ||
|
|
47d69d870f | ||
|
|
c9d1c2de68 | ||
|
|
a8e802404f | ||
|
|
d021ea54de | ||
|
|
7a18090609 | ||
|
|
cb8be678e7 | ||
|
|
6f45cbe900 | ||
|
|
8a248e3127 | ||
|
|
de141300e9 | ||
|
|
eb76d76dea | ||
|
|
72a551b38b | ||
|
|
59bac4ee36 | ||
|
|
44bf8e8915 | ||
|
|
70107c607f | ||
|
|
d24f817bf9 | ||
|
|
711e29e3bb | ||
|
|
04bb167273 | ||
|
|
ed732d0c01 | ||
|
|
f82547b438 | ||
|
|
35fca31475 | ||
|
|
888649616f | ||
|
|
2b272a1335 | ||
|
|
c94996e470 | ||
|
|
2f80731e5b | ||
|
|
d473e31c06 | ||
|
|
baf62fdbb2 | ||
|
|
f3f98f24e3 | ||
|
|
eedf397c93 | ||
|
|
31e2fe5fcb | ||
|
|
06a9dabf48 | ||
|
|
ce545ca9ff | ||
|
|
8954fc2a72 | ||
|
|
7bb7d947ef | ||
|
|
3c1213b0ff | ||
|
|
fbc862aef1 | ||
|
|
8dddcc83d2 | ||
|
|
36b45a64c5 | ||
|
|
333ca05a11 | ||
|
|
4a2444c05f | ||
|
|
c9edf8450b | ||
|
|
ec9a50ead1 | ||
|
|
eae16c32b4 | ||
|
|
0df2aa9240 | ||
|
|
abf1543b3e | ||
|
|
69c9070009 | ||
|
|
8f6208d983 | ||
|
|
83d9611732 | ||
|
|
b994b42708 | ||
|
|
52c12855a1 | ||
|
|
70037750c5 | ||
|
|
1bb8576a27 | ||
|
|
16b3c66624 | ||
|
|
3dbb98ffc6 | ||
|
|
b3a03ec7bd | ||
|
|
521cf7e4f4 | ||
|
|
5115f849f5 | ||
|
|
9e42b53d22 | ||
|
|
b28ed6a3fd | ||
|
|
0e7b006cde | ||
|
|
2cc97f508b | ||
|
|
031d666c63 | ||
|
|
d0de59dbe4 | ||
|
|
00b49aa33c | ||
|
|
12ee6c292a | ||
|
|
4584774ec1 | ||
|
|
d558e1cf55 | ||
|
|
19b825cfa6 | ||
|
|
698458cf92 | ||
|
|
dfed0491f2 | ||
|
|
7671e7e717 | ||
|
|
0c858e6cd4 | ||
|
|
0deb568e41 | ||
|
|
2806f7ea08 | ||
|
|
e54ffe8ce7 | ||
|
|
56e64d8e62 | ||
|
|
43fe99e832 | ||
|
|
5d2f2616af | ||
|
|
d8e4157e93 | ||
|
|
a104639354 | ||
|
|
296710c474 | ||
|
|
278e2658ee | ||
|
|
6b45f54699 | ||
|
|
2391bb7802 | ||
|
|
68d68fc75d | ||
|
|
7d6d16a4f1 | ||
|
|
caaf399e9d | ||
|
|
1f4f128a7a | ||
|
|
16c0f4dfe9 | ||
|
|
7e6439924a | ||
|
|
d3c042c1e3 | ||
|
|
cda06fff7a | ||
|
|
e49402991a | ||
|
|
cae31fbf21 | ||
|
|
4e75b1151b | ||
|
|
831198c42d | ||
|
|
2cb5ad2b83 | ||
|
|
efe95c02ee | ||
|
|
13bd04d3e4 | ||
|
|
b8d9d8093c | ||
|
|
b139513244 | ||
|
|
f058e1c57d | ||
|
|
a94bdc1853 | ||
|
|
89b3492399 | ||
|
|
67b84f0c74 | ||
|
|
8eec1c331b | ||
|
|
5e0eb15f7d | ||
|
|
ec497faba4 | ||
|
|
ed8e0a229c | ||
|
|
b60f479c2d | ||
|
|
ab4866fcbf | ||
|
|
00fe4fdce3 |
No files matched your search
+1
-1
@@ -1 +1 @@
|
||||
* @bishopfaure @dbravo-bishopfox
|
||||
* @bishopfaure @jbarciabf @msperling-bf
|
||||
@@ -15,10 +15,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
|
||||
- name: Go 1.21
|
||||
uses: actions/setup-go@v4
|
||||
- name: Go 1.25
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.20
|
||||
go-version: ^1.25
|
||||
id: go
|
||||
|
||||
- id: install-secret-key
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
gpg --list-secret-keys --keyid-format LONG
|
||||
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
@@ -39,9 +39,6 @@ jobs:
|
||||
|
||||
|
||||
- name: Release binaries
|
||||
uses: "marvinpinto/action-automatic-releases@latest"
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
prerelease: false
|
||||
files: |
|
||||
./cloudfox/*
|
||||
files: ./cloudfox/*
|
||||
@@ -11,6 +11,6 @@ jobs:
|
||||
codespell:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- run: pip install --user codespell
|
||||
- run: codespell --ignore-words-list="aks" --skip="*.sum"
|
||||
- run: codespell --ignore-words-list="aks,referers,invokable" --skip="*.sum"
|
||||
+4
-1
@@ -62,8 +62,11 @@ terraform.rc
|
||||
.DS_Store
|
||||
|
||||
untracked/*
|
||||
*tmp*
|
||||
tmp/*
|
||||
output/*
|
||||
*cloudfox-output*
|
||||
cloudfox-*
|
||||
cloudfox
|
||||
*.log
|
||||
*.bak
|
||||
@@ -75,4 +78,4 @@ dist/
|
||||
|
||||
# graphvis files
|
||||
*.gv
|
||||
*.svg
|
||||
*.svg
|
||||
@@ -13,6 +13,12 @@ linux:
|
||||
zip cloudfox-linux.zip cloudfox sha1sum-linux.txt
|
||||
rm cloudfox sha1sum-linux.txt
|
||||
|
||||
linux-arm64:
|
||||
GOOS=linux GOARCH=arm64 go build
|
||||
sha1sum cloudfox > sha1sum-linux-arm64.txt
|
||||
zip cloudfox-linux-arm64.zip cloudfox sha1sum-linux-arm64.txt
|
||||
rm cloudfox sha1sum-linux-arm64.txt
|
||||
|
||||
macos:
|
||||
GOOS=darwin GOARCH=amd64 go build
|
||||
sha1sum cloudfox > sha1sum-mac.txt
|
||||
@@ -26,24 +32,34 @@ release: clean
|
||||
mkdir -p ./cloudfox
|
||||
|
||||
GOOS=windows GOARCH=amd64 go build -o ./cloudfox/cloudfox.exe .
|
||||
zip ./cloudfox/cloudfox-windows-amd64.zip ./cloudfox/cloudfox.exe
|
||||
rm -rf ./cloudfox/cloudfox.exe
|
||||
|
||||
sha1sum ./cloudfox/cloudfox.exe > ./cloudfox/sha1sum.txt
|
||||
zip ./cloudfox/cloudfox-windows-amd64.zip ./cloudfox/cloudfox.exe ./cloudfox/sha1sum.txt
|
||||
rm -rf ./cloudfox/cloudfox.exe ./cloudfox/sha1sum.txt
|
||||
|
||||
GOOS=linux GOARCH=amd64 go build -o ./cloudfox/cloudfox .
|
||||
zip ./cloudfox/cloudfox-linux-amd64.zip ./cloudfox/cloudfox .
|
||||
rm -rf ./cloudfox/cloudfox
|
||||
sha1sum ./cloudfox/cloudfox > ./cloudfox/sha1sum.txt
|
||||
zip ./cloudfox/cloudfox-linux-amd64.zip ./cloudfox/cloudfox ./cloudfox/sha1sum.txt
|
||||
rm -rf ./cloudfox/cloudfox ./cloudfox/sha1sum.txt
|
||||
|
||||
GOOS=linux GOARCH=386 go build -o ./cloudfox/cloudfox .
|
||||
zip ./cloudfox/cloudfox-linux-386.zip ./cloudfox/cloudfox .
|
||||
rm -rf ./cloudfox/cloudfox
|
||||
sha1sum ./cloudfox/cloudfox > ./cloudfox/sha1sum.txt
|
||||
zip ./cloudfox/cloudfox-linux-386.zip ./cloudfox/cloudfox ./cloudfox/sha1sum.txt
|
||||
rm -rf ./cloudfox/cloudfox ./cloudfox/sha1sum.txt
|
||||
|
||||
GOOS=linux GOARCH=arm64 go build -o ./cloudfox/cloudfox .
|
||||
sha1sum ./cloudfox/cloudfox > ./cloudfox/sha1sum.txt
|
||||
zip ./cloudfox/cloudfox-linux-arm64.zip ./cloudfox/cloudfox ./cloudfox/sha1sum.txt
|
||||
rm -rf ./cloudfox/cloudfox ./cloudfox/sha1sum.txt
|
||||
|
||||
GOOS=darwin GOARCH=amd64 go build -o ./cloudfox/cloudfox .
|
||||
zip ./cloudfox/cloudfox-macos-amd64.zip ./cloudfox/cloudfox
|
||||
rm -rf ./cloudfox/cloudfox
|
||||
|
||||
GOOS=darwin GOARCH=arm64 go build -o ./cloudfox/cloudfox .
|
||||
zip ./cloudfox/cloudfox-macos-arm64.zip ./cloudfox/cloudfox
|
||||
rm -rf ./cloudfox/cloudfox
|
||||
sha1sum ./cloudfox/cloudfox > ./cloudfox/sha1sum.txt
|
||||
zip ./cloudfox/cloudfox-macos-amd64.zip ./cloudfox/cloudfox ./cloudfox/sha1sum.txt
|
||||
rm -rf ./cloudfox/cloudfox ./cloudfox/sha1sum.txt
|
||||
|
||||
clean:
|
||||
GOOS=darwin GOARCH=arm64 go build -o ./cloudfox/cloudfox .
|
||||
sha1sum ./cloudfox/cloudfox > ./cloudfox/sha1sum.txt
|
||||
zip ./cloudfox/cloudfox-macos-arm64.zip ./cloudfox/cloudfox ./cloudfox/sha1sum.txt
|
||||
rm -rf ./cloudfox/cloudfox ./cloudfox/sha1sum.txt
|
||||
|
||||
clean:
|
||||
rm -rf ./cloudfox
|
||||
@@ -1,6 +1,10 @@
|
||||
# :fox_face: CloudFox :fox_face:
|
||||
CloudFox helps you gain situational awareness in unfamiliar cloud environments. It’s an open source command line tool created to help penetration testers and other offensive security professionals find exploitable attack paths in cloud infrastructure.
|
||||
|
||||
> **CLOUDFOX UPDATE REQUIRED: (12/2025):** If you are using `cloudfox`, you need to use v1.17.0 or greater. All earlier versions [stopped working after a format change in AWS's public service mapping file](https://github.com/BishopFox/cloudfox/pull/109).
|
||||
|
||||
**Overview**
|
||||
|
||||
#### CloudFox helps you answer the following common questions (and many more):
|
||||
|
||||
* What regions is this AWS account using and roughly how many resources are in the account?
|
||||
@@ -48,9 +52,9 @@ For the full documentation please refer to our [wiki](https://github.com/BishopF
|
||||
|
||||
| Provider| CloudFox Commands |
|
||||
| - | - |
|
||||
| AWS | 34 |
|
||||
| Azure | 4 |
|
||||
| GCP | 8 |
|
||||
| AWS | 34 |
|
||||
| Azure | 4 |
|
||||
| GCP | 60 |
|
||||
| Kubernetes | Support Planned |
|
||||
|
||||
|
||||
@@ -102,7 +106,53 @@ Additional policy notes (as of 09/2022):
|
||||
| `arn:aws:iam::aws:policy/AdministratorAccess` | This will work just fine with CloudFox, but if you were handed this level of access as a penetration tester, that should probably be a finding in itself :) |
|
||||
|
||||
### Azure
|
||||
* Viewer or similar permissions applied.
|
||||
* Viewer or similar permissions applied.
|
||||
|
||||
### GCP
|
||||
* Google Cloud SDK installed and authenticated
|
||||
* Application Default Credentials configured (`gcloud auth application-default login`)
|
||||
* Recommended permissions at appropriate hierarchy levels (see below)
|
||||
|
||||
#### GCP Permissions: Minimal vs Comprehensive
|
||||
|
||||
**Minimal Permissions (Single Project):**
|
||||
|
||||
For basic enumeration of a single project, the `roles/viewer` role provides read access to most resources (includes logging, monitoring, and compute/network viewing).
|
||||
|
||||
**Comprehensive Permissions (Organization-Wide):**
|
||||
|
||||
For thorough security assessments across an entire organization:
|
||||
|
||||
| Scope | Role | Purpose |
|
||||
| - | - | - |
|
||||
| **Organization** | `roles/resourcemanager.organizationViewer` | View organization structure and metadata |
|
||||
| **Organization** | `roles/iam.securityReviewer` | Review IAM policies across the organization |
|
||||
| **Organization** | `roles/cloudasset.viewer` | Query Cloud Asset Inventory for all resources |
|
||||
| **Organization** | `roles/cloudidentity.groupsViewer` | Enumerate Google Groups and memberships |
|
||||
| **Folder** | `roles/resourcemanager.folderViewer` | View folder hierarchy and metadata |
|
||||
| **Project** | `roles/viewer` | Read access to most project resources (includes logging.viewer, monitoring.viewer, compute.viewer) |
|
||||
| **Tooling Project** | `roles/serviceusage.serviceUsageAdmin` | (Optional) Manage API quotas for CloudFox operations |
|
||||
|
||||
> **Note:** The basic `roles/viewer` role includes permissions from `roles/logging.viewer`, `roles/monitoring.viewer`, and `roles/compute.networkViewer`, so these don't need to be granted separately.
|
||||
|
||||
#### GCP API Requirements
|
||||
|
||||
**APIs must be enabled in each project you want to assess.** GCP APIs are project-scoped.
|
||||
|
||||
| API | Service Name | Purpose |
|
||||
| - | - | - |
|
||||
| Cloud Identity API | `cloudidentity.googleapis.com` | Group enumeration, inherited role analysis |
|
||||
| Cloud Asset API | `cloudasset.googleapis.com` | Cross-project resource discovery |
|
||||
| Cloud Resource Manager API | `cloudresourcemanager.googleapis.com` | Organization mapping, IAM enumeration |
|
||||
| IAM API | `iam.googleapis.com` | IAM analysis, privilege escalation detection |
|
||||
| Compute Engine API | `compute.googleapis.com` | Instance enumeration, network security |
|
||||
| Secret Manager API | `secretmanager.googleapis.com` | Secrets enumeration |
|
||||
| Cloud Functions API | `cloudfunctions.googleapis.com` | Serverless enumeration |
|
||||
| Cloud Run API | `run.googleapis.com` | Serverless enumeration |
|
||||
| Kubernetes Engine API | `container.googleapis.com` | Container security analysis |
|
||||
| BigQuery API | `bigquery.googleapis.com` | Data security analysis |
|
||||
|
||||
For detailed setup instructions, see the [GCP Setup Guide](https://github.com/BishopFox/cloudfox/wiki/GCP-Setup-Guide).
|
||||
|
||||
# AWS Commands
|
||||
| Provider | Command Name | Description
|
||||
@@ -133,7 +183,7 @@ Additional policy notes (as of 09/2022):
|
||||
| AWS | [pmapper](https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#pmapper) | Looks for pmapper data stored on the local filesystem, [in the locations defined here](https://github.com/nccgroup/PMapper/wiki/Frequently-Asked-Questions#where-does-pmapper-store-its-data). If pmapper data has been found (you already ran `pmapper graph create`), then this command will use this data to build a graph in cloudfox memory let you know who can privesc to admin.
|
||||
| AWS | [principals](https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#principals) | Enumerates IAM users and Roles so you have the data at your fingertips. |
|
||||
| AWS | [ram](https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#ram) | List all resources in this account that are shared with other accounts, or resources from other accounts that are shared with this account. Useful for cross-account attack paths. |
|
||||
| AWS | [resource-trusts](https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#resource-trusts) | Looks through multiple services that support resource policies and helps you find any overly permissive resource trusts.|
|
||||
| AWS | [resource-trusts](https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#resource-trusts) | Looks through multiple services that support resource policies and helps you find any overly permissive resource trusts. KMS is supported but disabled by default. To include KMS resource policies in the output, add this flag to the command: `cloudfox aws resource-trusts --include-kms`.|
|
||||
| AWS | [role-trusts](https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#role-trusts) | Enumerates IAM role trust policies so you can look for overly permissive role trusts or find roles that trust a specific service. |
|
||||
| AWS | [route53](https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#route53) | Enumerate all records from all route53 managed zones. Use this for application and service enumeration. |
|
||||
| AWS | [secrets](https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#secrets) | List secrets from SecretsManager and SSM. Look for interesting secrets in the list and then see who has access to them using use `cloudfox iam-simulator` and/or `pmapper`. |
|
||||
@@ -155,22 +205,82 @@ Additional policy notes (as of 09/2022):
|
||||
|
||||
|
||||
# GCP Commands
|
||||
| Provider | Command Name | Description
|
||||
|
||||
For detailed documentation on each GCP command, see the [GCP Commands Wiki](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands).
|
||||
|
||||
| Provider | Command Name | Description
|
||||
| - | - | - |
|
||||
| GCP | [whoami](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#whoami) | Display the email address of the GCP authenticated user |
|
||||
| GCP | [all-checks](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#all-checks) | Runs all available GCP commands |
|
||||
| GCP | [artifact-registry](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#artifact-registry) | Display GCP artifact registry information |
|
||||
| GCP | [bigquery](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#bigquery) | Display Bigquery datasets and tables information |
|
||||
| GCP | [buckets](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#buckets) | Display GCP buckets information |
|
||||
| GCP | [iam](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#iam) | Display GCP IAM information |
|
||||
| GCP | [instances](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#instances) | Display GCP Compute Engine instances information |
|
||||
| GCP | [secrets](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#secrets) | Display GCP secrets information |
|
||||
| GCP | [whoami](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#whoami) | Display identity context for the authenticated GCP user/service account |
|
||||
| GCP | [iam](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#iam) | Enumerate GCP IAM principals across organizations, folders, and projects |
|
||||
| GCP | [permissions](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#permissions) | Enumerate ALL permissions for each IAM entity with full inheritance explosion |
|
||||
| GCP | [serviceaccounts](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#serviceaccounts) | Enumerate GCP service accounts with security analysis |
|
||||
| GCP | [service-agents](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#service-agents) | Enumerate Google-managed service agents |
|
||||
| GCP | [keys](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#keys) | Enumerate all GCP keys (SA keys, HMAC keys, API keys) |
|
||||
| GCP | [resource-iam](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#resource-iam) | Enumerate IAM policies on GCP resources (buckets, datasets, secrets, etc.) |
|
||||
| GCP | [domain-wide-delegation](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#domain-wide-delegation) | Find service accounts with Domain-Wide Delegation to Google Workspace |
|
||||
| GCP | [privesc](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#privesc) | Identify privilege escalation paths in GCP projects |
|
||||
| GCP | [hidden-admins](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#hidden-admins) | Identify principals who can modify IAM policies (hidden admins) |
|
||||
| GCP | [identity-federation](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#identity-federation) | Enumerate Workload Identity Federation (external identities) |
|
||||
| GCP | [instances](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#instances) | Enumerate GCP Compute Engine instances with security configuration |
|
||||
| GCP | [gke](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#gke) | Enumerate GKE clusters with security analysis |
|
||||
| GCP | [cloudrun](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#cloudrun) | Enumerate Cloud Run services and jobs with security analysis |
|
||||
| GCP | [functions](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#functions) | Enumerate GCP Cloud Functions with security analysis |
|
||||
| GCP | [app-engine](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#app-engine) | Enumerate App Engine applications and security configurations |
|
||||
| GCP | [composer](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#composer) | Enumerate Cloud Composer environments |
|
||||
| GCP | [dataproc](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#dataproc) | Enumerate Dataproc clusters |
|
||||
| GCP | [dataflow](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#dataflow) | Enumerate Dataflow jobs and pipelines |
|
||||
| GCP | [notebooks](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#notebooks) | Enumerate Vertex AI Workbench notebooks |
|
||||
| GCP | [workload-identity](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#workload-identity) | Enumerate GKE Workload Identity and Workload Identity Federation |
|
||||
| GCP | [inventory](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#inventory) | Quick resource inventory - works without Cloud Asset API |
|
||||
| GCP | [storage](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#storage) | Enumerate GCP Cloud Storage buckets with security configuration |
|
||||
| GCP | [storage-enum](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#storage-enum) | Enumerate GCS buckets for sensitive files (credentials, secrets, configs) |
|
||||
| GCP | [bigquery](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#bigquery) | Enumerate GCP BigQuery datasets and tables with security analysis |
|
||||
| GCP | [cloudsql](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#cloudsql) | Enumerate Cloud SQL instances with security analysis |
|
||||
| GCP | [spanner](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#spanner) | Enumerate Cloud Spanner instances and databases |
|
||||
| GCP | [bigtable](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#bigtable) | Enumerate Cloud Bigtable instances and tables |
|
||||
| GCP | [filestore](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#filestore) | Enumerate Filestore NFS instances |
|
||||
| GCP | [memorystore](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#memorystore) | Enumerate Memorystore (Redis) instances |
|
||||
| GCP | [vpc-networks](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#vpc-networks) | Enumerate VPC Networks |
|
||||
| GCP | [firewall](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#firewall) | Enumerate VPC networks and firewall rules with security analysis |
|
||||
| GCP | [loadbalancers](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#loadbalancers) | Enumerate Load Balancers |
|
||||
| GCP | [dns](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#dns) | Enumerate Cloud DNS zones and records with security analysis |
|
||||
| GCP | [endpoints](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#endpoints) | Enumerate all network endpoints (external and internal) with IPs, ports, and hostnames |
|
||||
| GCP | [private-service-connect](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#private-service-connect) | Enumerate Private Service Connect endpoints and service attachments |
|
||||
| GCP | [network-topology](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#network-topology) | Visualize VPC network topology, peering relationships, and trust boundaries |
|
||||
| GCP | [vpc-sc](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#vpc-sc) | Enumerate VPC Service Controls |
|
||||
| GCP | [access-levels](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#access-levels) | Enumerate Access Context Manager access levels |
|
||||
| GCP | [cloud-armor](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#cloud-armor) | Enumerate Cloud Armor security policies and find weaknesses |
|
||||
| GCP | [iap](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#iap) | Enumerate Identity-Aware Proxy configurations |
|
||||
| GCP | [beyondcorp](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#beyondcorp) | Enumerate BeyondCorp Enterprise configurations |
|
||||
| GCP | [kms](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#kms) | Enumerate Cloud KMS key rings and crypto keys with security analysis |
|
||||
| GCP | [secrets](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#secrets) | Enumerate GCP Secret Manager secrets with security configuration |
|
||||
| GCP | [cert-manager](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#cert-manager) | Enumerate SSL/TLS certificates and find expiring or misconfigured certs |
|
||||
| GCP | [org-policies](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#org-policies) | Enumerate organization policies and identify security weaknesses |
|
||||
| GCP | [artifact-registry](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#artifact-registry) | Enumerate GCP Artifact Registry and Container Registry with security configuration |
|
||||
| GCP | [cloudbuild](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#cloudbuild) | Enumerate Cloud Build triggers and builds |
|
||||
| GCP | [source-repos](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#source-repos) | Enumerate Cloud Source Repositories |
|
||||
| GCP | [scheduler](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#scheduler) | Enumerate Cloud Scheduler jobs with security analysis |
|
||||
| GCP | [pubsub](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#pubsub) | Enumerate Pub/Sub topics and subscriptions with security analysis |
|
||||
| GCP | [logging](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#logging) | Enumerate Cloud Logging sinks and metrics with security analysis |
|
||||
| GCP | [organizations](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#organizations) | Enumerate GCP organization hierarchy |
|
||||
| GCP | [asset-inventory](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#asset-inventory) | Enumerate Cloud Asset Inventory with optional dependency analysis |
|
||||
| GCP | [backup-inventory](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#backup-inventory) | Enumerate backup policies, protected resources, and identify backup gaps |
|
||||
| GCP | [lateral-movement](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#lateral-movement) | Map lateral movement paths, credential theft vectors, and pivot opportunities |
|
||||
| GCP | [data-exfiltration](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#data-exfiltration) | Identify data exfiltration paths, potential vectors, and missing security hardening |
|
||||
| GCP | [public-access](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#public-access) | Find resources with allUsers/allAuthenticatedUsers access across 16 GCP services |
|
||||
| GCP | [cross-project](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#cross-project) | Analyze cross-project IAM bindings, logging sinks, and Pub/Sub exports for lateral movement |
|
||||
| GCP | [foxmapper](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#foxmapper) | Run FoxMapper (graph-based IAM analysis) for privilege escalation path discovery |
|
||||
| GCP | [logging-enum](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#logging-enum) | Scan Cloud Logging entries for sensitive data (credentials, tokens, PII) |
|
||||
| GCP | [bigquery-enum](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#bigquery-enum) | Scan BigQuery datasets, tables, and columns for sensitive data indicators |
|
||||
| GCP | [bigtable-enum](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#bigtable-enum) | Scan Bigtable instances, tables, and column families for sensitive data indicators |
|
||||
| GCP | [spanner-enum](https://github.com/BishopFox/cloudfox/wiki/GCP-Commands#spanner-enum) | Scan Spanner database schemas for sensitive table and column names |
|
||||
|
||||
|
||||
|
||||
# Authors
|
||||
* [Carlos Vendramini](https://github.com/carlosvendramini-bf)
|
||||
* [Seth Art (@sethsec](https://twitter.com/sethsec))
|
||||
* Joseph Barcia
|
||||
|
||||
# Contributing
|
||||
[Wiki - How to Contribute](https://github.com/BishopFox/cloudfox/wiki#how-to-contribute)
|
||||
|
||||
+2
-2
@@ -161,7 +161,7 @@ func (m *AccessKeysModule) getAccessKeysForAllUsers() {
|
||||
ListUsers, err := sdk.CachedIamListUsers(m.IAMClient, aws.ToString(m.Caller.Account))
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
// added this to break out if there no users
|
||||
@@ -171,7 +171,7 @@ func (m *AccessKeysModule) getAccessKeysForAllUsers() {
|
||||
results, err := sdk.CachedIamListAccessKeys(m.IAMClient, aws.ToString(m.Caller.Account), aws.ToString(user.UserName))
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
+55
-50
@@ -30,6 +30,7 @@ type ApiGwModule struct {
|
||||
Goroutines int
|
||||
AWSProfile string
|
||||
WrapTable bool
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
// Main module data
|
||||
Gateways []ApiGateway
|
||||
@@ -178,27 +179,31 @@ func (m *ApiGwModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan
|
||||
// <-semaphore
|
||||
// }()
|
||||
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("apigateway", r)
|
||||
res, err := servicemap.IsServiceInRegion("api-gateway", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getAPIGatewayAPIsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getAPIGatewayVIPsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getAPIGatewayv2APIsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getAPIGatewayv2VIPsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -239,7 +244,7 @@ func (m *ApiGwModule) writeLoot(outputDirectory string, verbosity int) string {
|
||||
// err = os.WriteFile(f, []byte(out), 0644)
|
||||
// if err != nil {
|
||||
// m.modLog.Error(err.Error())
|
||||
// m.CommandCounter.Error++
|
||||
// m.CommandCounter.IncrError()
|
||||
// panic(err.Error())
|
||||
// }
|
||||
|
||||
@@ -258,8 +263,8 @@ func (m *ApiGwModule) writeLoot(outputDirectory string, verbosity int) string {
|
||||
|
||||
func (m *ApiGwModule) getAPIGatewayAPIsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan ApiGateway) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -267,21 +272,21 @@ func (m *ApiGwModule) getAPIGatewayAPIsPerRegion(r string, wg *sync.WaitGroup, s
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
|
||||
|
||||
Items, err := sdk.CachedApiGatewayGetRestAPIs(m.APIGatewayClient, aws.ToString(m.Caller.Account), r)
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
for _, api := range Items {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
for _, endpoint := range m.getEndpointsPerAPIGateway(r, api) {
|
||||
dataReceiver <- endpoint
|
||||
}
|
||||
@@ -290,8 +295,8 @@ func (m *ApiGwModule) getAPIGatewayAPIsPerRegion(r string, wg *sync.WaitGroup, s
|
||||
|
||||
func (m *ApiGwModule) getAPIGatewayVIPsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan ApiGateway) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -299,16 +304,16 @@ func (m *ApiGwModule) getAPIGatewayVIPsPerRegion(r string, wg *sync.WaitGroup, s
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
|
||||
|
||||
Items, err := sdk.CachedApiGatewayGetRestAPIs(m.APIGatewayClient, aws.ToString(m.Caller.Account), r)
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -317,7 +322,7 @@ func (m *ApiGwModule) getAPIGatewayVIPsPerRegion(r string, wg *sync.WaitGroup, s
|
||||
if err != nil {
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s", r))
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -329,7 +334,7 @@ func (m *ApiGwModule) getAPIGatewayVIPsPerRegion(r string, wg *sync.WaitGroup, s
|
||||
if err != nil {
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s", r))
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -342,7 +347,7 @@ func (m *ApiGwModule) getAPIGatewayVIPsPerRegion(r string, wg *sync.WaitGroup, s
|
||||
|
||||
for _, api := range Items {
|
||||
if api.Id != nil && aws.ToString(api.Id) == aws.ToString(mapping.RestApiId) {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
|
||||
endpoints := m.getEndpointsPerAPIGateway(r, api)
|
||||
for _, endpoint := range endpoints {
|
||||
@@ -371,8 +376,8 @@ func (m *ApiGwModule) getAPIGatewayVIPsPerRegion(r string, wg *sync.WaitGroup, s
|
||||
|
||||
func (m *ApiGwModule) getEndpointsPerAPIGateway(r string, api apigatewayTypes.RestApi) []ApiGateway {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
}()
|
||||
var gateways []ApiGateway
|
||||
|
||||
@@ -397,7 +402,7 @@ func (m *ApiGwModule) getEndpointsPerAPIGateway(r string, api apigatewayTypes.Re
|
||||
if err != nil {
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s", r))
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return gateways
|
||||
}
|
||||
|
||||
@@ -406,7 +411,7 @@ func (m *ApiGwModule) getEndpointsPerAPIGateway(r string, api apigatewayTypes.Re
|
||||
if err != nil {
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s", r))
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
for _, stage := range GetStages.Item {
|
||||
@@ -422,7 +427,7 @@ func (m *ApiGwModule) getEndpointsPerAPIGateway(r string, api apigatewayTypes.Re
|
||||
if err != nil {
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s", r))
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,8 +454,8 @@ func (m *ApiGwModule) getEndpointsPerAPIGateway(r string, api apigatewayTypes.Re
|
||||
|
||||
func (m *ApiGwModule) getAPIGatewayv2APIsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan ApiGateway) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -458,20 +463,20 @@ func (m *ApiGwModule) getAPIGatewayv2APIsPerRegion(r string, wg *sync.WaitGroup,
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
|
||||
|
||||
Items, err := sdk.CachedAPIGatewayv2GetAPIs(m.APIGatewayv2Client, aws.ToString(m.Caller.Account), r)
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
for _, api := range Items {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
for _, endpoint := range m.getEndpointsPerAPIGatewayv2(r, api) {
|
||||
dataReceiver <- endpoint
|
||||
}
|
||||
@@ -481,8 +486,8 @@ func (m *ApiGwModule) getAPIGatewayv2APIsPerRegion(r string, wg *sync.WaitGroup,
|
||||
|
||||
func (m *ApiGwModule) getAPIGatewayv2VIPsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan ApiGateway) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -490,16 +495,16 @@ func (m *ApiGwModule) getAPIGatewayv2VIPsPerRegion(r string, wg *sync.WaitGroup,
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
|
||||
|
||||
Items, err := sdk.CachedAPIGatewayv2GetAPIs(m.APIGatewayv2Client, aws.ToString(m.Caller.Account), r)
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -508,7 +513,7 @@ func (m *ApiGwModule) getAPIGatewayv2VIPsPerRegion(r string, wg *sync.WaitGroup,
|
||||
if err != nil {
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s", r))
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
for _, item := range GetDomainNames {
|
||||
@@ -519,7 +524,7 @@ func (m *ApiGwModule) getAPIGatewayv2VIPsPerRegion(r string, wg *sync.WaitGroup,
|
||||
if err != nil {
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s", r))
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -532,7 +537,7 @@ func (m *ApiGwModule) getAPIGatewayv2VIPsPerRegion(r string, wg *sync.WaitGroup,
|
||||
|
||||
for _, api := range Items {
|
||||
if api.ApiId != nil && aws.ToString(api.ApiId) == aws.ToString(mapping.ApiId) {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
endpoints := m.getEndpointsPerAPIGatewayv2(r, api)
|
||||
for _, endpoint := range endpoints {
|
||||
var old string
|
||||
@@ -563,8 +568,8 @@ func (m *ApiGwModule) getAPIGatewayv2VIPsPerRegion(r string, wg *sync.WaitGroup,
|
||||
|
||||
func (m *ApiGwModule) getEndpointsPerAPIGatewayv2(r string, api apigatewayV2Types.Api) []ApiGateway {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
}()
|
||||
|
||||
var gateways []ApiGateway
|
||||
@@ -583,7 +588,7 @@ func (m *ApiGwModule) getEndpointsPerAPIGatewayv2(r string, api apigatewayV2Type
|
||||
if err != nil {
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s", r))
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
for _, stage := range GetStages {
|
||||
@@ -598,7 +603,7 @@ func (m *ApiGwModule) getEndpointsPerAPIGatewayv2(r string, api apigatewayV2Type
|
||||
if err != nil {
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s", r))
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
for _, stage := range stages {
|
||||
@@ -638,7 +643,7 @@ func (m *ApiGwModule) ApiGatewayApiKeyRequired(r string, ApiId *string, Resource
|
||||
if err != nil {
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s", r))
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
} else {
|
||||
return aws.ToBool(GetMethod.ApiKeyRequired)
|
||||
}
|
||||
|
||||
@@ -10,11 +10,17 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sts"
|
||||
"github.com/aws/smithy-go/ptr"
|
||||
"github.com/bishopfox/awsservicemap"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
func TestApiGw(t *testing.T) {
|
||||
|
||||
// Create a service map that downloads from AWS
|
||||
serviceMap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
|
||||
m := ApiGwModule{
|
||||
AWSProfile: "unittesting",
|
||||
AWSRegions: []string{"us-east-1"},
|
||||
@@ -26,6 +32,7 @@ func TestApiGw(t *testing.T) {
|
||||
WrapTable: false,
|
||||
APIGatewayClient: &sdk.MockedAWSAPIGatewayClient{},
|
||||
APIGatewayv2Client: &sdk.MockedAWSAPIGatewayv2Client{},
|
||||
ServiceMap: serviceMap,
|
||||
}
|
||||
|
||||
fs := internal.MockFileSystem(true)
|
||||
|
||||
+7
-7
@@ -88,7 +88,7 @@ func (m *BucketsModule) PrintBuckets(outputDirectory string, verbosity int) {
|
||||
go m.Receiver(dataReceiver, receiverDone)
|
||||
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(wg, semaphore, dataReceiver)
|
||||
|
||||
wg.Wait()
|
||||
@@ -203,11 +203,11 @@ func (m *BucketsModule) Receiver(receiver chan BucketRow, receiverDone chan bool
|
||||
func (m *BucketsModule) executeChecks(wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan BucketRow) {
|
||||
defer wg.Done()
|
||||
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.createBucketsRows(m.output.Verbosity, wg, semaphore, dataReceiver)
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
}
|
||||
|
||||
func (m *BucketsModule) writeLoot(outputDirectory string, verbosity int, profile string) {
|
||||
@@ -256,8 +256,8 @@ func (m *BucketsModule) writeLoot(outputDirectory string, verbosity int, profile
|
||||
|
||||
func (m *BucketsModule) createBucketsRows(verbosity int, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan BucketRow) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -357,7 +357,7 @@ func (m *BucketsModule) storeAccessPolicy(bucket *BucketRow) {
|
||||
|
||||
if err := m.storeFile(f, bucket.PolicyJSON); err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/sns"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sts"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func InitIamCommandClient(iamSimPPClient sdk.AWSIAMClientInterface, caller sts.GetCallerIdentityOutput, AWSProfile string, Goroutines int) IamSimulatorModule {
|
||||
@@ -26,6 +27,7 @@ func InitIamCommandClient(iamSimPPClient sdk.AWSIAMClientInterface, caller sts.G
|
||||
Caller: caller,
|
||||
AWSProfileProvided: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
modLog: internal.TxtLog.WithFields(logrus.Fields{"module": "iam-simulator"}),
|
||||
}
|
||||
|
||||
return iamSimMod
|
||||
|
||||
+15
-10
@@ -28,6 +28,7 @@ type CloudformationModule struct {
|
||||
WrapTable bool
|
||||
AWSOutputType string
|
||||
AWSTableCols string
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
// Main module data
|
||||
CFStacks []CFStack
|
||||
@@ -79,7 +80,7 @@ func (m *CloudformationModule) PrintCloudformationStacks(outputDirectory string,
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, semaphore, dataReceiver)
|
||||
|
||||
}
|
||||
@@ -192,8 +193,12 @@ func (m *CloudformationModule) PrintCloudformationStacks(outputDirectory string,
|
||||
|
||||
func (m *CloudformationModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan CFStack) {
|
||||
defer wg.Done()
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
serviceRegions, err := servicemap.GetRegionsForService("cloudformation")
|
||||
if err != nil {
|
||||
@@ -201,7 +206,7 @@ func (m *CloudformationModule) executeChecks(r string, wg *sync.WaitGroup, semap
|
||||
}
|
||||
for _, serviceRegion := range serviceRegions {
|
||||
if r == serviceRegion {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.createCFStackRowsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -226,7 +231,7 @@ func (m *CloudformationModule) writeLoot(outputDirectory string, verbosity int)
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
pullFile := filepath.Join(path, "cloudformation-data.txt")
|
||||
|
||||
@@ -266,7 +271,7 @@ func (m *CloudformationModule) writeLoot(outputDirectory string, verbosity int)
|
||||
err = os.WriteFile(pullFile, []byte(out), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
if verbosity > 2 {
|
||||
@@ -283,8 +288,8 @@ func (m *CloudformationModule) writeLoot(outputDirectory string, verbosity int)
|
||||
|
||||
func (m *CloudformationModule) createCFStackRowsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan CFStack) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -297,7 +302,7 @@ func (m *CloudformationModule) createCFStackRowsPerRegion(r string, wg *sync.Wai
|
||||
DescribeStacks, err := sdk.CachedCloudFormationDescribeStacks(m.CloudFormationClient, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
for _, stack := range DescribeStacks {
|
||||
@@ -309,7 +314,7 @@ func (m *CloudformationModule) createCFStackRowsPerRegion(r string, wg *sync.Wai
|
||||
stackTemplateBody, err = sdk.CachedCloudFormationGetTemplate(m.CloudFormationClient, aws.ToString(m.Caller.Account), r, stackName)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
dataReceiver <- CFStack{
|
||||
|
||||
+9
-4
@@ -29,6 +29,7 @@ type CodeBuildModule struct {
|
||||
AWSProfile string
|
||||
SkipAdminCheck bool
|
||||
WrapTable bool
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
pmapperMod PmapperModule
|
||||
pmapperError error
|
||||
iamSimClient IamSimulatorModule
|
||||
@@ -86,7 +87,7 @@ func (m *CodeBuildModule) PrintCodeBuildProjects(outputDirectory string, verbosi
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, semaphore, dataReceiver)
|
||||
|
||||
}
|
||||
@@ -211,15 +212,19 @@ func (m *CodeBuildModule) PrintCodeBuildProjects(outputDirectory string, verbosi
|
||||
func (m *CodeBuildModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Project) {
|
||||
defer wg.Done()
|
||||
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("codebuild", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getcodeBuildProjectsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
|
||||
+32
-27
@@ -30,6 +30,7 @@ type DatabasesModule struct {
|
||||
Goroutines int
|
||||
AWSProfile string
|
||||
WrapTable bool
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
Databases []Database
|
||||
CommandCounter internal.CommandCounter
|
||||
@@ -231,8 +232,12 @@ func (m *DatabasesModule) Receiver(receiver chan Database, receiverDone chan boo
|
||||
func (m *DatabasesModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Database) {
|
||||
defer wg.Done()
|
||||
|
||||
serviceMap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
serviceMap := m.ServiceMap
|
||||
if serviceMap == nil {
|
||||
serviceMap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
m.executeRdsCheck(r, wg, semaphore, dataReceiver, serviceMap) // Also returns Neptune and DocDB
|
||||
m.executeRedshiftCheck(r, wg, semaphore, dataReceiver, serviceMap)
|
||||
@@ -255,7 +260,7 @@ func (m *DatabasesModule) executeCheck(check check) {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
check.wg.Add(1)
|
||||
go check.executor(check.region, check.wg, check.semaphore, check.dataReceiver)
|
||||
}
|
||||
@@ -319,7 +324,7 @@ func (m *DatabasesModule) writeLoot(outputDirectory string, verbosity int) strin
|
||||
// err = os.WriteFile(f, []byte(out), 0644)
|
||||
// if err != nil {
|
||||
// m.modLog.Error(err.Error())
|
||||
// m.CommandCounter.Error++
|
||||
// m.CommandCounter.IncrError()
|
||||
// panic(err.Error())
|
||||
// }
|
||||
|
||||
@@ -338,8 +343,8 @@ func (m *DatabasesModule) writeLoot(outputDirectory string, verbosity int) strin
|
||||
|
||||
func (m *DatabasesModule) getRdsClustersPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Database) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -347,13 +352,13 @@ func (m *DatabasesModule) getRdsClustersPerRegion(r string, wg *sync.WaitGroup,
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
|
||||
DBClusters, err := sdk.CachedRDSDescribeDBClusters(m.RDSClient, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -406,8 +411,8 @@ func (m *DatabasesModule) getRdsClustersPerRegion(r string, wg *sync.WaitGroup,
|
||||
|
||||
func (m *DatabasesModule) getRdsInstancesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Database) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -415,14 +420,14 @@ func (m *DatabasesModule) getRdsInstancesPerRegion(r string, wg *sync.WaitGroup,
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
|
||||
DBInstances, err := sdk.CachedRDSDescribeDBInstances(m.RDSClient, aws.ToString(m.Caller.Account), r)
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -475,8 +480,8 @@ func (m *DatabasesModule) getRdsInstancesPerRegion(r string, wg *sync.WaitGroup,
|
||||
|
||||
func (m *DatabasesModule) getRedshiftDatabasesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Database) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -484,9 +489,9 @@ func (m *DatabasesModule) getRedshiftDatabasesPerRegion(r string, wg *sync.WaitG
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
awsService := "Redshift"
|
||||
protocol := "https"
|
||||
|
||||
@@ -497,7 +502,7 @@ func (m *DatabasesModule) getRedshiftDatabasesPerRegion(r string, wg *sync.WaitG
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -527,8 +532,8 @@ func (m *DatabasesModule) getRedshiftDatabasesPerRegion(r string, wg *sync.WaitG
|
||||
|
||||
func (m *DatabasesModule) getDynamoDBTablesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Database) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -536,15 +541,15 @@ func (m *DatabasesModule) getDynamoDBTablesPerRegion(r string, wg *sync.WaitGrou
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
awsService := "DynamoDB"
|
||||
|
||||
Tables, err := sdk.CachedDynamoDBListTables(m.DynamoDBClient, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -553,7 +558,7 @@ func (m *DatabasesModule) getDynamoDBTablesPerRegion(r string, wg *sync.WaitGrou
|
||||
TableOutput, err := sdk.CachedDynamoDBDescribeTable(m.DynamoDBClient, aws.ToString(m.Caller.Account), r, table)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
size := aws.ToInt64(TableOutput.TableSizeBytes)
|
||||
|
||||
@@ -31,7 +31,8 @@ type DirectoryModule struct {
|
||||
AWSProfileProvided string
|
||||
AWSProfileStub string
|
||||
CloudFoxVersion string
|
||||
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
Directories []Directory
|
||||
CommandCounter internal.CommandCounter
|
||||
output internal.OutputData2
|
||||
@@ -83,7 +84,7 @@ func (m *DirectoryModule) PrintDirectories(outputDirectory string, verbosity int
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, semaphore, dataReceiver)
|
||||
|
||||
}
|
||||
@@ -190,15 +191,19 @@ func (m *DirectoryModule) PrintDirectories(outputDirectory string, verbosity int
|
||||
func (m *DirectoryModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Directory) {
|
||||
defer wg.Done()
|
||||
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("clouddirectory", r)
|
||||
res, err := servicemap.IsServiceInRegion("directoryservice", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getDirectoriesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -218,8 +223,8 @@ func (m *DirectoryModule) Receiver(receiver chan Directory, receiverDone chan bo
|
||||
}
|
||||
func (m *DirectoryModule) getDirectoriesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Directory) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
|
||||
+18
-13
@@ -30,6 +30,7 @@ type ECRModule struct {
|
||||
Goroutines int
|
||||
AWSProfile string
|
||||
WrapTable bool
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
// Main module data
|
||||
Repositories []Repository
|
||||
@@ -83,7 +84,7 @@ func (m *ECRModule) PrintECR(outputDirectory string, verbosity int) {
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, semaphore, dataReceiver)
|
||||
|
||||
}
|
||||
@@ -197,15 +198,19 @@ func (m *ECRModule) PrintECR(outputDirectory string, verbosity int) {
|
||||
func (m *ECRModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Repository) {
|
||||
defer wg.Done()
|
||||
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("ecr", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getECRRecordsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -229,7 +234,7 @@ func (m *ECRModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
pullFile := filepath.Join(path, "ecr-pull-commands.txt")
|
||||
|
||||
@@ -254,7 +259,7 @@ func (m *ECRModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
err = os.WriteFile(pullFile, []byte(out), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
if verbosity > 2 {
|
||||
@@ -272,8 +277,8 @@ func (m *ECRModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
|
||||
func (m *ECRModule) getECRRecordsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Repository) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -285,7 +290,7 @@ func (m *ECRModule) getECRRecordsPerRegion(r string, wg *sync.WaitGroup, semapho
|
||||
Repositories, err := sdk.CachedECRDescribeRepositories(m.ECRClient, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -297,7 +302,7 @@ func (m *ECRModule) getECRRecordsPerRegion(r string, wg *sync.WaitGroup, semapho
|
||||
images, err := sdk.CachedECRDescribeImages(m.ECRClient, aws.ToString(m.Caller.Account), r, repoName)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -345,7 +350,7 @@ func (m *ECRModule) getECRRecordsPerRegion(r string, wg *sync.WaitGroup, semapho
|
||||
// var repositories []types.Repository
|
||||
// Repositories, err := sdk.CachedECRDescribeRepositories(m.ECRClient, aws.ToString(m.Caller.Account), r)
|
||||
// if err != nil {
|
||||
// m.CommandCounter.Error++
|
||||
// m.CommandCounter.IncrError()
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
@@ -359,7 +364,7 @@ func (m *ECRModule) getECRRecordsPerRegion(r string, wg *sync.WaitGroup, semapho
|
||||
|
||||
// ImageDetails, err := sdk.CachedECRDescribeImages(m.ECRClient, aws.ToString(m.Caller.Account), r, repoName)
|
||||
// if err != nil {
|
||||
// m.CommandCounter.Error++
|
||||
// m.CommandCounter.IncrError()
|
||||
// return nil, err
|
||||
// }
|
||||
// images = append(images, ImageDetails...)
|
||||
@@ -370,7 +375,7 @@ func (m *ECRModule) getECRRepositoryPolicy(r string, repository string) (policy.
|
||||
var repoPolicy policy.Policy
|
||||
Policy, err := sdk.CachedECRGetRepositoryPolicy(m.ECRClient, aws.ToString(m.Caller.Account), r, repository)
|
||||
if err != nil {
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return repoPolicy, err
|
||||
}
|
||||
repoPolicy, err = policy.ParseJSONPolicy([]byte(Policy))
|
||||
|
||||
+43
-22
@@ -35,6 +35,7 @@ type ECSTasksModule struct {
|
||||
Goroutines int
|
||||
SkipAdminCheck bool
|
||||
WrapTable bool
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
pmapperMod PmapperModule
|
||||
pmapperError error
|
||||
iamSimClient IamSimulatorModule
|
||||
@@ -50,6 +51,7 @@ type MappedECSTask struct {
|
||||
Cluster string
|
||||
TaskDefinitionName string
|
||||
TaskDefinitionContent string
|
||||
ContainerName string
|
||||
LaunchType string
|
||||
ID string
|
||||
ExternalIP string
|
||||
@@ -98,7 +100,7 @@ func (m *ECSTasksModule) ECSTasks(outputDirectory string, verbosity int) {
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, dataReceiver)
|
||||
|
||||
}
|
||||
@@ -145,6 +147,7 @@ func (m *ECSTasksModule) printECSTaskData(outputDirectory string, dataReceiver c
|
||||
"Account",
|
||||
"Cluster",
|
||||
"TaskDefinition",
|
||||
"ContainerName",
|
||||
"LaunchType",
|
||||
"ID",
|
||||
"External IP",
|
||||
@@ -171,6 +174,7 @@ func (m *ECSTasksModule) printECSTaskData(outputDirectory string, dataReceiver c
|
||||
"Account",
|
||||
"Cluster",
|
||||
"TaskDefinition",
|
||||
"ContainerName",
|
||||
"LaunchType",
|
||||
"ID",
|
||||
"External IP",
|
||||
@@ -184,6 +188,7 @@ func (m *ECSTasksModule) printECSTaskData(outputDirectory string, dataReceiver c
|
||||
tableCols = []string{
|
||||
"Cluster",
|
||||
"TaskDefinition",
|
||||
"ContainerName",
|
||||
"LaunchType",
|
||||
"External IP",
|
||||
"Internal IP",
|
||||
@@ -206,6 +211,7 @@ func (m *ECSTasksModule) printECSTaskData(outputDirectory string, dataReceiver c
|
||||
aws.ToString(m.Caller.Account),
|
||||
ecsTask.Cluster,
|
||||
ecsTask.TaskDefinitionName,
|
||||
ecsTask.ContainerName,
|
||||
ecsTask.LaunchType,
|
||||
ecsTask.ID,
|
||||
ecsTask.ExternalIP,
|
||||
@@ -248,7 +254,7 @@ func (m *ECSTasksModule) writeLoot(outputDirectory string) {
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
privateIPsFilename := filepath.Join(path, "ecs-tasks-PrivateIPs.txt")
|
||||
publicIPsFilename := filepath.Join(path, "ecs-tasks-PublicIPs.txt")
|
||||
@@ -268,12 +274,12 @@ func (m *ECSTasksModule) writeLoot(outputDirectory string) {
|
||||
err = os.WriteFile(privateIPsFilename, []byte(privateIPs), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
err = os.WriteFile(publicIPsFilename, []byte(publicIPs), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
for _, task := range m.MappedECSTasks {
|
||||
@@ -282,14 +288,14 @@ func (m *ECSTasksModule) writeLoot(outputDirectory string) {
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
taskDefinitionFilename := filepath.Join(path, task.TaskDefinitionName+".json")
|
||||
|
||||
err = os.WriteFile(taskDefinitionFilename, []byte(task.TaskDefinitionContent), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,8 +308,12 @@ func (m *ECSTasksModule) writeLoot(outputDirectory string) {
|
||||
func (m *ECSTasksModule) executeChecks(r string, wg *sync.WaitGroup, dataReceiver chan MappedECSTask) {
|
||||
defer wg.Done()
|
||||
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("ecs", r)
|
||||
if err != nil {
|
||||
@@ -311,12 +321,12 @@ func (m *ECSTasksModule) executeChecks(r string, wg *sync.WaitGroup, dataReceive
|
||||
}
|
||||
if res {
|
||||
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
m.getListClusters(r, dataReceiver)
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,7 +335,7 @@ func (m *ECSTasksModule) getListClusters(region string, dataReceiver chan Mapped
|
||||
ClusterArns, err := sdk.CachedECSListClusters(m.ECSClient, aws.ToString(m.Caller.Account), region)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -339,7 +349,7 @@ func (m *ECSTasksModule) getListTasks(clusterARN string, region string, dataRece
|
||||
TaskArns, err := sdk.CachedECSListTasks(m.ECSClient, aws.ToString(m.Caller.Account), region, clusterARN)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -364,11 +374,11 @@ func (m *ECSTasksModule) loadTasksData(clusterARN string, taskARNs []string, reg
|
||||
Tasks, err := sdk.CachedECSDescribeTasks(m.ECSClient, aws.ToString(m.Caller.Account), region, clusterARN, taskARNs)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
eniIDs := []string{}
|
||||
var eniIDs []string
|
||||
for _, task := range Tasks {
|
||||
eniID := getElasticNetworkInterfaceIDOfECSTask(task)
|
||||
if eniID != "" {
|
||||
@@ -378,7 +388,7 @@ func (m *ECSTasksModule) loadTasksData(clusterARN string, taskARNs []string, reg
|
||||
publicIPs, err := m.loadPublicIPs(eniIDs, region)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -387,13 +397,14 @@ func (m *ECSTasksModule) loadTasksData(clusterARN string, taskARNs []string, reg
|
||||
taskDefinition, err := sdk.CachedECSDescribeTaskDefinition(m.ECSClient, aws.ToString(m.Caller.Account), region, aws.ToString(task.TaskDefinitionArn))
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
mappedTask := MappedECSTask{
|
||||
Cluster: getNameFromARN(clusterARN),
|
||||
TaskDefinitionName: getNameFromARN(aws.ToString(task.TaskDefinitionArn)),
|
||||
TaskDefinitionContent: getTaskDefinitionContent(taskDefinition),
|
||||
ContainerName: getContainerNamesFromECSTask(task),
|
||||
LaunchType: string(task.LaunchType),
|
||||
ID: getIDFromECSTask(aws.ToString(task.TaskArn)),
|
||||
PrivateIP: getPrivateIPv4AddressFromECSTask(task),
|
||||
@@ -435,7 +446,7 @@ func (m *ECSTasksModule) describeTaskDefinition(taskDefinitionArn string, region
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return types.TaskDefinition{}, err
|
||||
}
|
||||
return *DescribeTaskDefinition.TaskDefinition, nil
|
||||
@@ -455,7 +466,7 @@ func (m *ECSTasksModule) loadAllPublicIPs(eniIDs []string, region string) (map[s
|
||||
publicIPs, err := m.loadPublicIPs(eniIDs[i:j], region)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return nil, fmt.Errorf("getting elastic network interfaces: %s", err)
|
||||
}
|
||||
|
||||
@@ -505,8 +516,18 @@ func getIDFromECSTask(arn string) string {
|
||||
return tokens[2]
|
||||
}
|
||||
|
||||
func getContainerNamesFromECSTask(task types.Task) string {
|
||||
var names []string
|
||||
|
||||
for _, container := range task.Containers {
|
||||
names = append(names, aws.ToString(container.Name))
|
||||
}
|
||||
|
||||
return strings.Join(names, "|")
|
||||
}
|
||||
|
||||
func getPrivateIPv4AddressFromECSTask(task types.Task) string {
|
||||
ips := []string{}
|
||||
var ips []string
|
||||
|
||||
for _, attachment := range task.Attachments {
|
||||
if aws.ToString(attachment.Type) != "ElasticNetworkInterface" || aws.ToString(attachment.Status) != "ATTACHED" {
|
||||
|
||||
@@ -23,7 +23,6 @@ func TestECSTasks(t *testing.T) {
|
||||
outputDirectory: ".",
|
||||
verbosity: 2,
|
||||
testModule: ECSTasksModule{
|
||||
|
||||
AWSProfile: "default",
|
||||
AWSRegions: []string{"us-east-1", "us-west-1"},
|
||||
Caller: sts.GetCallerIdentityOutput{Arn: aws.String("arn:aws:iam::123456789012:user/cloudfox_unit_tests")},
|
||||
@@ -33,10 +32,11 @@ func TestECSTasks(t *testing.T) {
|
||||
ECSClient: &sdk.MockedECSClient{},
|
||||
},
|
||||
expectedResult: []MappedECSTask{{
|
||||
Cluster: "MyCluster",
|
||||
ID: "74de0355a10a4f979ac495c14EXAMPLE",
|
||||
ExternalIP: "203.0.113.12",
|
||||
Role: "test123",
|
||||
Cluster: "MyCluster",
|
||||
ID: "74de0355a10a4f979ac495c14EXAMPLE",
|
||||
ContainerName: "web",
|
||||
ExternalIP: "203.0.113.12",
|
||||
Role: "test123",
|
||||
}},
|
||||
},
|
||||
}
|
||||
@@ -48,6 +48,9 @@ func TestECSTasks(t *testing.T) {
|
||||
if expectedTask.Cluster != subtest.testModule.MappedECSTasks[index].Cluster {
|
||||
log.Fatal("Cluster name does not match expected value")
|
||||
}
|
||||
if expectedTask.ContainerName != subtest.testModule.MappedECSTasks[index].ContainerName {
|
||||
log.Fatal("Container name does not match expected value")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
+16
-11
@@ -32,6 +32,7 @@ type EKSModule struct {
|
||||
AWSProfile string
|
||||
SkipAdminCheck bool
|
||||
WrapTable bool
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
pmapperMod PmapperModule
|
||||
pmapperError error
|
||||
iamSimClient IamSimulatorModule
|
||||
@@ -101,7 +102,7 @@ func (m *EKSModule) EKS(outputDirectory string, verbosity int) {
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, semaphore, dataReceiver)
|
||||
|
||||
}
|
||||
@@ -242,15 +243,19 @@ func (m *EKSModule) EKS(outputDirectory string, verbosity int) {
|
||||
func (m *EKSModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Cluster) {
|
||||
defer wg.Done()
|
||||
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("eks", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getEKSRecordsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -274,7 +279,7 @@ func (m *EKSModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
pullFile := filepath.Join(path, "eks-kubeconfig-commands.txt")
|
||||
|
||||
@@ -298,7 +303,7 @@ func (m *EKSModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
err = os.WriteFile(pullFile, []byte(out), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
if verbosity > 2 {
|
||||
@@ -317,8 +322,8 @@ func (m *EKSModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
|
||||
func (m *EKSModule) getEKSRecordsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Cluster) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -331,7 +336,7 @@ func (m *EKSModule) getEKSRecordsPerRegion(r string, wg *sync.WaitGroup, semapho
|
||||
clusters, err := sdk.CachedEKSListClusters(m.EKSClient, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -342,7 +347,7 @@ func (m *EKSModule) getEKSRecordsPerRegion(r string, wg *sync.WaitGroup, semapho
|
||||
clusterDetails, err := sdk.CachedEKSDescribeCluster(m.EKSClient, aws.ToString(m.Caller.Account), clusterName, r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
//nodeGroups = append(nodeGroups, DescribeCluster.Cluster.)
|
||||
@@ -368,7 +373,7 @@ func (m *EKSModule) getEKSRecordsPerRegion(r string, wg *sync.WaitGroup, semapho
|
||||
nodeGroupDetails, err := sdk.CachedEKSDescribeNodeGroup(m.EKSClient, aws.ToString(m.Caller.Account), clusterName, nodeGroup, r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
role = aws.ToString(nodeGroupDetails.NodeRole)
|
||||
|
||||
@@ -27,6 +27,7 @@ type ElasticNetworkInterfacesModule struct {
|
||||
|
||||
AWSProfile string
|
||||
WrapTable bool
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
MappedENIs []MappedENI
|
||||
CommandCounter internal.CommandCounter
|
||||
@@ -72,7 +73,7 @@ func (m *ElasticNetworkInterfacesModule) ElasticNetworkInterfaces(outputDirector
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, dataReceiver)
|
||||
|
||||
}
|
||||
@@ -198,7 +199,7 @@ func (m *ElasticNetworkInterfacesModule) writeLoot(outputDirectory string) {
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
privateIPsFilename := filepath.Join(path, "elastic-network-interfaces-PrivateIPs.txt")
|
||||
publicIPsFilename := filepath.Join(path, "elastic-network-interfaces-PublicIPs.txt")
|
||||
@@ -218,12 +219,12 @@ func (m *ElasticNetworkInterfacesModule) writeLoot(outputDirectory string) {
|
||||
err = os.WriteFile(privateIPsFilename, []byte(privateIPs), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
err = os.WriteFile(publicIPsFilename, []byte(publicIPs), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
fmt.Printf("[%s][%s] Loot written to [%s]\n", cyan(m.output.CallingModule), cyan(m.AWSProfile), privateIPsFilename)
|
||||
@@ -233,20 +234,24 @@ func (m *ElasticNetworkInterfacesModule) writeLoot(outputDirectory string) {
|
||||
|
||||
func (m *ElasticNetworkInterfacesModule) executeChecks(r string, wg *sync.WaitGroup, dataReceiver chan MappedENI) {
|
||||
defer wg.Done()
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("ec2", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
m.getDescribeNetworkInterfaces(r, dataReceiver)
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +272,7 @@ func (m *ElasticNetworkInterfacesModule) getDescribeNetworkInterfaces(region str
|
||||
// )
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+184
-189
@@ -63,6 +63,7 @@ type EndpointsModule struct {
|
||||
Goroutines int
|
||||
AWSProfile string
|
||||
WrapTable bool
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
// Main module data
|
||||
Endpoints []Endpoint
|
||||
@@ -105,7 +106,7 @@ func (m *EndpointsModule) PrintEndpoints(outputDirectory string, verbosity int)
|
||||
semaphore := make(chan struct{}, m.Goroutines)
|
||||
// Create a channel to signal the spinner aka task status goroutine to finish
|
||||
spinnerDone := make(chan bool)
|
||||
//fire up the the task status spinner/updated
|
||||
//fire up the task status spinner/updated
|
||||
go internal.SpinUntil(m.output.CallingModule, &m.CommandCounter, spinnerDone, "tasks")
|
||||
|
||||
//create a channel to receive the objects
|
||||
@@ -267,15 +268,19 @@ func (m *EndpointsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore
|
||||
// <-semaphore
|
||||
// }()
|
||||
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("lambda", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getLambdaFunctionsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -284,25 +289,25 @@ func (m *EndpointsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getEksClustersPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
res, err = servicemap.IsServiceInRegion("mq", r)
|
||||
res, err = servicemap.IsServiceInRegion("amazon-mq", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getMqBrokersPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
res, err = servicemap.IsServiceInRegion("es", r)
|
||||
res, err = servicemap.IsServiceInRegion("opensearch-service", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getOpenSearchPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -311,41 +316,41 @@ func (m *EndpointsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getGrafanaEndPointsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
res, err = servicemap.IsServiceInRegion("elb", r)
|
||||
res, err = servicemap.IsServiceInRegion("elasticloadbalancing", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getELBv2ListenersPerRegion(r, wg, semaphore, dataReceiver)
|
||||
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getELBListenersPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
res, err = servicemap.IsServiceInRegion("apigateway", r)
|
||||
res, err = servicemap.IsServiceInRegion("api-gateway", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getAPIGatewayAPIsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getAPIGatewayVIPsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getAPIGatewayv2APIsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getAPIGatewayv2VIPsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -354,7 +359,7 @@ func (m *EndpointsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getRdsClustersPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -363,22 +368,22 @@ func (m *EndpointsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getRedshiftEndPointsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
|
||||
//apprunner is not supported by the aws json so we have to call it in every region
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getAppRunnerEndpointsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
|
||||
res, err = servicemap.IsServiceInRegion("lightsail", r)
|
||||
res, err = servicemap.IsServiceInRegion("amazonlightsail.com", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getLightsailContainerEndpointsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -389,7 +394,7 @@ func (m *EndpointsModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
panic(err.Error())
|
||||
}
|
||||
f := filepath.Join(path, "endpoints-UrlsOnly.txt")
|
||||
@@ -403,7 +408,7 @@ func (m *EndpointsModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
err = os.WriteFile(f, []byte(out), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
@@ -420,8 +425,8 @@ func (m *EndpointsModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
|
||||
func (m *EndpointsModule) getLambdaFunctionsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -429,16 +434,16 @@ func (m *EndpointsModule) getLambdaFunctionsPerRegion(r string, wg *sync.WaitGro
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
|
||||
var public string
|
||||
|
||||
Functions, err := sdk.CachedLambdaListFunctions(m.LambdaClient, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -447,10 +452,10 @@ func (m *EndpointsModule) getLambdaFunctionsPerRegion(r string, wg *sync.WaitGro
|
||||
FunctionDetails, err := sdk.CachedLambdaGetFunctionUrlConfig(m.LambdaClient, aws.ToString(m.Caller.Account), r, name)
|
||||
if err != nil {
|
||||
if errors.As(err, &oe) {
|
||||
m.Errors = append(m.Errors, (fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())))
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
endpoint := aws.ToString(FunctionDetails.FunctionUrl)
|
||||
@@ -477,8 +482,8 @@ func (m *EndpointsModule) getLambdaFunctionsPerRegion(r string, wg *sync.WaitGro
|
||||
|
||||
func (m *EndpointsModule) getEksClustersPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -486,15 +491,15 @@ func (m *EndpointsModule) getEksClustersPerRegion(r string, wg *sync.WaitGroup,
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
|
||||
|
||||
Clusters, err := sdk.CachedEKSListClusters(m.EKSClient, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -503,10 +508,10 @@ func (m *EndpointsModule) getEksClustersPerRegion(r string, wg *sync.WaitGroup,
|
||||
|
||||
if err != nil {
|
||||
if errors.As(err, &oe) {
|
||||
m.Errors = append(m.Errors, (fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())))
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
var endpoint string
|
||||
@@ -540,8 +545,8 @@ func (m *EndpointsModule) getEksClustersPerRegion(r string, wg *sync.WaitGroup,
|
||||
|
||||
func (m *EndpointsModule) getMqBrokersPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -549,14 +554,14 @@ func (m *EndpointsModule) getMqBrokersPerRegion(r string, wg *sync.WaitGroup, se
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
|
||||
BrokerSummaries, err := sdk.CachedMQListBrokers(m.MQClient, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -576,10 +581,10 @@ func (m *EndpointsModule) getMqBrokersPerRegion(r string, wg *sync.WaitGroup, se
|
||||
)
|
||||
if err != nil {
|
||||
if errors.As(err, &oe) {
|
||||
m.Errors = append(m.Errors, (fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())))
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
if aws.ToBool(BrokerDetails.PubliclyAccessible) {
|
||||
@@ -606,8 +611,8 @@ func (m *EndpointsModule) getMqBrokersPerRegion(r string, wg *sync.WaitGroup, se
|
||||
|
||||
func (m *EndpointsModule) getOpenSearchPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -615,50 +620,41 @@ func (m *EndpointsModule) getOpenSearchPerRegion(r string, wg *sync.WaitGroup, s
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
|
||||
DomainNames, err := sdk.CachedOpenSearchListDomainNames(m.OpenSearchClient, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
for _, domainName := range DomainNames {
|
||||
name := aws.ToString(domainName.DomainName)
|
||||
|
||||
//TODO: convert this to cacehd function
|
||||
DomainNameDetails, err := m.OpenSearchClient.DescribeDomain(
|
||||
context.TODO(),
|
||||
&(opensearch.DescribeDomainInput{
|
||||
DomainName: &name,
|
||||
}),
|
||||
func(o *opensearch.Options) {
|
||||
o.Region = r
|
||||
},
|
||||
)
|
||||
DomainStatus, err := sdk.CachedOpenSearchDescribeDomain(m.OpenSearchClient, aws.ToString(m.Caller.Account), r, name)
|
||||
|
||||
if err != nil {
|
||||
if errors.As(err, &oe) {
|
||||
m.Errors = append(m.Errors, (fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())))
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
raw_endpoint := DomainNameDetails.DomainStatus.Endpoint
|
||||
rawEndpoint := DomainStatus.Endpoint
|
||||
var endpoint string
|
||||
var kibana_endpoint string
|
||||
var kibanaEndpoint string
|
||||
|
||||
// This exits the function if an opensearch domain exists but there is no endpoint
|
||||
if raw_endpoint == nil {
|
||||
if rawEndpoint == nil {
|
||||
return
|
||||
} else {
|
||||
|
||||
endpoint = fmt.Sprintf("https://%s", aws.ToString(raw_endpoint))
|
||||
kibana_endpoint = fmt.Sprintf("https://%s/_plugin/kibana/", aws.ToString(raw_endpoint))
|
||||
endpoint = fmt.Sprintf("https://%s", aws.ToString(rawEndpoint))
|
||||
kibanaEndpoint = fmt.Sprintf("https://%s/_plugin/kibana/", aws.ToString(rawEndpoint))
|
||||
}
|
||||
|
||||
//fmt.Println(endpoint)
|
||||
@@ -667,7 +663,7 @@ func (m *EndpointsModule) getOpenSearchPerRegion(r string, wg *sync.WaitGroup, s
|
||||
domainConfig, err := sdk.CachedOpenSearchDescribeDomainConfig(m.OpenSearchClient, aws.ToString(m.Caller.Account), r, name)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
if aws.ToBool(domainConfig.AdvancedSecurityOptions.Options.Enabled) {
|
||||
@@ -689,7 +685,7 @@ func (m *EndpointsModule) getOpenSearchPerRegion(r string, wg *sync.WaitGroup, s
|
||||
AWSService: "OpenSearch",
|
||||
Region: r,
|
||||
Name: name,
|
||||
Endpoint: kibana_endpoint,
|
||||
Endpoint: kibanaEndpoint,
|
||||
Port: 443,
|
||||
Protocol: "https",
|
||||
Public: public,
|
||||
@@ -701,8 +697,8 @@ func (m *EndpointsModule) getOpenSearchPerRegion(r string, wg *sync.WaitGroup, s
|
||||
|
||||
func (m *EndpointsModule) getGrafanaEndPointsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -710,14 +706,14 @@ func (m *EndpointsModule) getGrafanaEndPointsPerRegion(r string, wg *sync.WaitGr
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
|
||||
ListWorkspaces, err := sdk.CachedGrafanaListWorkspaces(m.GrafanaClient, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -747,8 +743,8 @@ func (m *EndpointsModule) getGrafanaEndPointsPerRegion(r string, wg *sync.WaitGr
|
||||
|
||||
func (m *EndpointsModule) getELBv2ListenersPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -756,16 +752,16 @@ func (m *EndpointsModule) getELBv2ListenersPerRegion(r string, wg *sync.WaitGrou
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
|
||||
awsService := "ELBv2"
|
||||
|
||||
LoadBalancers, err := sdk.CachedELBv2DescribeLoadBalancers(m.ELBv2Client, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -788,10 +784,10 @@ func (m *EndpointsModule) getELBv2ListenersPerRegion(r string, wg *sync.WaitGrou
|
||||
)
|
||||
if err != nil {
|
||||
if errors.As(err, &oe) {
|
||||
m.Errors = append(m.Errors, (fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())))
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
if scheme == "internet-facing" {
|
||||
@@ -827,8 +823,8 @@ func (m *EndpointsModule) getELBv2ListenersPerRegion(r string, wg *sync.WaitGrou
|
||||
|
||||
func (m *EndpointsModule) getELBListenersPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -836,16 +832,16 @@ func (m *EndpointsModule) getELBListenersPerRegion(r string, wg *sync.WaitGroup,
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
awsService := "ELB"
|
||||
|
||||
LoadBalancerDescriptions, err := sdk.CachedELBDescribeLoadBalancers(m.ELBClient, aws.ToString(m.Caller.Account), r)
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
var public string
|
||||
@@ -887,8 +883,8 @@ func (m *EndpointsModule) getELBListenersPerRegion(r string, wg *sync.WaitGroup,
|
||||
|
||||
func (m *EndpointsModule) getAPIGatewayAPIsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -896,16 +892,16 @@ func (m *EndpointsModule) getAPIGatewayAPIsPerRegion(r string, wg *sync.WaitGrou
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
|
||||
|
||||
Items, err := sdk.CachedApiGatewayGetRestAPIs(m.APIGatewayClient, aws.ToString(m.Caller.Account), r)
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -918,8 +914,8 @@ func (m *EndpointsModule) getAPIGatewayAPIsPerRegion(r string, wg *sync.WaitGrou
|
||||
|
||||
func (m *EndpointsModule) getAPIGatewayVIPsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -927,15 +923,15 @@ func (m *EndpointsModule) getAPIGatewayVIPsPerRegion(r string, wg *sync.WaitGrou
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
|
||||
Items, err := sdk.CachedApiGatewayGetRestAPIs(m.APIGatewayClient, aws.ToString(m.Caller.Account), r)
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -943,10 +939,10 @@ func (m *EndpointsModule) getAPIGatewayVIPsPerRegion(r string, wg *sync.WaitGrou
|
||||
|
||||
if err != nil {
|
||||
if errors.As(err, &oe) {
|
||||
m.Errors = append(m.Errors, (fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())))
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
for _, item := range GetDomainNames {
|
||||
@@ -956,10 +952,10 @@ func (m *EndpointsModule) getAPIGatewayVIPsPerRegion(r string, wg *sync.WaitGrou
|
||||
|
||||
if err != nil {
|
||||
if errors.As(err, &oe) {
|
||||
m.Errors = append(m.Errors, (fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())))
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1005,7 +1001,7 @@ func (m *EndpointsModule) getEndpointsPerAPIGateway(r string, api apigatewayType
|
||||
|
||||
name := aws.ToString(api.Name)
|
||||
id := aws.ToString(api.Id)
|
||||
raw_endpoint := fmt.Sprintf("https://%s.execute-api.%s.amazonaws.com", id, r)
|
||||
rawEndpoint := fmt.Sprintf("https://%s.execute-api.%s.amazonaws.com", id, r)
|
||||
var port int32 = 443
|
||||
protocol := "https"
|
||||
|
||||
@@ -1021,21 +1017,20 @@ func (m *EndpointsModule) getEndpointsPerAPIGateway(r string, api apigatewayType
|
||||
|
||||
if err != nil {
|
||||
if errors.As(err, &oe) {
|
||||
m.Errors = append(m.Errors, (fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())))
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
GetResources, err := sdk.CachedApiGatewayGetResources(m.APIGatewayClient, aws.ToString(m.Caller.Account), r, id)
|
||||
|
||||
if err != nil {
|
||||
if errors.As(err, &oe) {
|
||||
m.Errors = append(m.Errors, (fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())))
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
for _, stage := range GetStages.Item {
|
||||
@@ -1044,7 +1039,7 @@ func (m *EndpointsModule) getEndpointsPerAPIGateway(r string, api apigatewayType
|
||||
if len(resource.ResourceMethods) != 0 {
|
||||
path := aws.ToString(resource.Path)
|
||||
|
||||
endpoint := fmt.Sprintf("%s/%s%s", raw_endpoint, stageName, path)
|
||||
endpoint := fmt.Sprintf("%s/%s%s", rawEndpoint, stageName, path)
|
||||
|
||||
endpoints = append(endpoints, Endpoint{
|
||||
AWSService: awsService,
|
||||
@@ -1064,8 +1059,8 @@ func (m *EndpointsModule) getEndpointsPerAPIGateway(r string, api apigatewayType
|
||||
|
||||
func (m *EndpointsModule) getAPIGatewayv2APIsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -1073,16 +1068,16 @@ func (m *EndpointsModule) getAPIGatewayv2APIsPerRegion(r string, wg *sync.WaitGr
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
|
||||
|
||||
Items, err := sdk.CachedAPIGatewayv2GetAPIs(m.APIGatewayv2Client, aws.ToString(m.Caller.Account), r)
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
for _, api := range Items {
|
||||
@@ -1095,8 +1090,8 @@ func (m *EndpointsModule) getAPIGatewayv2APIsPerRegion(r string, wg *sync.WaitGr
|
||||
|
||||
func (m *EndpointsModule) getAPIGatewayv2VIPsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -1104,15 +1099,15 @@ func (m *EndpointsModule) getAPIGatewayv2VIPsPerRegion(r string, wg *sync.WaitGr
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
|
||||
Items, err := sdk.CachedAPIGatewayv2GetAPIs(m.APIGatewayv2Client, aws.ToString(m.Caller.Account), r)
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1120,10 +1115,10 @@ func (m *EndpointsModule) getAPIGatewayv2VIPsPerRegion(r string, wg *sync.WaitGr
|
||||
|
||||
if err != nil {
|
||||
if errors.As(err, &oe) {
|
||||
m.Errors = append(m.Errors, (fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())))
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
for _, item := range GetDomainNames {
|
||||
@@ -1133,10 +1128,10 @@ func (m *EndpointsModule) getAPIGatewayv2VIPsPerRegion(r string, wg *sync.WaitGr
|
||||
|
||||
if err != nil {
|
||||
if errors.As(err, &oe) {
|
||||
m.Errors = append(m.Errors, (fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())))
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1185,7 +1180,7 @@ func (m *EndpointsModule) getEndpointsPerAPIGatewayv2(r string, api apigatewayV2
|
||||
var public string
|
||||
|
||||
name := aws.ToString(api.Name)
|
||||
raw_endpoint := aws.ToString(api.ApiEndpoint)
|
||||
rawEndpoint := aws.ToString(api.ApiEndpoint)
|
||||
id := aws.ToString(api.ApiId)
|
||||
var port int32 = 443
|
||||
protocol := "https"
|
||||
@@ -1195,10 +1190,10 @@ func (m *EndpointsModule) getEndpointsPerAPIGatewayv2(r string, api apigatewayV2
|
||||
|
||||
if err != nil {
|
||||
if errors.As(err, &oe) {
|
||||
m.Errors = append(m.Errors, (fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())))
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
for _, stage := range GetStages {
|
||||
@@ -1213,10 +1208,10 @@ func (m *EndpointsModule) getEndpointsPerAPIGatewayv2(r string, api apigatewayV2
|
||||
|
||||
if err != nil {
|
||||
if errors.As(err, &oe) {
|
||||
m.Errors = append(m.Errors, (fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())))
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
for _, stage := range stages {
|
||||
@@ -1228,9 +1223,9 @@ func (m *EndpointsModule) getEndpointsPerAPIGatewayv2(r string, api apigatewayV2
|
||||
}
|
||||
var endpoint string
|
||||
if stage == "" {
|
||||
endpoint = fmt.Sprintf("%s%s", raw_endpoint, path)
|
||||
endpoint = fmt.Sprintf("%s%s", rawEndpoint, path)
|
||||
} else {
|
||||
endpoint = fmt.Sprintf("%s/%s%s", raw_endpoint, stage, path)
|
||||
endpoint = fmt.Sprintf("%s/%s%s", rawEndpoint, stage, path)
|
||||
}
|
||||
public = "True"
|
||||
|
||||
@@ -1251,8 +1246,8 @@ func (m *EndpointsModule) getEndpointsPerAPIGatewayv2(r string, api apigatewayV2
|
||||
|
||||
func (m *EndpointsModule) getRdsClustersPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -1260,17 +1255,17 @@ func (m *EndpointsModule) getRdsClustersPerRegion(r string, wg *sync.WaitGroup,
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
|
||||
DBInstances, err := sdk.CachedRDSDescribeDBInstances(m.RDSClient, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
if errors.As(err, &oe) {
|
||||
m.Errors = append(m.Errors, (fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())))
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1305,8 +1300,8 @@ func (m *EndpointsModule) getRdsClustersPerRegion(r string, wg *sync.WaitGroup,
|
||||
|
||||
func (m *EndpointsModule) getRedshiftEndPointsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -1314,21 +1309,21 @@ func (m *EndpointsModule) getRedshiftEndPointsPerRegion(r string, wg *sync.WaitG
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
awsService := "Redshift"
|
||||
protocol := "https"
|
||||
|
||||
// This for loop exits at the end dependeding on whether the output hits its last page (see pagination control block at the end of the loop).
|
||||
// This for loop exits at the end depending on whether the output hits its last page (see pagination control block at the end of the loop).
|
||||
Clusters, err := sdk.CachedRedShiftDescribeClusters(m.RedshiftClient, aws.ToString(m.Caller.Account), r)
|
||||
|
||||
if err != nil {
|
||||
if errors.As(err, &oe) {
|
||||
m.Errors = append(m.Errors, (fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())))
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1365,8 +1360,8 @@ UNUSED CODE - PLEASE REVIEW AND DELETE IF IT DOESN'T APPLY
|
||||
|
||||
func (m *EndpointsModule) getS3EndpointsPerRegion(wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -1374,9 +1369,9 @@ UNUSED CODE - PLEASE REVIEW AND DELETE IF IT DOESN'T APPLY
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
|
||||
// This for loop exits at the end dependeding on whether the output hits its last page (see pagination control block at the end of the loop).
|
||||
ListBuckets, _ := m.S3Client.ListBuckets(
|
||||
@@ -1439,8 +1434,8 @@ UNUSED CODE - PLEASE REVIEW AND DELETE IF IT DOESN'T APPLY
|
||||
*/
|
||||
func (m *EndpointsModule) getCloudfrontEndpoints(wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -1448,17 +1443,17 @@ func (m *EndpointsModule) getCloudfrontEndpoints(wg *sync.WaitGroup, semaphore c
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
|
||||
var PaginationControl *string
|
||||
var awsService string = "Cloudfront"
|
||||
var protocol string = "https"
|
||||
var r string = "Global"
|
||||
var public string = "True"
|
||||
var awsService = "Cloudfront"
|
||||
var protocol = "https"
|
||||
var r = "Global"
|
||||
var public = "True"
|
||||
|
||||
// This for loop exits at the end dependeding on whether the output hits its last page (see pagination control block at the end of the loop).
|
||||
// This for loop exits at the end depending on whether the output hits its last page (see pagination control block at the end of the loop).
|
||||
for {
|
||||
ListDistributions, err := m.CloudfrontClient.ListDistributions(
|
||||
context.TODO(),
|
||||
@@ -1468,10 +1463,10 @@ func (m *EndpointsModule) getCloudfrontEndpoints(wg *sync.WaitGroup, semaphore c
|
||||
)
|
||||
if err != nil {
|
||||
if errors.As(err, &oe) {
|
||||
m.Errors = append(m.Errors, (fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())))
|
||||
m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation()))
|
||||
}
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
if ListDistributions.DistributionList.Quantity == nil {
|
||||
@@ -1551,8 +1546,8 @@ func (m *EndpointsModule) getCloudfrontEndpoints(wg *sync.WaitGroup, semaphore c
|
||||
|
||||
func (m *EndpointsModule) getAppRunnerEndpointsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -1560,15 +1555,15 @@ func (m *EndpointsModule) getAppRunnerEndpointsPerRegion(r string, wg *sync.Wait
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
|
||||
ServiceSummaryList, err := sdk.CachedAppRunnerListServices(m.AppRunnerClient, aws.ToString(m.Caller.Account), r)
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
for _, service := range ServiceSummaryList {
|
||||
@@ -1595,7 +1590,7 @@ func (m *EndpointsModule) getAppRunnerEndpointsPerRegion(r string, wg *sync.Wait
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1619,7 +1614,7 @@ func (m *EndpointsModule) getAppRunnerEndpointsPerRegion(r string, wg *sync.Wait
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
if DescribeCustomDomains.DNSTarget != nil {
|
||||
@@ -1672,8 +1667,8 @@ func (m *EndpointsModule) appRunnerDescribeCustomDomain(r string, serviceArn str
|
||||
|
||||
func (m *EndpointsModule) getLightsailContainerEndpointsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Endpoint) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -1681,18 +1676,18 @@ func (m *EndpointsModule) getLightsailContainerEndpointsPerRegion(r string, wg *
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
var public string = "True"
|
||||
var protocol string = "https"
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
var public = "True"
|
||||
var protocol = "https"
|
||||
var port int32 = 443
|
||||
|
||||
containerServices, err := sdk.CachedLightsailGetContainerServices(m.LightsailClient, aws.ToString(m.Caller.Account), r)
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
if len(containerServices) > 0 {
|
||||
|
||||
+98
-91
@@ -43,6 +43,7 @@ type EnvsModule struct {
|
||||
|
||||
Goroutines int
|
||||
WrapTable bool
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
// Service Clients
|
||||
ECSClient *ecs.Client
|
||||
@@ -62,6 +63,7 @@ type EnvsModule struct {
|
||||
|
||||
type EnvironmentVariable struct {
|
||||
service string
|
||||
taskDefinition string
|
||||
name string
|
||||
region string
|
||||
environmentVarName string
|
||||
@@ -103,7 +105,7 @@ func (m *EnvsModule) PrintEnvs(outputDirectory string, verbosity int) {
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, semaphore, dataReceiver)
|
||||
|
||||
}
|
||||
@@ -166,13 +168,20 @@ func (m *EnvsModule) PrintEnvs(outputDirectory string, verbosity int) {
|
||||
//Table rows
|
||||
for _, envVar := range m.EnvironmentVariables {
|
||||
|
||||
var finalName string
|
||||
if envVar.taskDefinition != "" {
|
||||
finalName = fmt.Sprintf("%s/%s", envVar.name, envVar.taskDefinition)
|
||||
} else {
|
||||
finalName = envVar.name
|
||||
}
|
||||
|
||||
if envVar.interesting {
|
||||
m.output.Body = append(
|
||||
m.output.Body, []string{
|
||||
aws.ToString(m.Caller.Account),
|
||||
envVar.service,
|
||||
envVar.region,
|
||||
envVar.name,
|
||||
finalName,
|
||||
magenta(envVar.environmentVarName),
|
||||
magenta(envVar.environmentVarValue),
|
||||
},
|
||||
@@ -183,7 +192,7 @@ func (m *EnvsModule) PrintEnvs(outputDirectory string, verbosity int) {
|
||||
aws.ToString(m.Caller.Account),
|
||||
envVar.service,
|
||||
envVar.region,
|
||||
envVar.name,
|
||||
finalName,
|
||||
envVar.environmentVarName,
|
||||
envVar.environmentVarValue,
|
||||
},
|
||||
@@ -272,12 +281,16 @@ func (m *EnvsModule) Receiver(receiver chan EnvironmentVariable, receiverDone ch
|
||||
|
||||
func (m *EnvsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan EnvironmentVariable) {
|
||||
defer wg.Done()
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, _ := servicemap.IsServiceInRegion("ecs", r)
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getECSEnvironmentVariablesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -287,32 +300,32 @@ func (m *EnvsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getLambdaEnvironmentVariablesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
|
||||
// AppRunner is not supported in the aws service region catalog so we have to run it in all regions
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getAppRunnerEnvironmentVariablesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
|
||||
res, err = servicemap.IsServiceInRegion("lightsail", r)
|
||||
res, err = servicemap.IsServiceInRegion("amazonlightsail.com", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getLightsailEnvironmentVariablesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
|
||||
res, err = servicemap.IsServiceInRegion("sagemaker", r)
|
||||
res, err = servicemap.IsServiceInRegion("sagemaker-ai", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getSagemakerEnvironmentVariablesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -321,8 +334,8 @@ func (m *EnvsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan
|
||||
|
||||
func (m *EnvsModule) getECSEnvironmentVariablesPerRegion(region string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan EnvironmentVariable) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -330,9 +343,9 @@ func (m *EnvsModule) getECSEnvironmentVariablesPerRegion(region string, wg *sync
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
|
||||
//var PaginationMarker *string
|
||||
|
||||
@@ -351,11 +364,11 @@ func (m *EnvsModule) getECSEnvironmentVariablesPerRegion(region string, wg *sync
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
for _, containerDefinition := range DescribeTaskDefinition.TaskDefinition.ContainerDefinitions {
|
||||
m.getECSEnvironmentVariablesPerDefinition(containerDefinition, region, dataReceiver)
|
||||
m.getECSEnvironmentVariablesPerDefinition(*DescribeTaskDefinition.TaskDefinition, containerDefinition, region, dataReceiver)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -377,7 +390,7 @@ func (m *EnvsModule) getTaskDefinitionFamilies(region string) []string {
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -394,12 +407,13 @@ func (m *EnvsModule) getTaskDefinitionFamilies(region string) []string {
|
||||
|
||||
}
|
||||
|
||||
func (m *EnvsModule) getECSEnvironmentVariablesPerDefinition(containerDefinition ecsTypes.ContainerDefinition, region string, dataReceiver chan EnvironmentVariable) {
|
||||
func (m *EnvsModule) getECSEnvironmentVariablesPerDefinition(taskDefinition ecsTypes.TaskDefinition, containerDefinition ecsTypes.ContainerDefinition, region string, dataReceiver chan EnvironmentVariable) {
|
||||
|
||||
if containerDefinition.Environment != nil {
|
||||
for _, x := range containerDefinition.Environment {
|
||||
dataReceiver <- EnvironmentVariable{
|
||||
service: "ECS",
|
||||
taskDefinition: getNameFromARN(aws.ToString(taskDefinition.TaskDefinitionArn)),
|
||||
name: aws.ToString(containerDefinition.Name),
|
||||
region: region,
|
||||
environmentVarName: aws.ToString(x.Name),
|
||||
@@ -411,8 +425,8 @@ func (m *EnvsModule) getECSEnvironmentVariablesPerDefinition(containerDefinition
|
||||
|
||||
func (m *EnvsModule) getLambdaEnvironmentVariablesPerRegion(region string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan EnvironmentVariable) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -420,14 +434,14 @@ func (m *EnvsModule) getLambdaEnvironmentVariablesPerRegion(region string, wg *s
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
|
||||
Functions, err := sdk.CachedLambdaListFunctions(m.LambdaClient, aws.ToString(m.Caller.Account), region)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -453,8 +467,8 @@ func (m *EnvsModule) getLambdaEnvironmentVariablesPerFunction(function lambdaTyp
|
||||
|
||||
func (m *EnvsModule) getAppRunnerEnvironmentVariablesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan EnvironmentVariable) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -462,16 +476,16 @@ func (m *EnvsModule) getAppRunnerEnvironmentVariablesPerRegion(r string, wg *syn
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
|
||||
ServiceSummaryList, err := sdk.CachedAppRunnerListServices(m.AppRunnerClient, aws.ToString(m.Caller.Account), r)
|
||||
|
||||
if err != nil {
|
||||
//modLog.Error(err.Error())
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
if len(ServiceSummaryList) > 0 {
|
||||
@@ -492,7 +506,7 @@ func (m *EnvsModule) getAppRunnerEnvironmentVariablesPerRegion(r string, wg *syn
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
|
||||
}
|
||||
@@ -516,8 +530,8 @@ func (m *EnvsModule) getAppRunnerEnvironmentVariablesPerRegion(r string, wg *syn
|
||||
|
||||
func (m *EnvsModule) getLightsailEnvironmentVariablesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan EnvironmentVariable) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -525,46 +539,43 @@ func (m *EnvsModule) getLightsailEnvironmentVariablesPerRegion(r string, wg *syn
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
awsService := "Lightsail [Container]"
|
||||
|
||||
ContainerServices, err := sdk.CachedLightsailGetContainerServices(m.LightsailClient, aws.ToString(m.Caller.Account), r)
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if len(ContainerServices) > 0 {
|
||||
|
||||
if len(ContainerServices) > 0 {
|
||||
|
||||
for _, containerService := range ContainerServices {
|
||||
for _, container := range containerService.CurrentDeployment.Containers {
|
||||
for k, v := range container.Environment {
|
||||
name := aws.ToString(containerService.ContainerServiceName)
|
||||
dataReceiver <- EnvironmentVariable{
|
||||
service: awsService,
|
||||
name: name,
|
||||
region: r,
|
||||
environmentVarName: k,
|
||||
environmentVarValue: v,
|
||||
}
|
||||
for _, containerService := range ContainerServices {
|
||||
for _, container := range containerService.CurrentDeployment.Containers {
|
||||
for k, v := range container.Environment {
|
||||
name := aws.ToString(containerService.ContainerServiceName)
|
||||
dataReceiver <- EnvironmentVariable{
|
||||
service: awsService,
|
||||
name: name,
|
||||
region: r,
|
||||
environmentVarName: k,
|
||||
environmentVarValue: v,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *EnvsModule) getSagemakerEnvironmentVariablesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan EnvironmentVariable) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -572,9 +583,9 @@ func (m *EnvsModule) getSagemakerEnvironmentVariablesPerRegion(r string, wg *syn
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
awsService := "Sagemaker"
|
||||
|
||||
var PaginationControl *string
|
||||
@@ -592,7 +603,7 @@ func (m *EnvsModule) getSagemakerEnvironmentVariablesPerRegion(r string, wg *syn
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -610,7 +621,7 @@ func (m *EnvsModule) getSagemakerEnvironmentVariablesPerRegion(r string, wg *syn
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -654,7 +665,7 @@ func (m *EnvsModule) getSagemakerEnvironmentVariablesPerRegion(r string, wg *syn
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -672,21 +683,19 @@ func (m *EnvsModule) getSagemakerEnvironmentVariablesPerRegion(r string, wg *syn
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
if err == nil {
|
||||
|
||||
if len(DescribeTransformJob.Environment) > 0 {
|
||||
name := fmt.Sprintf("[Transform Job] %s", aws.ToString(DescribeTransformJob.TransformJobName))
|
||||
for k, v := range DescribeTransformJob.Environment {
|
||||
dataReceiver <- EnvironmentVariable{
|
||||
service: awsService,
|
||||
name: name,
|
||||
region: r,
|
||||
environmentVarName: k,
|
||||
environmentVarValue: v,
|
||||
}
|
||||
if len(DescribeTransformJob.Environment) > 0 {
|
||||
name := fmt.Sprintf("[Transform Job] %s", aws.ToString(DescribeTransformJob.TransformJobName))
|
||||
for k, v := range DescribeTransformJob.Environment {
|
||||
dataReceiver <- EnvironmentVariable{
|
||||
service: awsService,
|
||||
name: name,
|
||||
region: r,
|
||||
environmentVarName: k,
|
||||
environmentVarValue: v,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -717,7 +726,7 @@ func (m *EnvsModule) getSagemakerEnvironmentVariablesPerRegion(r string, wg *syn
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -735,23 +744,21 @@ func (m *EnvsModule) getSagemakerEnvironmentVariablesPerRegion(r string, wg *syn
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
if err == nil {
|
||||
|
||||
if len(DescribeTrainingJob.Environment) > 0 {
|
||||
name := fmt.Sprintf("[Training Job] %s", aws.ToString(DescribeTrainingJob.TrainingJobName))
|
||||
for k, v := range DescribeTrainingJob.Environment {
|
||||
dataReceiver <- EnvironmentVariable{
|
||||
service: awsService,
|
||||
name: name,
|
||||
region: r,
|
||||
environmentVarName: k,
|
||||
environmentVarValue: v,
|
||||
}
|
||||
|
||||
if len(DescribeTrainingJob.Environment) > 0 {
|
||||
name := fmt.Sprintf("[Training Job] %s", aws.ToString(DescribeTrainingJob.TrainingJobName))
|
||||
for k, v := range DescribeTrainingJob.Environment {
|
||||
dataReceiver <- EnvironmentVariable{
|
||||
service: awsService,
|
||||
name: name,
|
||||
region: r,
|
||||
environmentVarName: k,
|
||||
environmentVarValue: v,
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -781,7 +788,7 @@ func (m *EnvsModule) getSagemakerEnvironmentVariablesPerRegion(r string, wg *syn
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -798,7 +805,7 @@ func (m *EnvsModule) getSagemakerEnvironmentVariablesPerRegion(r string, wg *syn
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
+23
-18
@@ -36,6 +36,7 @@ type FilesystemsModule struct {
|
||||
Goroutines int
|
||||
AWSProfile string
|
||||
WrapTable bool
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
// Main module data
|
||||
Filesystems []FilesystemObject
|
||||
@@ -218,8 +219,12 @@ func (m *FilesystemsModule) Receiver(receiver chan FilesystemObject, receiverDon
|
||||
|
||||
func (m *FilesystemsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan FilesystemObject) {
|
||||
defer wg.Done()
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("efs", r)
|
||||
if err != nil {
|
||||
@@ -240,7 +245,7 @@ func (m *FilesystemsModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
f := filepath.Join(path, "filesystems-mount-commands.txt")
|
||||
|
||||
@@ -278,7 +283,7 @@ func (m *FilesystemsModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
fmt.Printf("[%s][%s] Loot written to [%s]\n", cyan(m.output.CallingModule), cyan(m.AWSProfile), f)
|
||||
if verbosity > 2 {
|
||||
@@ -292,20 +297,20 @@ func (m *FilesystemsModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
|
||||
func (m *FilesystemsModule) getEFSSharesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan FilesystemObject) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
var policy string
|
||||
|
||||
DescribeFileSystems, err := sdk.CachedDescribeFileSystems(m.EFSClient, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -331,7 +336,7 @@ func (m *FilesystemsModule) getEFSSharesPerRegion(r string, wg *sync.WaitGroup,
|
||||
DescribeMountTargets, err := sdk.CachedDescribeMountTargets(m.EFSClient, aws.ToString(m.Caller.Account), r, id)
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -344,7 +349,7 @@ func (m *FilesystemsModule) getEFSSharesPerRegion(r string, wg *sync.WaitGroup,
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
dataReceiver <- FilesystemObject{
|
||||
AWSService: awsService,
|
||||
Region: r,
|
||||
@@ -376,14 +381,14 @@ func (m *FilesystemsModule) getEFSSharesPerRegion(r string, wg *sync.WaitGroup,
|
||||
|
||||
func (m *FilesystemsModule) getFSxSharesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan FilesystemObject) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
|
||||
var PaginationMarker *string
|
||||
var PaginationMarker2 *string
|
||||
@@ -405,7 +410,7 @@ func (m *FilesystemsModule) getFSxSharesPerRegion(r string, wg *sync.WaitGroup,
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
+35
-40
@@ -48,26 +48,23 @@ type SimulatorResult struct {
|
||||
Decision string
|
||||
}
|
||||
|
||||
var (
|
||||
defaultActionNames = []string{
|
||||
"sts:AssumeRole",
|
||||
"iam:PassRole",
|
||||
"secretsmanager:GetSecretValue",
|
||||
"ssm:GetParameter",
|
||||
"s3:ListBucket",
|
||||
"s3:GetObject",
|
||||
"ssm:SendCommand",
|
||||
"ssm:StartSession",
|
||||
"ecr:BatchGetImage",
|
||||
"ecr:GetAuthorizationToken",
|
||||
"eks:UpdateClusterConfig",
|
||||
"lambda:ListFunctions",
|
||||
"ec2:DescribeInstanceAttributeInput",
|
||||
"sns:Subscribe",
|
||||
"sqs:SendMessage",
|
||||
}
|
||||
TxtLogger = internal.TxtLogger()
|
||||
)
|
||||
var defaultActionNames = []string{
|
||||
"sts:AssumeRole",
|
||||
"iam:PassRole",
|
||||
"secretsmanager:GetSecretValue",
|
||||
"ssm:GetParameter",
|
||||
"s3:ListBucket",
|
||||
"s3:GetObject",
|
||||
"ssm:SendCommand",
|
||||
"ssm:StartSession",
|
||||
"ecr:BatchGetImage",
|
||||
"ecr:GetAuthorizationToken",
|
||||
"eks:UpdateClusterConfig",
|
||||
"lambda:ListFunctions",
|
||||
"ec2:DescribeInstanceAttributeInput",
|
||||
"sns:Subscribe",
|
||||
"sqs:SendMessage",
|
||||
}
|
||||
|
||||
func (m *IamSimulatorModule) PrintIamSimulator(principal string, action string, resource string, outputDirectory string, verbosity int) {
|
||||
|
||||
@@ -279,7 +276,7 @@ func (m *IamSimulatorModule) writeLoot(outputDirectory string, verbosity int, pm
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
outFile := filepath.Join(path, "iam-simulator-pmapper-commands.txt")
|
||||
@@ -295,7 +292,7 @@ func (m *IamSimulatorModule) writeLoot(outputDirectory string, verbosity int, pm
|
||||
err = os.WriteFile(outFile, []byte(out), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
if verbosity > 2 {
|
||||
@@ -332,18 +329,18 @@ func (m *IamSimulatorModule) executeChecks(wg *sync.WaitGroup, resource string,
|
||||
func (m *IamSimulatorModule) getIAMUsers(wg *sync.WaitGroup, actions []string, resource string, dataReceiver chan SimulatorResult) {
|
||||
defer func() {
|
||||
wg.Done()
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
}()
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
|
||||
ListUsers, err := sdk.CachedIamListUsers(m.IAMClient, aws.ToString(m.Caller.Account))
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -372,19 +369,19 @@ func (m *IamSimulatorModule) getIAMUsers(wg *sync.WaitGroup, actions []string, r
|
||||
func (m *IamSimulatorModule) getIAMRoles(wg *sync.WaitGroup, actions []string, resource string, dataReceiver chan SimulatorResult) {
|
||||
defer func() {
|
||||
wg.Done()
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
}()
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
|
||||
|
||||
ListRoles, err := sdk.CachedIamListRoles(m.IAMClient, aws.ToString(m.Caller.Account))
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
|
||||
}
|
||||
@@ -422,7 +419,7 @@ func (m *IamSimulatorModule) getPolicySimulatorResult(principal *string, actionN
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
m.modLog.Error(fmt.Sprintf("Failed to query actions for %s\n\n", aws.ToString(principal)))
|
||||
return
|
||||
}
|
||||
@@ -466,11 +463,9 @@ func (m *IamSimulatorModule) isPrincipalAnAdmin(principal *string) bool {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
//m.modLog.Error(err.Error())
|
||||
TxtLogger.Println(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
//m.modLog.Error(fmt.Sprintf("Failed admin check on %s\n\n", aws.ToString(principal)))
|
||||
TxtLogger.Printf("Failed admin check on %s\n\n", aws.ToString(principal))
|
||||
m.modLog.Error(err)
|
||||
m.CommandCounter.IncrError()
|
||||
m.modLog.Errorf("Failed admin check on %s", aws.ToString(principal))
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
+37
-25
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@@ -36,6 +35,7 @@ type InstancesModule struct {
|
||||
UserDataAttributesOnly bool
|
||||
AWSProfile string
|
||||
WrapTable bool
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
InstanceProfileToRolesMap map[string][]iamTypes.Role
|
||||
SkipAdminCheck bool
|
||||
pmapperMod PmapperModule
|
||||
@@ -122,15 +122,19 @@ func (m *InstancesModule) Instances(filter string, outputDirectory string, verbo
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(instancesToSearch, region, wg, dataReceiver)
|
||||
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
//time.Sleep(time.Second * 2)
|
||||
|
||||
// Perform role analysis
|
||||
// Signal the receiver to stop and wait for it to finish draining the channel.
|
||||
// This must happen BEFORE role analysis to ensure all MappedInstances are collected.
|
||||
receiverDone <- true
|
||||
<-receiverDone
|
||||
|
||||
// Perform role analysis (all instances are now in m.MappedInstances)
|
||||
if m.pmapperError == nil {
|
||||
for i := range m.MappedInstances {
|
||||
m.MappedInstances[i].Admin, m.MappedInstances[i].CanPrivEsc = GetPmapperResults(m.SkipAdminCheck, m.pmapperMod, &m.MappedInstances[i].Role)
|
||||
@@ -144,8 +148,6 @@ func (m *InstancesModule) Instances(filter string, outputDirectory string, verbo
|
||||
// Send a message to the spinner goroutine to close the channel and stop
|
||||
spinnerDone <- true
|
||||
<-spinnerDone
|
||||
receiverDone <- true
|
||||
<-receiverDone
|
||||
|
||||
// This conditional block will either dump the userData attribute content or the general instances data, depending on what you select via command line.
|
||||
//fmt.Printf("\n[*] Preparing output...\n\n")
|
||||
@@ -180,7 +182,7 @@ func (m *InstancesModule) printInstancesUserDataAttributesOnly(outputDirectory s
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
userDataFileName := filepath.Join(path, fmt.Sprintf("%s.txt", m.output.CallingModule))
|
||||
|
||||
@@ -212,7 +214,7 @@ func (m *InstancesModule) printInstancesUserDataAttributesOnly(outputDirectory s
|
||||
err = os.WriteFile(userDataFileName, []byte(userDataOut), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
fmt.Printf("[%s][%s] Loot written to [%s]\n", cyan(m.output.CallingModule), cyan(m.AWSProfile), userDataFileName)
|
||||
} else {
|
||||
@@ -343,7 +345,7 @@ func (m *InstancesModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
privateIPsFilename := filepath.Join(path, "instances-ec2PrivateIPs.txt")
|
||||
publicIPsFilename := filepath.Join(path, "instances-ec2PublicIPs.txt")
|
||||
@@ -394,22 +396,22 @@ func (m *InstancesModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
err = os.WriteFile(privateIPsFilename, []byte(privateIPs), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
err = os.WriteFile(publicIPsFilename, []byte(publicIPs), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
err = os.WriteFile(ssmCommandsFilename, []byte(ssmCommands), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
err = os.WriteFile(ec2InstanceConnectCommandsFilename, []byte(ec2InstanceConnectCommands), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
fmt.Printf("[%s][%s] Loot written to [%s]\n", cyan(m.output.CallingModule), cyan(m.AWSProfile), privateIPsFilename)
|
||||
@@ -435,20 +437,30 @@ func (m *InstancesModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
|
||||
func (m *InstancesModule) executeChecks(instancesToSearch []string, r string, wg *sync.WaitGroup, dataReceiver chan MappedInstance) {
|
||||
defer wg.Done()
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
m.modLog.Warn("ServiceMap is nil, creating new instance (this should not happen)")
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("ec2", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
m.modLog.Errorf("Error checking if EC2 is available in region %s: %v", r, err)
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
m.modLog.Debugf("ServiceMap check for EC2 in region %s: result=%v, error=%v", r, res, err)
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
m.getDescribeInstances(instancesToSearch, r, dataReceiver)
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
} else {
|
||||
m.modLog.Infof("Skipping region %s: EC2 not available or check failed", r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,7 +469,7 @@ func (m *InstancesModule) getInstanceUserDataAttribute(instanceID *string, regio
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return nil, err
|
||||
} else {
|
||||
if UserData == "" {
|
||||
@@ -477,7 +489,7 @@ func (m *InstancesModule) getDescribeInstances(instancesToSearch []string, regio
|
||||
Instances, err := sdk.CachedEC2DescribeInstances(m.EC2Client, aws.ToString(m.Caller.Account), region)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -537,7 +549,7 @@ func (m *InstancesModule) loadInstanceData(instance types.Instance, region strin
|
||||
// Describe the IAM instance profile
|
||||
profileOutput, err := sdk.CachedIamGetInstanceProfile(m.IAMClient, aws.ToString(m.Caller.Account), profileName)
|
||||
if err != nil {
|
||||
log.Printf("failed to get instance profile for %s, %v", profileArn, err)
|
||||
m.modLog.Errorf("failed to get instance profile for %s, %v", profileArn, err)
|
||||
}
|
||||
|
||||
for _, role := range profileOutput.Roles {
|
||||
@@ -576,7 +588,7 @@ func (m *InstancesModule) getRolesFromInstanceProfiles() {
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
for _, instanceProfile := range ListInstanceProfiles.InstanceProfiles {
|
||||
|
||||
+352
-356
File diff suppressed because it is too large.
Load diff
+16
-11
@@ -35,6 +35,7 @@ type LambdasModule struct {
|
||||
AWSProfile string
|
||||
SkipAdminCheck bool
|
||||
WrapTable bool
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
pmapperMod PmapperModule
|
||||
pmapperError error
|
||||
PmapperDataBasePath string
|
||||
@@ -104,7 +105,7 @@ func (m *LambdasModule) PrintLambdas(outputDirectory string, verbosity int) {
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, semaphore, dataReceiver)
|
||||
|
||||
}
|
||||
@@ -228,15 +229,19 @@ func (m *LambdasModule) PrintLambdas(outputDirectory string, verbosity int) {
|
||||
func (m *LambdasModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Lambda) {
|
||||
defer wg.Done()
|
||||
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("lambda", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getLambdasPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -260,7 +265,7 @@ func (m *LambdasModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
pullFile := filepath.Join(path, "lambda-get-function-commands.txt")
|
||||
|
||||
@@ -284,7 +289,7 @@ func (m *LambdasModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
err = os.WriteFile(pullFile, []byte(out), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
if verbosity > 2 {
|
||||
@@ -301,8 +306,8 @@ func (m *LambdasModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
|
||||
func (m *LambdasModule) getLambdasPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Lambda) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -314,7 +319,7 @@ func (m *LambdasModule) getLambdasPerRegion(r string, wg *sync.WaitGroup, semaph
|
||||
functions, err := m.listFunctions(r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
for _, function := range functions {
|
||||
@@ -355,7 +360,7 @@ func (m *LambdasModule) listFunctions(r string) ([]types.FunctionConfiguration,
|
||||
)
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return functions, err
|
||||
}
|
||||
functions = append(functions, ListFunctions.Functions...)
|
||||
@@ -384,7 +389,7 @@ func (m *LambdasModule) getResourcePolicy(r string, functionName string) (policy
|
||||
)
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return projectPolicy, err
|
||||
}
|
||||
|
||||
|
||||
+75
-65
@@ -50,9 +50,6 @@ var (
|
||||
# Make sure the host you scan IPv6 from has an IPv6 network interface.
|
||||
#############################################
|
||||
`
|
||||
|
||||
naclsMutex = sync.RWMutex{}
|
||||
securityGroupsMutex = sync.RWMutex{}
|
||||
)
|
||||
|
||||
type NetworkPortsModule struct {
|
||||
@@ -74,13 +71,16 @@ type NetworkPortsModule struct {
|
||||
AWSProfile string
|
||||
WrapTable bool
|
||||
Verbosity int
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
// Main module data
|
||||
IPv4_Private []NetworkService
|
||||
IPv4_Public []NetworkService
|
||||
IPv6 []NetworkService
|
||||
nacls map[string]*[]ec2_types.NetworkAcl
|
||||
securityGroups map[string]*[]ec2_types.SecurityGroup
|
||||
IPv4_Private []NetworkService
|
||||
IPv4_Public []NetworkService
|
||||
IPv6 []NetworkService
|
||||
nacls map[string]*[]ec2_types.NetworkAcl
|
||||
securityGroups map[string]*[]ec2_types.SecurityGroup
|
||||
naclsMutex sync.RWMutex
|
||||
securityGroupsMutex sync.RWMutex
|
||||
|
||||
CommandCounter internal.CommandCounter
|
||||
// Used to store output data for pretty printing
|
||||
@@ -171,7 +171,7 @@ func (m *NetworkPortsModule) PrintNetworkPorts(outputDirectory string) {
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, dataReceiver)
|
||||
|
||||
}
|
||||
@@ -273,8 +273,12 @@ func (m *NetworkPortsModule) PrintNetworkPorts(outputDirectory string) {
|
||||
|
||||
func (m *NetworkPortsModule) executeChecks(r string, wg *sync.WaitGroup, dataReceiver chan NetworkServices) {
|
||||
defer wg.Done()
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
|
||||
res, err := servicemap.IsServiceInRegion("ec2", r)
|
||||
@@ -282,7 +286,7 @@ func (m *NetworkPortsModule) executeChecks(r string, wg *sync.WaitGroup, dataRec
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.getEC2NetworkPortsPerRegion(r, dataReceiver)
|
||||
}
|
||||
|
||||
@@ -291,7 +295,7 @@ func (m *NetworkPortsModule) executeChecks(r string, wg *sync.WaitGroup, dataRec
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.getECSNetworkPortsPerRegion(r, dataReceiver)
|
||||
}
|
||||
|
||||
@@ -300,7 +304,7 @@ func (m *NetworkPortsModule) executeChecks(r string, wg *sync.WaitGroup, dataRec
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.getEFSNetworkPortsPerRegion(r, dataReceiver)
|
||||
}
|
||||
|
||||
@@ -309,25 +313,25 @@ func (m *NetworkPortsModule) executeChecks(r string, wg *sync.WaitGroup, dataRec
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.getElastiCacheNetworkPortsPerRegion(r, dataReceiver)
|
||||
}
|
||||
|
||||
res, err = servicemap.IsServiceInRegion("elb", r)
|
||||
res, err = servicemap.IsServiceInRegion("elasticloadbalancing", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.getLBNetworkPortsPerRegion(r, dataReceiver)
|
||||
}
|
||||
|
||||
res, err = servicemap.IsServiceInRegion("lightsail", r)
|
||||
res, err = servicemap.IsServiceInRegion("amazonlightsail.com", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.getLightsailNetworkPortsPerRegion(r, dataReceiver)
|
||||
}
|
||||
|
||||
@@ -336,7 +340,7 @@ func (m *NetworkPortsModule) executeChecks(r string, wg *sync.WaitGroup, dataRec
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.getRdsNetworkPortsPerRegion(r, dataReceiver)
|
||||
}
|
||||
|
||||
@@ -424,8 +428,8 @@ func (m *NetworkPortsModule) writeLootFile(filename string, bannner string, ipv4
|
||||
|
||||
func (m *NetworkPortsModule) getEC2NetworkPortsPerRegion(r string, dataReceiver chan NetworkServices) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
}()
|
||||
securityGroups := m.getEC2SecurityGroupsPerRegion(r)
|
||||
nacls := m.getEC2NACLsPerRegion(r)
|
||||
@@ -528,8 +532,8 @@ func (m *NetworkPortsModule) getEC2NetworkPortsPerRegion(r string, dataReceiver
|
||||
|
||||
func (m *NetworkPortsModule) getECSNetworkPortsPerRegion(r string, dataReceiver chan NetworkServices) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
}()
|
||||
securityGroups := m.getEC2SecurityGroupsPerRegion(r)
|
||||
nacls := m.getEC2NACLsPerRegion(r)
|
||||
@@ -543,7 +547,7 @@ func (m *NetworkPortsModule) getECSNetworkPortsPerRegion(r string, dataReceiver
|
||||
service, err := m.describeECSService(serviceArn, &clusterArn, r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -580,7 +584,7 @@ func (m *NetworkPortsModule) getECSNetworkPortsPerRegion(r string, dataReceiver
|
||||
task, err := m.describeECSTask(taskArn, &clusterArn, r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -663,8 +667,8 @@ func (m *NetworkPortsModule) getECSNetworkPortsPerRegion(r string, dataReceiver
|
||||
|
||||
func (m *NetworkPortsModule) getEFSNetworkPortsPerRegion(r string, dataReceiver chan NetworkServices) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
}()
|
||||
securityGroups := m.getEC2SecurityGroupsPerRegion(r)
|
||||
nacls := m.getEC2NACLsPerRegion(r)
|
||||
@@ -726,8 +730,8 @@ func (m *NetworkPortsModule) getEFSNetworkPortsPerRegion(r string, dataReceiver
|
||||
|
||||
func (m *NetworkPortsModule) getElastiCacheNetworkPortsPerRegion(r string, dataReceiver chan NetworkServices) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
}()
|
||||
securityGroups := m.getEC2SecurityGroupsPerRegion(r)
|
||||
nacls := m.getEC2NACLsPerRegion(r)
|
||||
@@ -838,8 +842,8 @@ func (m *NetworkPortsModule) getElastiCacheNetworkPortsPerRegion(r string, dataR
|
||||
|
||||
func (m *NetworkPortsModule) getLBNetworkPortsPerRegion(r string, dataReceiver chan NetworkServices) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
}()
|
||||
securityGroups := m.getEC2SecurityGroupsPerRegion(r)
|
||||
nacls := m.getEC2NACLsPerRegion(r)
|
||||
@@ -954,8 +958,8 @@ func (m *NetworkPortsModule) getLBNetworkPortsPerRegion(r string, dataReceiver c
|
||||
|
||||
func (m *NetworkPortsModule) getLightsailNetworkPortsPerRegion(r string, dataReceiver chan NetworkServices) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
}()
|
||||
instances := m.getLightsailInstancesPerRegion(r)
|
||||
|
||||
@@ -1065,8 +1069,8 @@ func (m *NetworkPortsModule) getLightsailNetworkPortsPerRegion(r string, dataRec
|
||||
|
||||
func (m *NetworkPortsModule) getRdsNetworkPortsPerRegion(r string, dataReceiver chan NetworkServices) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
}()
|
||||
securityGroups := m.getEC2SecurityGroupsPerRegion(r)
|
||||
nacls := m.getEC2NACLsPerRegion(r)
|
||||
@@ -1138,13 +1142,16 @@ func (m *NetworkPortsModule) getRdsNetworkPortsPerRegion(r string, dataReceiver
|
||||
}
|
||||
|
||||
func (m *NetworkPortsModule) getEC2SecurityGroupsPerRegion(region string) []ec2_types.SecurityGroup {
|
||||
m.securityGroupsMutex.RLock()
|
||||
cached, ok := m.securityGroups[region]
|
||||
m.securityGroupsMutex.RUnlock()
|
||||
if ok && cached != nil {
|
||||
return *cached
|
||||
}
|
||||
|
||||
var securityGroups []ec2_types.SecurityGroup
|
||||
var PaginationControl *string
|
||||
|
||||
if m.securityGroups[region] != nil {
|
||||
return *m.securityGroups[region]
|
||||
}
|
||||
|
||||
for {
|
||||
DescribeSecurityGroups, err := m.EC2Client.DescribeSecurityGroups(
|
||||
context.TODO(),
|
||||
@@ -1157,7 +1164,7 @@ func (m *NetworkPortsModule) getEC2SecurityGroupsPerRegion(region string) []ec2_
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1171,9 +1178,9 @@ func (m *NetworkPortsModule) getEC2SecurityGroupsPerRegion(region string) []ec2_
|
||||
}
|
||||
}
|
||||
|
||||
securityGroupsMutex.Lock()
|
||||
m.securityGroupsMutex.Lock()
|
||||
m.securityGroups[region] = &securityGroups
|
||||
securityGroupsMutex.Unlock()
|
||||
m.securityGroupsMutex.Unlock()
|
||||
return securityGroups
|
||||
}
|
||||
|
||||
@@ -1210,13 +1217,16 @@ func (m *NetworkPortsModule) parseSecurityGroup(group ec2_types.SecurityGroup) S
|
||||
}
|
||||
|
||||
func (m *NetworkPortsModule) getEC2NACLsPerRegion(region string) []ec2_types.NetworkAcl {
|
||||
m.naclsMutex.RLock()
|
||||
cached, ok := m.nacls[region]
|
||||
m.naclsMutex.RUnlock()
|
||||
if ok && cached != nil {
|
||||
return *cached
|
||||
}
|
||||
|
||||
var nacls []ec2_types.NetworkAcl
|
||||
var PaginationControl *string
|
||||
|
||||
if m.nacls[region] != nil {
|
||||
return *m.nacls[region]
|
||||
}
|
||||
|
||||
for {
|
||||
DescribeNetworkAcls, err := m.EC2Client.DescribeNetworkAcls(
|
||||
context.TODO(),
|
||||
@@ -1229,7 +1239,7 @@ func (m *NetworkPortsModule) getEC2NACLsPerRegion(region string) []ec2_types.Net
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1243,9 +1253,9 @@ func (m *NetworkPortsModule) getEC2NACLsPerRegion(region string) []ec2_types.Net
|
||||
}
|
||||
}
|
||||
|
||||
naclsMutex.Lock()
|
||||
m.naclsMutex.Lock()
|
||||
m.nacls[region] = &nacls
|
||||
naclsMutex.Unlock()
|
||||
m.naclsMutex.Unlock()
|
||||
return nacls
|
||||
}
|
||||
|
||||
@@ -1315,7 +1325,7 @@ func (m *NetworkPortsModule) getEC2InstancesPerRegion(region string) []ec2_types
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1354,7 +1364,7 @@ func (m *NetworkPortsModule) getEC2NetworkInterfacesPerRegion(interfaceId string
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1389,7 +1399,7 @@ func (m *NetworkPortsModule) getECSClustersPerRegion(region string) []string {
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1425,7 +1435,7 @@ func (m *NetworkPortsModule) getECSServicesPerRegion(clusterArn *string, region
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1488,7 +1498,7 @@ func (m *NetworkPortsModule) getECSTasksPerRegion(service *string, clusterArn *s
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1548,7 +1558,7 @@ func (m *NetworkPortsModule) getEFSSharesPerRegion(region string) []efs_types.Fi
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1584,7 +1594,7 @@ func (m *NetworkPortsModule) getEFSMountTargetsPerRegion(filesystem *string, reg
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1619,7 +1629,7 @@ func (m *NetworkPortsModule) getElastiCacheClustersPerRegion(region string) []el
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1654,7 +1664,7 @@ func (m *NetworkPortsModule) getElastiCacheSubnetGroupsPerRegion(region string)
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1689,7 +1699,7 @@ func (m *NetworkPortsModule) getElastiCacheReplicationGroupsPerRegion(region str
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1724,7 +1734,7 @@ func (m *NetworkPortsModule) getLightsailInstancesPerRegion(region string) []lig
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1759,7 +1769,7 @@ func (m *NetworkPortsModule) getLoadBalancersPerRegion(region string) []elbv2_ty
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1795,7 +1805,7 @@ func (m *NetworkPortsModule) getLBListenerPorts(arn *string, region string) ([]i
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1845,7 +1855,7 @@ func (m *NetworkPortsModule) getRdsClustersPerRegion(region string) []rds_types.
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1880,7 +1890,7 @@ func (m *NetworkPortsModule) getRdsInstancesPerRegion(region string) []rds_types
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
ec2_types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
|
||||
)
|
||||
|
||||
// TestNetworkPortsCacheConcurrentAccess is a regression test for issue #129.
|
||||
// Pre-fix, the cache lookup in getEC2NACLsPerRegion and getEC2SecurityGroupsPerRegion
|
||||
// read m.nacls / m.securityGroups without holding a mutex while writes from
|
||||
// other goroutines were locked, producing a "concurrent map read and map write"
|
||||
// panic under load. Run with `go test -race ./aws/...` to verify the lock fix.
|
||||
func TestNetworkPortsCacheConcurrentAccess(t *testing.T) {
|
||||
m := &NetworkPortsModule{
|
||||
nacls: make(map[string]*[]ec2_types.NetworkAcl),
|
||||
securityGroups: make(map[string]*[]ec2_types.SecurityGroup),
|
||||
}
|
||||
|
||||
// Pre-populate one region so reader goroutines hit the cached fast-path
|
||||
// without needing a live EC2 client.
|
||||
cachedNACLs := []ec2_types.NetworkAcl{}
|
||||
m.nacls["cached"] = &cachedNACLs
|
||||
cachedSGs := []ec2_types.SecurityGroup{}
|
||||
m.securityGroups["cached"] = &cachedSGs
|
||||
|
||||
var wg sync.WaitGroup
|
||||
const readers = 100
|
||||
for i := 0; i < readers; i++ {
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
_ = m.getEC2NACLsPerRegion("cached")
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
_ = m.getEC2SecurityGroupsPerRegion("cached")
|
||||
}()
|
||||
}
|
||||
|
||||
// Writer goroutines populate uncached regions directly through the mutex,
|
||||
// mirroring what the live functions do after a successful AWS fetch. The
|
||||
// readers above must not race with these writes.
|
||||
const writers = 10
|
||||
for i := 0; i < writers; i++ {
|
||||
region := fmt.Sprintf("uncached-%d", i)
|
||||
wg.Add(2)
|
||||
go func(r string) {
|
||||
defer wg.Done()
|
||||
sl := []ec2_types.NetworkAcl{}
|
||||
m.naclsMutex.Lock()
|
||||
m.nacls[r] = &sl
|
||||
m.naclsMutex.Unlock()
|
||||
}(region)
|
||||
go func(r string) {
|
||||
defer wg.Done()
|
||||
sl := []ec2_types.SecurityGroup{}
|
||||
m.securityGroupsMutex.Lock()
|
||||
m.securityGroups[r] = &sl
|
||||
m.securityGroupsMutex.Unlock()
|
||||
}(region)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
+4
-4
@@ -191,7 +191,7 @@ func (m *OrgModule) ProcessMultipleAccounts(AWSProfiles []string, version string
|
||||
|
||||
for _, profile := range AWSProfiles {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.FindMgmtAccounts(profile, version, wg, semaphore, dataReceiver)
|
||||
|
||||
wg.Wait()
|
||||
@@ -244,7 +244,7 @@ func (m *OrgModule) FindMgmtAccounts(profile string, version string, wg *sync.Wa
|
||||
DescribeOrganization, err := sdk.CachedOrganizationsDescribeOrganization(m.OrganizationsClient, aws.ToString(m.Caller.Account))
|
||||
if err != nil {
|
||||
m.modLog.Errorf("Failed to describe organization: %s", err)
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ func (m *OrgModule) addOrgAccounts() {
|
||||
accounts, err := sdk.CachedOrganizationsListAccounts(m.OrganizationsClient, aws.ToString(m.Caller.Account))
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
for _, account := range accounts {
|
||||
@@ -292,7 +292,7 @@ func (m *OrgModule) addOrgAccount() {
|
||||
DescribeOrganization, err := sdk.CachedOrganizationsDescribeOrganization(m.OrganizationsClient, aws.ToString(m.Caller.Account))
|
||||
if err != nil {
|
||||
sharedLogger.Errorf("Failed to describe organization: %s", err)
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
m.Accounts = append(m.Accounts, Account{
|
||||
|
||||
@@ -31,6 +31,7 @@ type OutboundAssumedRolesModule struct {
|
||||
Goroutines int
|
||||
AWSProfile string
|
||||
WrapTable bool
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
// Main module data
|
||||
OutboundAssumeRoleEntries []OutboundAssumeRoleEntry
|
||||
@@ -170,7 +171,7 @@ func (m *OutboundAssumedRolesModule) PrintOutboundRoleTrusts(days int, outputDir
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, semaphore, dataReceiver)
|
||||
|
||||
}
|
||||
@@ -294,8 +295,12 @@ func (m *OutboundAssumedRolesModule) Receiver(receiver chan OutboundAssumeRoleEn
|
||||
|
||||
func (m *OutboundAssumedRolesModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan OutboundAssumeRoleEntry) {
|
||||
defer wg.Done()
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("cloudtrail", r)
|
||||
if err != nil {
|
||||
@@ -303,10 +308,10 @@ func (m *OutboundAssumedRolesModule) executeChecks(r string, wg *sync.WaitGroup,
|
||||
}
|
||||
if res {
|
||||
// wg.Add(1)
|
||||
// m.CommandCounter.Total++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
// m.getAssumeRoleLogEntriesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.getCrossAccountBatchGetImageEntriesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
|
||||
@@ -314,8 +319,8 @@ func (m *OutboundAssumedRolesModule) executeChecks(r string, wg *sync.WaitGroup,
|
||||
|
||||
func (m *OutboundAssumedRolesModule) getAssumeRoleLogEntriesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan OutboundAssumeRoleEntry) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -353,7 +358,7 @@ func (m *OutboundAssumedRolesModule) getAssumeRoleLogEntriesPerRegion(r string,
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -407,8 +412,8 @@ func (m *OutboundAssumedRolesModule) getAssumeRoleLogEntriesPerRegion(r string,
|
||||
// get cross account batch get image entries
|
||||
func (m *OutboundAssumedRolesModule) getCrossAccountBatchGetImageEntriesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan OutboundAssumeRoleEntry) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -446,7 +451,7 @@ func (m *OutboundAssumedRolesModule) getCrossAccountBatchGetImageEntriesPerRegio
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -196,8 +196,8 @@ func (m *IamPermissionsModule) PrintIamPermissions(outputDirectory string, verbo
|
||||
func (m *IamPermissionsModule) GetGAAD() {
|
||||
GAAD, err := sdk.CachedIAMGetAccountAuthorizationDetails(m.IAMClient, aws.ToString(m.Caller.Account))
|
||||
if err != nil {
|
||||
TxtLogger.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.modLog.Error(err)
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -446,7 +446,7 @@ func (m *PmapperModule) writeLoot(outputDirectory string, verbosity int) string
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
panic(err.Error())
|
||||
}
|
||||
lootFilePath := filepath.Join(path, "pmapper.txt")
|
||||
|
||||
+8
-8
@@ -97,7 +97,7 @@ func (m *IamPrincipalsModule) PrintIamPrincipals(outputDirectory string, verbosi
|
||||
// done := make(chan bool)
|
||||
// go internal.SpinUntil(m.output.CallingModule, &m.CommandCounter, done)
|
||||
// wg.Add(1)
|
||||
// m.CommandCounter.Pending++
|
||||
// m.CommandCounter.IncrPending()
|
||||
//m.executeChecks(wg)
|
||||
// wg.Wait()
|
||||
// done <- true
|
||||
@@ -237,13 +237,13 @@ func (m *IamPrincipalsModule) PrintIamPrincipals(outputDirectory string, verbosi
|
||||
/* UNUSED CODE BLOCK - PLEASE REVIEW AND DELETE IF APPLICABLE
|
||||
func (m *IamPrincipalsModule) executeChecks(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
m.getIAMUsers()
|
||||
m.getIAMRoles()
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -256,7 +256,7 @@ func (m *IamPrincipalsModule) addIAMUsersToTable() {
|
||||
ListUsers, err := sdk.CachedIamListUsers(m.IAMClient, aws.ToString(m.Caller.Account))
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
for _, user := range ListUsers {
|
||||
@@ -288,7 +288,7 @@ func (m *IamPrincipalsModule) addIAMRolesToTable() {
|
||||
ListRoles, err := sdk.CachedIamListRoles(m.IAMClient, aws.ToString(m.Caller.Account))
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
for _, role := range ListRoles {
|
||||
|
||||
+15
-10
@@ -29,6 +29,7 @@ type RAMModule struct {
|
||||
Goroutines int
|
||||
AWSProfile string
|
||||
WrapTable bool
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
// Main module data
|
||||
Resources []Resource
|
||||
@@ -78,7 +79,7 @@ func (m *RAMModule) PrintRAM(outputDirectory string, verbosity int) {
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, dataReceiver)
|
||||
|
||||
}
|
||||
@@ -178,20 +179,24 @@ func (m *RAMModule) PrintRAM(outputDirectory string, verbosity int) {
|
||||
|
||||
func (m *RAMModule) executeChecks(r string, wg *sync.WaitGroup, dataReceiver chan Resource) {
|
||||
defer wg.Done()
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("ram", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
m.getRAMResourcesPerRegion(r, dataReceiver)
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,7 +235,7 @@ func (m *RAMModule) getRAMResourcesPerRegion(r string, dataReceiver chan Resourc
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -254,7 +259,7 @@ func (m *RAMModule) getRAMResourcesPerRegion(r string, dataReceiver chan Resourc
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
+425
-47
@@ -19,6 +19,11 @@ import (
|
||||
)
|
||||
|
||||
type ResourceTrustsModule struct {
|
||||
KMSClient *sdk.KMSClientInterface
|
||||
APIGatewayClient *sdk.APIGatewayClientInterface
|
||||
EC2Client *sdk.AWSEC2ClientInterface
|
||||
OpenSearchClient *sdk.OpenSearchClientInterface
|
||||
|
||||
// General configuration data
|
||||
Caller sts.GetCallerIdentityOutput
|
||||
AWSRegions []string
|
||||
@@ -31,6 +36,7 @@ type ResourceTrustsModule struct {
|
||||
AWSProfileProvided string
|
||||
AWSProfileStub string
|
||||
CloudFoxVersion string
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
Resources2 []Resource2
|
||||
CommandCounter internal.CommandCounter
|
||||
@@ -55,7 +61,7 @@ type Resource2 struct {
|
||||
HasConditions string
|
||||
}
|
||||
|
||||
func (m *ResourceTrustsModule) PrintResources(outputDirectory string, verbosity int) {
|
||||
func (m *ResourceTrustsModule) PrintResources(outputDirectory string, verbosity int, includeKms bool) {
|
||||
// These struct values are used by the output module
|
||||
m.output.Verbosity = verbosity
|
||||
m.output.Directory = outputDirectory
|
||||
@@ -72,13 +78,21 @@ func (m *ResourceTrustsModule) PrintResources(outputDirectory string, verbosity
|
||||
m.output.FilePath = filepath.Join(outputDirectory, "cloudfox-output", "aws", fmt.Sprintf("%s-%s", m.AWSProfileProvided, aws.ToString(m.Caller.Account)))
|
||||
|
||||
fmt.Printf("[%s][%s] Enumerating Resources with resource policies for account %s.\n", cyan(m.output.CallingModule), cyan(m.AWSProfileStub), aws.ToString(m.Caller.Account))
|
||||
fmt.Printf("[%s][%s] Supported Services: CodeBuild, ECR, EFS, Glue, Lambda, SecretsManager, S3, SNS, SQS\n", cyan(m.output.CallingModule), cyan(m.AWSProfileStub))
|
||||
// if kms feature flag is enabled include kms in the supported services
|
||||
if includeKms {
|
||||
fmt.Printf("[%s][%s] Supported Services: APIGateway, CodeBuild, ECR, EFS, Glue, KMS, Lambda, Opensearch, SecretsManager, S3, SNS, SQS, VpcEndpoint\n",
|
||||
cyan(m.output.CallingModule), cyan(m.AWSProfileStub))
|
||||
} else {
|
||||
fmt.Printf("[%s][%s] Supported Services: APIGateway, CodeBuild, ECR, EFS, Glue, Lambda, Opensearch, SecretsManager, S3, SNS, "+
|
||||
"SQS, VpcEndpoint (KMS requires --include-kms feature flag)\n",
|
||||
cyan(m.output.CallingModule), cyan(m.AWSProfileStub))
|
||||
}
|
||||
wg := new(sync.WaitGroup)
|
||||
semaphore := make(chan struct{}, m.Goroutines)
|
||||
|
||||
// Create a channel to signal the spinner aka task status goroutine to finish
|
||||
spinnerDone := make(chan bool)
|
||||
//fire up the the task status spinner/updated
|
||||
//fire up the task status spinner/updated
|
||||
go internal.SpinUntil(m.output.CallingModule, &m.CommandCounter, spinnerDone, "tasks")
|
||||
|
||||
//create a channel to receive the objects
|
||||
@@ -90,8 +104,8 @@ func (m *ResourceTrustsModule) PrintResources(outputDirectory string, verbosity
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
go m.executeChecks(region, wg, semaphore, dataReceiver)
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, semaphore, dataReceiver, includeKms)
|
||||
|
||||
}
|
||||
wg.Add(1)
|
||||
@@ -186,21 +200,24 @@ func (m *ResourceTrustsModule) PrintResources(outputDirectory string, verbosity
|
||||
fmt.Printf("[%s][%s] No resource policies found, skipping the creation of an output file.\n", cyan(m.output.CallingModule), cyan(m.AWSProfileStub))
|
||||
}
|
||||
fmt.Printf("[%s][%s] For context and next steps: https://github.com/BishopFox/cloudfox/wiki/AWS-Commands#%s\n", cyan(m.output.CallingModule), cyan(m.AWSProfileStub), m.output.CallingModule)
|
||||
|
||||
}
|
||||
|
||||
func (m *ResourceTrustsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Resource2) {
|
||||
func (m *ResourceTrustsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Resource2, includeKms bool) {
|
||||
defer wg.Done()
|
||||
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("sns", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getSNSTopicsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -210,7 +227,7 @@ func (m *ResourceTrustsModule) executeChecks(r string, wg *sync.WaitGroup, semap
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getSQSQueuesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -220,7 +237,7 @@ func (m *ResourceTrustsModule) executeChecks(r string, wg *sync.WaitGroup, semap
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getECRRecordsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -230,47 +247,98 @@ func (m *ResourceTrustsModule) executeChecks(r string, wg *sync.WaitGroup, semap
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getCodeBuildResourcePoliciesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
|
||||
res, err = servicemap.IsServiceInRegion("lambda", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getLambdaPolicyPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
|
||||
res, err = servicemap.IsServiceInRegion("efs", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getEFSfilesystemPoliciesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
res, err = servicemap.IsServiceInRegion("secretsmanager", r)
|
||||
|
||||
res, err = servicemap.IsServiceInRegion("secrets-manager", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getSecretsManagerSecretsPoliciesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
|
||||
res, err = servicemap.IsServiceInRegion("glue", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getGlueResourcePoliciesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
|
||||
if includeKms && m.KMSClient != nil {
|
||||
res, err = servicemap.IsServiceInRegion("kms", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getKMSPoliciesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
}
|
||||
|
||||
if m.APIGatewayClient != nil {
|
||||
res, err = servicemap.IsServiceInRegion("api-gateway", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getAPIGatewayPoliciesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
}
|
||||
|
||||
if m.EC2Client != nil {
|
||||
res, err = servicemap.IsServiceInRegion("ec2", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getVPCEndpointPoliciesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
}
|
||||
|
||||
if m.OpenSearchClient != nil {
|
||||
res, err = servicemap.IsServiceInRegion("opensearch-service", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getOpenSearchPoliciesPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ResourceTrustsModule) Receiver(receiver chan Resource2, receiverDone chan bool) {
|
||||
@@ -288,8 +356,8 @@ func (m *ResourceTrustsModule) Receiver(receiver chan Resource2, receiverDone ch
|
||||
|
||||
func (m *ResourceTrustsModule) getSNSTopicsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Resource2) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -306,19 +374,20 @@ func (m *ResourceTrustsModule) getSNSTopicsPerRegion(r string, wg *sync.WaitGrou
|
||||
|
||||
for _, t := range ListTopics {
|
||||
var statementSummaryInEnglish string
|
||||
var isInteresting string = "No"
|
||||
var isInteresting = "No"
|
||||
topic, err := cloudFoxSNSClient.getTopicWithAttributes(aws.ToString(t.TopicArn), r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
parsedArn, err := arn.Parse(aws.ToString(t.TopicArn))
|
||||
if err != nil {
|
||||
topic.Name = aws.ToString(t.TopicArn)
|
||||
} else {
|
||||
topic.Name = parsedArn.Resource
|
||||
topic.Region = parsedArn.Region
|
||||
}
|
||||
topic.Name = parsedArn.Resource
|
||||
topic.Region = parsedArn.Region
|
||||
|
||||
// check if topic is public or not
|
||||
if topic.Policy.IsPublic() {
|
||||
@@ -364,8 +433,8 @@ func (m *ResourceTrustsModule) getSNSTopicsPerRegion(r string, wg *sync.WaitGrou
|
||||
|
||||
func (m *ResourceTrustsModule) getS3Buckets(wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Resource2) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -447,8 +516,8 @@ func (m *ResourceTrustsModule) getS3Buckets(wg *sync.WaitGroup, semaphore chan s
|
||||
|
||||
func (m *ResourceTrustsModule) getSQSQueuesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Resource2) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -470,7 +539,7 @@ func (m *ResourceTrustsModule) getSQSQueuesPerRegion(r string, wg *sync.WaitGrou
|
||||
queue, err := cloudFoxSQSClient.getQueueWithAttributes(q, r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -509,8 +578,8 @@ func (m *ResourceTrustsModule) getSQSQueuesPerRegion(r string, wg *sync.WaitGrou
|
||||
|
||||
func (m *ResourceTrustsModule) getECRRecordsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Resource2) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -532,7 +601,7 @@ func (m *ResourceTrustsModule) getECRRecordsPerRegion(r string, wg *sync.WaitGro
|
||||
repoPolicy, err := cloudFoxECRClient.getECRRepositoryPolicy(r, aws.ToString(repo.RepositoryName))
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -573,8 +642,8 @@ func (m *ResourceTrustsModule) getECRRecordsPerRegion(r string, wg *sync.WaitGro
|
||||
|
||||
func (m *ResourceTrustsModule) getCodeBuildResourcePoliciesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Resource2) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -597,7 +666,7 @@ func (m *ResourceTrustsModule) getCodeBuildResourcePoliciesPerRegion(r string, w
|
||||
project, err := sdk.CachedCodeBuildBatchGetProjects(cloudFoxCodeBuildClient.CodeBuildClient, aws.ToString(cloudFoxCodeBuildClient.Caller.Account), r, p)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -647,8 +716,8 @@ func (m *ResourceTrustsModule) getCodeBuildResourcePoliciesPerRegion(r string, w
|
||||
|
||||
func (m *ResourceTrustsModule) getLambdaPolicyPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Resource2) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -670,7 +739,7 @@ func (m *ResourceTrustsModule) getLambdaPolicyPerRegion(r string, wg *sync.WaitG
|
||||
functionPolicy, err := cloudFoxLambdaClient.getResourcePolicy(r, aws.ToString(f.FunctionName))
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -712,8 +781,8 @@ func (m *ResourceTrustsModule) getLambdaPolicyPerRegion(r string, wg *sync.WaitG
|
||||
|
||||
func (m *ResourceTrustsModule) getEFSfilesystemPoliciesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Resource2) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -735,7 +804,7 @@ func (m *ResourceTrustsModule) getEFSfilesystemPoliciesPerRegion(r string, wg *s
|
||||
fsPolicy, err := sdk.CachedDescribeFileSystemPolicy(cloudFoxEFSClient.EFSClient, aws.ToString(fs.FileSystemId), r, aws.ToString(m.Caller.Account))
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -782,8 +851,8 @@ func (m *ResourceTrustsModule) getEFSfilesystemPoliciesPerRegion(r string, wg *s
|
||||
// It takes the region to search in, the WaitGroup to use, the semaphore to use, and the dataReceiver channel to send results to.
|
||||
func (m *ResourceTrustsModule) getSecretsManagerSecretsPoliciesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Resource2) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -805,7 +874,7 @@ func (m *ResourceTrustsModule) getSecretsManagerSecretsPoliciesPerRegion(r strin
|
||||
secretPolicy, err := sdk.CachedSecretsManagerGetResourcePolicy(cloudFoxSecretsManagerClient, aws.ToString(s.ARN), r, aws.ToString(m.Caller.Account))
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -846,10 +915,319 @@ func (m *ResourceTrustsModule) getSecretsManagerSecretsPoliciesPerRegion(r strin
|
||||
}
|
||||
}
|
||||
|
||||
// getKMSPoliciesPerRegion retrieves the resource policies for all KMS keys in a specified region.
|
||||
// It sends the resulting Resource2 objects to the dataReceiver channel.
|
||||
// It uses a semaphore to limit the number of concurrent requests and a WaitGroup to wait for all requests to complete.
|
||||
// It takes the region to search in, the WaitGroup to use, the semaphore to use, and the dataReceiver channel to send results to.
|
||||
func (m *ResourceTrustsModule) getKMSPoliciesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Resource2) {
|
||||
defer func() {
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
}()
|
||||
semaphore <- struct{}{}
|
||||
defer func() { <-semaphore }()
|
||||
|
||||
listKeys, err := sdk.CachedKMSListKeys(*m.KMSClient, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, key := range listKeys {
|
||||
var isPublic string
|
||||
var statementSummaryInEnglish string
|
||||
var isInteresting = "No"
|
||||
|
||||
keyPolicy, err := sdk.CachedKMSGetKeyPolicy(*m.KMSClient, aws.ToString(m.Caller.Account), r, aws.ToString(key.KeyId))
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
|
||||
if keyPolicy.IsPublic() {
|
||||
isPublic = magenta("Yes")
|
||||
isInteresting = magenta("Yes")
|
||||
} else {
|
||||
isPublic = "No"
|
||||
}
|
||||
|
||||
if !keyPolicy.IsEmpty() {
|
||||
for i, statement := range keyPolicy.Statement {
|
||||
prefix := ""
|
||||
if len(keyPolicy.Statement) > 1 {
|
||||
prefix = fmt.Sprintf("Statement %d says: ", i)
|
||||
statementSummaryInEnglish = prefix + statement.GetStatementSummaryInEnglish(*m.Caller.Account) + "\n"
|
||||
} else {
|
||||
statementSummaryInEnglish = statement.GetStatementSummaryInEnglish(*m.Caller.Account)
|
||||
}
|
||||
|
||||
statementSummaryInEnglish = strings.TrimSuffix(statementSummaryInEnglish, "\n")
|
||||
if isResourcePolicyInteresting(statementSummaryInEnglish) {
|
||||
//magenta(statementSummaryInEnglish)
|
||||
isInteresting = magenta("Yes")
|
||||
}
|
||||
|
||||
dataReceiver <- Resource2{
|
||||
AccountID: aws.ToString(m.Caller.Account),
|
||||
ARN: aws.ToString(key.KeyArn),
|
||||
ResourcePolicySummary: statementSummaryInEnglish,
|
||||
Public: isPublic,
|
||||
Name: aws.ToString(key.KeyId),
|
||||
Region: r,
|
||||
Interesting: isInteresting,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ResourceTrustsModule) getAPIGatewayPoliciesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Resource2) {
|
||||
defer func() {
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
}()
|
||||
semaphore <- struct{}{}
|
||||
defer func() { <-semaphore }()
|
||||
|
||||
restAPIs, err := sdk.CachedApiGatewayGetRestAPIs(*m.APIGatewayClient, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, restAPI := range restAPIs {
|
||||
|
||||
if sdk.IsPublicApiGateway(&restAPI) {
|
||||
continue
|
||||
}
|
||||
|
||||
var isPublic = "No"
|
||||
var statementSummaryInEnglish string
|
||||
var isInteresting = "No"
|
||||
|
||||
if restAPI.Policy != nil && *restAPI.Policy != "" {
|
||||
|
||||
// remove backslashes from the policy JSON
|
||||
policyJson := strings.ReplaceAll(aws.ToString(restAPI.Policy), `\"`, `"`)
|
||||
|
||||
restAPIPolicy, err := policy.ParseJSONPolicy([]byte(policyJson))
|
||||
if err != nil {
|
||||
sharedLogger.Error(fmt.Errorf("parsing policy (%s) as JSON: %s", aws.ToString(restAPI.Name), err))
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
|
||||
if !restAPIPolicy.IsEmpty() {
|
||||
for i, statement := range restAPIPolicy.Statement {
|
||||
prefix := ""
|
||||
if len(restAPIPolicy.Statement) > 1 {
|
||||
prefix = fmt.Sprintf("Statement %d says: ", i)
|
||||
statementSummaryInEnglish = prefix + statement.GetStatementSummaryInEnglish(*m.Caller.Account) + "\n"
|
||||
} else {
|
||||
statementSummaryInEnglish = statement.GetStatementSummaryInEnglish(*m.Caller.Account)
|
||||
}
|
||||
|
||||
statementSummaryInEnglish = strings.TrimSuffix(statementSummaryInEnglish, "\n")
|
||||
if isResourcePolicyInteresting(statementSummaryInEnglish) {
|
||||
//magenta(statementSummaryInEnglish)
|
||||
isInteresting = magenta("Yes")
|
||||
}
|
||||
|
||||
dataReceiver <- Resource2{
|
||||
AccountID: aws.ToString(m.Caller.Account),
|
||||
ARN: fmt.Sprintf("arn:aws:execute-api:%s:%s:%s/*", r, *m.Caller.Account, *restAPI.Id),
|
||||
ResourcePolicySummary: statementSummaryInEnglish,
|
||||
Public: isPublic,
|
||||
Name: aws.ToString(restAPI.Name),
|
||||
Region: r,
|
||||
Interesting: isInteresting,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dataReceiver <- Resource2{
|
||||
AccountID: aws.ToString(m.Caller.Account),
|
||||
ARN: fmt.Sprintf("arn:aws:execute-api:%s:%s:%s/*", r, *m.Caller.Account, *restAPI.Id),
|
||||
ResourcePolicySummary: statementSummaryInEnglish,
|
||||
Public: isPublic,
|
||||
Name: aws.ToString(restAPI.Name),
|
||||
Region: r,
|
||||
Interesting: isInteresting,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ResourceTrustsModule) getVPCEndpointPoliciesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Resource2) {
|
||||
defer func() {
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
}()
|
||||
semaphore <- struct{}{}
|
||||
defer func() { <-semaphore }()
|
||||
|
||||
vpcEndpoints, err := sdk.CachedEC2DescribeVpcEndpoints(*m.EC2Client, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, vpcEndpoint := range vpcEndpoints {
|
||||
var isPublic = "No"
|
||||
var statementSummaryInEnglish string
|
||||
var isInteresting = "No"
|
||||
|
||||
if vpcEndpoint.PolicyDocument != nil && *vpcEndpoint.PolicyDocument != "" {
|
||||
vpcEndpointPolicyJson := aws.ToString(vpcEndpoint.PolicyDocument)
|
||||
vpcEndpointPolicy, err := policy.ParseJSONPolicy([]byte(vpcEndpointPolicyJson))
|
||||
if err != nil {
|
||||
sharedLogger.Error(fmt.Errorf("parsing policy (%s) as JSON: %s", aws.ToString(vpcEndpoint.VpcEndpointId), err))
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
|
||||
if !vpcEndpointPolicy.IsEmpty() {
|
||||
for i, statement := range vpcEndpointPolicy.Statement {
|
||||
prefix := ""
|
||||
if len(vpcEndpointPolicy.Statement) > 1 {
|
||||
prefix = fmt.Sprintf("Statement %d says: ", i)
|
||||
statementSummaryInEnglish = prefix + statement.GetStatementSummaryInEnglish(*m.Caller.Account) + "\n"
|
||||
} else {
|
||||
statementSummaryInEnglish = statement.GetStatementSummaryInEnglish(*m.Caller.Account)
|
||||
}
|
||||
|
||||
statementSummaryInEnglish = strings.TrimSuffix(statementSummaryInEnglish, "\n")
|
||||
if isResourcePolicyInteresting(statementSummaryInEnglish) {
|
||||
//magenta(statementSummaryInEnglish)
|
||||
isInteresting = magenta("Yes")
|
||||
}
|
||||
|
||||
dataReceiver <- Resource2{
|
||||
AccountID: aws.ToString(m.Caller.Account),
|
||||
ARN: fmt.Sprintf("arn:aws:ec2:%s:%s:vpc-endpoint/%s", r, aws.ToString(m.Caller.Account), aws.ToString(vpcEndpoint.VpcEndpointId)),
|
||||
ResourcePolicySummary: statementSummaryInEnglish,
|
||||
Public: isPublic,
|
||||
Name: aws.ToString(vpcEndpoint.VpcEndpointId),
|
||||
Region: r,
|
||||
Interesting: isInteresting,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dataReceiver <- Resource2{
|
||||
AccountID: aws.ToString(m.Caller.Account),
|
||||
ARN: fmt.Sprintf("arn:aws:ec2:%s:%s:vpc-endpoint/%s", r, aws.ToString(m.Caller.Account), aws.ToString(vpcEndpoint.VpcEndpointId)),
|
||||
ResourcePolicySummary: statementSummaryInEnglish,
|
||||
Public: isPublic,
|
||||
Name: aws.ToString(vpcEndpoint.VpcEndpointId),
|
||||
Region: r,
|
||||
Interesting: isInteresting,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ResourceTrustsModule) getOpenSearchPoliciesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Resource2) {
|
||||
defer func() {
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
}()
|
||||
semaphore <- struct{}{}
|
||||
defer func() { <-semaphore }()
|
||||
|
||||
openSearchDomains, err := sdk.CachedOpenSearchListDomainNames(*m.OpenSearchClient, aws.ToString(m.Caller.Account), r)
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
for _, openSearchDomain := range openSearchDomains {
|
||||
var isPublic string
|
||||
var statementSummaryInEnglish string
|
||||
var isInteresting = "No"
|
||||
|
||||
openSearchDomainConfig, err := sdk.CachedOpenSearchDescribeDomainConfig(*m.OpenSearchClient, aws.ToString(m.Caller.Account), r, aws.ToString(openSearchDomain.DomainName))
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
|
||||
if aws.ToBool(openSearchDomainConfig.AdvancedSecurityOptions.Options.Enabled) {
|
||||
isPublic = "No"
|
||||
} else {
|
||||
isPublic = magenta("Yes")
|
||||
isInteresting = magenta("Yes")
|
||||
}
|
||||
|
||||
openSearchDomainStatus, err := sdk.CachedOpenSearchDescribeDomain(*m.OpenSearchClient, aws.ToString(m.Caller.Account), r, aws.ToString(openSearchDomain.DomainName))
|
||||
if err != nil {
|
||||
sharedLogger.Error(err.Error())
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
|
||||
if openSearchDomainStatus.AccessPolicies != nil && *openSearchDomainStatus.AccessPolicies != "" {
|
||||
|
||||
// remove backslashes from the policy JSON
|
||||
policyJson := strings.ReplaceAll(aws.ToString(openSearchDomainStatus.AccessPolicies), `\"`, `"`)
|
||||
|
||||
openSearchDomainPolicy, err := policy.ParseJSONPolicy([]byte(policyJson))
|
||||
if err != nil {
|
||||
sharedLogger.Error(fmt.Errorf("parsing policy (%s) as JSON: %s", aws.ToString(openSearchDomainStatus.ARN), err))
|
||||
m.CommandCounter.IncrError()
|
||||
continue
|
||||
}
|
||||
|
||||
if !openSearchDomainPolicy.IsEmpty() {
|
||||
for i, statement := range openSearchDomainPolicy.Statement {
|
||||
prefix := ""
|
||||
if len(openSearchDomainPolicy.Statement) > 1 {
|
||||
prefix = fmt.Sprintf("Statement %d says: ", i)
|
||||
statementSummaryInEnglish = prefix + statement.GetStatementSummaryInEnglish(*m.Caller.Account) + "\n"
|
||||
} else {
|
||||
statementSummaryInEnglish = statement.GetStatementSummaryInEnglish(*m.Caller.Account)
|
||||
}
|
||||
|
||||
statementSummaryInEnglish = strings.TrimSuffix(statementSummaryInEnglish, "\n")
|
||||
if isResourcePolicyInteresting(statementSummaryInEnglish) {
|
||||
//magenta(statementSummaryInEnglish)
|
||||
isInteresting = magenta("Yes")
|
||||
}
|
||||
|
||||
dataReceiver <- Resource2{
|
||||
AccountID: aws.ToString(m.Caller.Account),
|
||||
ARN: aws.ToString(openSearchDomainStatus.ARN),
|
||||
ResourcePolicySummary: statementSummaryInEnglish,
|
||||
Public: isPublic,
|
||||
Name: aws.ToString(openSearchDomain.DomainName),
|
||||
Region: r,
|
||||
Interesting: isInteresting,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dataReceiver <- Resource2{
|
||||
AccountID: aws.ToString(m.Caller.Account),
|
||||
ARN: aws.ToString(openSearchDomainStatus.ARN),
|
||||
ResourcePolicySummary: statementSummaryInEnglish,
|
||||
Public: isPublic,
|
||||
Name: aws.ToString(openSearchDomain.DomainName),
|
||||
Region: r,
|
||||
Interesting: isInteresting,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ResourceTrustsModule) getGlueResourcePoliciesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Resource2) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -905,7 +1283,7 @@ func (m *ResourceTrustsModule) getGlueResourcePoliciesPerRegion(r string, wg *sy
|
||||
}
|
||||
|
||||
func isResourcePolicyInteresting(statementSummaryInEnglish string) bool {
|
||||
// check if the statement has any of the following items, but make sure the check is case insensitive
|
||||
// check if the statement has any of the following items, but make sure the check is case-insensitive
|
||||
// if it does, then return true
|
||||
// if it doesn't, then return false
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@ package aws
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/BishopFox/cloudfox/aws/sdk"
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sts"
|
||||
)
|
||||
|
||||
func TestIsResourcePolicyInteresting(t *testing.T) {
|
||||
@@ -41,3 +45,218 @@ func TestIsResourcePolicyInteresting(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKMSResourceTrusts(t *testing.T) {
|
||||
|
||||
mockedKMSClient := &sdk.MockedKMSClient{}
|
||||
var kmsClient sdk.KMSClientInterface = mockedKMSClient
|
||||
|
||||
testCases := []struct {
|
||||
outputDirectory string
|
||||
verbosity int
|
||||
testModule ResourceTrustsModule
|
||||
expectedResult []Resource2
|
||||
}{
|
||||
{
|
||||
outputDirectory: ".",
|
||||
verbosity: 2,
|
||||
testModule: ResourceTrustsModule{
|
||||
KMSClient: &kmsClient,
|
||||
APIGatewayClient: nil,
|
||||
EC2Client: nil,
|
||||
AWSRegions: []string{"us-west-2"},
|
||||
Caller: sts.GetCallerIdentityOutput{
|
||||
Account: aws.String("123456789012"),
|
||||
Arn: aws.String("arn:aws:iam::123456789012:user/cloudfox_unit_tests"),
|
||||
},
|
||||
Goroutines: 30,
|
||||
},
|
||||
expectedResult: []Resource2{
|
||||
{
|
||||
Name: "key1",
|
||||
ARN: "arn:aws:kms:us-west-2:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc.testModule.PrintResources(tc.outputDirectory, tc.verbosity, true)
|
||||
for index, expectedResource2 := range tc.expectedResult {
|
||||
if expectedResource2.Name != tc.testModule.Resources2[index].Name {
|
||||
t.Fatal("Resource name does not match expected value")
|
||||
}
|
||||
if expectedResource2.ARN != tc.testModule.Resources2[index].ARN {
|
||||
t.Fatal("Resource ARN does not match expected value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIGatewayResourceTrusts(t *testing.T) {
|
||||
|
||||
mockedAPIGatewayClient := &sdk.MockedAWSAPIGatewayClient{}
|
||||
var apiGatewayClient sdk.APIGatewayClientInterface = mockedAPIGatewayClient
|
||||
|
||||
testCases := []struct {
|
||||
outputDirectory string
|
||||
verbosity int
|
||||
testModule ResourceTrustsModule
|
||||
expectedResult []Resource2
|
||||
}{
|
||||
{
|
||||
outputDirectory: ".",
|
||||
verbosity: 2,
|
||||
testModule: ResourceTrustsModule{
|
||||
KMSClient: nil,
|
||||
APIGatewayClient: &apiGatewayClient,
|
||||
EC2Client: nil,
|
||||
AWSRegions: []string{"us-west-2"},
|
||||
Caller: sts.GetCallerIdentityOutput{
|
||||
Account: aws.String("123456789012"),
|
||||
Arn: aws.String("arn:aws:iam::123456789012:user/cloudfox_unit_tests"),
|
||||
},
|
||||
Goroutines: 30,
|
||||
},
|
||||
expectedResult: []Resource2{
|
||||
{
|
||||
Name: "api1",
|
||||
ARN: "arn:aws:execute-api:us-west-2:123456789012:abcdefg/*",
|
||||
Public: "No",
|
||||
Interesting: "Yes",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc.testModule.PrintResources(tc.outputDirectory, tc.verbosity, false)
|
||||
for index, expectedResource2 := range tc.expectedResult {
|
||||
if expectedResource2.Name != tc.testModule.Resources2[index].Name {
|
||||
t.Fatal("Resource name does not match expected value")
|
||||
}
|
||||
if expectedResource2.ARN != tc.testModule.Resources2[index].ARN {
|
||||
t.Fatal("Resource ARN does not match expected value")
|
||||
}
|
||||
if expectedResource2.Public != tc.testModule.Resources2[index].Public {
|
||||
t.Fatal("Resource Public does not match expected value")
|
||||
}
|
||||
if expectedResource2.Interesting != tc.testModule.Resources2[index].Interesting {
|
||||
t.Fatal("Resource Interesting does not match expected value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVpcEndpointResourceTrusts(t *testing.T) {
|
||||
|
||||
mockedEC2Client := &sdk.MockedEC2Client2{}
|
||||
var ec2Client sdk.AWSEC2ClientInterface = mockedEC2Client
|
||||
|
||||
testCases := []struct {
|
||||
outputDirectory string
|
||||
verbosity int
|
||||
testModule ResourceTrustsModule
|
||||
expectedResult []Resource2
|
||||
}{
|
||||
{
|
||||
outputDirectory: ".",
|
||||
verbosity: 2,
|
||||
testModule: ResourceTrustsModule{
|
||||
KMSClient: nil,
|
||||
APIGatewayClient: nil,
|
||||
EC2Client: &ec2Client,
|
||||
AWSRegions: []string{"us-west-2"},
|
||||
Caller: sts.GetCallerIdentityOutput{
|
||||
Account: aws.String("123456789012"),
|
||||
Arn: aws.String("arn:aws:iam::123456789012:user/cloudfox_unit_tests"),
|
||||
},
|
||||
Goroutines: 30,
|
||||
},
|
||||
expectedResult: []Resource2{
|
||||
{
|
||||
Name: "vpce-1234567890abcdefg",
|
||||
ARN: "vpce-1234567890abcdefg",
|
||||
Public: "No",
|
||||
},
|
||||
{
|
||||
Name: "vpce-1234567890abcdefh",
|
||||
ARN: "vpce-1234567890abcdefh",
|
||||
Public: "No",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc.testModule.PrintResources(tc.outputDirectory, tc.verbosity, false)
|
||||
for index, expectedResource2 := range tc.expectedResult {
|
||||
if expectedResource2.Name != tc.testModule.Resources2[index].Name {
|
||||
t.Fatal("Resource name does not match expected value")
|
||||
}
|
||||
if expectedResource2.ARN != tc.testModule.Resources2[index].ARN {
|
||||
t.Fatal("Resource ARN does not match expected value")
|
||||
}
|
||||
if expectedResource2.Public != tc.testModule.Resources2[index].Public {
|
||||
t.Fatal("Resource Public does not match expected value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenSearchResourceTrusts(t *testing.T) {
|
||||
|
||||
mockedOpenSearchClient := &sdk.MockedOpenSearchClient{}
|
||||
var openSearchClient sdk.OpenSearchClientInterface = mockedOpenSearchClient
|
||||
|
||||
testCases := []struct {
|
||||
outputDirectory string
|
||||
verbosity int
|
||||
testModule ResourceTrustsModule
|
||||
expectedResult []Resource2
|
||||
}{
|
||||
{
|
||||
outputDirectory: ".",
|
||||
verbosity: 2,
|
||||
testModule: ResourceTrustsModule{
|
||||
KMSClient: nil,
|
||||
APIGatewayClient: nil,
|
||||
EC2Client: nil,
|
||||
OpenSearchClient: &openSearchClient,
|
||||
AWSRegions: []string{"us-west-2"},
|
||||
Caller: sts.GetCallerIdentityOutput{
|
||||
Account: aws.String("123456789012"),
|
||||
Arn: aws.String("arn:aws:iam::123456789012:user/cloudfox_unit_tests"),
|
||||
},
|
||||
Goroutines: 30,
|
||||
},
|
||||
expectedResult: []Resource2{
|
||||
{
|
||||
Name: "domain1",
|
||||
ARN: "arn:aws:es:us-east-1:123456789012:domain/domain1",
|
||||
Public: "No",
|
||||
},
|
||||
{
|
||||
Name: "domain2",
|
||||
ARN: "arn:aws:es:us-east-1:123456789012:domain/domain2",
|
||||
Public: "Yes",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc.testModule.PrintResources(tc.outputDirectory, tc.verbosity, false)
|
||||
for index, expectedResource2 := range tc.expectedResult {
|
||||
if expectedResource2.Name != tc.testModule.Resources2[index].Name {
|
||||
t.Fatal("Resource name does not match expected value")
|
||||
}
|
||||
if expectedResource2.ARN != tc.testModule.Resources2[index].ARN {
|
||||
t.Fatal("Resource ARN does not match expected value")
|
||||
}
|
||||
if expectedResource2.Public != tc.testModule.Resources2[index].Public {
|
||||
t.Fatal("Resource Public does not match expected value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -466,6 +466,8 @@ func parseFederatedTrustPolicy(statement policy.RoleTrustStatementEntry) (string
|
||||
subjects = append(subjects, statement.Condition.StringLike.TokenActionsGithubusercontentComSub...)
|
||||
} else if len(statement.Condition.StringEquals.TokenActionsGithubusercontentComSub) > 0 {
|
||||
subjects = append(subjects, statement.Condition.StringEquals.TokenActionsGithubusercontentComSub...)
|
||||
} else if len(statement.Condition.ForAllValuesStringLike.TokenActionsGithubusercontentComSub) > 0 {
|
||||
subjects = append(subjects, statement.Condition.ForAllValuesStringLike.TokenActionsGithubusercontentComSub...)
|
||||
} else {
|
||||
subjects = append(subjects, "ALL REPOS!!!")
|
||||
}
|
||||
@@ -588,14 +590,14 @@ func (m *RoleTrustsModule) getAllRoleTrusts() {
|
||||
ListRoles, err := sdk.CachedIamListRoles(m.IAMClient, aws.ToString(m.Caller.Account))
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
for _, role := range ListRoles {
|
||||
trustsdoc, err := policy.ParseRoleTrustPolicyDocument(role)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
+22
-10
@@ -146,7 +146,7 @@ func (m *Route53Module) writeLoot(outputDirectory string, verbosity int) {
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
panic(err.Error())
|
||||
}
|
||||
route53ARecordsPublicZonesFileName := filepath.Join(path, "route53-A-records-public-Zones.txt")
|
||||
@@ -168,7 +168,7 @@ func (m *Route53Module) writeLoot(outputDirectory string, verbosity int) {
|
||||
err = os.WriteFile(route53ARecordsPublicZonesFileName, []byte(route53APublicRecords), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ func (m *Route53Module) writeLoot(outputDirectory string, verbosity int) {
|
||||
err = os.WriteFile(route53ARecordsPrivateZonesFileName, []byte(route53APrivateRecords), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ func (m *Route53Module) getRoute53Records() {
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -228,26 +228,38 @@ func (m *Route53Module) getRoute53Records() {
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
for _, record := range Records {
|
||||
recordName = aws.ToString(record.Name)
|
||||
recordType = string(record.Type)
|
||||
|
||||
for _, resourceRecord := range record.ResourceRecords {
|
||||
recordValue := resourceRecord.Value
|
||||
if record.AliasTarget != nil {
|
||||
m.Records = append(
|
||||
m.Records,
|
||||
Record{
|
||||
AWSService: "Route53",
|
||||
Name: recordName,
|
||||
Type: recordType,
|
||||
Value: aws.ToString(recordValue),
|
||||
Type: fmt.Sprintf("Alias[%s]", recordType),
|
||||
Value: aws.ToString(record.AliasTarget.DNSName),
|
||||
PrivateZone: privateZone,
|
||||
})
|
||||
|
||||
} else {
|
||||
for _, resourceRecord := range record.ResourceRecords {
|
||||
recordValue := resourceRecord.Value
|
||||
m.Records = append(
|
||||
m.Records,
|
||||
Record{
|
||||
AWSService: "Route53",
|
||||
Name: recordName,
|
||||
Type: recordType,
|
||||
Value: aws.ToString(recordValue),
|
||||
PrivateZone: privateZone,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+122
-1
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/apigateway"
|
||||
apiGatewayTypes "github.com/aws/aws-sdk-go-v2/service/apigateway/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type APIGatewayClientInterface interface {
|
||||
@@ -53,15 +54,37 @@ func init() {
|
||||
gob.Register([]apiGatewayTypes.UsagePlanKey{})
|
||||
}
|
||||
|
||||
// create a CachedApiGatewayGetRestAPIs function that accepts a client, account id, region. Make sure it handles caching, the region option and pagination
|
||||
func IsPublicApiGateway(ra *apiGatewayTypes.RestApi) bool {
|
||||
for _, endpointType := range ra.EndpointConfiguration.Types {
|
||||
if endpointType == apiGatewayTypes.EndpointTypeRegional || endpointType == apiGatewayTypes.EndpointTypeEdge {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// CachedApiGatewayGetRestAPIs function that accepts a client, account id, region. Make sure it handles caching, the region option and pagination
|
||||
func CachedApiGatewayGetRestAPIs(client APIGatewayClientInterface, accountID string, region string) ([]apiGatewayTypes.RestApi, error) {
|
||||
var PaginationControl *string
|
||||
var restAPIs []apiGatewayTypes.RestApi
|
||||
cacheKey := fmt.Sprintf("%s-apigateway-GetRestApis-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigateway:GetRestApis",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]apiGatewayTypes.RestApi), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigateway:GetRestApis",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
GetRestApis, err := client.GetRestApis(
|
||||
@@ -95,10 +118,24 @@ func CachedApiGatewayGetStages(client APIGatewayClientInterface, accountID strin
|
||||
cacheKey := fmt.Sprintf("%s-apigateway-GetStages-%s-%s", accountID, region, restAPIID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigateway:GetStages",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"restApiId": restAPIID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
// Convert cached data back to GetStagesOutput before returning
|
||||
cachedOutput := cached.(*CachedGetStagesOutput) // Ensure this type assertion matches your caching logic
|
||||
return fromCachedGetStagesOutput(cachedOutput), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigateway:GetStages",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"restApiId": restAPIID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
GetStages, err := client.GetStages(
|
||||
context.TODO(),
|
||||
@@ -142,8 +179,22 @@ func CachedApiGatewayGetResources(client APIGatewayClientInterface, accountID st
|
||||
cacheKey := fmt.Sprintf("%s-apigateway-GetResources-%s-%s", accountID, region, restAPIID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigateway:GetResources",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"restApiId": restAPIID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]apiGatewayTypes.Resource), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigateway:GetResources",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"restApiId": restAPIID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
GetResources, err := client.GetResources(
|
||||
@@ -180,8 +231,20 @@ func CachedApiGatewayGetDomainNames(client APIGatewayClientInterface, accountID
|
||||
cacheKey := fmt.Sprintf("%s-apigateway-GetDomainNames-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigateway:GetDomainNames",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]apiGatewayTypes.DomainName), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigateway:GetDomainNames",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
GetDomainNames, err := client.GetDomainNames(
|
||||
@@ -217,8 +280,22 @@ func CachedApiGatewayGetBasePathMappings(client APIGatewayClientInterface, accou
|
||||
cacheKey := fmt.Sprintf("%s-apigateway-GetBasePathMappings-%s-%s", accountID, region, aws.ToString(domain))
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigateway:GetBasePathMappings",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"domainName": aws.ToString(domain),
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]apiGatewayTypes.BasePathMapping), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigateway:GetBasePathMappings",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"domainName": aws.ToString(domain),
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
GetBasePathMappings, err := client.GetBasePathMappings(
|
||||
@@ -254,10 +331,28 @@ func CachedApiGatewayGetMethod(client APIGatewayClientInterface, accountID strin
|
||||
cacheKey := fmt.Sprintf("%s-apigateway-GetMethod-%s-%s-%s-%s", accountID, region, restAPIID, resourceID, method)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigateway:GetMethod",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"restApiId": restAPIID,
|
||||
"resourceId": resourceID,
|
||||
"httpMethod": method,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
// Convert cached data back to GetMethodOutput before returning
|
||||
cachedOutput := cached.(*CachedGetMethodOutput) // Ensure this type assertion matches your caching logic
|
||||
return fromCachedGetMethodOutput(cachedOutput), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigateway:GetMethod",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"restApiId": restAPIID,
|
||||
"resourceId": resourceID,
|
||||
"httpMethod": method,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
GetMethod, err := client.GetMethod(
|
||||
context.TODO(),
|
||||
@@ -324,8 +419,20 @@ func CachedApiGatewayGetUsagePlans(client APIGatewayClientInterface, accountID s
|
||||
cacheKey := fmt.Sprintf("%s-apigateway-GetUsagePlans-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigateway:GetUsagePlans",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]apiGatewayTypes.UsagePlan), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigateway:GetUsagePlans",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
GetUsagePlans, err := client.GetUsagePlans(
|
||||
@@ -362,8 +469,22 @@ func CachedApiGatewayGetUsagePlanKeys(client APIGatewayClientInterface, accountI
|
||||
cacheKey := fmt.Sprintf("%s-apigateway-GetUsagePlanKeys-%s-%s", accountID, region, usagePlanID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigateway:GetUsagePlanKeys",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"usagePlanId": usagePlanID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]apiGatewayTypes.UsagePlanKey), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigateway:GetUsagePlanKeys",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"usagePlanId": usagePlanID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
GetUsagePlanKeys, err := client.GetUsagePlanKeys(
|
||||
|
||||
@@ -22,6 +22,7 @@ func (m *MockedAWSAPIGatewayClient) GetRestApis(ctx context.Context, input *apig
|
||||
apiGatewayTypes.EndpointTypePrivate,
|
||||
},
|
||||
},
|
||||
Policy: aws.String("{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"execute-api:Invoke\",\"Resource\":\"arn:aws:execute-api:us-west-2:123456789012:abcdefg/*/*/*\"}]}"),
|
||||
},
|
||||
{
|
||||
Id: aws.String("qwerty"),
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
apiGatwayV2Types "github.com/aws/aws-sdk-go-v2/service/apigatewayv2/types"
|
||||
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type APIGatewayv2ClientInterface interface {
|
||||
@@ -38,8 +39,20 @@ func CachedAPIGatewayv2GetAPIs(client APIGatewayv2ClientInterface, accountID str
|
||||
cacheKey := fmt.Sprintf("%s-apigatewayv2-GetApis-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigatewayv2:GetApis",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]apiGatwayV2Types.Api), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigatewayv2:GetApis",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
GetApis, err := client.GetApis(
|
||||
context.TODO(),
|
||||
@@ -74,8 +87,20 @@ func CachedAPIGatewayv2GetDomainNames(client APIGatewayv2ClientInterface, accoun
|
||||
cacheKey := fmt.Sprintf("%s-apigatewayv2-GetDomainNames-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigatewayv2:GetDomainNames",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]domainName), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigatewayv2:GetDomainNames",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
GetDomainNames, err := client.GetDomainNames(
|
||||
context.TODO(),
|
||||
@@ -114,8 +139,20 @@ func CachedAPIGatewayv2GetApiMappings(client APIGatewayv2ClientInterface, accoun
|
||||
cacheKey := fmt.Sprintf("%s-apigatewayv2-GetApiMappings-%s-%s", accountID, region, domain)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigatewayv2:GetApiMappings",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]apiGatwayV2Types.ApiMapping), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigatewayv2:GetApiMappings",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
GetApiMappings, err := client.GetApiMappings(
|
||||
context.TODO(),
|
||||
@@ -151,8 +188,20 @@ func CachedAPIGatewayv2GetStages(client APIGatewayv2ClientInterface, accountID s
|
||||
cacheKey := fmt.Sprintf("%s-apigatewayv2-GetStages-%s-%s", accountID, region, apiID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigatewayv2:GetStages",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]apiGatwayV2Types.Stage), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigatewayv2:GetStages",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
GetStages, err := client.GetStages(
|
||||
context.TODO(),
|
||||
@@ -188,8 +237,20 @@ func CachedAPIGatewayv2GetRoutes(client APIGatewayv2ClientInterface, accountID s
|
||||
cacheKey := fmt.Sprintf("%s-apigatewayv2-GetRoutes-%s-%s", accountID, region, apiID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigatewayv2:GetRoutes",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]apiGatwayV2Types.Route), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apigatewayv2:GetRoutes",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
GetRoutes, err := client.GetRoutes(
|
||||
context.TODO(),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/apprunner"
|
||||
apprunnerTypes "github.com/aws/aws-sdk-go-v2/service/apprunner/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AppRunnerClientInterface interface {
|
||||
@@ -26,8 +27,20 @@ func CachedAppRunnerListServices(client AppRunnerClientInterface, accountID stri
|
||||
cacheKey := fmt.Sprintf("%s-apprunner-ListServices-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apprunner:ListServices",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]apprunnerTypes.ServiceSummary), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "apprunner:ListServices",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListServices, err := client.ListServices(
|
||||
context.TODO(),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/athena"
|
||||
athenaTypes "github.com/aws/aws-sdk-go-v2/service/athena/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSAthenaClientInterface interface {
|
||||
@@ -26,8 +27,20 @@ func CachedAthenaListDataCatalogs(client AWSAthenaClientInterface, accountID str
|
||||
cacheKey := fmt.Sprintf("%s-athena-ListDataCatalogs-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "athena:ListDataCatalogs",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]athenaTypes.DataCatalogSummary), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "athena:ListDataCatalogs",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListDataCatalogs, err := client.ListDataCatalogs(
|
||||
context.TODO(),
|
||||
@@ -62,8 +75,22 @@ func CachedAthenaListDatabases(client AWSAthenaClientInterface, accountID string
|
||||
cacheKey := fmt.Sprintf("%s-athena-ListDatabases-%s-%s", accountID, region, catalogName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "athena:ListDatabases",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"catalogName": catalogName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "athena:ListDatabases",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"catalogName": catalogName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListDatabases, err := client.ListDatabases(
|
||||
context.TODO(),
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/cloud9"
|
||||
cloud9Types "github.com/aws/aws-sdk-go-v2/service/cloud9/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSCloud9ClientInterface interface {
|
||||
@@ -25,8 +26,20 @@ func CachedCloud9ListEnvironments(client AWSCloud9ClientInterface, accountID str
|
||||
cacheKey := "cloud9-ListEnvironments-" + accountID + "-" + region
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "cloud9:ListEnvironments",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "cloud9:ListEnvironments",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListEnvironments, err := client.ListEnvironments(
|
||||
context.TODO(),
|
||||
@@ -60,8 +73,20 @@ func CachedCloud9DescribeEnvironments(client AWSCloud9ClientInterface, accountID
|
||||
cacheKey := "cloud9-DescribeEnvironments-" + accountID + "-" + region
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "cloud9:DescribeEnvironments",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]cloud9Types.Environment), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "cloud9:DescribeEnvironments",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for _, environmentID := range environmentIDs {
|
||||
DescribeEnvironments, err := client.DescribeEnvironments(
|
||||
context.TODO(),
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/cloudformation"
|
||||
cloudFormationTypes "github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type CloudFormationClientInterface interface {
|
||||
@@ -29,8 +30,20 @@ func CachedCloudFormationDescribeStacks(client CloudFormationClientInterface, ac
|
||||
cacheKey := fmt.Sprintf("%s-cloudformation-DescribeStacks-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "cloudformation:DescribeStacks",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]cloudFormationTypes.Stack), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "cloudformation:DescribeStacks",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeStacks, err := client.DescribeStacks(
|
||||
context.TODO(),
|
||||
@@ -63,8 +76,20 @@ func CachedCloudFormationGetTemplate(client CloudFormationClientInterface, accou
|
||||
cacheKey := fmt.Sprintf("%s-cloudformation-GetTemplate-%s-%s", accountID, region, stackName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "cloudformation:GetTemplate",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "cloudformation:GetTemplate",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
GetTemplate, err := client.GetTemplate(
|
||||
context.TODO(),
|
||||
@@ -89,8 +114,20 @@ func CachedCloudFormationListStacks(client CloudFormationClientInterface, accoun
|
||||
cacheKey := fmt.Sprintf("%s-cloudformation-ListStacks-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "cloudformation:ListStacks",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]cloudFormationTypes.StackSummary), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "cloudformation:ListStacks",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListStacks, err := client.ListStacks(
|
||||
context.TODO(),
|
||||
|
||||
+11
-1
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/cloudfront"
|
||||
cloudfrontTypes "github.com/aws/aws-sdk-go-v2/service/cloudfront/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSCloudFrontClientInterface interface {
|
||||
@@ -25,9 +26,18 @@ func CachedCloudFrontListDistributions(CloudFrontClient AWSCloudFrontClientInter
|
||||
cacheKey := "cloudfront-ListDistributions-" + accountID
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached CloudFront distributions data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "cloudfront:ListDistributions",
|
||||
"account": accountID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]cloudfrontTypes.DistributionSummary), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "cloudfront:ListDistributions",
|
||||
"account": accountID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListDistributions, err := CloudFrontClient.ListDistributions(
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/codeartifact"
|
||||
codeArtifactTypes "github.com/aws/aws-sdk-go-v2/service/codeartifact/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSCodeArtifactClientInterface interface {
|
||||
@@ -27,8 +28,20 @@ func CachedCodeArtifactListDomains(client AWSCodeArtifactClientInterface, accoun
|
||||
cacheKey := fmt.Sprintf("%s-codeartifact-ListDomains-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codeartifact:ListDomains",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]codeArtifactTypes.DomainSummary), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codeartifact:ListDomains",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListDomains, err := client.ListDomains(
|
||||
context.TODO(),
|
||||
@@ -63,8 +76,20 @@ func CachedCodeArtifactListRepositories(client AWSCodeArtifactClientInterface, a
|
||||
cacheKey := fmt.Sprintf("%s-codeartifact-ListRepositories-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codeartifact:ListRepositories",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]codeArtifactTypes.RepositorySummary), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codeartifact:ListRepositories",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListRepositories, err := client.ListRepositories(
|
||||
context.TODO(),
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/codebuild"
|
||||
codeBuildTypes "github.com/aws/aws-sdk-go-v2/service/codebuild/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type CodeBuildClientInterface interface {
|
||||
@@ -31,8 +32,20 @@ func CachedCodeBuildListProjects(CodeBuildClient CodeBuildClientInterface, accou
|
||||
cacheKey := fmt.Sprintf("%s-codebuild-ListProjects", accountID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codebuild:ListProjects",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codebuild:ListProjects",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListProjects, err := CodeBuildClient.ListProjects(
|
||||
@@ -71,8 +84,20 @@ func CachedCodeBuildBatchGetProjects(CodeBuildClient CodeBuildClientInterface, a
|
||||
cacheKey := fmt.Sprintf("%s-codebuild-BatchGetProjects-%s-%s", accountID, region, projectID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codebuild:BatchGetProjects",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(codeBuildTypes.Project), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codebuild:BatchGetProjects",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
BatchGetProjects, err := CodeBuildClient.BatchGetProjects(
|
||||
context.TODO(),
|
||||
@@ -97,8 +122,20 @@ func CachedCodeBuildGetResourcePolicy(CodeBuildClient CodeBuildClientInterface,
|
||||
cacheKey := fmt.Sprintf("%s-codebuild-GetResourcePolicy-%s-%s", accountID, region, projectID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codebuild:GetResourcePolicy",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codebuild:GetResourcePolicy",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
GetResourcePolicy, err := CodeBuildClient.GetResourcePolicy(
|
||||
context.TODO(),
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/codebuild"
|
||||
codeBuildTypes "github.com/aws/aws-sdk-go-v2/service/codebuild/types"
|
||||
)
|
||||
|
||||
type MockedCodeBuildClient struct {
|
||||
@@ -19,6 +20,19 @@ func (m *MockedCodeBuildClient) ListProjects(ctx context.Context, input *codebui
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *MockedCodeBuildClient) BatchGetProjects(ctx context.Context, input *codebuild.BatchGetProjectsInput, options ...func(*codebuild.Options)) (*codebuild.BatchGetProjectsOutput, error) {
|
||||
return &codebuild.BatchGetProjectsOutput{
|
||||
Projects: []codeBuildTypes.Project{
|
||||
{
|
||||
Name: aws.String("project1"),
|
||||
},
|
||||
{
|
||||
Name: aws.String("project2"),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *MockedCodeBuildClient) GetResourcePolicy(ctx context.Context, input *codebuild.GetResourcePolicyInput, options ...func(*codebuild.Options)) (*codebuild.GetResourcePolicyOutput, error) {
|
||||
return &codebuild.GetResourcePolicyOutput{
|
||||
Policy: aws.String(`{
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/codecommit"
|
||||
codeCommitTypes "github.com/aws/aws-sdk-go-v2/service/codecommit/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSCodeCommitClientInterface interface {
|
||||
@@ -25,8 +26,20 @@ func CachedCodeCommitListRepositories(client AWSCodeCommitClientInterface, accou
|
||||
cacheKey := fmt.Sprintf("%s-codecommit-ListRepositories-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codecommit:ListRepositories",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]codeCommitTypes.RepositoryNameIdPair), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codecommit:ListRepositories",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListRepositories, err := client.ListRepositories(
|
||||
context.TODO(),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/codedeploy"
|
||||
codeDeployTypes "github.com/aws/aws-sdk-go-v2/service/codedeploy/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSCodeDeployClientInterface interface {
|
||||
@@ -34,8 +35,20 @@ func CachedCodeDeployListApplications(client AWSCodeDeployClientInterface, accou
|
||||
cacheKey := fmt.Sprintf("%s-codedeploy-ListApplications-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codedeploy:ListApplications",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codedeploy:ListApplications",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListApplications, err := client.ListApplications(
|
||||
context.TODO(),
|
||||
@@ -70,8 +83,20 @@ func CachedCodeDeployListDeploymentConfigs(client AWSCodeDeployClientInterface,
|
||||
cacheKey := fmt.Sprintf("%s-codedeploy-ListDeploymentConfigs-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codedeploy:ListDeploymentConfigs",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codedeploy:ListDeploymentConfigs",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListDeploymentConfigs, err := client.ListDeploymentConfigs(
|
||||
context.TODO(),
|
||||
@@ -106,8 +131,20 @@ func CachedCodeDeployListDeployments(client AWSCodeDeployClientInterface, accoun
|
||||
cacheKey := fmt.Sprintf("%s-codedeploy-ListDeployments-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codedeploy:ListDeployments",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codedeploy:ListDeployments",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListDeployments, err := client.ListDeployments(
|
||||
context.TODO(),
|
||||
@@ -140,8 +177,22 @@ func CachedCodeDeployGetApplication(client AWSCodeDeployClientInterface, account
|
||||
cacheKey := fmt.Sprintf("%s-codedeploy-GetApplication-%s-%s", accountID, region, applicationName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codedeploy:GetApplication",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"application": applicationName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(codeDeployTypes.ApplicationInfo), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codedeploy:GetApplication",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"application": applicationName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
GetApplication, err := client.GetApplication(
|
||||
context.TODO(),
|
||||
&codedeploy.GetApplicationInput{
|
||||
@@ -165,8 +216,22 @@ func CachedCodeDeployGetDeploymentConfig(client AWSCodeDeployClientInterface, ac
|
||||
cacheKey := fmt.Sprintf("%s-codedeploy-GetDeploymentConfig-%s-%s", accountID, region, deploymentConfigName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codedeploy:GetDeploymentConfig",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"deploymentConfig": deploymentConfigName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(codeDeployTypes.DeploymentConfigInfo), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codedeploy:GetDeploymentConfig",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"deploymentConfig": deploymentConfigName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
GetDeploymentConfig, err := client.GetDeploymentConfig(
|
||||
context.TODO(),
|
||||
&codedeploy.GetDeploymentConfigInput{
|
||||
@@ -190,8 +255,22 @@ func CachedCodeDeployGetDeployment(client AWSCodeDeployClientInterface, accountI
|
||||
cacheKey := fmt.Sprintf("%s-codedeploy-GetDeployment-%s-%s", accountID, region, deploymentID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codedeploy:GetDeployment",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"deployment": deploymentID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(codeDeployTypes.DeploymentInfo), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "codedeploy:GetDeployment",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"deployment": deploymentID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
GetDeployment, err := client.GetDeployment(
|
||||
context.TODO(),
|
||||
&codedeploy.GetDeploymentInput{
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/datapipeline"
|
||||
dataPipelineTypes "github.com/aws/aws-sdk-go-v2/service/datapipeline/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSDataPipelineClientInterface interface {
|
||||
@@ -25,8 +26,20 @@ func CachedDataPipelineListPipelines(client AWSDataPipelineClientInterface, acco
|
||||
cacheKey := fmt.Sprintf("%s-datapipeline-ListPipelines-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "datapipeline:ListPipelines",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]dataPipelineTypes.PipelineIdName), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "datapipeline:ListPipelines",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListPipelines, err := client.ListPipelines(
|
||||
context.TODO(),
|
||||
|
||||
+37
-1
@@ -9,8 +9,8 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/docdb"
|
||||
docdbTypes "github.com/aws/aws-sdk-go-v2/service/docdb/types"
|
||||
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type DocDBClientInterface interface {
|
||||
@@ -32,8 +32,20 @@ func CachedDocDBDescribeGlobalClusters(client DocDBClientInterface, accountID st
|
||||
cacheKey := fmt.Sprintf("%s-docdb-DescribeGlobalClusters-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "docdb:DescribeGlobalClusters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]docdbTypes.GlobalCluster), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "docdb:DescribeGlobalClusters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeGlobalClusters, err := client.DescribeGlobalClusters(
|
||||
context.TODO(),
|
||||
@@ -68,8 +80,20 @@ func CachedDocDBDescribeDBClusters(client DocDBClientInterface, accountID string
|
||||
cacheKey := fmt.Sprintf("%s-docdb-DescribeDBClusters-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "docdb:DescribeDBClusters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]docdbTypes.DBCluster), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "docdb:DescribeDBClusters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeDBClusters, err := client.DescribeDBClusters(
|
||||
context.TODO(),
|
||||
@@ -110,8 +134,20 @@ func CachedDocDBDescribeDBInstances(client DocDBClientInterface, accountID strin
|
||||
cacheKey := fmt.Sprintf("%s-docdb-DescribeDBInstances-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "docdb:DescribeDBInstances",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]docdbTypes.DBInstance), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "docdb:DescribeDBInstances",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeDBInstances, err := client.DescribeDBInstances(
|
||||
context.TODO(),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/BishopFox/cloudfox/internal"
|
||||
"github.com/aws/aws-sdk-go-v2/service/directoryservice"
|
||||
dsTypes "github.com/aws/aws-sdk-go-v2/service/directoryservice/types"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSDSClientInterface interface {
|
||||
@@ -28,8 +29,20 @@ func CachedDSDescribeDirectories(client AWSDSClientInterface, accountID string,
|
||||
cacheKey := fmt.Sprintf("%s-ds-DescribeDirectories-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ds:DescribeDirectories",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]dsTypes.DirectoryDescription), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ds:DescribeDirectories",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeDirectories, err := client.DescribeDirectories(
|
||||
context.TODO(),
|
||||
@@ -64,8 +77,22 @@ func CachedDSDescribeTrusts(client AWSDSClientInterface, accountID string, regio
|
||||
cacheKey := fmt.Sprintf("%s-ds-DescribeTrusts-%s-%s", accountID, region, directoryId)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ds:DescribeTrusts",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"directoryId": directoryId,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]dsTypes.Trust), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ds:DescribeTrusts",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"directoryId": directoryId,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeDirectoryTrusts, err := client.DescribeTrusts(
|
||||
context.TODO(),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
|
||||
dynamoDBTypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type DynamoDBClientInterface interface {
|
||||
@@ -27,8 +28,21 @@ func CachedDynamoDBListTables(client DynamoDBClientInterface, accountID string,
|
||||
cacheKey := fmt.Sprintf("%s-dynamodb-ListTables-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "dynamodb:ListTables",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "dynamodb:ListTables",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListTables, err := client.ListTables(
|
||||
context.TODO(),
|
||||
@@ -62,9 +76,22 @@ func CachedDynamoDBDescribeTable(client DynamoDBClientInterface, accountID strin
|
||||
cacheKey := fmt.Sprintf("%s-dynamodb-DescribeTable-%s-%s", accountID, region, tableName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "dynamodb:DescribeTable",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(dynamoDBTypes.TableDescription), nil
|
||||
}
|
||||
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "dynamodb:DescribeTable",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
DescribeTable, err := client.DescribeTable(
|
||||
context.TODO(),
|
||||
&dynamodb.DescribeTableInput{
|
||||
|
||||
+120
-1
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/ec2"
|
||||
ec2Types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSEC2ClientInterface interface {
|
||||
@@ -19,6 +20,7 @@ type AWSEC2ClientInterface interface {
|
||||
DescribeVolumes(context.Context, *ec2.DescribeVolumesInput, ...func(*ec2.Options)) (*ec2.DescribeVolumesOutput, error)
|
||||
DescribeImages(context.Context, *ec2.DescribeImagesInput, ...func(*ec2.Options)) (*ec2.DescribeImagesOutput, error)
|
||||
DescribeInstanceAttribute(context.Context, *ec2.DescribeInstanceAttributeInput, ...func(*ec2.Options)) (*ec2.DescribeInstanceAttributeOutput, error)
|
||||
DescribeVpcEndpoints(context.Context, *ec2.DescribeVpcEndpointsInput, ...func(options *ec2.Options)) (*ec2.DescribeVpcEndpointsOutput, error)
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -27,7 +29,7 @@ func init() {
|
||||
gob.Register([]ec2Types.Snapshot{})
|
||||
gob.Register([]ec2Types.Volume{})
|
||||
gob.Register([]ec2Types.Image{})
|
||||
|
||||
gob.Register([]ec2Types.VpcEndpoint{})
|
||||
}
|
||||
|
||||
func CachedEC2DescribeInstances(client AWSEC2ClientInterface, accountID string, region string) ([]ec2Types.Instance, error) {
|
||||
@@ -36,8 +38,20 @@ func CachedEC2DescribeInstances(client AWSEC2ClientInterface, accountID string,
|
||||
cacheKey := fmt.Sprintf("%s-ec2-DescribeInstances-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ec2:DescribeInstances",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]ec2Types.Instance), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ec2:DescribeInstances",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeInstances, err := client.DescribeInstances(
|
||||
context.TODO(),
|
||||
@@ -72,8 +86,22 @@ func CachedEC2DescribeInstanceAttributeUserData(client AWSEC2ClientInterface, ac
|
||||
cacheKey := fmt.Sprintf("%s-ec2-DescribeInstanceAttributeUserData-%s-%s", accountID, region, instanceID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ec2:DescribeInstanceAttribute",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"instance": instanceID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ec2:DescribeInstanceAttribute",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"instance": instanceID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
DescribeInstanceAttribute, err := client.DescribeInstanceAttribute(
|
||||
context.TODO(),
|
||||
&ec2.DescribeInstanceAttributeInput{
|
||||
@@ -98,8 +126,20 @@ func CachedEC2DescribeNetworkInterfaces(client AWSEC2ClientInterface, accountID
|
||||
cacheKey := fmt.Sprintf("%s-ec2-DescribeNetworkInterfaces-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ec2:DescribeNetworkInterfaces",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]ec2Types.NetworkInterface), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ec2:DescribeNetworkInterfaces",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeNetworkInterfaces, err := client.DescribeNetworkInterfaces(
|
||||
context.TODO(),
|
||||
@@ -131,8 +171,20 @@ func CachedEC2DescribeSnapshots(client AWSEC2ClientInterface, accountID string,
|
||||
cacheKey := fmt.Sprintf("%s-ec2-DescribeSnapshots-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ec2:DescribeSnapshots",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]ec2Types.Snapshot), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ec2:DescribeSnapshots",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeSnapshots, err := client.DescribeSnapshots(
|
||||
context.TODO(),
|
||||
@@ -165,8 +217,20 @@ func CachedEC2DescribeVolumes(client AWSEC2ClientInterface, accountID string, re
|
||||
cacheKey := fmt.Sprintf("%s-ec2-DescribeVolumes-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ec2:DescribeVolumes",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]ec2Types.Volume), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ec2:DescribeVolumes",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeVolumes, err := client.DescribeVolumes(
|
||||
context.TODO(),
|
||||
@@ -198,8 +262,20 @@ func CachedEC2DescribeImages(client AWSEC2ClientInterface, accountID string, reg
|
||||
cacheKey := fmt.Sprintf("%s-ec2-DescribeImages-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ec2:DescribeImages",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]ec2Types.Image), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ec2:DescribeImages",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeImages, err := client.DescribeImages(
|
||||
context.TODO(),
|
||||
@@ -224,5 +300,48 @@ func CachedEC2DescribeImages(client AWSEC2ClientInterface, accountID string, reg
|
||||
|
||||
internal.Cache.Set(cacheKey, Images, cache.DefaultExpiration)
|
||||
return Images, nil
|
||||
}
|
||||
|
||||
func CachedEC2DescribeVpcEndpoints(client AWSEC2ClientInterface, accountID string, region string) ([]ec2Types.VpcEndpoint, error) {
|
||||
var PaginationControl *string
|
||||
var VpcEndpoints []ec2Types.VpcEndpoint
|
||||
cacheKey := fmt.Sprintf("%s-ec2-DescribeVpcEndpoints-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ec2:DescribeVpcEndpoints",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]ec2Types.VpcEndpoint), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ec2:DescribeVpcEndpoints",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeVpcEndpoints, err := client.DescribeVpcEndpoints(
|
||||
context.TODO(),
|
||||
&(ec2.DescribeVpcEndpointsInput{
|
||||
NextToken: PaginationControl,
|
||||
}),
|
||||
func(o *ec2.Options) {
|
||||
o.Region = region
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return VpcEndpoints, err
|
||||
}
|
||||
VpcEndpoints = append(VpcEndpoints, DescribeVpcEndpoints.VpcEndpoints...)
|
||||
|
||||
if DescribeVpcEndpoints.NextToken == nil {
|
||||
break
|
||||
}
|
||||
PaginationControl = DescribeVpcEndpoints.NextToken
|
||||
}
|
||||
internal.Cache.Set(cacheKey, VpcEndpoints, cache.DefaultExpiration)
|
||||
return VpcEndpoints, nil
|
||||
}
|
||||
@@ -25,6 +25,7 @@ type MockedEC2Client2 struct {
|
||||
describeSnapshots DescribeSnapshots
|
||||
describeVolumes DescribeVolumes
|
||||
describeImages DescribeImages
|
||||
describeVpcEndpoints DescribeVpcEndpoints
|
||||
}
|
||||
|
||||
type DescribeNetworkInterfaces struct {
|
||||
@@ -271,6 +272,9 @@ type DescribeInstanceAttribute struct {
|
||||
InstanceID string `json:"InstanceId"`
|
||||
}
|
||||
|
||||
type DescribeVpcEndpoints struct {
|
||||
}
|
||||
|
||||
func (c *MockedEC2Client2) DescribeNetworkInterfaces(ctx context.Context, input *ec2.DescribeNetworkInterfacesInput, f ...func(o *ec2.Options)) (*ec2.DescribeNetworkInterfacesOutput, error) {
|
||||
var nics []ec2types.NetworkInterface
|
||||
err := json.Unmarshal(readTestFile(DESCRIBE_NETWORK_INTEFACES_TEST_FILE), &c.describeNetworkInterfaces)
|
||||
@@ -386,3 +390,43 @@ func (c *MockedEC2Client2) DescribeInstanceAttribute(ctx context.Context, input
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *MockedEC2Client2) DescribeVpcEndpoints(ctx context.Context, input *ec2.DescribeVpcEndpointsInput, f ...func(o *ec2.Options)) (*ec2.DescribeVpcEndpointsOutput, error) {
|
||||
return &ec2.DescribeVpcEndpointsOutput{
|
||||
VpcEndpoints: []ec2types.VpcEndpoint{
|
||||
{
|
||||
VpcEndpointId: aws.String("vpce-1234567890abcdefg"),
|
||||
PolicyDocument: aws.String(`{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": "*",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}`),
|
||||
},
|
||||
{
|
||||
VpcEndpointId: aws.String("vpce-1234567890abcdefh"),
|
||||
PolicyDocument: aws.String(`{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": "*",
|
||||
"Resource": "*",
|
||||
"Condition": {
|
||||
"StringEquals": {
|
||||
"aws:SourceVpce": "vpce-1234567890abcdefg"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}`),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
+41
-3
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/ecr"
|
||||
ecrTypes "github.com/aws/aws-sdk-go-v2/service/ecr/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSECRClientInterface interface {
|
||||
@@ -33,9 +34,20 @@ func CachedECRDescribeRepositories(ECRClient AWSECRClientInterface, accountID st
|
||||
cacheKey := fmt.Sprintf("%s-ecr-DescribeRepositories-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached ECR repositories data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ecr:DescribeRepositories",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]ecrTypes.Repository), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ecr:DescribeRepositories",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
DescribeRepositories, err := ECRClient.DescribeRepositories(
|
||||
@@ -73,9 +85,22 @@ func CachedECRDescribeImages(ECRClient AWSECRClientInterface, accountID string,
|
||||
cacheKey := fmt.Sprintf("%s-ecr-DescribeImages-%s-%s", accountID, region, strings.ReplaceAll(repositoryName, "/", "-"))
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached Images data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ecr:DescribeImages",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"repository": repositoryName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]ecrTypes.ImageDetail), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ecr:DescribeImages",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"repository": repositoryName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
DescribeImages, err := ECRClient.DescribeImages(
|
||||
@@ -113,9 +138,22 @@ func CachedECRGetRepositoryPolicy(ECRClient AWSECRClientInterface, accountID str
|
||||
cacheKey := fmt.Sprintf("%s-ecr-GetRepositoryPolicy-%s-%s", accountID, region, strings.ReplaceAll(repositoryName, "/", "-"))
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached ECR repository policy data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ecr:GetRepositoryPolicy",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"repository": repositoryName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ecr:GetRepositoryPolicy",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"repository": repositoryName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
GetRepositoryPolicy, err := ECRClient.GetRepositoryPolicy(
|
||||
context.TODO(),
|
||||
|
||||
+69
-5
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/ecs"
|
||||
ecsTypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSECSClientInterface interface {
|
||||
@@ -34,9 +35,20 @@ func CachedECSListClusters(ECSClient AWSECSClientInterface, accountID string, re
|
||||
cacheKey := fmt.Sprintf("%s-ecs-ListClusters-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached ECS clusters data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ecs:ListClusters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ecs:ListClusters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListClusters, err := ECSClient.ListClusters(
|
||||
@@ -77,9 +89,22 @@ func CachedECSListTasks(ECSClient AWSECSClientInterface, accountID string, regio
|
||||
cacheKey := fmt.Sprintf("%s-ecs-ListTasks-%s-%s", accountID, region, clusterName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached ECS tasks data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ecs:ListTasks",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cluster": clusterName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ecs:ListTasks",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cluster": clusterName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListTasks, err := ECSClient.ListTasks(
|
||||
@@ -120,9 +145,22 @@ func CachedECSDescribeTasks(ECSClient AWSECSClientInterface, accountID string, r
|
||||
cacheKey := fmt.Sprintf("%s-ecs-DescribeTasks-%s-%s", accountID, region, clusterFileSystemSafe)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached ECS task details data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ecs:DescribeTasks",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cluster": cluster,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]ecsTypes.Task), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ecs:DescribeTasks",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cluster": cluster,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
DescribeTasks, err := ECSClient.DescribeTasks(
|
||||
context.TODO(),
|
||||
@@ -153,9 +191,22 @@ func CachedECSDescribeTaskDefinition(ECSClient AWSECSClientInterface, accountID
|
||||
cacheKey := fmt.Sprintf("%s-ecs-DescribeTaskDefinition-%s-%s", accountID, region, taskDefinitionFileSystemSafe)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached ECS task definition details data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ecs:DescribeTaskDefinition",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"taskDefinition": taskDefinition,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(ecsTypes.TaskDefinition), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ecs:DescribeTaskDefinition",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"taskDefinition": taskDefinition,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
DescribeTaskDefinition, err := ECSClient.DescribeTaskDefinition(
|
||||
context.TODO(),
|
||||
@@ -186,9 +237,22 @@ func CachedECSListServices(ECSClient AWSECSClientInterface, accountID string, re
|
||||
cacheKey := fmt.Sprintf("%s-ecs-ListServices-%s-%s", accountID, region, clusterName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached ECS services data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ecs:ListServices",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cluster": clusterName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ecs:ListServices",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cluster": clusterName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListServices, err := ECSClient.ListServices(
|
||||
|
||||
@@ -135,12 +135,30 @@ func (c *MockedECSClient) DescribeTasks(ctx context.Context, input *ecs.Describe
|
||||
Status: aws.String(a.Status),
|
||||
})
|
||||
}
|
||||
|
||||
var containers []ecsTypes.Container
|
||||
|
||||
for _, container := range mockedTask.Containers {
|
||||
containers = append(containers, ecsTypes.Container{
|
||||
ContainerArn: aws.String(container.ContainerArn),
|
||||
Cpu: aws.String(container.CPU),
|
||||
HealthStatus: ecsTypes.HealthStatus(container.HealthStatus),
|
||||
Image: aws.String(container.Image),
|
||||
LastStatus: aws.String(container.LastStatus),
|
||||
Memory: aws.String(container.Memory),
|
||||
Name: aws.String(container.Name),
|
||||
RuntimeId: aws.String(container.RuntimeID),
|
||||
TaskArn: aws.String(container.TaskArn),
|
||||
})
|
||||
}
|
||||
|
||||
tasks = append(tasks, ecsTypes.Task{
|
||||
ClusterArn: aws.String(mockedTask.ClusterArn),
|
||||
TaskDefinitionArn: aws.String(mockedTask.TaskDefinitionArn),
|
||||
LaunchType: ecsTypes.LaunchType(*aws.String(mockedTask.LaunchType)),
|
||||
TaskArn: aws.String(mockedTask.TaskArn),
|
||||
Attachments: attachments,
|
||||
Containers: containers,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/efs"
|
||||
efsTypes "github.com/aws/aws-sdk-go-v2/service/efs/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSEFSClientInterface interface {
|
||||
@@ -34,8 +35,20 @@ func CachedDescribeFileSystems(EFSClient AWSEFSClientInterface, accountID string
|
||||
cacheKey := fmt.Sprintf("%s-efs-DescribeFileSystems-%s", accountID, r)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "efs:DescribeFileSystems",
|
||||
"account": accountID,
|
||||
"region": r,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]efsTypes.FileSystemDescription), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "efs:DescribeFileSystems",
|
||||
"account": accountID,
|
||||
"region": r,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
DescribeFileSystems, err := EFSClient.DescribeFileSystems(
|
||||
@@ -73,8 +86,20 @@ func CachedDescribeMountTargets(EFSClient AWSEFSClientInterface, accountID strin
|
||||
cacheKey := fmt.Sprintf("%s-efs-DescribeMountTargets-%s-%s", accountID, r, filesystemId)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "efs:DescribeMountTargets",
|
||||
"account": accountID,
|
||||
"region": r,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]efsTypes.MountTargetDescription), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "efs:DescribeMountTargets",
|
||||
"account": accountID,
|
||||
"region": r,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
DescribeMountTargets, err := EFSClient.DescribeMountTargets(
|
||||
@@ -113,8 +138,20 @@ func CachedDescribeAccessPoints(EFSClient AWSEFSClientInterface, accountID strin
|
||||
cacheKey := fmt.Sprintf("%s-efs-DescribeAccessPoints-%s-%s", accountID, r, filesystemId)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "efs:DescribeAccessPoints",
|
||||
"account": accountID,
|
||||
"region": r,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]efsTypes.AccessPointDescription), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "efs:DescribeAccessPoints",
|
||||
"account": accountID,
|
||||
"region": r,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeAccessPoints, err := EFSClient.DescribeAccessPoints(
|
||||
context.TODO(),
|
||||
@@ -151,8 +188,20 @@ func CachedDescribeFileSystemPolicy(EFSClient AWSEFSClientInterface, filesystemI
|
||||
cacheKey := fmt.Sprintf("efs-%s-DescribeFileSystemPolicy-%s-%s", accountID, r, filesystemId)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "efs:DescribeFileSystemPolicy",
|
||||
"account": accountID,
|
||||
"region": r,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(policy.Policy), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "efs:DescribeFileSystemPolicy",
|
||||
"account": accountID,
|
||||
"region": r,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
Policy, err := EFSClient.DescribeFileSystemPolicy(
|
||||
context.TODO(),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/eks"
|
||||
eksTypes "github.com/aws/aws-sdk-go-v2/service/eks/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type EKSClientInterface interface {
|
||||
@@ -30,8 +31,22 @@ func CachedEKSDescribeCluster(client EKSClientInterface, accountID string, clust
|
||||
cacheKey := fmt.Sprintf("%s-eks-DescribeCluster-%s-%s", accountID, region, clusterName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "eks:DescribeCluster",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cluster": clusterName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(eksTypes.Cluster), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "eks:DescribeCluster",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cluster": clusterName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
clusterOutput, err := client.DescribeCluster(
|
||||
context.TODO(),
|
||||
&eks.DescribeClusterInput{
|
||||
@@ -57,8 +72,20 @@ func CachedEKSListClusters(client EKSClientInterface, accountID string, region s
|
||||
cacheKey := fmt.Sprintf("%s-eks-ListClusters-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "eks:ListClusters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "eks:ListClusters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListClusters, err := client.ListClusters(
|
||||
context.TODO(),
|
||||
@@ -93,8 +120,22 @@ func CachedEKSListNodeGroups(client EKSClientInterface, accountID string, region
|
||||
cacheKey := fmt.Sprintf("%s-eks-ListNodeGroups-%s-%s", accountID, region, clusterName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "eks:ListNodegroups",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cluster": clusterName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "eks:ListNodegroups",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cluster": clusterName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListNodeGroups, err := client.ListNodegroups(
|
||||
context.TODO(),
|
||||
@@ -129,8 +170,24 @@ func CachedEKSDescribeNodeGroup(client EKSClientInterface, accountID string, reg
|
||||
cacheKey := fmt.Sprintf("%s-eks-DescribeNodeGroup-%s-%s-%s", accountID, region, clusterName, nodeGroupName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "eks:DescribeNodegroup",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cluster": clusterName,
|
||||
"nodeGroup": nodeGroupName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(eksTypes.Nodegroup), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "eks:DescribeNodegroup",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cluster": clusterName,
|
||||
"nodeGroup": nodeGroupName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
nodeGroupOutput, err := client.DescribeNodegroup(
|
||||
context.TODO(),
|
||||
&eks.DescribeNodegroupInput{
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/elasticache"
|
||||
elasticacheTypes "github.com/aws/aws-sdk-go-v2/service/elasticache/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSElastiCacheClientInterface interface {
|
||||
@@ -26,8 +27,20 @@ func CachedElastiCacheDescribeCacheClusters(client AWSElastiCacheClientInterface
|
||||
cacheKey := fmt.Sprintf("%s-elasticache-DescribeCacheClusters-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "elasticache:DescribeCacheClusters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]elasticacheTypes.CacheCluster), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "elasticache:DescribeCacheClusters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeCacheClusters, err := client.DescribeCacheClusters(
|
||||
context.TODO(),
|
||||
|
||||
@@ -3,9 +3,13 @@ package sdk
|
||||
import (
|
||||
"context"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
|
||||
"github.com/BishopFox/cloudfox/internal"
|
||||
"github.com/aws/aws-sdk-go-v2/service/elasticbeanstalk"
|
||||
elasticbeanstalkTypes "github.com/aws/aws-sdk-go-v2/service/elasticbeanstalk/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSElasticBeanstalkClientInterface interface {
|
||||
@@ -18,6 +22,23 @@ func init() {
|
||||
|
||||
func CachedElasticBeanstalkDescribeApplications(client AWSElasticBeanstalkClientInterface, accountID string, region string) ([]elasticbeanstalkTypes.ApplicationDescription, error) {
|
||||
var applications []elasticbeanstalkTypes.ApplicationDescription
|
||||
cacheKey := fmt.Sprintf("%s-elasticbeanstalk-DescribeApplications-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "elasticbeanstalk:DescribeApplications",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]elasticbeanstalkTypes.ApplicationDescription), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "elasticbeanstalk:DescribeApplications",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
DescribeApplications, err := client.DescribeApplications(
|
||||
context.TODO(),
|
||||
&elasticbeanstalk.DescribeApplicationsInput{},
|
||||
@@ -31,5 +52,6 @@ func CachedElasticBeanstalkDescribeApplications(client AWSElasticBeanstalkClient
|
||||
}
|
||||
|
||||
applications = append(applications, DescribeApplications.Applications...)
|
||||
internal.Cache.Set(cacheKey, applications, cache.DefaultExpiration)
|
||||
return applications, nil
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing"
|
||||
elbTypes "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type ELBClientInterface interface {
|
||||
@@ -25,8 +26,20 @@ func CachedELBDescribeLoadBalancers(client ELBClientInterface, accountID string,
|
||||
cacheKey := fmt.Sprintf("%s-elb-DescribeLoadBalancers-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "elb:DescribeLoadBalancers",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]elbTypes.LoadBalancerDescription), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "elb:DescribeLoadBalancers",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeLoadBalancers, err := client.DescribeLoadBalancers(
|
||||
context.TODO(),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
|
||||
elbV2Types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type ELBv2ClientInterface interface {
|
||||
@@ -25,8 +26,20 @@ func CachedELBv2DescribeLoadBalancers(client ELBv2ClientInterface, accountID str
|
||||
cacheKey := fmt.Sprintf("%s-elbv2-DescribeLoadBalancers-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "elbv2:DescribeLoadBalancers",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]elbV2Types.LoadBalancer), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "elbv2:DescribeLoadBalancers",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeLoadBalancers, err := client.DescribeLoadBalancers(
|
||||
context.TODO(),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/emr"
|
||||
emrTypes "github.com/aws/aws-sdk-go-v2/service/emr/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSEMRClientInterface interface {
|
||||
@@ -30,8 +31,20 @@ func CachedEMRListClusters(client AWSEMRClientInterface, accountID string, regio
|
||||
cacheKey := fmt.Sprintf("%s-emr-ListClusters-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "emr:ListClusters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]emrTypes.ClusterSummary), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "emr:ListClusters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListClusters, err := client.ListClusters(
|
||||
context.TODO(),
|
||||
@@ -66,8 +79,22 @@ func CachedEMRListInstances(client AWSEMRClientInterface, accountID string, regi
|
||||
cacheKey := fmt.Sprintf("%s-emr-ListInstances-%s-%s", accountID, region, clusterID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "emr:ListInstances",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"clusterId": clusterID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]emrTypes.Instance), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "emr:ListInstances",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"clusterId": clusterID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListInstances, err := client.ListInstances(
|
||||
context.TODO(),
|
||||
|
||||
+63
-5
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/glue"
|
||||
glueTypes "github.com/aws/aws-sdk-go-v2/service/glue/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSGlueClientInterface interface {
|
||||
@@ -36,9 +37,20 @@ func CachedGlueListDevEndpoints(GlueClient AWSGlueClientInterface, accountID str
|
||||
cacheKey := "glue-ListDevEndpoints-" + accountID + "-" + region
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached Glue dev endpoints data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "glue:ListDevEndpoints",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "glue:ListDevEndpoints",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListDevEndpoints, err := GlueClient.ListDevEndpoints(
|
||||
@@ -77,9 +89,20 @@ func CachedGlueListJobs(GlueClient AWSGlueClientInterface, accountID string, reg
|
||||
cacheKey := "glue-ListJobs-" + accountID + "-" + region
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached Glue jobs data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "glue:ListJobs",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "glue:ListJobs",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListJobs, err := GlueClient.ListJobs(
|
||||
@@ -118,9 +141,22 @@ func CachedGlueGetTables(GlueClient AWSGlueClientInterface, accountID string, re
|
||||
cacheKey := "glue-GetTables-" + accountID + "-" + region
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached Glue tables data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "glue:GetTables",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"database": dbName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]glueTypes.Table), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "glue:GetTables",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"database": dbName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
GetTables, err := GlueClient.GetTables(
|
||||
@@ -160,9 +196,20 @@ func CachedGlueGetDatabases(GlueClient AWSGlueClientInterface, accountID string,
|
||||
cacheKey := "glue-GetDatabases-" + accountID + "-" + region
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached Glue databases data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "glue:GetDatabases",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]glueTypes.Database), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "glue:GetDatabases",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
GetDatabases, err := GlueClient.GetDatabases(
|
||||
@@ -203,9 +250,20 @@ func CachedGlueGetResourcePolicies(GlueClient AWSGlueClientInterface, accountID
|
||||
cacheKey := "glue-GetResourcePolicies-" + accountID + "-" + region
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached Glue resource policies data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "glue:GetResourcePolicies",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]policy.Policy), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "glue:GetResourcePolicies",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
GetResourcePolicies, err := GlueClient.GetResourcePolicies(
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/grafana"
|
||||
grafanaTypes "github.com/aws/aws-sdk-go-v2/service/grafana/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type GrafanaClientInterface interface {
|
||||
@@ -25,8 +26,20 @@ func CachedGrafanaListWorkspaces(client GrafanaClientInterface, accountID string
|
||||
cacheKey := fmt.Sprintf("%s-grafana-ListWorkspaces-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "grafana:ListWorkspaces",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]grafanaTypes.WorkspaceSummary), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "grafana:ListWorkspaces",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/iam"
|
||||
iamTypes "github.com/aws/aws-sdk-go-v2/service/iam/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSIAMClientInterface interface {
|
||||
@@ -44,8 +45,18 @@ func CachedIamListUsers(IAMClient AWSIAMClientInterface, accountID string) ([]ia
|
||||
cacheKey := fmt.Sprintf("%s-iam-ListUsers", accountID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "iam:ListUsers",
|
||||
"account": accountID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]iamTypes.User), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "iam:ListUsers",
|
||||
"account": accountID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListUsers, err := IAMClient.ListUsers(
|
||||
@@ -81,8 +92,18 @@ func CachedIamListRoles(IAMClient AWSIAMClientInterface, accountID string) ([]ia
|
||||
cacheKey := fmt.Sprintf("%s-iam-ListRoles", accountID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "iam:ListRoles",
|
||||
"account": accountID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]iamTypes.Role), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "iam:ListRoles",
|
||||
"account": accountID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListRoles, err := IAMClient.ListRoles(
|
||||
@@ -118,8 +139,20 @@ func CachedIamListAccessKeys(IAMClient AWSIAMClientInterface, accountID string,
|
||||
cacheKey := fmt.Sprintf("%s-iam-ListAccessKeys-%s", accountID, userName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "iam:ListAccessKeys",
|
||||
"account": accountID,
|
||||
"userName": userName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]iamTypes.AccessKeyMetadata), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "iam:ListAccessKeys",
|
||||
"account": accountID,
|
||||
"userName": userName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListAccessKeys, err := IAMClient.ListAccessKeys(
|
||||
@@ -172,8 +205,18 @@ func CachedIAMGetAccountAuthorizationDetails(IAMClient AWSIAMClientInterface, ac
|
||||
cacheKey := fmt.Sprintf("%s-iam-GetAccountAuthorizationDetails", accountID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "iam:GetAccountAuthorizationDetails",
|
||||
"account": accountID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(customGAADOutput), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "iam:GetAccountAuthorizationDetails",
|
||||
"account": accountID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
GetAccountAuthorizationDetails, err := IAMClient.GetAccountAuthorizationDetails(
|
||||
@@ -224,8 +267,20 @@ func CachedIamSimulatePrincipalPolicy(IAMClient AWSIAMClientInterface, accountID
|
||||
cacheKey := fmt.Sprintf("%s-iamSimulator-%s-%s-%s", accountID, name, hex.EncodeToString(md5hashedActionNames[:truncatedHashLength]), hex.EncodeToString(md5hashedResourceArns[:truncatedHashLength]))
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "iam:SimulatePrincipalPolicy",
|
||||
"account": accountID,
|
||||
"principal": name,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]iamTypes.EvaluationResult), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "iam:SimulatePrincipalPolicy",
|
||||
"account": accountID,
|
||||
"principal": name,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
SimulatePrincipalPolicy, err := IAMClient.SimulatePrincipalPolicy(
|
||||
@@ -261,8 +316,18 @@ func CachedIamListGroups(IAMClient AWSIAMClientInterface, accountID string) ([]i
|
||||
cacheKey := fmt.Sprintf("%s-iam-ListGroups", accountID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "iam:ListGroups",
|
||||
"account": accountID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]iamTypes.Group), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "iam:ListGroups",
|
||||
"account": accountID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListGroups, err := IAMClient.ListGroups(
|
||||
@@ -296,9 +361,21 @@ func CachedIamGetInstanceProfile(IAMClient AWSIAMClientInterface, accountID stri
|
||||
cacheKey := fmt.Sprintf("%s-iam-GetInstanceProfile-%s", accountID, instanceProfileName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "iam:GetInstanceProfile",
|
||||
"account": accountID,
|
||||
"profile": instanceProfileName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(iamTypes.InstanceProfile), nil
|
||||
}
|
||||
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "iam:GetInstanceProfile",
|
||||
"account": accountID,
|
||||
"profile": instanceProfileName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
GetInstanceProfile, err := IAMClient.GetInstanceProfile(
|
||||
context.TODO(),
|
||||
&iam.GetInstanceProfileInput{
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/BishopFox/cloudfox/internal"
|
||||
"github.com/aws/aws-sdk-go-v2/service/kinesis"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSKinesisClientInterface interface {
|
||||
@@ -24,8 +25,20 @@ func CachedKinesisListStreams(client AWSKinesisClientInterface, accountID string
|
||||
cacheKey := fmt.Sprintf("%s-kinesis-ListStreams-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "kinesis:ListStreams",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "kinesis:ListStreams",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListStreams, err := client.ListStreams(
|
||||
context.TODO(),
|
||||
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
package sdk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"github.com/BishopFox/cloudfox/internal"
|
||||
"github.com/BishopFox/cloudfox/internal/aws/policy"
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/kms"
|
||||
kmsTypes "github.com/aws/aws-sdk-go-v2/service/kms/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// KMSClientInterface is an interface for the AWS SDK KMS client.
|
||||
type KMSClientInterface interface {
|
||||
ListKeys(context.Context, *kms.ListKeysInput, ...func(options *kms.Options)) (*kms.ListKeysOutput, error)
|
||||
GetKeyPolicy(context.Context, *kms.GetKeyPolicyInput, ...func(options *kms.Options)) (*kms.GetKeyPolicyOutput, error)
|
||||
}
|
||||
|
||||
func init() {
|
||||
gob.Register([]kmsTypes.KeyListEntry{})
|
||||
gob.Register(policy.Policy{})
|
||||
}
|
||||
|
||||
// CachedKMSListKeys returns a list of KMS keys for the given account and region.
|
||||
func CachedKMSListKeys(client KMSClientInterface, accountID string, region string) ([]kmsTypes.KeyListEntry, error) {
|
||||
var keys []kmsTypes.KeyListEntry
|
||||
cacheKey := fmt.Sprintf("%s-kms-ListKeys-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "kms:ListKeys",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]kmsTypes.KeyListEntry), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "kms:ListKeys",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
keyPaginator := kms.NewListKeysPaginator(client.(kms.ListKeysAPIClient), &kms.ListKeysInput{}, func(options *kms.ListKeysPaginatorOptions) {})
|
||||
|
||||
for keyPaginator.HasMorePages() {
|
||||
output, err := keyPaginator.NextPage(context.TODO())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, key := range output.Keys {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
|
||||
internal.Cache.Set(cacheKey, keys, cache.DefaultExpiration)
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// CachedKMSGetKeyPolicy returns the policy for the given KMS key.
|
||||
func CachedKMSGetKeyPolicy(client KMSClientInterface, accountID string, region string, keyID string) (policy.Policy, error) {
|
||||
var keyPolicy policy.Policy
|
||||
var policyJson string
|
||||
cacheKey := fmt.Sprintf("%s-kms-GetKeyPolicy-%s-%s", accountID, region, keyID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "kms:GetKeyPolicy",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"keyId": keyID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(policy.Policy), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "kms:GetKeyPolicy",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"keyId": keyID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
getKeyPolicy, err := client.GetKeyPolicy(
|
||||
context.TODO(),
|
||||
&kms.GetKeyPolicyInput{
|
||||
KeyId: &keyID,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return keyPolicy, err
|
||||
}
|
||||
|
||||
policyJson = aws.ToString(getKeyPolicy.Policy)
|
||||
keyPolicy, err = policy.ParseJSONPolicy([]byte(policyJson))
|
||||
if err != nil {
|
||||
return keyPolicy, fmt.Errorf("parsing policy (%s) as JSON: %s", keyID, err)
|
||||
}
|
||||
internal.Cache.Set(cacheKey, keyPolicy, cache.DefaultExpiration)
|
||||
return keyPolicy, nil
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package sdk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/kms"
|
||||
kmsTypes "github.com/aws/aws-sdk-go-v2/service/kms/types"
|
||||
)
|
||||
|
||||
type MockedKMSClient struct {
|
||||
}
|
||||
|
||||
func (m *MockedKMSClient) ListKeys(ctx context.Context, input *kms.ListKeysInput, options ...func(*kms.Options)) (*kms.ListKeysOutput, error) {
|
||||
return &kms.ListKeysOutput{
|
||||
Keys: []kmsTypes.KeyListEntry{
|
||||
{
|
||||
KeyId: aws.String("key1"),
|
||||
KeyArn: aws.String("arn:aws:kms:us-west-2:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab"),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *MockedKMSClient) GetKeyPolicy(ctx context.Context, input *kms.GetKeyPolicyInput, options ...func(options *kms.Options)) (*kms.GetKeyPolicyOutput, error) {
|
||||
return &kms.GetKeyPolicyOutput{
|
||||
Policy: aws.String(`{
|
||||
"Version": "2012-10-17",
|
||||
"Id": "key-default-1",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "Enable IAM User Permissions",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"AWS": "arn:aws:iam::123456789012:root"
|
||||
},
|
||||
"Action": "kms:*",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}`),
|
||||
}, nil
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/lambda"
|
||||
lambdaTypes "github.com/aws/aws-sdk-go-v2/service/lambda/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type LambdaClientInterface interface {
|
||||
@@ -29,8 +30,20 @@ func CachedLambdaListFunctions(client LambdaClientInterface, accountID string, r
|
||||
cacheKey := fmt.Sprintf("%s-lambda-ListFunctions-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "lambda:ListFunctions",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]lambdaTypes.FunctionConfiguration), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "lambda:ListFunctions",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListFunctions, err := client.ListFunctions(
|
||||
@@ -114,8 +127,22 @@ func CachedLambdaGetFunctionUrlConfig(client LambdaClientInterface, accountID st
|
||||
cacheKey := fmt.Sprintf("%s-lambda-GetFunctionUrlConfig-%s-%s", accountID, region, functionName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "lambda:GetFunctionUrlConfig",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"function": functionName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(customGetFuntionURLOutput), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "lambda:GetFunctionUrlConfig",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"function": functionName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
GetFunctionUrlConfig, err := client.GetFunctionUrlConfig(
|
||||
context.TODO(),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/lightsail"
|
||||
lightsailTypes "github.com/aws/aws-sdk-go-v2/service/lightsail/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type lightsailClientInterface interface {
|
||||
@@ -69,8 +70,20 @@ func CachedLightsailGetContainerServices(client lightsailClientInterface, accoun
|
||||
cacheKey := fmt.Sprintf("%s-lightsail-GetContainerService-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "lightsail:GetContainerServices",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]lightsailTypes.ContainerService), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "lightsail:GetContainerServices",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
GetInstances, err := client.GetContainerServices(
|
||||
context.TODO(),
|
||||
&lightsail.GetContainerServicesInput{},
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/mq"
|
||||
mqTypes "github.com/aws/aws-sdk-go-v2/service/mq/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type MQClientInterface interface {
|
||||
@@ -26,8 +27,20 @@ func CachedMQListBrokers(client MQClientInterface, accountID string, region stri
|
||||
cacheKey := fmt.Sprintf("%s-mq-ListBrokers-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "mq:ListBrokers",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]mqTypes.BrokerSummary), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "mq:ListBrokers",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListBrokers, err := client.ListBrokers(
|
||||
|
||||
+47
-3
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/opensearch"
|
||||
openSearchTypes "github.com/aws/aws-sdk-go-v2/service/opensearch/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type OpenSearchClientInterface interface {
|
||||
@@ -23,14 +24,26 @@ func init() {
|
||||
gob.Register(openSearchTypes.DomainStatus{})
|
||||
}
|
||||
|
||||
// create CachedOpenSearchListDomainNames function that uses go-cache and pagination
|
||||
// CachedOpenSearchListDomainNames function that uses go-cache and pagination
|
||||
func CachedOpenSearchListDomainNames(client OpenSearchClientInterface, accountID string, region string) ([]openSearchTypes.DomainInfo, error) {
|
||||
var domains []openSearchTypes.DomainInfo
|
||||
cacheKey := fmt.Sprintf("%s-opensearch-ListDomainNames-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "opensearch:ListDomainNames",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]openSearchTypes.DomainInfo), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "opensearch:ListDomainNames",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
ListDomainNames, err := client.ListDomainNames(
|
||||
context.TODO(),
|
||||
@@ -50,14 +63,31 @@ func CachedOpenSearchListDomainNames(client OpenSearchClientInterface, accountID
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
// create CachedOpenSearchDescribeDomainConfig function that uses go-cache and pagination and supports region option
|
||||
// CachedOpenSearchDescribeDomainConfig function that uses go-cache and pagination and supports region option
|
||||
func CachedOpenSearchDescribeDomainConfig(client OpenSearchClientInterface, accountID string, region string, domainName string) (openSearchTypes.DomainConfig, error) {
|
||||
|
||||
fmt.Printf("CachedOpenSearchDescribeDomainConfig: %s %s %s\n", accountID, region, domainName)
|
||||
|
||||
var DomainConfig openSearchTypes.DomainConfig
|
||||
cacheKey := fmt.Sprintf("%s-opensearch-DescribeDomainConfig-%s-%s", accountID, region, domainName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "opensearch:DescribeDomainConfig",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"domain": domainName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(openSearchTypes.DomainConfig), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "opensearch:DescribeDomainConfig",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"domain": domainName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
DescribeDomainConfig, err := client.DescribeDomainConfig(
|
||||
context.TODO(),
|
||||
&opensearch.DescribeDomainConfigInput{
|
||||
@@ -78,14 +108,28 @@ func CachedOpenSearchDescribeDomainConfig(client OpenSearchClientInterface, acco
|
||||
return DomainConfig, nil
|
||||
}
|
||||
|
||||
// create CachedOpenSearchDescribeDomain function that uses go-cache and pagination and supports region option
|
||||
// CachedOpenSearchDescribeDomain function that uses go-cache and pagination and supports region option
|
||||
func CachedOpenSearchDescribeDomain(client OpenSearchClientInterface, accountID string, region string, domainName string) (openSearchTypes.DomainStatus, error) {
|
||||
var DomainStatus openSearchTypes.DomainStatus
|
||||
cacheKey := fmt.Sprintf("%s-opensearch-DescribeDomain-%s-%s", accountID, region, domainName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "opensearch:DescribeDomain",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"domain": domainName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(openSearchTypes.DomainStatus), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "opensearch:DescribeDomain",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"domain": domainName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
DescribeDomain, err := client.DescribeDomain(
|
||||
context.TODO(),
|
||||
&opensearch.DescribeDomainInput{
|
||||
|
||||
@@ -27,8 +27,8 @@ func (m *MockedOpenSearchClient) ListDomainNames(ctx context.Context, input *ope
|
||||
}
|
||||
|
||||
func (m *MockedOpenSearchClient) DescribeDomainConfig(ctx context.Context, input *opensearch.DescribeDomainConfigInput, options ...func(*opensearch.Options)) (*opensearch.DescribeDomainConfigOutput, error) {
|
||||
return &opensearch.DescribeDomainConfigOutput{
|
||||
DomainConfig: &openSearchTypes.DomainConfig{
|
||||
domainConfigMap := map[string]*openSearchTypes.DomainConfig{
|
||||
"domain1": {
|
||||
EngineVersion: &openSearchTypes.VersionStatus{
|
||||
Options: aws.String("OpenSearch-1.1"),
|
||||
Status: &openSearchTypes.OptionStatus{
|
||||
@@ -49,15 +49,63 @@ func (m *MockedOpenSearchClient) DescribeDomainConfig(ctx context.Context, input
|
||||
EnforceHTTPS: aws.Bool(true),
|
||||
},
|
||||
},
|
||||
AdvancedSecurityOptions: &openSearchTypes.AdvancedSecurityOptionsStatus{
|
||||
Options: &openSearchTypes.AdvancedSecurityOptions{
|
||||
Enabled: aws.Bool(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
"domain2": {
|
||||
EngineVersion: &openSearchTypes.VersionStatus{
|
||||
Options: aws.String("OpenSearch-1.1"),
|
||||
Status: &openSearchTypes.OptionStatus{
|
||||
PendingDeletion: aws.Bool(false),
|
||||
},
|
||||
},
|
||||
ClusterConfig: &openSearchTypes.ClusterConfigStatus{
|
||||
Options: &openSearchTypes.ClusterConfig{
|
||||
DedicatedMasterCount: aws.Int32(3),
|
||||
DedicatedMasterEnabled: aws.Bool(true),
|
||||
InstanceCount: aws.Int32(3),
|
||||
WarmCount: aws.Int32(3),
|
||||
WarmEnabled: aws.Bool(true),
|
||||
},
|
||||
},
|
||||
DomainEndpointOptions: &openSearchTypes.DomainEndpointOptionsStatus{
|
||||
Options: &openSearchTypes.DomainEndpointOptions{
|
||||
EnforceHTTPS: aws.Bool(true),
|
||||
},
|
||||
},
|
||||
AdvancedSecurityOptions: &openSearchTypes.AdvancedSecurityOptionsStatus{
|
||||
Options: &openSearchTypes.AdvancedSecurityOptions{
|
||||
Enabled: aws.Bool(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return &opensearch.DescribeDomainConfigOutput{
|
||||
DomainConfig: domainConfigMap[*input.DomainName],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *MockedOpenSearchClient) DescribeDomain(ctx context.Context, input *opensearch.DescribeDomainInput, options ...func(*opensearch.Options)) (*opensearch.DescribeDomainOutput, error) {
|
||||
return &opensearch.DescribeDomainOutput{
|
||||
DomainStatus: &openSearchTypes.DomainStatus{
|
||||
DomainName: aws.String("domain1"),
|
||||
Endpoint: aws.String("https://domain1.us-east-1.es.amazonaws.com"),
|
||||
domainStatusMap := map[string]*openSearchTypes.DomainStatus{
|
||||
"domain1": {
|
||||
DomainName: aws.String("domain1"),
|
||||
Endpoint: aws.String("https://domain1.us-east-1.es.amazonaws.com"),
|
||||
ARN: aws.String("arn:aws:es:us-east-1:123456789012:domain/domain1"),
|
||||
AccessPolicies: aws.String(`{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":[\"es:ESHttpGet\",\"es:ESHttpHead\",\"es:ESHttpPost\"],\"Resource\":\"*\",\"Condition\":{\"IpAddress\":{\"aws:SourceIp\":\"192.168.1.100/32\"}}}]}`),
|
||||
},
|
||||
"domain2": {
|
||||
DomainName: aws.String("domain2"),
|
||||
Endpoint: aws.String("https://domain2.us-east-1.es.amazonaws.com"),
|
||||
ARN: aws.String("arn:aws:es:us-east-1:123456789012:domain/domain2"),
|
||||
AccessPolicies: aws.String(`{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":[\"es:ESHttpGet\",\"es:ESHttpHead\",\"es:ESHttpPost\"],\"Resource\":\"*\"}]}`),
|
||||
},
|
||||
}
|
||||
|
||||
return &opensearch.DescribeDomainOutput{
|
||||
DomainStatus: domainStatusMap[*input.DomainName],
|
||||
}, nil
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/organizations"
|
||||
orgTypes "github.com/aws/aws-sdk-go-v2/service/organizations/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type OrganizationsClientInterface interface {
|
||||
@@ -28,8 +29,18 @@ func CachedOrganizationsListAccounts(client OrganizationsClientInterface, accoun
|
||||
cacheKey := fmt.Sprintf("%s-organizations-ListAccounts", accountID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "organizations:ListAccounts",
|
||||
"account": accountID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]orgTypes.Account), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "organizations:ListAccounts",
|
||||
"account": accountID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListAccounts, err := client.ListAccounts(
|
||||
@@ -62,8 +73,18 @@ func CachedOrganizationsDescribeOrganization(client OrganizationsClientInterface
|
||||
cacheKey := fmt.Sprintf("%s-organizations-DescribeOrganization", accountID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "organizations:DescribeOrganization",
|
||||
"account": accountID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(*orgTypes.Organization), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "organizations:DescribeOrganization",
|
||||
"account": accountID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
DescribeOrganization, err := client.DescribeOrganization(
|
||||
context.TODO(),
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/BishopFox/cloudfox/internal"
|
||||
"github.com/aws/aws-sdk-go-v2/service/rds"
|
||||
rdsTypes "github.com/aws/aws-sdk-go-v2/service/rds/types"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/patrickmn/go-cache"
|
||||
)
|
||||
@@ -29,8 +30,20 @@ func CachedRDSDescribeDBInstances(client RDSClientInterface, accountID string, r
|
||||
cacheKey := fmt.Sprintf("%s-rds-DescribeDBInstances-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "rds:DescribeDBInstances",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]rdsTypes.DBInstance), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "rds:DescribeDBInstances",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeDBInstances, err := client.DescribeDBInstances(
|
||||
context.TODO(),
|
||||
@@ -65,8 +78,20 @@ func CachedRDSDescribeDBClusters(client RDSClientInterface, accountID string, re
|
||||
cacheKey := fmt.Sprintf("%s-rds-DescribeDBClusters-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "rds:DescribeDBClusters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]rdsTypes.DBCluster), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "rds:DescribeDBClusters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeDBClusters, err := client.DescribeDBClusters(
|
||||
context.TODO(),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/redshift"
|
||||
redshiftTypes "github.com/aws/aws-sdk-go-v2/service/redshift/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSRedShiftClientInterface interface {
|
||||
@@ -26,8 +27,20 @@ func CachedRedShiftDescribeClusters(client AWSRedShiftClientInterface, accountID
|
||||
cacheKey := fmt.Sprintf("%s-redshift-DescribeClusters-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "redshift:DescribeClusters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]redshiftTypes.Cluster), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "redshift:DescribeClusters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
DescribeClusters, err := client.DescribeClusters(
|
||||
context.TODO(),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/route53"
|
||||
route53types "github.com/aws/aws-sdk-go-v2/service/route53/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSRoute53ClientInterface interface {
|
||||
@@ -28,8 +29,18 @@ func CachedRoute53ListHostedZones(client AWSRoute53ClientInterface, accountID st
|
||||
cacheKey := fmt.Sprintf("%s-route53-ListHostedZones", accountID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "route53:ListHostedZones",
|
||||
"account": accountID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]route53types.HostedZone), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "route53:ListHostedZones",
|
||||
"account": accountID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListHostedZones, err := client.ListHostedZones(
|
||||
@@ -63,8 +74,20 @@ func CachedRoute53ListResourceRecordSets(client AWSRoute53ClientInterface, accou
|
||||
cacheKey := fmt.Sprintf("%s-route53-ListResourceRecordSets-%s", accountID, hostedZoneID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "route53:ListResourceRecordSets",
|
||||
"account": accountID,
|
||||
"hostedZoneId": hostedZoneID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]route53types.ResourceRecordSet), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "route53:ListResourceRecordSets",
|
||||
"account": accountID,
|
||||
"hostedZoneId": hostedZoneID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListResourceRecordSets, err := client.ListResourceRecordSets(
|
||||
|
||||
+51
-4
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
s3Types "github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSS3ClientInterface interface {
|
||||
@@ -30,10 +31,19 @@ func CachedListBuckets(S3Client AWSS3ClientInterface, accountID string) ([]s3Typ
|
||||
cacheKey := fmt.Sprintf("%s-s3-ListBuckets", accountID)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached data for ListBuckets data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "s3:ListBuckets",
|
||||
"account": accountID,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]s3Types.Bucket), nil
|
||||
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "s3:ListBuckets",
|
||||
"account": accountID,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
ListBuckets, err := S3Client.ListBuckets(
|
||||
context.TODO(),
|
||||
&s3.ListBucketsInput{},
|
||||
@@ -52,9 +62,20 @@ func CachedGetBucketLocation(S3Client AWSS3ClientInterface, accountID string, bu
|
||||
cacheKey := fmt.Sprintf("%s-s3-GetBucketLocation-%s", accountID, bucketName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached data for GetBucketLocation data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "s3:GetBucketLocation",
|
||||
"account": accountID,
|
||||
"bucket": bucketName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "s3:GetBucketLocation",
|
||||
"account": accountID,
|
||||
"bucket": bucketName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
GetBucketRegion, err := S3Client.GetBucketLocation(
|
||||
context.TODO(),
|
||||
&s3.GetBucketLocationInput{
|
||||
@@ -76,9 +97,22 @@ func CachedGetBucketPolicy(S3Client AWSS3ClientInterface, accountID string, r st
|
||||
cacheKey := fmt.Sprintf("%s-s3-GetBucketPolicy-%s-%s", accountID, r, bucketName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached data for GetBucketPolicy data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "s3:GetBucketPolicy",
|
||||
"account": accountID,
|
||||
"region": r,
|
||||
"bucket": bucketName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "s3:GetBucketPolicy",
|
||||
"account": accountID,
|
||||
"region": r,
|
||||
"bucket": bucketName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
BucketPolicyObject, err := S3Client.GetBucketPolicy(
|
||||
context.TODO(),
|
||||
@@ -103,9 +137,22 @@ func CachedGetPublicAccessBlock(S3Client AWSS3ClientInterface, accountID string,
|
||||
cacheKey := fmt.Sprintf("%s-s3-GetPublicAccessBlock-%s-%s", accountID, r, bucketName)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached data for GetPublicAccessBlock data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "s3:GetPublicAccessBlock",
|
||||
"account": accountID,
|
||||
"region": r,
|
||||
"bucket": bucketName,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(*s3Types.PublicAccessBlockConfiguration), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "s3:GetPublicAccessBlock",
|
||||
"account": accountID,
|
||||
"region": r,
|
||||
"bucket": bucketName,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
PublicAccessBlock, err := S3Client.GetPublicAccessBlock(
|
||||
context.TODO(),
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
|
||||
secretsmanagerTypes "github.com/aws/aws-sdk-go-v2/service/secretsmanager/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type SecretsManagerClientInterface interface {
|
||||
@@ -30,8 +31,20 @@ func CachedSecretsManagerListSecrets(client SecretsManagerClientInterface, accou
|
||||
cacheKey := fmt.Sprintf("%s-secretsmanager-ListSecrets-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "secretsmanager:ListSecrets",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]secretsmanagerTypes.SecretListEntry), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "secretsmanager:ListSecrets",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListSecrets, err := client.ListSecrets(
|
||||
context.TODO(),
|
||||
@@ -66,8 +79,22 @@ func CachedSecretsManagerGetResourcePolicy(client SecretsManagerClientInterface,
|
||||
cacheKey := fmt.Sprintf("%s-secretsmanager-GetResourcePolicy-%s-%s", accountID, region, secretId)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "secretsmanager:GetResourcePolicy",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"secretId": secretId,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(policy.Policy), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "secretsmanager:GetResourcePolicy",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"secretId": secretId,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
GetResourcePolicy, err := client.GetResourcePolicy(
|
||||
context.TODO(),
|
||||
&secretsmanager.GetResourcePolicyInput{
|
||||
|
||||
+57
-4
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/sns"
|
||||
snsTypes "github.com/aws/aws-sdk-go-v2/service/sns/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSSNSClientInterface interface {
|
||||
@@ -29,10 +30,22 @@ func CachedSNSListTopics(SNSClient AWSSNSClientInterface, accountID string, regi
|
||||
cacheKey := "sns-ListTopics-" + accountID + "-" + region
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached SNS topics data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "sns:ListTopics",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "sns:ListTopics",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListTopics, err := SNSClient.ListTopics(
|
||||
context.TODO(),
|
||||
@@ -72,10 +85,22 @@ func CachedSNSListSubscriptions(SNSClient AWSSNSClientInterface, accountID strin
|
||||
cacheKey := "sns-ListSubscriptions-" + accountID + "-" + region
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached SNS subscriptions data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "sns:ListSubscriptions",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "sns:ListSubscriptions",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListSubscriptions, err := SNSClient.ListSubscriptions(
|
||||
context.TODO(),
|
||||
@@ -115,10 +140,24 @@ func CachedSNSListSubscriptionsByTopic(SNSClient AWSSNSClientInterface, accountI
|
||||
cacheKey := "sns-ListSubscriptionsByTopic-" + accountID + "-" + region + "-" + topicArn
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached SNS subscriptions data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "sns:ListSubscriptionsByTopic",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"topicArn": topicArn,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "sns:ListSubscriptionsByTopic",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"topicArn": topicArn,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListSubscriptionsByTopic, err := SNSClient.ListSubscriptionsByTopic(
|
||||
context.TODO(),
|
||||
@@ -157,10 +196,24 @@ func CachedSNSGetTopicAttributes(SNSClient AWSSNSClientInterface, accountID stri
|
||||
cacheKey := "sns-GetTopicAttributes-" + accountID + "-" + region + "-" + topicArn
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached SNS topic attributes data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "sns:GetTopicAttributes",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"topicArn": topicArn,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.(map[string]string), nil
|
||||
}
|
||||
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "sns:GetTopicAttributes",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"topicArn": topicArn,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
GetTopicAttributes, err := SNSClient.GetTopicAttributes(
|
||||
context.TODO(),
|
||||
&sns.GetTopicAttributesInput{
|
||||
|
||||
+13
-1
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/BishopFox/cloudfox/internal"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSSQSClientInterface interface {
|
||||
@@ -23,9 +24,20 @@ func CachedSQSListQueues(SQSClient AWSSQSClientInterface, accountID string, regi
|
||||
cacheKey := "sqs-ListQueues-" + accountID + "-" + region
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached SQS queues data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "sqs:ListQueues",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]string), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "sqs:ListQueues",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
ListQueues, err := SQSClient.ListQueues(
|
||||
|
||||
+13
-1
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/ssm"
|
||||
"github.com/aws/aws-sdk-go-v2/service/ssm/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AWSSSMClientInterface interface {
|
||||
@@ -26,9 +27,20 @@ func CachedSSMDescribeParameters(SSMClient AWSSSMClientInterface, accountID stri
|
||||
cacheKey := fmt.Sprintf("%s-ssm-DescribeParameters-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.Debug("Using cached SSM parameters data")
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ssm:DescribeParameters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]types.ParameterMetadata), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "ssm:DescribeParameters",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
|
||||
for {
|
||||
DescribeParameters, err := SSMClient.DescribeParameters(
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/sfn"
|
||||
sfnTypes "github.com/aws/aws-sdk-go-v2/service/sfn/types"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type StepFunctionsClientInterface interface {
|
||||
@@ -25,8 +26,20 @@ func CachedStepFunctionsListStateMachines(client StepFunctionsClientInterface, a
|
||||
cacheKey := fmt.Sprintf("%s-stepfunctions-ListStateMachines-%s", accountID, region)
|
||||
cached, found := internal.Cache.Get(cacheKey)
|
||||
if found {
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "states:ListStateMachines",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "hit",
|
||||
}).Info("AWS API call")
|
||||
return cached.([]sfnTypes.StateMachineListItem), nil
|
||||
}
|
||||
sharedLogger.WithFields(logrus.Fields{
|
||||
"api": "states:ListStateMachines",
|
||||
"account": accountID,
|
||||
"region": region,
|
||||
"cache": "miss",
|
||||
}).Info("AWS API call")
|
||||
for {
|
||||
ListStateMachines, err := client.ListStateMachines(
|
||||
context.TODO(),
|
||||
|
||||
+26
-21
@@ -30,6 +30,7 @@ type SecretsModule struct {
|
||||
WrapTable bool
|
||||
AWSOutputType string
|
||||
AWSTableCols string
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
// Main module data
|
||||
Secrets []Secret
|
||||
@@ -81,7 +82,7 @@ func (m *SecretsModule) PrintSecrets(outputDirectory string, verbosity int) {
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, semaphore, dataReceiver)
|
||||
}
|
||||
|
||||
@@ -192,24 +193,28 @@ func (m *SecretsModule) Receiver(receiver chan Secret, receiverDone chan bool) {
|
||||
func (m *SecretsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Secret) {
|
||||
defer wg.Done()
|
||||
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("secretsmanager", r)
|
||||
res, err := servicemap.IsServiceInRegion("secrets-manager", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getSecretsManagerSecretsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
res, err = servicemap.IsServiceInRegion("ssm", r)
|
||||
res, err = servicemap.IsServiceInRegion("systems-manager", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getSSMParametersPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -221,7 +226,7 @@ func (m *SecretsModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
pullFile := filepath.Join(path, "pull-secrets-commands.txt")
|
||||
|
||||
@@ -244,7 +249,7 @@ func (m *SecretsModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
err = os.WriteFile(pullFile, []byte(out), 0644)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
|
||||
if verbosity > 2 {
|
||||
@@ -261,8 +266,8 @@ func (m *SecretsModule) writeLoot(outputDirectory string, verbosity int) {
|
||||
|
||||
func (m *SecretsModule) getSecretsManagerSecretsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Secret) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -270,9 +275,9 @@ func (m *SecretsModule) getSecretsManagerSecretsPerRegion(r string, wg *sync.Wai
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
|
||||
var PaginationControl *string
|
||||
for {
|
||||
@@ -287,7 +292,7 @@ func (m *SecretsModule) getSecretsManagerSecretsPerRegion(r string, wg *sync.Wai
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -319,8 +324,8 @@ func (m *SecretsModule) getSecretsManagerSecretsPerRegion(r string, wg *sync.Wai
|
||||
|
||||
func (m *SecretsModule) getSSMParametersPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Secret) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -328,9 +333,9 @@ func (m *SecretsModule) getSSMParametersPerRegion(r string, wg *sync.WaitGroup,
|
||||
defer func() {
|
||||
<-semaphore
|
||||
}()
|
||||
// m.CommandCounter.Total++
|
||||
m.CommandCounter.Pending--
|
||||
m.CommandCounter.Executing++
|
||||
// m.CommandCounter.IncrTotal()
|
||||
m.CommandCounter.DecrPending()
|
||||
m.CommandCounter.IncrExecuting()
|
||||
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
|
||||
var PaginationControl *string
|
||||
|
||||
@@ -346,7 +351,7 @@ func (m *SecretsModule) getSSMParametersPerRegion(r string, wg *sync.WaitGroup,
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
+9
-15
@@ -19,7 +19,7 @@ var yellow = color.New(color.FgRed).SprintFunc()
|
||||
var blue = color.New(color.FgBlue).SprintFunc()
|
||||
var magenta = color.New(color.FgMagenta).SprintFunc()
|
||||
var green = color.New(color.FgGreen).SprintFunc()
|
||||
var AWSRegions = []string{"us-east-1", "us-east-2", "us-west-1", "us-west-2", "af-south-1", "ap-east-1", "ap-south-1", "ap-northeast-3", "ap-northeast-2", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1", "ca-central-1", "eu-central-1", "eu-west-1", "eu-west-2", "eu-south-1", "eu-west-3", "eu-north-1", "me-south-1", "sa-east-1"}
|
||||
var AWSRegions = []string{"us-east-1", "us-east-2", "us-west-1", "us-west-2", "af-south-1", "ap-east-1", "ap-south-1", "ap-northeast-3", "ap-northeast-2", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1", "ca-central-1", "eu-central-1", "eu-west-1", "eu-west-2", "eu-south-1", "eu-west-3", "eu-north-1", "sa-east-1"}
|
||||
var sharedLogger = internal.TxtLogger()
|
||||
|
||||
func GetIamSimResult(SkipAdminCheck bool, roleArnPtr *string, iamSimulatorMod IamSimulatorModule, localAdminMap map[string]bool) (string, string) {
|
||||
@@ -54,12 +54,6 @@ func GetIamSimResult(SkipAdminCheck bool, roleArnPtr *string, iamSimulatorMod Ia
|
||||
}
|
||||
}
|
||||
|
||||
if isRoleAdminBool {
|
||||
adminRole = "YES"
|
||||
} else {
|
||||
adminRole = "No"
|
||||
}
|
||||
|
||||
} else {
|
||||
adminRole = "Skipped"
|
||||
canRolePrivEsc = "Skipped"
|
||||
@@ -100,19 +94,19 @@ func generatePmapperDataBasePaths(accountId *string) (string, string) {
|
||||
if err != nil {
|
||||
log.Fatal("Could not get homedir")
|
||||
}
|
||||
edgesPath = fmt.Sprintf(dir + "/Library/Application Support/com.nccgroup.principalmapper/" + aws.ToString(accountId) + "/graph/edges.json")
|
||||
nodesPath = fmt.Sprintf(dir + "/Library/Application Support/com.nccgroup.principalmapper/" + aws.ToString(accountId) + "/graph/nodes.json")
|
||||
edgesPath = fmt.Sprintf("%s/Library/Application Support/com.nccgroup.principalmapper/%s/graph/edges.json", dir, aws.ToString(accountId))
|
||||
nodesPath = fmt.Sprintf("%s/Library/Application Support/com.nccgroup.principalmapper/%s/graph/nodes.json", dir, aws.ToString(accountId))
|
||||
|
||||
} else if runtime.GOOS == "linux" || runtime.GOOS == "freebsd" || runtime.GOOS == "openbsd" {
|
||||
xdg, ok := os.LookupEnv("XDG_DATA_HOME")
|
||||
if ok {
|
||||
edgesPath = fmt.Sprintf(xdg + aws.ToString(accountId) + "/graph/edges.json")
|
||||
nodesPath = fmt.Sprintf(xdg + aws.ToString(accountId) + "/graph/nodes.json")
|
||||
edgesPath = fmt.Sprintf("%s%s/graph/edges.json", xdg, aws.ToString(accountId))
|
||||
nodesPath = fmt.Sprintf("%s%s/graph/nodes.json", xdg, aws.ToString(accountId))
|
||||
} else {
|
||||
dir, err := os.UserHomeDir()
|
||||
if err == nil {
|
||||
edgesPath = fmt.Sprintf(dir + "/.local/share/principalmapper/" + aws.ToString(accountId) + "/graph/edges.json")
|
||||
nodesPath = fmt.Sprintf(dir + "/.local/share/principalmapper/" + aws.ToString(accountId) + "/graph/nodes.json")
|
||||
edgesPath = fmt.Sprintf("%s/.local/share/principalmapper/%s/graph/edges.json", dir, aws.ToString(accountId))
|
||||
nodesPath = fmt.Sprintf("%s/.local/share/principalmapper/%s/graph/nodes.json", dir, aws.ToString(accountId))
|
||||
} else {
|
||||
log.Fatal("Could not homedir.")
|
||||
}
|
||||
@@ -123,8 +117,8 @@ func generatePmapperDataBasePaths(accountId *string) (string, string) {
|
||||
if err != nil {
|
||||
log.Fatal("Could not get userconfigdir")
|
||||
}
|
||||
edgesPath = fmt.Sprintf(dir + "\\principalmapper" + aws.ToString(accountId) + "\\graph\\edges.json")
|
||||
nodesPath = fmt.Sprintf(dir + "\\principalmapper" + aws.ToString(accountId) + "\\graph\\nodes.json")
|
||||
edgesPath = fmt.Sprintf("%s\\principalmapper%s\\graph\\edges.json", dir, aws.ToString(accountId))
|
||||
nodesPath = fmt.Sprintf("%s\\principalmapper%s\\graph\\nodes.json", dir, aws.ToString(accountId))
|
||||
|
||||
}
|
||||
|
||||
|
||||
+13
-8
@@ -34,6 +34,7 @@ type SNSModule struct {
|
||||
|
||||
Goroutines int
|
||||
WrapTable bool
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
// Main module data
|
||||
Topics []SNSTopic
|
||||
@@ -95,7 +96,7 @@ func (m *SNSModule) PrintSNS(outputDirectory string, verbosity int) {
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, semaphore, dataReceiver)
|
||||
|
||||
}
|
||||
@@ -192,15 +193,19 @@ func (m *SNSModule) PrintSNS(outputDirectory string, verbosity int) {
|
||||
func (m *SNSModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan SNSTopic) {
|
||||
defer wg.Done()
|
||||
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("sns", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getSNSTopicsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -283,8 +288,8 @@ func (m *SNSModule) writeLoot(outputDirectory string, verbosity int, profile str
|
||||
|
||||
func (m *SNSModule) getSNSTopicsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan SNSTopic) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -303,7 +308,7 @@ func (m *SNSModule) getSNSTopicsPerRegion(r string, wg *sync.WaitGroup, semaphor
|
||||
topic, err := m.getTopicWithAttributes(aws.ToString(t.TopicArn), r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
parsedArn, err := arn.Parse(aws.ToString(t.TopicArn))
|
||||
@@ -422,7 +427,7 @@ func (m *SNSModule) storeAccessPolicy(topic *SNSTopic) {
|
||||
|
||||
if err := m.storeFile(f, topic.PolicyJSON); err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-9
@@ -36,6 +36,7 @@ type SQSModule struct {
|
||||
Goroutines int
|
||||
AWSProfile string
|
||||
WrapTable bool
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
// Main module data
|
||||
Queues []Queue
|
||||
@@ -98,7 +99,7 @@ func (m *SQSModule) PrintSQS(outputDirectory string, verbosity int) {
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, semaphore, dataReceiver)
|
||||
|
||||
}
|
||||
@@ -194,15 +195,19 @@ func (m *SQSModule) PrintSQS(outputDirectory string, verbosity int) {
|
||||
|
||||
func (m *SQSModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Queue) {
|
||||
defer wg.Done()
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
res, err := servicemap.IsServiceInRegion("sqs", r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getSQSRecordsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -282,8 +287,8 @@ func (m *SQSModule) writeLoot(outputDirectory string, verbosity int, profile str
|
||||
|
||||
func (m *SQSModule) getSQSRecordsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Queue) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -296,7 +301,7 @@ func (m *SQSModule) getSQSRecordsPerRegion(r string, wg *sync.WaitGroup, semapho
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -304,7 +309,7 @@ func (m *SQSModule) getSQSRecordsPerRegion(r string, wg *sync.WaitGroup, semapho
|
||||
queue, err := m.getQueueWithAttributes(url, r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
|
||||
@@ -432,7 +437,7 @@ func (m *SQSModule) storeAccessPolicy(queue *Queue) {
|
||||
|
||||
if err := m.storeFile(f, queue.PolicyJSON); err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -88,7 +88,7 @@ func (m *TagsModule) PrintTags(outputDirectory string, verbosity int) {
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, semaphore, dataReceiver)
|
||||
|
||||
}
|
||||
@@ -246,7 +246,7 @@ func (m *TagsModule) countUniqueResourcesWithTags() int {
|
||||
func (m *TagsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Tag) {
|
||||
defer wg.Done()
|
||||
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
m.getTagsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -266,8 +266,8 @@ func (m *TagsModule) Receiver(receiver chan Tag, receiverDone chan bool) {
|
||||
|
||||
func (m *TagsModule) getTagsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Tag) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -280,7 +280,7 @@ func (m *TagsModule) getTagsPerRegion(r string, wg *sync.WaitGroup, semaphore ch
|
||||
resources, err := m.getResources(r)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
}
|
||||
//var parsedArn types.Arn
|
||||
for _, resource := range resources {
|
||||
@@ -337,7 +337,7 @@ func (m *TagsModule) getResources(r string) ([]types.ResourceTagMapping, error)
|
||||
)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return resources, err
|
||||
}
|
||||
|
||||
|
||||
+23
-19
@@ -3,7 +3,6 @@ package aws
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -46,6 +45,7 @@ type WorkloadsModule struct {
|
||||
|
||||
iamSimClient IamSimulatorModule
|
||||
InstanceProfileToRolesMap map[string][]iamTypes.Role
|
||||
ServiceMap *awsservicemap.AwsServiceMap // Shared service map to avoid repeated HTTP requests
|
||||
|
||||
// Main module data
|
||||
Workloads []Workload
|
||||
@@ -109,7 +109,7 @@ func (m *WorkloadsModule) PrintWorkloads(outputDirectory string, verbosity int)
|
||||
|
||||
for _, region := range m.AWSRegions {
|
||||
wg.Add(1)
|
||||
m.CommandCounter.Pending++
|
||||
m.CommandCounter.IncrPending()
|
||||
go m.executeChecks(region, wg, semaphore, dataReceiver)
|
||||
|
||||
}
|
||||
@@ -271,20 +271,24 @@ func (m *WorkloadsModule) Receiver(receiver chan Workload, receiverDone chan boo
|
||||
|
||||
func (m *WorkloadsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Workload) {
|
||||
defer wg.Done()
|
||||
servicemap := &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
// Use shared ServiceMap instance if provided, otherwise create a new one
|
||||
servicemap := m.ServiceMap
|
||||
if servicemap == nil {
|
||||
servicemap = &awsservicemap.AwsServiceMap{
|
||||
JsonFileSource: "DOWNLOAD_FROM_AWS",
|
||||
}
|
||||
}
|
||||
|
||||
res, _ := servicemap.IsServiceInRegion("ec2", r)
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getEC2WorkloadsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
|
||||
res, _ = servicemap.IsServiceInRegion("ecs", r)
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getECSWorkloadsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
@@ -294,13 +298,13 @@ func (m *WorkloadsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore
|
||||
m.modLog.Error(err)
|
||||
}
|
||||
if res {
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getLambdaWorkloadsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
}
|
||||
|
||||
// AppRunner is not supported in the aws service region catalog so we have to run it in all regions
|
||||
m.CommandCounter.Total++
|
||||
m.CommandCounter.IncrTotal()
|
||||
wg.Add(1)
|
||||
go m.getAppRunnerWorkloadsPerRegion(r, wg, semaphore, dataReceiver)
|
||||
|
||||
@@ -308,8 +312,8 @@ func (m *WorkloadsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore
|
||||
|
||||
func (m *WorkloadsModule) getEC2WorkloadsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Workload) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -345,7 +349,7 @@ func (m *WorkloadsModule) getEC2WorkloadsPerRegion(r string, wg *sync.WaitGroup,
|
||||
// Describe the IAM instance profile
|
||||
profileOutput, err := sdk.CachedIamGetInstanceProfile(m.IAMClient, aws.ToString(m.Caller.Account), profileName)
|
||||
if err != nil {
|
||||
log.Printf("failed to get instance profile for %s, %v", profileArn, err)
|
||||
m.modLog.Errorf("failed to get instance profile for %s, %v", profileArn, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -365,8 +369,8 @@ func (m *WorkloadsModule) getEC2WorkloadsPerRegion(r string, wg *sync.WaitGroup,
|
||||
|
||||
func (m *WorkloadsModule) getECSWorkloadsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Workload) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -391,7 +395,7 @@ func (m *WorkloadsModule) getECSWorkloadsPerRegion(r string, wg *sync.WaitGroup,
|
||||
Tasks, err := sdk.CachedECSDescribeTasks(m.ECSClient, aws.ToString(m.Caller.Account), r, cluster, taskARNs)
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -420,8 +424,8 @@ func (m *WorkloadsModule) getECSWorkloadsPerRegion(r string, wg *sync.WaitGroup,
|
||||
|
||||
func (m *WorkloadsModule) getLambdaWorkloadsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Workload) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -453,8 +457,8 @@ func (m *WorkloadsModule) getLambdaWorkloadsPerRegion(r string, wg *sync.WaitGro
|
||||
|
||||
func (m *WorkloadsModule) getAppRunnerWorkloadsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Workload) {
|
||||
defer func() {
|
||||
m.CommandCounter.Executing--
|
||||
m.CommandCounter.Complete++
|
||||
m.CommandCounter.DecrExecuting()
|
||||
m.CommandCounter.IncrComplete()
|
||||
wg.Done()
|
||||
|
||||
}()
|
||||
@@ -539,7 +543,7 @@ func (m *WorkloadsModule) getRolesFromInstanceProfiles() {
|
||||
|
||||
if err != nil {
|
||||
m.modLog.Error(err.Error())
|
||||
m.CommandCounter.Error++
|
||||
m.CommandCounter.IncrError()
|
||||
break
|
||||
}
|
||||
for _, instanceProfile := range ListInstanceProfiles.InstanceProfiles {
|
||||
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
package azure
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/subscriptions"
|
||||
containerregistry "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2022-02-01-preview/containerregistry"
|
||||
"github.com/BishopFox/cloudfox/globals"
|
||||
"github.com/BishopFox/cloudfox/internal"
|
||||
"github.com/aws/smithy-go/ptr"
|
||||
)
|
||||
|
||||
const acrDockerTokenUsername = "00000000-0000-0000-0000-000000000000"
|
||||
|
||||
var getContainerRegistries = getContainerRegistriesOriginal
|
||||
|
||||
func AzACRCommand(AzTenantID, AzSubscription, AzOutputDirectory, Version string, AzVerbosity int, AzWrapTable bool, AzMergedTable bool) error {
|
||||
if AzTenantID != "" && AzSubscription == "" {
|
||||
tenantInfo := populateTenant(AzTenantID)
|
||||
if ptr.ToString(tenantInfo.ID) == "" {
|
||||
return fmt.Errorf("could not find Azure tenant %q", AzTenantID)
|
||||
}
|
||||
if !AzMergedTable {
|
||||
for _, subscription := range GetSubscriptionsPerTenantID(ptr.ToString(tenantInfo.ID)) {
|
||||
if err := runACRCommandForSingleSubscription(ptr.ToString(subscription.SubscriptionID), AzOutputDirectory, AzVerbosity, AzWrapTable, Version); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
azureLogger.InfoM(fmt.Sprintf("Enumerating container registries for tenant %s (%s)", ptr.ToString(tenantInfo.DefaultDomain), ptr.ToString(tenantInfo.ID)), globals.AZ_ACR_MODULE_NAME)
|
||||
|
||||
var body [][]string
|
||||
var loot strings.Builder
|
||||
for _, subscription := range GetSubscriptionsPerTenantID(ptr.ToString(tenantInfo.ID)) {
|
||||
rows, commands, err := getACRData(subscription)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body = append(body, rows...)
|
||||
loot.WriteString(commands)
|
||||
}
|
||||
writeACROutput(AzOutputDirectory, ptr.ToString(tenantInfo.DefaultDomain), "1-tenant-level", ptr.ToString(tenantInfo.DefaultDomain), body, loot.String(), AzVerbosity, AzWrapTable)
|
||||
return nil
|
||||
}
|
||||
|
||||
if AzTenantID == "" && AzSubscription != "" {
|
||||
return runACRCommandForSingleSubscription(AzSubscription, AzOutputDirectory, AzVerbosity, AzWrapTable, Version)
|
||||
}
|
||||
|
||||
return fmt.Errorf("please specify either --tenant or --subscription")
|
||||
}
|
||||
|
||||
func runACRCommandForSingleSubscription(AzSubscription, AzOutputDirectory string, AzVerbosity int, AzWrapTable bool, Version string) error {
|
||||
subscriptionInfo := PopulateSubsriptionType(AzSubscription)
|
||||
if subscriptionInfo.ID == "" {
|
||||
return fmt.Errorf("could not find Azure subscription %q", AzSubscription)
|
||||
}
|
||||
tenantID := ptr.ToString(GetTenantIDPerSubscription(subscriptionInfo.ID))
|
||||
tenantInfo := populateTenant(tenantID)
|
||||
|
||||
azureLogger.InfoM(fmt.Sprintf("Enumerating container registries for subscription %s (%s)", subscriptionInfo.Name, subscriptionInfo.ID), globals.AZ_ACR_MODULE_NAME)
|
||||
|
||||
var selectedSubscription subscriptions.Subscription
|
||||
for _, subscription := range GetSubscriptions() {
|
||||
if ptr.ToString(subscription.SubscriptionID) == subscriptionInfo.ID {
|
||||
selectedSubscription = subscription
|
||||
break
|
||||
}
|
||||
}
|
||||
rows, loot, err := getACRData(selectedSubscription)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeACROutput(AzOutputDirectory, ptr.ToString(tenantInfo.DefaultDomain), subscriptionInfo.Name, subscriptionInfo.Name, rows, loot, AzVerbosity, AzWrapTable)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getACRData(subscription subscriptions.Subscription) ([][]string, string, error) {
|
||||
subscriptionID := ptr.ToString(subscription.SubscriptionID)
|
||||
registries, err := getContainerRegistries(subscriptionID)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("could not enumerate container registries for subscription %s: %w", subscriptionID, err)
|
||||
}
|
||||
return getACRRelevantData(subscription, registries)
|
||||
}
|
||||
|
||||
func getACRRelevantData(subscription subscriptions.Subscription, registries []containerregistry.Registry) ([][]string, string, error) {
|
||||
var body [][]string
|
||||
var loot strings.Builder
|
||||
subscriptionID := ptr.ToString(subscription.SubscriptionID)
|
||||
subscriptionName := ptr.ToString(subscription.DisplayName)
|
||||
|
||||
for _, registry := range registries {
|
||||
name := ptr.ToString(registry.Name)
|
||||
loginServer := ""
|
||||
anonymousPulls := false
|
||||
if registry.RegistryProperties != nil {
|
||||
loginServer = ptr.ToString(registry.LoginServer)
|
||||
anonymousPulls = ptr.ToBool(registry.AnonymousPullEnabled)
|
||||
}
|
||||
body = append(body, []string{name, subscriptionName, subscriptionID, loginServer, fmt.Sprintf("%t", anonymousPulls)})
|
||||
loot.WriteString(acrLootCommands(name, subscriptionID, loginServer))
|
||||
}
|
||||
|
||||
sort.Slice(body, func(i, j int) bool {
|
||||
if body[i][1] == body[j][1] {
|
||||
return body[i][0] < body[j][0]
|
||||
}
|
||||
return body[i][1] < body[j][1]
|
||||
})
|
||||
return body, loot.String(), nil
|
||||
}
|
||||
|
||||
func acrLootCommands(registryName, subscriptionID, loginServer string) string {
|
||||
if registryName == "" || subscriptionID == "" || loginServer == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("# %s (%s)\nACR_TOKEN=$(az acr login -n %s --subscription %s --expose-token --output tsv --query accessToken)\nprintf '%%s' \"$ACR_TOKEN\" | docker login %s --username %s --password-stdin\n\n",
|
||||
registryName, loginServer, registryName, subscriptionID, loginServer, acrDockerTokenUsername)
|
||||
}
|
||||
|
||||
func writeACROutput(outputDirectory, tenantDomain, directorySuffix, prefix string, body [][]string, loot string, verbosity int, wrap bool) {
|
||||
o := internal.OutputClient{
|
||||
Verbosity: verbosity,
|
||||
CallingModule: globals.AZ_ACR_MODULE_NAME,
|
||||
PrefixIdentifier: prefix,
|
||||
Table: internal.TableClient{
|
||||
Wrap: wrap,
|
||||
DirectoryName: filepath.Join(outputDirectory, globals.CLOUDFOX_BASE_DIRECTORY, globals.AZ_DIR_BASE, tenantDomain, directorySuffix),
|
||||
},
|
||||
}
|
||||
o.Table.TableFiles = append(o.Table.TableFiles, internal.TableFile{
|
||||
Header: []string{"Registry Name", "Subscription Name", "Subscription ID", "Login Server", "Anonymous Pulls"},
|
||||
Body: body,
|
||||
Name: globals.AZ_ACR_MODULE_NAME,
|
||||
})
|
||||
if loot != "" {
|
||||
o.Loot.DirectoryName = filepath.Join(outputDirectory, globals.CLOUDFOX_BASE_DIRECTORY, globals.AZ_DIR_BASE, tenantDomain, directorySuffix, "loot")
|
||||
o.Loot.LootFiles = append(o.Loot.LootFiles, internal.LootFile{Contents: loot, Name: "acr-login-commands"})
|
||||
o.WriteFullOutput(o.Table.TableFiles, o.Loot.LootFiles)
|
||||
} else {
|
||||
o.WriteFullOutput(o.Table.TableFiles, nil)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func getContainerRegistriesOriginal(subscriptionID string) ([]containerregistry.Registry, error) {
|
||||
client := internal.GetContainerRegistriesClient(subscriptionID)
|
||||
page, err := client.List(context.TODO())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var registries []containerregistry.Registry
|
||||
for page.NotDone() {
|
||||
registries = append(registries, page.Values()...)
|
||||
if err := page.NextWithContext(context.TODO()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return registries, nil
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package azure
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/subscriptions"
|
||||
containerregistry "github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2022-02-01-preview/containerregistry"
|
||||
"github.com/aws/smithy-go/ptr"
|
||||
)
|
||||
|
||||
func TestGetACRRelevantData(t *testing.T) {
|
||||
registries := []containerregistry.Registry{
|
||||
{
|
||||
Name: ptr.String("packnet"),
|
||||
RegistryProperties: &containerregistry.RegistryProperties{
|
||||
LoginServer: ptr.String("packnet.azurecr.io"),
|
||||
AnonymousPullEnabled: ptr.Bool(true),
|
||||
},
|
||||
},
|
||||
}
|
||||
subscription := subscriptions.Subscription{
|
||||
SubscriptionID: ptr.String("8bb4e6f5-be51-4eab-bd68-ecf6fd08338f"),
|
||||
DisplayName: ptr.String("Production"),
|
||||
}
|
||||
|
||||
body, loot, err := getACRRelevantData(subscription, registries)
|
||||
if err != nil {
|
||||
t.Fatalf("getACRRelevantData returned an error: %v", err)
|
||||
}
|
||||
if len(body) != 1 || body[0][0] != "packnet" || body[0][3] != "packnet.azurecr.io" || body[0][4] != "true" {
|
||||
t.Fatalf("unexpected ACR table body: %+v", body)
|
||||
}
|
||||
for _, expected := range []string{
|
||||
"az acr login -n packnet --subscription 8bb4e6f5-be51-4eab-bd68-ecf6fd08338f --expose-token",
|
||||
"docker login packnet.azurecr.io --username " + acrDockerTokenUsername + " --password-stdin",
|
||||
} {
|
||||
if !strings.Contains(loot, expected) {
|
||||
t.Errorf("loot does not contain %q:\n%s", expected, loot)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
package azure
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/profiles/latest/automation/mgmt/automation"
|
||||
"github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/subscriptions"
|
||||
"github.com/BishopFox/cloudfox/globals"
|
||||
"github.com/BishopFox/cloudfox/internal"
|
||||
"github.com/aws/smithy-go/ptr"
|
||||
)
|
||||
|
||||
var (
|
||||
getAutomationAccounts = getAutomationAccountsOriginal
|
||||
getAutomationAssets = getAutomationAssetsOriginal
|
||||
)
|
||||
|
||||
type automationAssets struct {
|
||||
Runbooks []automation.Runbook `json:"runbooks"`
|
||||
RunbookSources map[string]string `json:"-"`
|
||||
Credentials []automation.Credential `json:"credentials"`
|
||||
Webhooks []automation.Webhook `json:"webhooks"`
|
||||
}
|
||||
|
||||
type automationLoot struct {
|
||||
Runbooks string
|
||||
Credentials string
|
||||
Webhooks string
|
||||
}
|
||||
|
||||
func AzAutomationCommand(tenantID, subscription, outputDirectory, version string, verbosity int, wrap, merged bool) error {
|
||||
if tenantID != "" && subscription == "" {
|
||||
tenant := populateTenant(tenantID)
|
||||
if ptr.ToString(tenant.ID) == "" {
|
||||
return fmt.Errorf("could not find Azure tenant %q", tenantID)
|
||||
}
|
||||
azureLogger.InfoM(fmt.Sprintf("Enumerating Automation Accounts for tenant %s (%s)", ptr.ToString(tenant.DefaultDomain), ptr.ToString(tenant.ID)), globals.AZ_AUTOMATION_MODULE_NAME)
|
||||
subs := GetSubscriptionsPerTenantID(ptr.ToString(tenant.ID))
|
||||
if !merged {
|
||||
for _, sub := range subs {
|
||||
if err := runAutomationForSubscription(sub, outputDirectory, verbosity, wrap); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
var rows [][]string
|
||||
var loot automationLoot
|
||||
for _, sub := range subs {
|
||||
subRows, subLoot, warnings, err := getAutomationData(sub)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rows = append(rows, subRows...)
|
||||
loot.Runbooks += subLoot.Runbooks
|
||||
loot.Credentials += subLoot.Credentials
|
||||
loot.Webhooks += subLoot.Webhooks
|
||||
for _, warning := range warnings {
|
||||
azureLogger.WarnM(warning.Error(), globals.AZ_AUTOMATION_MODULE_NAME)
|
||||
}
|
||||
}
|
||||
if len(subs) > 0 {
|
||||
writeAutomationOutput(outputDirectory, ptr.ToString(tenant.DefaultDomain), "1-tenant-level", ptr.ToString(tenant.DefaultDomain), rows, loot, verbosity, wrap)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if tenantID == "" && subscription != "" {
|
||||
id := ptr.ToString(GetSubscriptionID(subscription))
|
||||
for _, sub := range GetSubscriptions() {
|
||||
if ptr.ToString(sub.SubscriptionID) == id {
|
||||
return runAutomationForSubscription(sub, outputDirectory, verbosity, wrap)
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("could not find Azure subscription %q", subscription)
|
||||
}
|
||||
return fmt.Errorf("please specify either --tenant or --subscription")
|
||||
}
|
||||
|
||||
func runAutomationForSubscription(sub subscriptions.Subscription, outputDirectory string, verbosity int, wrap bool) error {
|
||||
azureLogger.InfoM(fmt.Sprintf("Enumerating Automation Accounts for subscription %s (%s)", ptr.ToString(sub.DisplayName), ptr.ToString(sub.SubscriptionID)), globals.AZ_AUTOMATION_MODULE_NAME)
|
||||
rows, loot, warnings, err := getAutomationData(sub)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, warning := range warnings {
|
||||
azureLogger.WarnM(warning.Error(), globals.AZ_AUTOMATION_MODULE_NAME)
|
||||
}
|
||||
tenant := populateTenant(ptr.ToString(sub.TenantID))
|
||||
writeAutomationOutput(outputDirectory, ptr.ToString(tenant.DefaultDomain), ptr.ToString(sub.DisplayName), ptr.ToString(sub.DisplayName), rows, loot, verbosity, wrap)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAutomationData(subscription subscriptions.Subscription) ([][]string, automationLoot, []error, error) {
|
||||
subscriptionID := ptr.ToString(subscription.SubscriptionID)
|
||||
accounts, err := getAutomationAccounts(subscriptionID)
|
||||
if err != nil {
|
||||
return nil, automationLoot{}, nil, fmt.Errorf("could not enumerate Automation Accounts for subscription %s: %w", subscriptionID, err)
|
||||
}
|
||||
var rows [][]string
|
||||
var loot automationLoot
|
||||
var warnings []error
|
||||
for _, account := range accounts {
|
||||
name := ptr.ToString(account.Name)
|
||||
rg := resourceGroupFromID(ptr.ToString(account.ID))
|
||||
assets, err := getAutomationAssets(subscriptionID, rg, name)
|
||||
if err != nil {
|
||||
warnings = append(warnings, fmt.Errorf("could not enumerate assets for Automation Account %s: %w", name, err))
|
||||
continue
|
||||
}
|
||||
rows = append(rows, []string{name, ptr.ToString(subscription.DisplayName), subscriptionID, rg, fmt.Sprintf("%d", len(assets.Runbooks)), fmt.Sprintf("%d", len(assets.Credentials)), fmt.Sprintf("%d", len(assets.Webhooks))})
|
||||
if len(assets.Runbooks)+len(assets.Credentials)+len(assets.Webhooks) > 0 {
|
||||
accountLoot := formatAutomationLoot(subscriptionID, name, rg, assets)
|
||||
loot.Runbooks += accountLoot.Runbooks
|
||||
loot.Credentials += accountLoot.Credentials
|
||||
loot.Webhooks += accountLoot.Webhooks
|
||||
}
|
||||
}
|
||||
sort.Slice(rows, func(i, j int) bool { return rows[i][0] < rows[j][0] })
|
||||
return rows, loot, warnings, nil
|
||||
}
|
||||
|
||||
func formatAutomationLoot(subscriptionID, accountName, resourceGroup string, assets automationAssets) automationLoot {
|
||||
var loot automationLoot
|
||||
var runbooks, credentials, webhooks strings.Builder
|
||||
for _, runbook := range assets.Runbooks {
|
||||
name := ptr.ToString(runbook.Name)
|
||||
fmt.Fprintf(&runbooks, "===============================================================\nAutomation Account: %s\nResource Group: %s\nRunbook: %s\n\n%s\n\n", accountName, resourceGroup, name, assets.RunbookSources[name])
|
||||
}
|
||||
for _, credential := range assets.Credentials {
|
||||
name := ptr.ToString(credential.Name)
|
||||
value, _ := json.MarshalIndent(map[string]any{"name": name, "properties": credential.CredentialProperties}, "", " ")
|
||||
fmt.Fprintf(&credentials, "az automation credential show --automation-account-name %q --resource-group %q --name %q --subscription %q\n%s\n\n", accountName, resourceGroup, name, subscriptionID, value)
|
||||
}
|
||||
for _, webhook := range assets.Webhooks {
|
||||
name := ptr.ToString(webhook.Name)
|
||||
value, _ := json.MarshalIndent(map[string]any{"name": name, "properties": webhook.WebhookProperties}, "", " ")
|
||||
fmt.Fprintf(&webhooks, "az automation webhook show --automation-account-name %q --resource-group %q --name %q --subscription %q\n%s\n\n", accountName, resourceGroup, name, subscriptionID, value)
|
||||
}
|
||||
loot.Runbooks = runbooks.String()
|
||||
loot.Credentials = credentials.String()
|
||||
loot.Webhooks = webhooks.String()
|
||||
return loot
|
||||
}
|
||||
|
||||
func writeAutomationOutput(outputDirectory, tenantDomain, directorySuffix, prefix string, rows [][]string, loot automationLoot, verbosity int, wrap bool) {
|
||||
o := internal.OutputClient{
|
||||
Verbosity: verbosity,
|
||||
CallingModule: globals.AZ_AUTOMATION_MODULE_NAME,
|
||||
PrefixIdentifier: prefix,
|
||||
Table: internal.TableClient{
|
||||
Wrap: wrap,
|
||||
DirectoryName: filepath.Join(outputDirectory, globals.CLOUDFOX_BASE_DIRECTORY, globals.AZ_DIR_BASE, tenantDomain, directorySuffix),
|
||||
},
|
||||
}
|
||||
o.Table.TableFiles = append(o.Table.TableFiles, internal.TableFile{
|
||||
Header: []string{"Automation Account", "Subscription Name", "Subscription ID", "Resource Group", "Runbooks", "Credentials", "Webhooks"},
|
||||
Body: rows,
|
||||
Name: globals.AZ_AUTOMATION_MODULE_NAME,
|
||||
})
|
||||
o.Loot.DirectoryName = filepath.Join(o.Table.DirectoryName, "loot")
|
||||
if loot.Runbooks != "" {
|
||||
o.Loot.LootFiles = append(o.Loot.LootFiles, internal.LootFile{Name: "automation-runbooks", Contents: loot.Runbooks})
|
||||
}
|
||||
if loot.Credentials != "" {
|
||||
o.Loot.LootFiles = append(o.Loot.LootFiles, internal.LootFile{Name: "automation-credentials", Contents: loot.Credentials})
|
||||
}
|
||||
if loot.Webhooks != "" {
|
||||
o.Loot.LootFiles = append(o.Loot.LootFiles, internal.LootFile{Name: "automation-webhooks", Contents: loot.Webhooks})
|
||||
}
|
||||
if len(o.Loot.LootFiles) > 0 {
|
||||
o.WriteFullOutput(o.Table.TableFiles, o.Loot.LootFiles)
|
||||
} else {
|
||||
o.WriteFullOutput(o.Table.TableFiles, nil)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func getAutomationAccountsOriginal(subscriptionID string) ([]automation.Account, error) {
|
||||
accountsClient, _, _, _ := internal.GetAutomationClients(subscriptionID)
|
||||
page, err := accountsClient.List(context.TODO())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var accounts []automation.Account
|
||||
for page.NotDone() {
|
||||
accounts = append(accounts, page.Values()...)
|
||||
if err := page.NextWithContext(context.TODO()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
func getAutomationAssetsOriginal(subscriptionID, resourceGroup, accountName string) (automationAssets, error) {
|
||||
_, runbooksClient, credentialsClient, webhooksClient := internal.GetAutomationClients(subscriptionID)
|
||||
var assets automationAssets
|
||||
runbooks, err := runbooksClient.ListByAutomationAccount(context.TODO(), resourceGroup, accountName)
|
||||
if err != nil {
|
||||
return assets, err
|
||||
}
|
||||
for runbooks.NotDone() {
|
||||
assets.Runbooks = append(assets.Runbooks, runbooks.Values()...)
|
||||
if err := runbooks.NextWithContext(context.TODO()); err != nil {
|
||||
return assets, err
|
||||
}
|
||||
}
|
||||
assets.RunbookSources = make(map[string]string, len(assets.Runbooks))
|
||||
for _, runbook := range assets.Runbooks {
|
||||
name := ptr.ToString(runbook.Name)
|
||||
content, contentErr := runbooksClient.GetContent(context.TODO(), resourceGroup, accountName, name)
|
||||
if contentErr != nil {
|
||||
assets.RunbookSources[name] = fmt.Sprintf("[source unavailable: %s]", contentErr)
|
||||
continue
|
||||
}
|
||||
assets.RunbookSources[name] = ptr.ToString(content.Value)
|
||||
}
|
||||
credentials, err := credentialsClient.ListByAutomationAccount(context.TODO(), resourceGroup, accountName)
|
||||
if err != nil {
|
||||
return assets, err
|
||||
}
|
||||
for credentials.NotDone() {
|
||||
assets.Credentials = append(assets.Credentials, credentials.Values()...)
|
||||
if err := credentials.NextWithContext(context.TODO()); err != nil {
|
||||
return assets, err
|
||||
}
|
||||
}
|
||||
webhooks, err := webhooksClient.ListByAutomationAccount(context.TODO(), resourceGroup, accountName, "")
|
||||
if err != nil {
|
||||
return assets, err
|
||||
}
|
||||
for webhooks.NotDone() {
|
||||
assets.Webhooks = append(assets.Webhooks, webhooks.Values()...)
|
||||
if err := webhooks.NextWithContext(context.TODO()); err != nil {
|
||||
return assets, err
|
||||
}
|
||||
}
|
||||
return assets, nil
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package azure
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/profiles/latest/automation/mgmt/automation"
|
||||
"github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/subscriptions"
|
||||
"github.com/aws/smithy-go/ptr"
|
||||
)
|
||||
|
||||
func TestGetAutomationDataCountsAndLoot(t *testing.T) {
|
||||
originalAccounts, originalAssets := getAutomationAccounts, getAutomationAssets
|
||||
t.Cleanup(func() { getAutomationAccounts, getAutomationAssets = originalAccounts, originalAssets })
|
||||
getAutomationAccounts = func(string) ([]automation.Account, error) {
|
||||
return []automation.Account{{Name: ptr.String("operations"), ID: ptr.String("/subscriptions/sub/resourceGroups/ops/providers/Microsoft.Automation/automationAccounts/operations")}}, nil
|
||||
}
|
||||
getAutomationAssets = func(_, resourceGroup, account string) (automationAssets, error) {
|
||||
if resourceGroup != "ops" || account != "operations" {
|
||||
t.Fatalf("unexpected automation target %s/%s", resourceGroup, account)
|
||||
}
|
||||
return automationAssets{Runbooks: []automation.Runbook{{Name: ptr.String("daily")}, {Name: ptr.String("weekly")}}, RunbookSources: map[string]string{"daily": "Write-Output 'daily'", "weekly": "print('weekly')"}, Credentials: []automation.Credential{{Name: ptr.String("service-account")}}, Webhooks: []automation.Webhook{{Name: ptr.String("deploy")}}}, nil
|
||||
}
|
||||
sub := subscriptions.Subscription{SubscriptionID: ptr.String("sub"), DisplayName: ptr.String("Production")}
|
||||
rows, loot, warnings, err := getAutomationData(sub)
|
||||
if err != nil || len(warnings) != 0 {
|
||||
t.Fatalf("unexpected errors: err=%v warnings=%v", err, warnings)
|
||||
}
|
||||
if len(rows) != 1 || rows[0][4] != "2" || rows[0][5] != "1" || rows[0][6] != "1" {
|
||||
t.Fatalf("unexpected automation rows: %+v", rows)
|
||||
}
|
||||
if !strings.Contains(loot.Runbooks, "Runbook: daily") || !strings.Contains(loot.Runbooks, "Write-Output 'daily'") {
|
||||
t.Errorf("runbook loot is incomplete: %s", loot.Runbooks)
|
||||
}
|
||||
for _, value := range []string{"az automation credential show", "service-account", `"name": "service-account"`} {
|
||||
if !strings.Contains(loot.Credentials, value) {
|
||||
t.Errorf("credential loot missing %q: %s", value, loot.Credentials)
|
||||
}
|
||||
}
|
||||
for _, value := range []string{"az automation webhook show", "deploy", `"name": "deploy"`} {
|
||||
if !strings.Contains(loot.Webhooks, value) {
|
||||
t.Errorf("webhook loot missing %q: %s", value, loot.Webhooks)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,526 @@
|
||||
package azure
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/subscriptions"
|
||||
"github.com/Azure/azure-sdk-for-go/profiles/latest/web/mgmt/web"
|
||||
"github.com/BishopFox/cloudfox/globals"
|
||||
"github.com/BishopFox/cloudfox/internal"
|
||||
"github.com/aws/smithy-go/ptr"
|
||||
)
|
||||
|
||||
var (
|
||||
getFunctionApps = getFunctionAppsOriginal
|
||||
getFunctionConfiguration = getFunctionConfigurationOriginal
|
||||
getFunctions = getFunctionsOriginal
|
||||
)
|
||||
|
||||
type functionConfiguration struct {
|
||||
AppSettings map[string]*string
|
||||
ConnectionStrings map[string]*web.ConnStringValueTypePair
|
||||
AuthSettings web.SiteAuthSettingsV2
|
||||
}
|
||||
|
||||
type functionLoot struct {
|
||||
Configuration string
|
||||
TestData string
|
||||
}
|
||||
|
||||
func AzFuncCommand(tenantID, subscription, outputDirectory, version string, verbosity int, wrap, merged bool) error {
|
||||
if tenantID != "" && subscription == "" {
|
||||
tenant := populateTenant(tenantID)
|
||||
if ptr.ToString(tenant.ID) == "" {
|
||||
return fmt.Errorf("could not find Azure tenant %q", tenantID)
|
||||
}
|
||||
azureLogger.InfoM(fmt.Sprintf("Enumerating Function Apps for tenant %s (%s)", ptr.ToString(tenant.DefaultDomain), ptr.ToString(tenant.ID)), globals.AZ_FUNC_MODULE_NAME)
|
||||
subs := GetSubscriptionsPerTenantID(ptr.ToString(tenant.ID))
|
||||
if !merged {
|
||||
for _, sub := range subs {
|
||||
if err := runFuncForSubscription(sub, outputDirectory, verbosity, wrap); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
var rows [][]string
|
||||
var loot functionLoot
|
||||
for _, sub := range subs {
|
||||
subRows, subLoot, warnings, err := getFunctionData(sub)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rows = append(rows, subRows...)
|
||||
loot.Configuration += subLoot.Configuration
|
||||
loot.TestData += subLoot.TestData
|
||||
for _, warning := range warnings {
|
||||
azureLogger.WarnM(warning.Error(), globals.AZ_FUNC_MODULE_NAME)
|
||||
}
|
||||
}
|
||||
if len(subs) > 0 {
|
||||
writeFunctionOutput(outputDirectory, ptr.ToString(tenant.DefaultDomain), "1-tenant-level", ptr.ToString(tenant.DefaultDomain), rows, loot, verbosity, wrap)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if tenantID == "" && subscription != "" {
|
||||
id := ptr.ToString(GetSubscriptionID(subscription))
|
||||
for _, sub := range GetSubscriptions() {
|
||||
if ptr.ToString(sub.SubscriptionID) == id {
|
||||
return runFuncForSubscription(sub, outputDirectory, verbosity, wrap)
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("could not find Azure subscription %q", subscription)
|
||||
}
|
||||
return fmt.Errorf("please specify either --tenant or --subscription")
|
||||
}
|
||||
|
||||
func runFuncForSubscription(sub subscriptions.Subscription, outputDirectory string, verbosity int, wrap bool) error {
|
||||
azureLogger.InfoM(fmt.Sprintf("Enumerating Function Apps for subscription %s (%s)", ptr.ToString(sub.DisplayName), ptr.ToString(sub.SubscriptionID)), globals.AZ_FUNC_MODULE_NAME)
|
||||
rows, loot, warnings, err := getFunctionData(sub)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, warning := range warnings {
|
||||
azureLogger.WarnM(warning.Error(), globals.AZ_FUNC_MODULE_NAME)
|
||||
}
|
||||
tenant := populateTenant(ptr.ToString(sub.TenantID))
|
||||
writeFunctionOutput(outputDirectory, ptr.ToString(tenant.DefaultDomain), ptr.ToString(sub.DisplayName), ptr.ToString(sub.DisplayName), rows, loot, verbosity, wrap)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFunctionData(subscription subscriptions.Subscription) ([][]string, functionLoot, []error, error) {
|
||||
subscriptionID := ptr.ToString(subscription.SubscriptionID)
|
||||
apps, err := getFunctionApps(subscriptionID)
|
||||
if err != nil {
|
||||
return nil, functionLoot{}, nil, fmt.Errorf("could not enumerate Function Apps for subscription %s: %w", subscriptionID, err)
|
||||
}
|
||||
var rows [][]string
|
||||
var loot functionLoot
|
||||
var warnings []error
|
||||
for _, app := range apps {
|
||||
if !strings.Contains(strings.ToLower(ptr.ToString(app.Kind)), "functionapp") {
|
||||
continue
|
||||
}
|
||||
name := ptr.ToString(app.Name)
|
||||
host := ""
|
||||
if app.SiteProperties != nil {
|
||||
host = ptr.ToString(app.DefaultHostName)
|
||||
}
|
||||
appURL := host
|
||||
if host != "" {
|
||||
appURL = "https://" + host
|
||||
}
|
||||
rg := resourceGroupFromID(ptr.ToString(app.ID))
|
||||
config, err := getFunctionConfiguration(subscriptionID, rg, name)
|
||||
if err != nil {
|
||||
warnings = append(warnings, fmt.Errorf("could not read configuration for Function App %s: %w", name, err))
|
||||
}
|
||||
providers := functionAuthProviders(config.AuthSettings)
|
||||
functions, err := getFunctions(subscriptionID, rg, name)
|
||||
if err != nil {
|
||||
warnings = append(warnings, fmt.Errorf("could not enumerate functions for Function App %s: %w", name, err))
|
||||
rows = append(rows, functionAppFallbackRow(name, subscription, appURL, providers))
|
||||
} else if len(functions) == 0 {
|
||||
rows = append(rows, functionAppFallbackRow(name, subscription, appURL, providers))
|
||||
} else {
|
||||
for _, function := range functions {
|
||||
functionName := lastResourceIDSegment(ptr.ToString(function.Name))
|
||||
trigger := "Unknown"
|
||||
state := "Unknown"
|
||||
if function.FunctionEnvelopeProperties != nil {
|
||||
trigger = functionTrigger(function.Config, ptr.ToString(function.InvokeURLTemplate))
|
||||
state = "Enabled"
|
||||
if ptr.ToBool(function.IsDisabled) {
|
||||
state = "Disabled"
|
||||
}
|
||||
loot.TestData += formatFunctionTestDataLoot(subscriptionID, rg, name, functionName, ptr.ToString(function.TestData))
|
||||
}
|
||||
authMethods := append(functionAuthLevels(function.Config), providers...)
|
||||
if len(authMethods) == 0 {
|
||||
authMethods = []string{"Unknown"}
|
||||
}
|
||||
rows = append(rows, []string{name, functionName, trigger, strings.Join(uniqueStrings(authMethods), ", "), state, ptr.ToString(subscription.DisplayName), subscriptionID})
|
||||
}
|
||||
}
|
||||
loot.Configuration += formatFunctionLoot(name, appURL, config)
|
||||
}
|
||||
sort.Slice(rows, func(i, j int) bool {
|
||||
if rows[i][0] == rows[j][0] {
|
||||
return rows[i][1] < rows[j][1]
|
||||
}
|
||||
return rows[i][0] < rows[j][0]
|
||||
})
|
||||
return rows, loot, warnings, nil
|
||||
}
|
||||
|
||||
func functionAppFallbackRow(name string, subscription subscriptions.Subscription, appURL string, providers []string) []string {
|
||||
authentication := strings.Join(providers, ", ")
|
||||
if authentication == "" {
|
||||
authentication = "Unknown"
|
||||
}
|
||||
trigger := "Unknown"
|
||||
if appURL != "" {
|
||||
trigger = "App URL: " + appURL
|
||||
}
|
||||
return []string{name, "", trigger, authentication, "Unknown", ptr.ToString(subscription.DisplayName), ptr.ToString(subscription.SubscriptionID)}
|
||||
}
|
||||
|
||||
func functionTableHeader() []string {
|
||||
return []string{"Function App", "Function Name", "Trigger", "Authentication", "State", "Subscription Name", "Subscription ID"}
|
||||
}
|
||||
|
||||
func functionVisibleColumns() []string {
|
||||
return []string{"Function App", "Function Name", "Trigger", "Authentication", "State", "Subscription Name"}
|
||||
}
|
||||
|
||||
func formatFunctionLoot(name, appURL string, config functionConfiguration) string {
|
||||
if len(config.AppSettings) == 0 && len(config.ConnectionStrings) == 0 {
|
||||
return ""
|
||||
}
|
||||
var loot strings.Builder
|
||||
fmt.Fprintf(&loot, "===============================================================\nFunction App: %s\nURL: %s\n\n", name, appURL)
|
||||
fmt.Fprintf(&loot, "App Service Authentication: %s\n\n", strings.Join(functionAuthProviders(config.AuthSettings), ", "))
|
||||
loot.WriteString("App Settings:\n")
|
||||
settings := sortedKeys(config.AppSettings)
|
||||
for _, key := range settings {
|
||||
fmt.Fprintf(&loot, "%s=%s\n", key, ptr.ToString(config.AppSettings[key]))
|
||||
}
|
||||
loot.WriteString("\nConnection Strings:\n")
|
||||
connections := make([]string, 0, len(config.ConnectionStrings))
|
||||
for key := range config.ConnectionStrings {
|
||||
connections = append(connections, key)
|
||||
}
|
||||
sort.Strings(connections)
|
||||
for _, key := range connections {
|
||||
value := config.ConnectionStrings[key]
|
||||
if value != nil {
|
||||
fmt.Fprintf(&loot, "%s=%s (type=%s)\n", key, ptr.ToString(value.Value), value.Type)
|
||||
}
|
||||
}
|
||||
loot.WriteString("\n")
|
||||
return loot.String()
|
||||
}
|
||||
|
||||
func functionAuthProviders(settings web.SiteAuthSettingsV2) []string {
|
||||
if settings.SiteAuthSettingsV2Properties == nil || settings.Platform == nil || !ptr.ToBool(settings.Platform.Enabled) {
|
||||
return nil
|
||||
}
|
||||
providers := settings.IdentityProviders
|
||||
if providers == nil {
|
||||
return []string{"App Service Authentication enabled"}
|
||||
}
|
||||
var methods []string
|
||||
if providers.AzureActiveDirectory != nil && ptr.ToBool(providers.AzureActiveDirectory.Enabled) {
|
||||
methods = append(methods, "Entra ID")
|
||||
}
|
||||
if providers.Facebook != nil && ptr.ToBool(providers.Facebook.Enabled) {
|
||||
methods = append(methods, "Facebook")
|
||||
}
|
||||
if providers.GitHub != nil && ptr.ToBool(providers.GitHub.Enabled) {
|
||||
methods = append(methods, "GitHub")
|
||||
}
|
||||
if providers.Google != nil && ptr.ToBool(providers.Google.Enabled) {
|
||||
methods = append(methods, "Google")
|
||||
}
|
||||
if providers.LegacyMicrosoftAccount != nil && ptr.ToBool(providers.LegacyMicrosoftAccount.Enabled) {
|
||||
methods = append(methods, "Microsoft Account")
|
||||
}
|
||||
if providers.Twitter != nil && ptr.ToBool(providers.Twitter.Enabled) {
|
||||
methods = append(methods, "Twitter")
|
||||
}
|
||||
if providers.Apple != nil && ptr.ToBool(providers.Apple.Enabled) {
|
||||
methods = append(methods, "Apple")
|
||||
}
|
||||
if providers.AzureStaticWebApps != nil && ptr.ToBool(providers.AzureStaticWebApps.Enabled) {
|
||||
methods = append(methods, "Azure Static Web Apps")
|
||||
}
|
||||
for name, provider := range providers.CustomOpenIDConnectProviders {
|
||||
if provider != nil && ptr.ToBool(provider.Enabled) {
|
||||
methods = append(methods, "OpenID Connect: "+name)
|
||||
}
|
||||
}
|
||||
sort.Strings(methods)
|
||||
return methods
|
||||
}
|
||||
|
||||
func functionAuthLevels(config any) []string {
|
||||
encoded, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var value any
|
||||
if json.Unmarshal(encoded, &value) != nil {
|
||||
return nil
|
||||
}
|
||||
var levels []string
|
||||
var visit func(any)
|
||||
visit = func(current any) {
|
||||
switch typed := current.(type) {
|
||||
case map[string]any:
|
||||
for key, child := range typed {
|
||||
if strings.EqualFold(key, "authLevel") {
|
||||
if level, ok := child.(string); ok {
|
||||
levels = append(levels, formatFunctionAuthLevel(level))
|
||||
}
|
||||
} else {
|
||||
visit(child)
|
||||
}
|
||||
}
|
||||
case []any:
|
||||
for _, child := range typed {
|
||||
visit(child)
|
||||
}
|
||||
}
|
||||
}
|
||||
visit(value)
|
||||
return uniqueStrings(levels)
|
||||
}
|
||||
|
||||
func functionTrigger(config any, invokeURL string) string {
|
||||
encoded, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return "Unknown"
|
||||
}
|
||||
var value any
|
||||
if json.Unmarshal(encoded, &value) != nil {
|
||||
return "Unknown"
|
||||
}
|
||||
result := ""
|
||||
var visit func(any)
|
||||
visit = func(current any) {
|
||||
if result != "" {
|
||||
return
|
||||
}
|
||||
switch typed := current.(type) {
|
||||
case map[string]any:
|
||||
bindingType := mapStringValue(typed, "type")
|
||||
if strings.HasSuffix(strings.ToLower(bindingType), "trigger") {
|
||||
label := functionTriggerLabel(bindingType)
|
||||
detail := functionTriggerDetail(bindingType, typed, invokeURL)
|
||||
result = label
|
||||
if detail != "" {
|
||||
result += ": " + detail
|
||||
}
|
||||
return
|
||||
}
|
||||
for _, child := range typed {
|
||||
visit(child)
|
||||
}
|
||||
case []any:
|
||||
for _, child := range typed {
|
||||
visit(child)
|
||||
}
|
||||
}
|
||||
}
|
||||
visit(value)
|
||||
if result == "" {
|
||||
return "Unknown"
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func functionTriggerLabel(bindingType string) string {
|
||||
switch strings.ToLower(bindingType) {
|
||||
case "httptrigger":
|
||||
return "HTTP"
|
||||
case "timertrigger":
|
||||
return "Time Trigger"
|
||||
case "blobtrigger":
|
||||
return "Blob Trigger"
|
||||
case "queuetrigger":
|
||||
return "Queue Trigger"
|
||||
case "servicebustrigger":
|
||||
return "Service Bus Trigger"
|
||||
case "eventhubtrigger":
|
||||
return "Event Hub Trigger"
|
||||
case "eventgridtrigger":
|
||||
return "Event Grid Trigger"
|
||||
case "cosmosdbtrigger":
|
||||
return "Cosmos DB Trigger"
|
||||
case "kafkatrigger":
|
||||
return "Kafka Trigger"
|
||||
default:
|
||||
name := strings.TrimSuffix(bindingType, "Trigger")
|
||||
return name + " Trigger"
|
||||
}
|
||||
}
|
||||
|
||||
func functionTriggerDetail(bindingType string, binding map[string]any, invokeURL string) string {
|
||||
switch strings.ToLower(bindingType) {
|
||||
case "httptrigger":
|
||||
return invokeURL
|
||||
case "timertrigger":
|
||||
return mapStringValue(binding, "schedule")
|
||||
case "blobtrigger":
|
||||
return mapStringValue(binding, "path")
|
||||
case "queuetrigger":
|
||||
return mapStringValue(binding, "queueName")
|
||||
case "servicebustrigger":
|
||||
return firstNonEmpty(mapStringValue(binding, "queueName"), mapStringValue(binding, "topicName"))
|
||||
case "eventhubtrigger":
|
||||
return mapStringValue(binding, "eventHubName")
|
||||
case "cosmosdbtrigger":
|
||||
database := mapStringValue(binding, "databaseName")
|
||||
container := firstNonEmpty(mapStringValue(binding, "containerName"), mapStringValue(binding, "collectionName"))
|
||||
return strings.Trim(strings.Join([]string{database, container}, "/"), "/")
|
||||
case "kafkatrigger":
|
||||
return firstNonEmpty(mapStringValue(binding, "topic"), mapStringValue(binding, "brokerList"))
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func mapStringValue(values map[string]any, name string) string {
|
||||
for key, value := range values {
|
||||
if strings.EqualFold(key, name) {
|
||||
if text, ok := value.(string); ok {
|
||||
return text
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func firstNonEmpty(values ...string) string {
|
||||
for _, value := range values {
|
||||
if value != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func formatFunctionTestDataLoot(subscriptionID, resourceGroup, appName, functionName, testData string) string {
|
||||
if testData == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("az functionapp function show --name %q --resource-group %q --function-name %q --subscription %q --query testData --output tsv\n%s\n\n", appName, resourceGroup, functionName, subscriptionID, testData)
|
||||
}
|
||||
|
||||
func formatFunctionAuthLevel(level string) string {
|
||||
switch strings.ToLower(level) {
|
||||
case "anonymous":
|
||||
return "Anonymous"
|
||||
case "function":
|
||||
return "Function key"
|
||||
case "admin":
|
||||
return "Admin/master key"
|
||||
default:
|
||||
return level
|
||||
}
|
||||
}
|
||||
|
||||
func uniqueStrings(values []string) []string {
|
||||
seen := map[string]bool{}
|
||||
var result []string
|
||||
for _, value := range values {
|
||||
if value != "" && !seen[value] {
|
||||
seen[value] = true
|
||||
result = append(result, value)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func sortedKeys(values map[string]*string) []string {
|
||||
keys := make([]string, 0, len(values))
|
||||
for key := range values {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func getFunctionAppsOriginal(subscriptionID string) ([]web.Site, error) {
|
||||
client := internal.GetWebAppsClient(subscriptionID)
|
||||
page, err := client.List(context.TODO())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var apps []web.Site
|
||||
for page.NotDone() {
|
||||
apps = append(apps, page.Values()...)
|
||||
if err := page.NextWithContext(context.TODO()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return apps, nil
|
||||
}
|
||||
|
||||
func getFunctionConfigurationOriginal(subscriptionID, resourceGroup, name string) (functionConfiguration, error) {
|
||||
client := internal.GetWebAppsClient(subscriptionID)
|
||||
var config functionConfiguration
|
||||
settings, err := client.ListApplicationSettings(context.TODO(), resourceGroup, name)
|
||||
if err != nil {
|
||||
return config, err
|
||||
}
|
||||
config.AppSettings = settings.Properties
|
||||
connections, err := client.ListConnectionStrings(context.TODO(), resourceGroup, name)
|
||||
if err != nil {
|
||||
return config, err
|
||||
}
|
||||
config.ConnectionStrings = connections.Properties
|
||||
authSettings, err := client.GetAuthSettingsV2WithoutSecrets(context.TODO(), resourceGroup, name)
|
||||
if err != nil {
|
||||
return config, err
|
||||
}
|
||||
config.AuthSettings = authSettings
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func getFunctionsOriginal(subscriptionID, resourceGroup, appName string) ([]web.FunctionEnvelope, error) {
|
||||
client := internal.GetWebAppsClient(subscriptionID)
|
||||
page, err := client.ListFunctions(context.TODO(), resourceGroup, appName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var functions []web.FunctionEnvelope
|
||||
for page.NotDone() {
|
||||
functions = append(functions, page.Values()...)
|
||||
if err := page.NextWithContext(context.TODO()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return functions, nil
|
||||
}
|
||||
|
||||
func resourceGroupFromID(id string) string {
|
||||
parts := strings.Split(strings.Trim(id, "/"), "/")
|
||||
for i := 0; i+1 < len(parts); i++ {
|
||||
if strings.EqualFold(parts[i], "resourceGroups") {
|
||||
return parts[i+1]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func writeFunctionOutput(outputDirectory, tenantDomain, directorySuffix, prefix string, rows [][]string, loot functionLoot, verbosity int, wrap bool) {
|
||||
o := internal.OutputClient{
|
||||
Verbosity: verbosity,
|
||||
CallingModule: globals.AZ_FUNC_MODULE_NAME,
|
||||
PrefixIdentifier: prefix,
|
||||
Table: internal.TableClient{
|
||||
Wrap: wrap,
|
||||
DirectoryName: filepath.Join(outputDirectory, globals.CLOUDFOX_BASE_DIRECTORY, globals.AZ_DIR_BASE, tenantDomain, directorySuffix),
|
||||
},
|
||||
}
|
||||
o.Table.TableFiles = append(o.Table.TableFiles, internal.TableFile{Header: functionTableHeader(), Body: rows, Name: globals.AZ_FUNC_MODULE_NAME, TableCols: functionVisibleColumns()})
|
||||
o.Loot.DirectoryName = filepath.Join(o.Table.DirectoryName, "loot")
|
||||
if loot.Configuration != "" {
|
||||
o.Loot.LootFiles = append(o.Loot.LootFiles, internal.LootFile{Name: "function-app-configuration", Contents: loot.Configuration})
|
||||
}
|
||||
if loot.TestData != "" {
|
||||
o.Loot.LootFiles = append(o.Loot.LootFiles, internal.LootFile{Name: "function-test-data", Contents: loot.TestData})
|
||||
}
|
||||
if len(o.Loot.LootFiles) > 0 {
|
||||
o.WriteFullOutput(o.Table.TableFiles, o.Loot.LootFiles)
|
||||
} else {
|
||||
o.WriteFullOutput(o.Table.TableFiles, nil)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
Loaded 100 of 270 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user