diff --git a/deploy/test-environments/delete_env.sh b/deploy/test-environments/delete_env.sh index 0943f94258..5bad622d4c 100755 --- a/deploy/test-environments/delete_env.sh +++ b/deploy/test-environments/delete_env.sh @@ -101,7 +101,9 @@ else GCP_FILTER="name:'$ENV_PREFIX*'" fi -ALL_GCP_DEPLOYMENTS=$(gcloud deployment-manager deployments list --filter="$GCP_FILTER" --format="value(name)") +while IFS= read -r line; do + ALL_GCP_DEPLOYMENTS+=("$line") +done < <(gcloud deployment-manager deployments list --filter="$GCP_FILTER" --format="value(name)") # Divide environments into those to be deleted and those to be skipped TO_DELETE_ENVS=() @@ -163,7 +165,7 @@ printf "%s\n" "${FAILED_STACKS[@]}" # Delete GCP deployments PROJECT_NAME=$(gcloud config get-value core/project) PROJECT_NUMBER=$(gcloud projects list --filter="${PROJECT_NAME}" --format="value(PROJECT_NUMBER)") -./delete_gcp_env.sh "$PROJECT_NAME" "$PROJECT_NUMBER" "$ALL_GCP_DEPLOYMENTS" +./delete_gcp_env.sh "$PROJECT_NAME" "$PROJECT_NUMBER" "${ALL_GCP_DEPLOYMENTS[@]}" # Delete Azure groups FAILED_AZURE_GROUPS=() diff --git a/deploy/test-environments/delete_gcp_env.sh b/deploy/test-environments/delete_gcp_env.sh index 3e0574380d..4848a795e3 100755 --- a/deploy/test-environments/delete_gcp_env.sh +++ b/deploy/test-environments/delete_gcp_env.sh @@ -13,15 +13,15 @@ PROJECT_NUMBER=$2 shift 2 GCP_DEPLOYMENTS=("$@") -echo "Project Name: $PROJECT_NAME" -echo "Project Number: $PROJECT_NUMBER" -echo "GCP Deployments: ${GCP_DEPLOYMENTS[*]}" +# Add the needed roles to delete the templates to the project using the deployment manager +gcloud projects add-iam-policy-binding "${PROJECT_NAME}" --member=serviceAccount:"${PROJECT_NUMBER}"@cloudservices.gserviceaccount.com --role=roles/iam.roleAdmin --no-user-output-enabled +gcloud projects add-iam-policy-binding "${PROJECT_NAME}" --member=serviceAccount:"${PROJECT_NUMBER}"@cloudservices.gserviceaccount.com --role=roles/resourcemanager.projectIamAdmin --no-user-output-enabled -for DEPLOYMENT in "${GCP_DEPLOYMENTS[@]}"; do - # Add the needed roles to delete the templates to the project using the deployment manager - gcloud projects add-iam-policy-binding "${PROJECT_NAME}" --member=serviceAccount:"${PROJECT_NUMBER}"@cloudservices.gserviceaccount.com --role=roles/iam.roleAdmin --no-user-output-enabled - gcloud projects add-iam-policy-binding "${PROJECT_NAME}" --member=serviceAccount:"${PROJECT_NUMBER}"@cloudservices.gserviceaccount.com --role=roles/resourcemanager.projectIamAdmin --no-user-output-enabled +DELETED_DEPLOYMENTS=() +FAILED_DEPLOYMENTS=() +for DEPLOYMENT in "${GCP_DEPLOYMENTS[@]}"; do + echo "Deleting GCP deployment: $DEPLOYMENT" if gcloud deployment-manager deployments delete "$DEPLOYMENT" -q; then echo "Successfully deleted GCP deployment: $DEPLOYMENT" DELETED_DEPLOYMENTS+=("$DEPLOYMENT") @@ -30,18 +30,20 @@ for DEPLOYMENT in "${GCP_DEPLOYMENTS[@]}"; do FAILED_DEPLOYMENTS+=("$DEPLOYMENT") fi - # Remove the roles required to deploy the DM templates - gcloud projects remove-iam-policy-binding "${PROJECT_NAME}" --member=serviceAccount:"${PROJECT_NUMBER}"@cloudservices.gserviceaccount.com --role=roles/iam.roleAdmin --no-user-output-enabled - gcloud projects remove-iam-policy-binding "${PROJECT_NAME}" --member=serviceAccount:"${PROJECT_NUMBER}"@cloudservices.gserviceaccount.com --role=roles/resourcemanager.projectIamAdmin --no-user-output-enabled - done -# Print summary of gcp deployments deletions +# Remove the roles required to deploy the DM templates +gcloud projects remove-iam-policy-binding "${PROJECT_NAME}" --member=serviceAccount:"${PROJECT_NUMBER}"@cloudservices.gserviceaccount.com --role=roles/iam.roleAdmin --no-user-output-enabled +gcloud projects remove-iam-policy-binding "${PROJECT_NAME}" --member=serviceAccount:"${PROJECT_NUMBER}"@cloudservices.gserviceaccount.com --role=roles/resourcemanager.projectIamAdmin --no-user-output-enabled + echo "Successfully deleted GCP deployments (${#DELETED_DEPLOYMENTS[@]}):" -printf "%s\n" "${DELETED_DEPLOYMENTS[@]}" +# Print summary of gcp deployments deletions +if [ ${#DELETED_DEPLOYMENTS[@]} -gt 0 ]; then + printf "%s\n" "${DELETED_DEPLOYMENTS[@]}" +fi +echo "Failed to delete GCP deployments (${#FAILED_DEPLOYMENTS[@]}):" if [ ${#FAILED_DEPLOYMENTS[@]} -gt 0 ]; then - echo "Failed to delete GCP deployments (${#FAILED_DEPLOYMENTS[@]}):" printf "%s\n" "${FAILED_DEPLOYMENTS[@]}" exit 1 fi diff --git a/dev-docs/Cloud-Env-Upgrade.md b/dev-docs/Cloud-Env-Upgrade.md index 95822220a4..0844765e48 100644 --- a/dev-docs/Cloud-Env-Upgrade.md +++ b/dev-docs/Cloud-Env-Upgrade.md @@ -4,6 +4,18 @@ The [`Test Upgrade Environment`](https://github.com/elastic/cloudbeat/actions/wo It also facilitates the upgrade of the environment to a new version of the ELK stack and all installed agents, while also performing checks for findings retrieval. For example, if the target ELK version is 8.12.0 and the base version was not selected, the workflow will automatically calculate the previously released version (e.g., 8.11.3), install that version, and then proceed to upgrade to the specified target version (8.12.0). Essentially, this workflow is designed to test the upgrade feature on upcoming versions that are currently in development or will be release candidates (BC). +## Overview of the Upgrade Process + +The upgrade process comprises the following main steps: + +1. Install the released version, including all integrations (CSPM/KSPM), and deploy their agents. +2. Upgrade the ELK stack version. +3. Upgrade CSPM/KSPM integration versions: + - If the integration has a `preview` version, the workflow will execute a script to update the integration to the latest `preview` version. + - If the latest version is released (no `preview` suffix), the integration upgrade will be automatically performed after the stack upgrade. +4. Upgrade KSPM agents by reapplying Kubernetes manifests with the latest image versions. +5. Upgrade Linux-type agents (CSPM/CNVM) by using the Fleet upgrade API. + ## How to Run the Workflow Follow these steps to run the workflow: diff --git a/go.mod b/go.mod index 795edbff19..31435a8ccf 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/elastic/cloudbeat go 1.21 require ( - cloud.google.com/go/asset v1.18.0 - cloud.google.com/go/iam v1.1.6 + cloud.google.com/go/asset v1.18.1 + cloud.google.com/go/iam v1.1.7 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.3.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.4.0 @@ -20,30 +20,30 @@ require ( github.com/aquasecurity/go-dep-parser v0.0.0-20231120074854-8322cc2242bf github.com/aquasecurity/trivy v0.48.3 github.com/aquasecurity/trivy-db v0.0.0-20240220070059-88dc6466aa40 - github.com/aws/aws-sdk-go v1.50.35 - github.com/aws/aws-sdk-go-v2 v1.25.3 - github.com/aws/aws-sdk-go-v2/config v1.27.7 - github.com/aws/aws-sdk-go-v2/credentials v1.17.7 - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.3 - github.com/aws/aws-sdk-go-v2/service/accessanalyzer v1.28.3 - github.com/aws/aws-sdk-go-v2/service/autoscaling v1.40.3 - github.com/aws/aws-sdk-go-v2/service/cloudformation v1.47.1 - github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.39.0 - github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.36.2 - github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.34.3 - github.com/aws/aws-sdk-go-v2/service/configservice v1.46.2 - github.com/aws/aws-sdk-go-v2/service/ec2 v1.150.0 - github.com/aws/aws-sdk-go-v2/service/ecr v1.27.2 - github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.24.2 - github.com/aws/aws-sdk-go-v2/service/iam v1.31.2 - github.com/aws/aws-sdk-go-v2/service/kms v1.29.2 - github.com/aws/aws-sdk-go-v2/service/organizations v1.27.1 - github.com/aws/aws-sdk-go-v2/service/rds v1.75.1 - github.com/aws/aws-sdk-go-v2/service/s3 v1.51.4 - github.com/aws/aws-sdk-go-v2/service/s3control v1.44.2 - github.com/aws/aws-sdk-go-v2/service/securityhub v1.46.2 - github.com/aws/aws-sdk-go-v2/service/sns v1.29.2 - github.com/aws/aws-sdk-go-v2/service/sts v1.28.4 + github.com/aws/aws-sdk-go v1.51.6 + github.com/aws/aws-sdk-go-v2 v1.26.0 + github.com/aws/aws-sdk-go-v2/config v1.27.9 + github.com/aws/aws-sdk-go-v2/credentials v1.17.9 + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0 + github.com/aws/aws-sdk-go-v2/service/accessanalyzer v1.29.0 + github.com/aws/aws-sdk-go-v2/service/autoscaling v1.40.4 + github.com/aws/aws-sdk-go-v2/service/cloudformation v1.48.0 + github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.39.1 + github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.36.3 + github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.35.0 + github.com/aws/aws-sdk-go-v2/service/configservice v1.46.3 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.152.0 + github.com/aws/aws-sdk-go-v2/service/ecr v1.27.3 + github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.24.3 + github.com/aws/aws-sdk-go-v2/service/iam v1.31.3 + github.com/aws/aws-sdk-go-v2/service/kms v1.30.0 + github.com/aws/aws-sdk-go-v2/service/organizations v1.27.2 + github.com/aws/aws-sdk-go-v2/service/rds v1.76.0 + github.com/aws/aws-sdk-go-v2/service/s3 v1.53.0 + github.com/aws/aws-sdk-go-v2/service/s3control v1.44.3 + github.com/aws/aws-sdk-go-v2/service/securityhub v1.47.0 + github.com/aws/aws-sdk-go-v2/service/sns v1.29.3 + github.com/aws/aws-sdk-go-v2/service/sts v1.28.5 github.com/aws/smithy-go v1.20.1 github.com/dgraph-io/ristretto v0.1.1 github.com/djherbis/times v1.6.0 @@ -120,14 +120,14 @@ require ( ) require ( - cloud.google.com/go v0.112.0 // indirect + cloud.google.com/go v0.112.1 // indirect cloud.google.com/go/accesscontextmanager v1.8.5 // indirect cloud.google.com/go/compute v1.24.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/longrunning v0.5.5 // indirect cloud.google.com/go/orgpolicy v1.12.1 // indirect cloud.google.com/go/osconfig v1.12.5 // indirect - cloud.google.com/go/storage v1.36.0 // indirect + cloud.google.com/go/storage v1.38.0 // indirect code.cloudfoundry.org/go-diodes v0.0.0-20190809170250-f77fb823c7ee // indirect code.cloudfoundry.org/go-loggregator v7.4.0+incompatible // indirect code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f // indirect @@ -181,18 +181,18 @@ require ( github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.3 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.3 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.4 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.3 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.4 // indirect github.com/aws/aws-sdk-go-v2/service/ebs v1.21.7 // indirect github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.21.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.5 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.5 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.20.2 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.20.3 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect @@ -225,7 +225,7 @@ require ( github.com/dnephin/pflag v1.0.7 // indirect github.com/docker/cli v24.0.5+incompatible // indirect github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v24.0.7+incompatible // indirect + github.com/docker/docker v24.0.9+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect @@ -283,7 +283,7 @@ require ( github.com/golang-jwt/jwt/v5 v5.2.0 // indirect github.com/golang/glog v1.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/gomodule/redigo v1.8.3 // indirect github.com/google/btree v1.1.2 // indirect @@ -479,9 +479,9 @@ require ( golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20240228224816-df926f6c8641 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240304161311-37d4d3c04a78 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240304161311-37d4d3c04a78 // indirect - google.golang.org/grpc v1.62.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2 // indirect + google.golang.org/grpc v1.62.1 // indirect google.golang.org/protobuf v1.33.0 gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index 60e412c782..48b8f51f4d 100644 --- a/go.sum +++ b/go.sum @@ -38,8 +38,8 @@ cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRY cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= -cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= +cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= +cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= @@ -82,8 +82,8 @@ cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjby cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= -cloud.google.com/go/asset v1.18.0 h1:lA0oLEsXEzKBV9LprEdeX3e9HHt8jFW2QafIhqG15JI= -cloud.google.com/go/asset v1.18.0/go.mod h1:JnuX7WLUc55AFKJOqF0n7gBPZostxZQCHPEu2WQ1980= +cloud.google.com/go/asset v1.18.1 h1:+NpxL5L53VY91EoJTHeGGXSWEUllf2hhXpCyTnSrd3Q= +cloud.google.com/go/asset v1.18.1/go.mod h1:QXivw0mVqwrhZyuX6iqFbyfCdzYE9AFCJVG47Eh5dMM= cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= @@ -277,8 +277,8 @@ cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQE cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= -cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= -cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= +cloud.google.com/go/iam v1.1.7 h1:z4VHOhwKLF/+UYXAJDFwGtNF0b6gjsW1Pk9Ml0U/IoM= +cloud.google.com/go/iam v1.1.7/go.mod h1:J4PMPg8TtyurAUvSmPj8FF3EDgY1SPRZxcUGrn7WXGA= cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= @@ -473,8 +473,8 @@ cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= -cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= -cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= +cloud.google.com/go/storage v1.38.0 h1:Az68ZRGlnNTpIBbLjSMIV2BDcwwXYlRlQzis0llkpJg= +cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY= cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= @@ -827,85 +827,85 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.43.16/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go v1.50.35 h1:llQnNddBI/64pK7pwUFBoWYmg8+XGQUCs214eMbSDZc= -github.com/aws/aws-sdk-go v1.50.35/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go v1.51.6 h1:Ld36dn9r7P9IjU8WZSaswQ8Y/XUCRpewim5980DwYiU= +github.com/aws/aws-sdk-go v1.51.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M= -github.com/aws/aws-sdk-go-v2 v1.25.3 h1:xYiLpZTQs1mzvz5PaI6uR0Wh57ippuEthxS4iK5v0n0= -github.com/aws/aws-sdk-go-v2 v1.25.3/go.mod h1:35hUlJVYd+M++iLI3ALmVwMOyRYMmRqUXpTtRGW+K9I= +github.com/aws/aws-sdk-go-v2 v1.26.0 h1:/Ce4OCiM3EkpW7Y+xUnfAFpchU78K7/Ug01sZni9PgA= +github.com/aws/aws-sdk-go-v2 v1.26.0/go.mod h1:35hUlJVYd+M++iLI3ALmVwMOyRYMmRqUXpTtRGW+K9I= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 h1:gTK2uhtAPtFcdRRJilZPx8uJLL2J85xK11nKtWL0wfU= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1/go.mod h1:sxpLb+nZk7tIfCWChfd+h4QwHNUR57d8hA1cleTkjJo= -github.com/aws/aws-sdk-go-v2/config v1.27.7 h1:JSfb5nOQF01iOgxFI5OIKWwDiEXWTyTgg1Mm1mHi0A4= -github.com/aws/aws-sdk-go-v2/config v1.27.7/go.mod h1:PH0/cNpoMO+B04qET699o5W92Ca79fVtbUnvMIZro4I= -github.com/aws/aws-sdk-go-v2/credentials v1.17.7 h1:WJd+ubWKoBeRh7A5iNMnxEOs982SyVKOJD+K8HIezu4= -github.com/aws/aws-sdk-go-v2/credentials v1.17.7/go.mod h1:UQi7LMR0Vhvs+44w5ec8Q+VS+cd10cjwgHwiVkE0YGU= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.3 h1:p+y7FvkK2dxS+FEwRIDHDe//ZX+jDhP8HHE50ppj4iI= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.3/go.mod h1:/fYB+FZbDlwlAiynK9KDXlzZl3ANI9JkD0Uhz5FjNT4= +github.com/aws/aws-sdk-go-v2/config v1.27.9 h1:gRx/NwpNEFSk+yQlgmk1bmxxvQ5TyJ76CWXs9XScTqg= +github.com/aws/aws-sdk-go-v2/config v1.27.9/go.mod h1:dK1FQfpwpql83kbD873E9vz4FyAxuJtR22wzoXn3qq0= +github.com/aws/aws-sdk-go-v2/credentials v1.17.9 h1:N8s0/7yW+h8qR8WaRlPQeJ6czVMNQVNtNdUqf6cItao= +github.com/aws/aws-sdk-go-v2/credentials v1.17.9/go.mod h1:446YhIdmSV0Jf/SLafGZalQo+xr2iw7/fzXGDPTU1yQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0 h1:af5YzcLf80tv4Em4jWVD75lpnOHSBkPUZxZfGkrI3HI= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0/go.mod h1:nQ3how7DMnFMWiU1SpECohgC82fpn4cKZ875NDMmwtA= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.11 h1:I6lAa3wBWfCz/cKkOpAcumsETRkFAl70sWi8ItcMEsM= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.11/go.mod h1:be1NIO30kJA23ORBLqPo1LttEM6tPNSEcjkd1eKzNW0= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.3 h1:ifbIbHZyGl1alsAhPIYsHOg5MuApgqOvVeI8wIugXfs= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.3/go.mod h1:oQZXg3c6SNeY6OZrDY+xHcF4VGIEoNotX2B4PrDeoJI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4 h1:0ScVK/4qZ8CIW0k8jOeFVsyS/sAiXpYxRBLolMkuLQM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4/go.mod h1:84KyjNZdHC6QZW08nfHI6yZgPd+qRgaWcYsyLUo3QY8= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.3 h1:Qvodo9gHG9F3E8SfYOspPeBt0bjSbsevK8WhRAUHcoY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.3/go.mod h1:vCKrdLXtybdf/uQd/YfVR2r5pcbNuEYKzMQpcxmeSJw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.4 h1:sHmMWWX5E7guWEFQ9SVo6A3S4xpPrWnd77a6y4WM6PU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.4/go.mod h1:WjpDrhWisWOIoS9n3nk67A3Ll1vfULJ9Kq6h29HTD48= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.3 h1:mDnFOE2sVkyphMWtTH+stv0eW3k0OTx94K63xpxHty4= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.3/go.mod h1:V8MuRVcCRt5h1S+Fwu8KbC7l/gBGo3yBAyUbJM2IJOk= -github.com/aws/aws-sdk-go-v2/service/accessanalyzer v1.28.3 h1:BAbqzBIQfU2sC5HMxkpwzMhSLnBt6u57xCqsLmSd0TA= -github.com/aws/aws-sdk-go-v2/service/accessanalyzer v1.28.3/go.mod h1:vmGwd1VOL94pb2bbUXBofsFN2fV6CpiaGHMKwUBCruk= -github.com/aws/aws-sdk-go-v2/service/autoscaling v1.40.3 h1:tDU4fG/TfB+a/jOwDI6l1DJCcAQl4a9W/xCOAbNdwck= -github.com/aws/aws-sdk-go-v2/service/autoscaling v1.40.3/go.mod h1:PzJFym0AIsRGjwjrQmZRaE1kWKAmAiCGxlCoWxCzt5A= -github.com/aws/aws-sdk-go-v2/service/cloudformation v1.47.1 h1:MkQ6aU0fCIQmpQkVc9mOXrzBH5EoRJYI8MMm3a5LOyM= -github.com/aws/aws-sdk-go-v2/service/cloudformation v1.47.1/go.mod h1:M1MzxSL622D8loHtyq/5TWqJvnYU+Oth0Wa6/U9NjNU= -github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.39.0 h1:6Q78wgRuKzSAN8VuSgAyFxY9kvyatmlUEohlXl29LR4= -github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.39.0/go.mod h1:QBIqzw/6LB/2ndOFRA2Lx64okTkLJiJIWp+9koWwzdY= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.36.2 h1:VUaOIbGS7QZ4H1j5OcfGEPrCH7RA0NvcXye7qHox6tc= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.36.2/go.mod h1:kfCI0AT+7S4MT1iJ2CdDxTJUsqpSFN1dmjI1qcdyv4A= -github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.34.3 h1:j77SexeZB/dRvaBW9PqObPGWRVUiMqRBrrlvuESCC2Y= -github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.34.3/go.mod h1:zVf0TcEEu38jbL0xKp8y+KKaoBwU6k+yTPZWYeDk1YE= -github.com/aws/aws-sdk-go-v2/service/configservice v1.46.2 h1:Blx1Ge2cmG8RUswNeZ9rHxFvfrv/OokKzVJfsOU2O/I= -github.com/aws/aws-sdk-go-v2/service/configservice v1.46.2/go.mod h1:oNOtkIRongx7Nuwo1kqGsYmAKvQ1ccTnPMRgaRugnqo= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.4 h1:SIkD6T4zGQ+1YIit22wi37CGNkrE7mXV1vNA5VpI3TI= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.4/go.mod h1:XfeqbsG0HNedNs0GT+ju4Bs+pFAwsrlzcRdMvdNVf5s= +github.com/aws/aws-sdk-go-v2/service/accessanalyzer v1.29.0 h1:79JqmS9IHHK2GehZ/3lleA9qZduEdAiDPpm65G3sC3Q= +github.com/aws/aws-sdk-go-v2/service/accessanalyzer v1.29.0/go.mod h1:MR0MHfFVMib39bwmwRkPWB/3GrGK47kK3/QRvWh1iwU= +github.com/aws/aws-sdk-go-v2/service/autoscaling v1.40.4 h1:f4pkN5PVSqlGxD2gZvboz6SRaeoykgknflMPBVuhcGs= +github.com/aws/aws-sdk-go-v2/service/autoscaling v1.40.4/go.mod h1:NZBgGUf6LD2KS6Ns5xTK+cR1LK5hZwNkeOt8nDKXzMA= +github.com/aws/aws-sdk-go-v2/service/cloudformation v1.48.0 h1:uMlYsoHdd2Gr9sDGq2ieUR5jVu7F5AqPYz6UBJmdRhY= +github.com/aws/aws-sdk-go-v2/service/cloudformation v1.48.0/go.mod h1:G2qcp9xrwch6TH9AlzWoYbV9QScyZhLCoMCQ1+BD404= +github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.39.1 h1:3gmWxPT3URe8Yswfm0uiyqURRat8P7Gxv9SFSN0KOxY= +github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.39.1/go.mod h1:d6xG6uOYwvPcLfgAqYVYJziH1kO2xwaNlReUk2jJeyQ= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.36.3 h1:l3vM7tnmYWZBdyN1d2Q4gTCnDNbwKNtns4oCFt0zfQk= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.36.3/go.mod h1:xeAHc7vhdOYwpG2t4uXdnGhOvOIpJ8n+A5AHnCkk8iw= +github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.35.0 h1:Tpy3mOh9ladwf9bhlAr38OTnZk/Uh9UuN4UNg3MFB/U= +github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.35.0/go.mod h1:bIFyamdY1PRTmifPT7uHCq4+af0SooBn9hmK9UW/hmg= +github.com/aws/aws-sdk-go-v2/service/configservice v1.46.3 h1:rxZv7fqz593Yvidy5GAFo8f0VbaacgYMejyUwYZvnqs= +github.com/aws/aws-sdk-go-v2/service/configservice v1.46.3/go.mod h1:6d3HjJwLffS4M2jTahQ8IuKJDukJuLTVN8T2X1N7Vsk= github.com/aws/aws-sdk-go-v2/service/ebs v1.21.7 h1:CRzzXjmgx9p362yO39D6hbZULdMI23gaKqSxijJCXHM= github.com/aws/aws-sdk-go-v2/service/ebs v1.21.7/go.mod h1:wnsHqpi3RgDwklS5SPHUgjcUUpontGPKJ+GJYOdV7pY= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.150.0 h1:9JPrA5MyHUqr5hcU1o/xyryVctoyRrj5eHsxRSSDGfg= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.150.0/go.mod h1:KNJMjsbzK97hci9ev2Vl/27GgUt3ZciRP4RGujAPF2I= -github.com/aws/aws-sdk-go-v2/service/ecr v1.27.2 h1:lkRbIgRuYW3C8y+vTQnSc4D4fiuby7XpEVzKTJkWjcU= -github.com/aws/aws-sdk-go-v2/service/ecr v1.27.2/go.mod h1:H4zhX7f/oFn2xNTW+vXOTSXx5SsJ3PjwIlKJCpzm0DU= -github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.24.2 h1:sCoTbW8l2+oW6OHE2rYr1BIrGpaL3Echx9qh11acpB0= -github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.24.2/go.mod h1:wpeK4uayHfHp/tsSVgkUe5uEw7Jy0cQbUBAheKojNjo= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.152.0 h1:ltCQObuImVYmIrMX65ikB9W83MEun3Ry2Sk11ecZ8Xw= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.152.0/go.mod h1:TeZ9dVQzGaLG+SBIgdLIDbJ6WmfFvksLeG3EHGnNfZM= +github.com/aws/aws-sdk-go-v2/service/ecr v1.27.3 h1:gfgt0D8MGL3gHrJPEv4rcWptA4Nz7uYn25ls8lLiANw= +github.com/aws/aws-sdk-go-v2/service/ecr v1.27.3/go.mod h1:O5Fvd41s5KfDG093xLM7FhGiH6EmhmEli5D5MQH3TWw= +github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.24.3 h1:pjgSJEvgJzv+e0frrqspeYdHz2JSW1KAGMXRe1FuQ1M= +github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.24.3/go.mod h1:dhRVzB/bmggoMEBhYXKZrTE+jqN34O4+webZSjGi12c= github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.21.3 h1:CAWMcMnRYCBaeMnycTwZs+0BcuepIMfyP3F0r1VfgPc= github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.21.3/go.mod h1:CbJHS0jJJNd2dZOakkG5TBbT8OHz+T0UBzR1ClIdezI= -github.com/aws/aws-sdk-go-v2/service/iam v1.31.2 h1:LD+6Ln3nHvQ/1rn3hATa+xjnTkr3LUo4k/6RvdOVFGE= -github.com/aws/aws-sdk-go-v2/service/iam v1.31.2/go.mod h1:jB6UEWR0ROLtOO53UsEzv4wKHRczfrbm8s1JuWILo6Q= +github.com/aws/aws-sdk-go-v2/service/iam v1.31.3 h1:cJn9Snros9WmDA7/qCCN7jSkowcu1CqnwhFpv4ipHEE= +github.com/aws/aws-sdk-go-v2/service/iam v1.31.3/go.mod h1:+nAQlxsBxPFf6GrL93lvCuv5PxSTX3GO0RYrURyzl/Q= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 h1:EyBZibRTVAs6ECHZOw5/wlylS9OcTzwyjeQMudmREjE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1/go.mod h1:JKpmtYhhPs7D97NL/ltqz7yCkERFW5dOlHyVl66ZYF8= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.5 h1:mbWNpfRUTT6bnacmvOTKXZjR/HycibdWzNpfbrbLDIs= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.5/go.mod h1:FCOPWGjsshkkICJIn9hq9xr6dLKtyaWpuUojiN3W1/8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.5 h1:K/NXvIftOlX+oGgWGIa3jDyYLDNsdVhsjHmsBH2GLAQ= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.5/go.mod h1:cl9HGLV66EnCmMNzq4sYOti+/xo8w34CsgzVtm2GgsY= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.3 h1:4t+QEX7BsXz98W8W1lNvMAG+NX8qHz2CjLBxQKku40g= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.3/go.mod h1:oFcjjUq5Hm09N9rpxTdeMeLeQcxS7mIkBkL8qUKng+A= -github.com/aws/aws-sdk-go-v2/service/kms v1.29.2 h1:3UaqodPQqPh5XowXJ9fWM4TQqwuftYYFvej+RI5uIO8= -github.com/aws/aws-sdk-go-v2/service/kms v1.29.2/go.mod h1:elLDaj+1RNl9Ovn3dB6dWLVo5WQ+VLSUMKegl7N96fY= -github.com/aws/aws-sdk-go-v2/service/organizations v1.27.1 h1:f38qsXO0dX5aNeeDnIJm9m4+IW08i8gxqqerfIPcVN8= -github.com/aws/aws-sdk-go-v2/service/organizations v1.27.1/go.mod h1:Un2zmKMhjJ+Dz1F1PjgZB2EnoFOz40IuJkoo2r/5Erk= -github.com/aws/aws-sdk-go-v2/service/rds v1.75.1 h1:2G+KvaPQpIHy2kn51WcqQ4mg9/Fa001GH2gJ/D4Rtlc= -github.com/aws/aws-sdk-go-v2/service/rds v1.75.1/go.mod h1:rkt5KtuoWuz6e6OMAMvR2h5o+7kUVEUCuBuDZhw5CIE= -github.com/aws/aws-sdk-go-v2/service/s3 v1.51.4 h1:lW5xUzOPGAMY7HPuNF4FdyBwRc3UJ/e8KsapbesVeNU= -github.com/aws/aws-sdk-go-v2/service/s3 v1.51.4/go.mod h1:MGTaf3x/+z7ZGugCGvepnx2DS6+caCYYqKhzVoLNYPk= -github.com/aws/aws-sdk-go-v2/service/s3control v1.44.2 h1:M0Fw6MUF5BDYcePMoMzd+IAroDHGN89E3lpZs7oXlIU= -github.com/aws/aws-sdk-go-v2/service/s3control v1.44.2/go.mod h1:kGcW1LdoIIQPf3cFg8S0z347L1TirowMIauYEcuZuLc= -github.com/aws/aws-sdk-go-v2/service/securityhub v1.46.2 h1:3VVHLTEeJiQgN/KQ8BT1t3FsJHgp8uTNIDhmrKxNaUU= -github.com/aws/aws-sdk-go-v2/service/securityhub v1.46.2/go.mod h1:tTlUF+K4eG9SA5wDITDXnXtkbp1OTBX8C2EsRrZIwXg= -github.com/aws/aws-sdk-go-v2/service/sns v1.29.2 h1:kHm1SYs/NkxZpKINc4zOXOLJHVMzKtU4d7FlAMtDm50= -github.com/aws/aws-sdk-go-v2/service/sns v1.29.2/go.mod h1:ZIs7/BaYel9NODoYa8PW39o15SFAXDEb4DxOG2It15U= -github.com/aws/aws-sdk-go-v2/service/sso v1.20.2 h1:XOPfar83RIRPEzfihnp+U6udOveKZJvPQ76SKWrLRHc= -github.com/aws/aws-sdk-go-v2/service/sso v1.20.2/go.mod h1:Vv9Xyk1KMHXrR3vNQe8W5LMFdTjSeWk0gBZBzvf3Qa0= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.2 h1:pi0Skl6mNl2w8qWZXcdOyg197Zsf4G97U7Sso9JXGZE= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.2/go.mod h1:JYzLoEVeLXk+L4tn1+rrkfhkxl6mLDEVaDSvGq9og90= -github.com/aws/aws-sdk-go-v2/service/sts v1.28.4 h1:Ppup1nVNAOWbBOrcoOxaxPeEnSFB2RnnQdguhXpmeQk= -github.com/aws/aws-sdk-go-v2/service/sts v1.28.4/go.mod h1:+K1rNPVyGxkRuv9NNiaZ4YhBFuyw2MMA9SlIJ1Zlpz8= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.6 h1:NkHCgg0Ck86c5PTOzBZ0JRccI51suJDg5lgFtxBu1ek= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.6/go.mod h1:mjTpxjC8v4SeINTngrnKFgm2QUi+Jm+etTbCxh8W4uU= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6 h1:b+E7zIUHMmcB4Dckjpkapoy47W6C9QBv/zoUP+Hn8Kc= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6/go.mod h1:S2fNV0rxrP78NhPbCZeQgY8H9jdDMeGtwcfZIRxzBqU= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.4 h1:uDj2K47EM1reAYU9jVlQ1M5YENI1u6a/TxJpf6AeOLA= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.4/go.mod h1:XKCODf4RKHppc96c2EZBGV/oCUC7OClxAo2MEyg4pIk= +github.com/aws/aws-sdk-go-v2/service/kms v1.30.0 h1:yS0JkEdV6h9JOo8sy2JSpjX+i7vsKifU8SIeHrqiDhU= +github.com/aws/aws-sdk-go-v2/service/kms v1.30.0/go.mod h1:+I8VUUSVD4p5ISQtzpgSva4I8cJ4SQ4b1dcBcof7O+g= +github.com/aws/aws-sdk-go-v2/service/organizations v1.27.2 h1:n6ruD9qFx7isIXA5MNOAfqJ7Co8nK2b10aFRiYy9F7M= +github.com/aws/aws-sdk-go-v2/service/organizations v1.27.2/go.mod h1:5cR1oKqnd0PJXJkLAdtrDrw7tL7ANMqHJ+5nLPzzonY= +github.com/aws/aws-sdk-go-v2/service/rds v1.76.0 h1:cQUdm2sU/71O1vCCV627GrQz5b9RmfuxViYDiLsAdZg= +github.com/aws/aws-sdk-go-v2/service/rds v1.76.0/go.mod h1:TsRoxafRyxgt1c1JWQXmxj/dCEwOkBapTwskET8vgFo= +github.com/aws/aws-sdk-go-v2/service/s3 v1.53.0 h1:r3o2YsgW9zRcIP3Q0WCmttFVhTuugeKIvT5z9xDspc0= +github.com/aws/aws-sdk-go-v2/service/s3 v1.53.0/go.mod h1:w2E4f8PUfNtyjfL6Iu+mWI96FGttE03z3UdNcUEC4tA= +github.com/aws/aws-sdk-go-v2/service/s3control v1.44.3 h1:lSZEeke3ivp++MQ4a8Cd2G0Vh0aP+flLHXfMBe7ZWz0= +github.com/aws/aws-sdk-go-v2/service/s3control v1.44.3/go.mod h1:0wQ+7wiAWqWwksgmhV5KEUDKxpzqdVmrMPmvncWr9Pg= +github.com/aws/aws-sdk-go-v2/service/securityhub v1.47.0 h1:lp2W10f/IIIDprGmP/T64P9tx4xV01C3ef4H5gFN7Bk= +github.com/aws/aws-sdk-go-v2/service/securityhub v1.47.0/go.mod h1:RWptadXBwlyIfgDAv7ExErIoLBbfieAkXKHycZXHyLM= +github.com/aws/aws-sdk-go-v2/service/sns v1.29.3 h1:R2MIMza/lZex1wIawXmo6S+suwFv/JcxOFSJPpsSVBY= +github.com/aws/aws-sdk-go-v2/service/sns v1.29.3/go.mod h1:tr9l7BHYU/SvlJAL9CH56XZNcOBb/d24j3RrXkzzaTA= +github.com/aws/aws-sdk-go-v2/service/sso v1.20.3 h1:mnbuWHOcM70/OFUlZZ5rcdfA8PflGXXiefU/O+1S3+8= +github.com/aws/aws-sdk-go-v2/service/sso v1.20.3/go.mod h1:5HFu51Elk+4oRBZVxmHrSds5jFXmFj8C3w7DVF2gnrs= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3 h1:uLq0BKatTmDzWa/Nu4WO0M1AaQDaPpwTKAeByEc6WFM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3/go.mod h1:b+qdhjnxj8GSR6t5YfphOffeoQSQ1KmpoVVuBn+PWxs= +github.com/aws/aws-sdk-go-v2/service/sts v1.28.5 h1:J/PpTf/hllOjx8Xu9DMflff3FajfLxqM5+tepvVXmxg= +github.com/aws/aws-sdk-go-v2/service/sts v1.28.5/go.mod h1:0ih0Z83YDH/QeQ6Ori2yGE2XvWYv/Xm+cZc01LC6oK0= github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw= github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= @@ -1274,8 +1274,8 @@ github.com/docker/docker v20.10.21+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05 github.com/docker/docker v23.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v23.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= -github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0= +github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= @@ -1684,8 +1684,9 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -3807,10 +3808,10 @@ google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/genproto v0.0.0-20240228224816-df926f6c8641 h1:GihpvzHjeZHw+/mzsWpdxwr1LaG6E3ff/gyeZlVHbyc= google.golang.org/genproto v0.0.0-20240228224816-df926f6c8641/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20240304161311-37d4d3c04a78 h1:SzXBGiWM1LNVYLCRP3e0/Gsze804l4jGoJ5lYysEO5I= -google.golang.org/genproto/googleapis/api v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240304161311-37d4d3c04a78 h1:Xs9lu+tLXxLIfuci70nG4cpwaRC+mRQPUL7LoIeDJC4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= +google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 h1:rIo7ocm2roD9DcFIX67Ym8icoGCKSARAiPljFhh5suQ= +google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2 h1:9IZDv+/GcI6u+a4jRFRLxQs0RUCfavGfoOgEW6jpkI0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -3856,8 +3857,8 @@ google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCD google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= -google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/internal/flavors/asset_inventory.go b/internal/flavors/asset_inventory.go index b1281148d8..e9b5c0db23 100644 --- a/internal/flavors/asset_inventory.go +++ b/internal/flavors/asset_inventory.go @@ -29,7 +29,7 @@ import ( "github.com/elastic/cloudbeat/internal/config" "github.com/elastic/cloudbeat/internal/inventory" - awsinventory "github.com/elastic/cloudbeat/internal/inventory/aws" + "github.com/elastic/cloudbeat/internal/inventory/awsfetcher" "github.com/elastic/cloudbeat/internal/resources/providers/awslib" ) @@ -92,7 +92,7 @@ func initAwsFetchers(ctx context.Context, cfg *config.Config, logger *logp.Logge return nil, err } - return awsinventory.Fetchers(logger, awsIdentity, awsConfig), nil + return awsfetcher.New(logger, awsIdentity, awsConfig), nil } func (bt *assetInventory) Run(*beat.Beat) error { diff --git a/internal/flavors/benchmark/aws_org.go b/internal/flavors/benchmark/aws_org.go index 51ce8b12a1..814d5e30ee 100644 --- a/internal/flavors/benchmark/aws_org.go +++ b/internal/flavors/benchmark/aws_org.go @@ -103,7 +103,7 @@ func (a *AWSOrg) initialize(ctx context.Context, log *logp.Logger, cfg *config.C return m, nil })) - return reg, cloud.NewDataProvider(), nil, nil + return reg, cloud.NewDataProvider(cloud.WithAccount(*awsIdentity)), nil, nil } func (a *AWSOrg) getAwsAccounts(ctx context.Context, log *logp.Logger, initialCfg awssdk.Config, rootIdentity *cloud.Identity) ([]preset.AwsAccount, error) { diff --git a/internal/inventory/asset.go b/internal/inventory/asset.go index dd2aac75a8..cb71a6fc7c 100644 --- a/internal/inventory/asset.go +++ b/internal/inventory/asset.go @@ -28,6 +28,7 @@ type assetCategory string const ( CategoryInfrastructure assetCategory = "infrastructure" + CategoryIdentity assetCategory = "identity" ) // assetSubCategory is used to build the document index. Use only numbers, letters and dashes (-) @@ -35,6 +36,9 @@ type assetSubCategory string const ( SubCategoryCompute assetSubCategory = "compute" + SubCategoryStorage assetSubCategory = "storage" + + SubCategoryCloudProviderAccount assetSubCategory = "cloud-provider-account" ) // assetType is used to build the document index. Use only numbers, letters and dashes (-) @@ -42,6 +46,11 @@ type assetType string const ( TypeVirtualMachine assetType = "virtual-machine" + TypeObjectStorage assetType = "object-storage" + + TypeUser assetType = "user" + TypeServiceAccount assetType = "service-account" + TypePermissions assetType = "permissions" ) // assetSubType is used to build the document index. Use only numbers, letters and dashes (-) @@ -49,29 +58,30 @@ type assetSubType string const ( SubTypeEC2 assetSubType = "ec2" + SubTypeS3 assetSubType = "s3" + SubTypeIAM assetSubType = "iam" ) -type assetCloudProvider string - const ( - AwsCloudProvider assetCloudProvider = "aws" + AwsCloudProvider = "aws" ) // AssetEvent holds the whole asset type AssetEvent struct { - Asset Asset - Network *AssetNetwork - Cloud *AssetCloud - Host *AssetHost - IAM *AssetIAM + Asset Asset + Network *AssetNetwork + Cloud *AssetCloud + Host *AssetHost + IAM *AssetIAM + ResourcePolicies []AssetResourcePolicy } // AssetClassification holds the taxonomy of an asset type AssetClassification struct { Category assetCategory `json:"category"` - SubCategory assetSubCategory `json:"subCategory"` + SubCategory assetSubCategory `json:"sub_category"` Type assetType `json:"type"` - SubStype assetSubType `json:"subStype"` + SubType assetSubType `json:"sub_type"` } // Asset contains the identifiers of the asset @@ -86,28 +96,57 @@ type Asset struct { // AssetNetwork contains network information type AssetNetwork struct { - NetworkId *string `json:"networkId"` - SubnetId *string `json:"subnetId"` - Ipv6Address *string `json:"ipv6Address"` - PublicIpAddress *string `json:"publicIpAddress"` - PrivateIpAddress *string `json:"privateIpAddress"` - PublicDnsName *string `json:"publicDnsName"` - PrivateDnsName *string `json:"privateDnsName"` + NetworkId *string `json:"network_id"` + SubnetId *string `json:"subnet_id"` + Ipv6Address *string `json:"ipv6_address"` + PublicIpAddress *string `json:"public_ip_address"` + PrivateIpAddress *string `json:"private_ip_address"` + PublicDnsName *string `json:"public_dns_name"` + PrivateDnsName *string `json:"private_dns_name"` } // AssetCloud contains information about the cloud provider type AssetCloud struct { - Provider assetCloudProvider `json:"provider"` - Region string `json:"region"` + AvailabilityZone *string `json:"availability_zone,omitempty"` + Provider string `json:"provider,omitempty"` + Region string `json:"region,omitempty"` + Account AssetCloudAccount `json:"account"` + Instance *AssetCloudInstance `json:"instance,omitempty"` + Machine *AssetCloudMachine `json:"machine,omitempty"` + Project *AssetCloudProject `json:"project,omitempty"` + Service *AssetCloudService `json:"service,omitempty"` +} + +type AssetCloudAccount struct { + Id string `json:"id,omitempty"` + Name string `json:"name,omitempty"` +} + +type AssetCloudInstance struct { + Id string `json:"id,omitempty"` + Name string `json:"name,omitempty"` +} + +type AssetCloudMachine struct { + MachineType string `json:"machine_type,omitempty"` +} + +type AssetCloudProject struct { + Id string `json:"id,omitempty"` + Name string `json:"name,omitempty"` +} + +type AssetCloudService struct { + Name string `json:"name,omitempty"` } // AssetHost contains information of the asset in case it is a host type AssetHost struct { Architecture string `json:"architecture"` ImageId *string `json:"imageId"` - InstanceType string `json:"instanceType"` + InstanceType string `json:"instance_type"` Platform string `json:"platform"` - PlatformDetails *string `json:"platformDetails"` + PlatformDetails *string `json:"platform_details"` } type AssetIAM struct { @@ -115,6 +154,19 @@ type AssetIAM struct { Arn *string `json:"arn"` } +// AssetResourcePolicy maps security policies applied directly on resources +type AssetResourcePolicy struct { + Version *string `json:"version,omitempty"` + Id *string `json:"id,omitempty"` + Effect string `json:"effect,omitempty"` + Principal map[string]any `json:"principal,omitempty"` + Action []string `json:"action,omitempty"` + NotAction []string `json:"notAction,omitempty"` + Resource []string `json:"resource,omitempty"` + NoResource []string `json:"noResource,omitempty"` + Condition map[string]any `json:"condition,omitempty"` +} + // AssetEnricher functional builder function type AssetEnricher func(asset *AssetEvent) @@ -143,6 +195,10 @@ func WithRawAsset(raw any) AssetEnricher { func WithTags(tags map[string]string) AssetEnricher { return func(a *AssetEvent) { + if len(tags) == 0 { + return + } + a.Asset.Tags = tags } } @@ -171,13 +227,23 @@ func WithIAM(iam AssetIAM) AssetEnricher { } } +func WithResourcePolicies(policies ...AssetResourcePolicy) AssetEnricher { + return func(a *AssetEvent) { + if len(policies) == 0 { + return + } + + a.ResourcePolicies = policies + } +} + func EmptyEnricher() AssetEnricher { return func(_ *AssetEvent) {} } func generateUniqueId(c AssetClassification, resourceId string) string { hasher := sha256.New() - toBeHashed := fmt.Sprintf("%s-%s-%s-%s-%s", resourceId, c.Category, c.SubCategory, c.Type, c.SubStype) + toBeHashed := fmt.Sprintf("%s-%s-%s-%s-%s", resourceId, c.Category, c.SubCategory, c.Type, c.SubType) hasher.Write([]byte(toBeHashed)) //nolint:revive hash := hasher.Sum(nil) encoded := base64.StdEncoding.EncodeToString(hash) diff --git a/internal/inventory/awsfetcher/awsfetchers.go b/internal/inventory/awsfetcher/awsfetchers.go new file mode 100644 index 0000000000..e4b184d026 --- /dev/null +++ b/internal/inventory/awsfetcher/awsfetchers.go @@ -0,0 +1,44 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package awsfetcher + +import ( + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/elastic/elastic-agent-libs/logp" + + "github.com/elastic/cloudbeat/internal/dataprovider/providers/cloud" + "github.com/elastic/cloudbeat/internal/inventory" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib/ec2" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib/iam" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib/s3" +) + +func New(logger *logp.Logger, identity *cloud.Identity, cfg aws.Config) []inventory.AssetFetcher { + iamProvider := iam.NewIAMProvider(logger, cfg, &awslib.MultiRegionClientFactory[iam.AccessAnalyzerClient]{}) + ec2Provider := ec2.NewEC2Provider(logger, identity.Account, cfg, &awslib.MultiRegionClientFactory[ec2.Client]{}) + s3Provider := s3.NewProvider(logger, cfg, &awslib.MultiRegionClientFactory[s3.Client]{}, identity.Account) + + return []inventory.AssetFetcher{ + newEc2InstancesFetcher(logger, identity, ec2Provider), + newS3BucketFetcher(logger, identity, s3Provider), + newIamUserFetcher(logger, identity, iamProvider), + newIamRoleFetcher(logger, identity, iamProvider), + newIamPolicyFetcher(logger, identity, iamProvider), + } +} diff --git a/internal/inventory/aws/fetchers.go b/internal/inventory/awsfetcher/awsfetchers_test.go similarity index 55% rename from internal/inventory/aws/fetchers.go rename to internal/inventory/awsfetcher/awsfetchers_test.go index 6e2b561033..024a5697f9 100644 --- a/internal/inventory/aws/fetchers.go +++ b/internal/inventory/awsfetcher/awsfetchers_test.go @@ -15,18 +15,38 @@ // specific language governing permissions and limitations // under the License. -package aws +package awsfetcher import ( - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/elastic/elastic-agent-libs/logp" + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" - "github.com/elastic/cloudbeat/internal/dataprovider/providers/cloud" "github.com/elastic/cloudbeat/internal/inventory" ) -func Fetchers(logger *logp.Logger, identity *cloud.Identity, cfg aws.Config) []inventory.AssetFetcher { - return []inventory.AssetFetcher{ - newEc2Fetcher(logger, identity, cfg), +func collectResourcesAndMatch(t *testing.T, fetcher inventory.AssetFetcher, expected []inventory.AssetEvent) { + t.Helper() + + ch := make(chan inventory.AssetEvent) + ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) + defer cancel() + go func() { + fetcher.Fetch(ctx, ch) + }() + + received := make([]inventory.AssetEvent, 0, len(expected)) + for len(expected) != len(received) { + select { + case <-ctx.Done(): + assert.ElementsMatch(t, expected, received) + return + case event := <-ch: + received = append(received, event) + } } + + assert.ElementsMatch(t, expected, received) } diff --git a/internal/inventory/aws/fetcher_ec2.go b/internal/inventory/awsfetcher/fetcher_ec2_instance.go similarity index 59% rename from internal/inventory/aws/fetcher_ec2.go rename to internal/inventory/awsfetcher/fetcher_ec2_instance.go index 048203a83c..6dffbe70cf 100644 --- a/internal/inventory/aws/fetcher_ec2.go +++ b/internal/inventory/awsfetcher/fetcher_ec2_instance.go @@ -15,46 +15,50 @@ // specific language governing permissions and limitations // under the License. -package aws +package awsfetcher import ( "context" - "github.com/aws/aws-sdk-go-v2/aws" "github.com/elastic/elastic-agent-libs/logp" "github.com/elastic/cloudbeat/internal/dataprovider/providers/cloud" "github.com/elastic/cloudbeat/internal/inventory" - "github.com/elastic/cloudbeat/internal/resources/providers/awslib" "github.com/elastic/cloudbeat/internal/resources/providers/awslib/ec2" "github.com/elastic/cloudbeat/internal/resources/utils/pointers" ) -type Ec2Fetcher struct { - logger *logp.Logger - provider instancesProvider +type ec2InstanceFetcher struct { + logger *logp.Logger + provider ec2InstancesProvider + AccountId string + AccountName string } -type instancesProvider interface { +type ec2InstancesProvider interface { DescribeInstances(ctx context.Context) ([]*ec2.Ec2Instance, error) } -var ec2Classification = inventory.AssetClassification{ +var ec2InstanceClassification = inventory.AssetClassification{ Category: inventory.CategoryInfrastructure, SubCategory: inventory.SubCategoryCompute, Type: inventory.TypeVirtualMachine, - SubStype: inventory.SubTypeEC2, + SubType: inventory.SubTypeEC2, } -func newEc2Fetcher(logger *logp.Logger, identity *cloud.Identity, cfg aws.Config) inventory.AssetFetcher { - provider := ec2.NewEC2Provider(logger, identity.Account, cfg, &awslib.MultiRegionClientFactory[ec2.Client]{}) - return &Ec2Fetcher{ - logger: logger, - provider: provider, +func newEc2InstancesFetcher(logger *logp.Logger, identity *cloud.Identity, provider ec2InstancesProvider) inventory.AssetFetcher { + return &ec2InstanceFetcher{ + logger: logger, + provider: provider, + AccountId: identity.Account, + AccountName: identity.AccountAlias, } } -func (e *Ec2Fetcher) Fetch(ctx context.Context, assetChannel chan<- inventory.AssetEvent) { +func (e *ec2InstanceFetcher) Fetch(ctx context.Context, assetChannel chan<- inventory.AssetEvent) { + e.logger.Info("Fetching EC2 Instances") + defer e.logger.Info("Fetching EC2 Instances - Finished") + instances, err := e.provider.DescribeInstances(ctx) if err != nil { e.logger.Errorf("Could not list ec2 instances: %v", err) @@ -74,25 +78,31 @@ func (e *Ec2Fetcher) Fetch(ctx context.Context, assetChannel chan<- inventory.As }) } - tags := make(map[string]string, len(instance.Tags)) - for _, t := range instance.Tags { - if t.Key == nil { - continue - } - - tags[*t.Key] = pointers.Deref(t.Value) - } - assetChannel <- inventory.NewAssetEvent( - ec2Classification, + ec2InstanceClassification, instance.GetResourceArn(), instance.GetResourceName(), inventory.WithRawAsset(instance), - inventory.WithTags(tags), + inventory.WithTags(e.getTags(instance)), inventory.WithCloud(inventory.AssetCloud{ - Provider: inventory.AwsCloudProvider, - Region: instance.Region, + Provider: inventory.AwsCloudProvider, + Region: instance.Region, + AvailabilityZone: e.getAvailabilityZone(instance), + Account: inventory.AssetCloudAccount{ + Id: e.AccountId, + Name: e.AccountName, + }, + Instance: &inventory.AssetCloudInstance{ + Id: pointers.Deref(instance.InstanceId), + Name: instance.GetResourceName(), + }, + Machine: &inventory.AssetCloudMachine{ + MachineType: string(instance.InstanceType), + }, + Service: &inventory.AssetCloudService{ + Name: "AWS EC2", + }, }), inventory.WithHost(inventory.AssetHost{ Architecture: string(instance.Architecture), @@ -114,3 +124,23 @@ func (e *Ec2Fetcher) Fetch(ctx context.Context, assetChannel chan<- inventory.As ) } } + +func (e *ec2InstanceFetcher) getTags(instance *ec2.Ec2Instance) map[string]string { + tags := make(map[string]string, len(instance.Tags)) + for _, t := range instance.Tags { + if t.Key == nil { + continue + } + + tags[*t.Key] = pointers.Deref(t.Value) + } + return tags +} + +func (e *ec2InstanceFetcher) getAvailabilityZone(instance *ec2.Ec2Instance) *string { + if instance.Placement == nil { + return nil + } + + return instance.Placement.AvailabilityZone +} diff --git a/internal/inventory/aws/fetcher_ec2_test.go b/internal/inventory/awsfetcher/fetcher_ec2_instance_test.go similarity index 75% rename from internal/inventory/aws/fetcher_ec2_test.go rename to internal/inventory/awsfetcher/fetcher_ec2_instance_test.go index 06171c2630..cfe3f19daa 100644 --- a/internal/inventory/aws/fetcher_ec2_test.go +++ b/internal/inventory/awsfetcher/fetcher_ec2_instance_test.go @@ -15,24 +15,22 @@ // specific language governing permissions and limitations // under the License. -package aws +package awsfetcher import ( - "context" "testing" - "time" "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/elastic/elastic-agent-libs/logp" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/elastic/cloudbeat/internal/dataprovider/providers/cloud" "github.com/elastic/cloudbeat/internal/inventory" ec2beat "github.com/elastic/cloudbeat/internal/resources/providers/awslib/ec2" "github.com/elastic/cloudbeat/internal/resources/utils/pointers" ) -func TestFetch(t *testing.T) { +func TestEC2InstanceFetcher_Fetch(t *testing.T) { instance1 := &ec2beat.Ec2Instance{ Instance: types.Instance{ IamInstanceProfile: &types.IamInstanceProfile{ @@ -62,6 +60,9 @@ func TestFetch(t *testing.T) { PrivateIpAddress: pointers.Ref("private-ip-addre"), PublicDnsName: pointers.Ref("public-dns"), PrivateDnsName: pointers.Ref("private-dns"), + Placement: &types.Placement{ + AvailabilityZone: pointers.Ref("1a"), + }, }, Region: "us-east", } @@ -75,14 +76,29 @@ func TestFetch(t *testing.T) { expected := []inventory.AssetEvent{ inventory.NewAssetEvent( - ec2Classification, + ec2InstanceClassification, "arn:aws:ec2:us-east::ec2/234567890", "test-server", inventory.WithRawAsset(instance1), inventory.WithTags(map[string]string{"Name": "test-server", "key": "value"}), inventory.WithCloud(inventory.AssetCloud{ - Provider: inventory.AwsCloudProvider, - Region: "us-east", + Provider: inventory.AwsCloudProvider, + Region: "us-east", + AvailabilityZone: pointers.Ref("1a"), + Account: inventory.AssetCloudAccount{ + Id: "123", + Name: "alias", + }, + Instance: &inventory.AssetCloudInstance{ + Id: "234567890", + Name: "test-server", + }, + Machine: &inventory.AssetCloudMachine{ + MachineType: "instance-type", + }, + Service: &inventory.AssetCloudService{ + Name: "AWS EC2", + }, }), inventory.WithHost(inventory.AssetHost{ Architecture: string(types.ArchitectureValuesX8664), @@ -107,7 +123,7 @@ func TestFetch(t *testing.T) { ), inventory.NewAssetEvent( - ec2Classification, + ec2InstanceClassification, "", "", inventory.WithRawAsset(instance2), @@ -115,6 +131,20 @@ func TestFetch(t *testing.T) { inventory.WithCloud(inventory.AssetCloud{ Provider: inventory.AwsCloudProvider, Region: "us-east", + Account: inventory.AssetCloudAccount{ + Id: "123", + Name: "alias", + }, + Instance: &inventory.AssetCloudInstance{ + Id: "", + Name: "", + }, + Machine: &inventory.AssetCloudMachine{ + MachineType: "", + }, + Service: &inventory.AssetCloudService{ + Name: "AWS EC2", + }, }), inventory.WithHost(inventory.AssetHost{}), inventory.WithNetwork(inventory.AssetNetwork{}), @@ -122,31 +152,11 @@ func TestFetch(t *testing.T) { } logger := logp.NewLogger("test_fetcher_ec2") - provider := newMockInstancesProvider(t) + provider := newMockEc2InstancesProvider(t) provider.EXPECT().DescribeInstances(mock.Anything).Return(in, nil) - fetcher := Ec2Fetcher{ - logger: logger, - provider: provider, - } - - ch := make(chan inventory.AssetEvent) - ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) - defer cancel() - go func() { - fetcher.Fetch(ctx, ch) - }() - - received := make([]inventory.AssetEvent, 0, len(expected)) - for len(expected) != len(received) { - select { - case <-ctx.Done(): - assert.ElementsMatch(t, expected, received) - return - case event := <-ch: - received = append(received, event) - } - } + identity := &cloud.Identity{Account: "123", AccountAlias: "alias"} + fetcher := newEc2InstancesFetcher(logger, identity, provider) - assert.ElementsMatch(t, expected, received) + collectResourcesAndMatch(t, fetcher, expected) } diff --git a/internal/inventory/awsfetcher/fetcher_iam_policy.go b/internal/inventory/awsfetcher/fetcher_iam_policy.go new file mode 100644 index 0000000000..f863dbeb8f --- /dev/null +++ b/internal/inventory/awsfetcher/fetcher_iam_policy.go @@ -0,0 +1,205 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package awsfetcher + +import ( + "context" + + "github.com/elastic/elastic-agent-libs/logp" + + "github.com/elastic/cloudbeat/internal/dataprovider/providers/cloud" + "github.com/elastic/cloudbeat/internal/inventory" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib/iam" + "github.com/elastic/cloudbeat/internal/resources/utils/pointers" +) + +type iamPolicyFetcher struct { + logger *logp.Logger + provider iamPolicyProvider + AccountId string + AccountName string +} + +type iamPolicyProvider interface { + GetPolicies(ctx context.Context) ([]awslib.AwsResource, error) +} + +var iamPolicyClassification = inventory.AssetClassification{ + Category: inventory.CategoryIdentity, + SubCategory: inventory.SubCategoryCloudProviderAccount, + Type: inventory.TypePermissions, + SubType: inventory.SubTypeIAM, +} + +func newIamPolicyFetcher(logger *logp.Logger, identity *cloud.Identity, provider iamPolicyProvider) inventory.AssetFetcher { + return &iamPolicyFetcher{ + logger: logger, + provider: provider, + AccountId: identity.Account, + AccountName: identity.AccountAlias, + } +} + +func (i *iamPolicyFetcher) Fetch(ctx context.Context, assetChannel chan<- inventory.AssetEvent) { + i.logger.Info("Fetching IAM Policies") + defer i.logger.Info("Fetching IAM Policies - Finished") + + policies, err := i.provider.GetPolicies(ctx) + if err != nil { + i.logger.Errorf("Could not list policies: %v", err) + if len(policies) == 0 { + return + } + } + + for _, resource := range policies { + if resource == nil { + continue + } + + policy, ok := resource.(iam.Policy) + if !ok { + i.logger.Errorf("Could not get info about policy: %s", resource.GetResourceArn()) + continue + } + + assetChannel <- inventory.NewAssetEvent( + iamPolicyClassification, + policy.GetResourceArn(), + resource.GetResourceName(), + + inventory.WithRawAsset(policy), + inventory.WithResourcePolicies(convertPolicy(policy.Document)...), + inventory.WithTags(i.getTags(policy)), + inventory.WithCloud(inventory.AssetCloud{ + Provider: inventory.AwsCloudProvider, + Region: awslib.GlobalRegion, + Account: inventory.AssetCloudAccount{ + Id: i.AccountId, + Name: i.AccountName, + }, + Service: &inventory.AssetCloudService{ + Name: "AWS IAM", + }, + }), + ) + } +} + +func (i *iamPolicyFetcher) getTags(policy iam.Policy) map[string]string { + tags := make(map[string]string, len(policy.Tags)) + + for _, tag := range policy.Tags { + tags[pointers.Deref(tag.Key)] = pointers.Deref(tag.Value) + } + + return tags +} + +func convertPolicy(policy map[string]any) []inventory.AssetResourcePolicy { + if len(policy) == 0 { + return nil + } + + version, hasVersion := policy["Version"].(string) + if !hasVersion { + version = "" + } + + switch statements := policy["Statement"].(type) { + case []map[string]any: + return convertStatements(statements, version) + case []any: + return convertAnyStatements(statements, version) + case map[string]any: + return []inventory.AssetResourcePolicy{convertStatement(statements, &version)} + } + return nil +} + +func convertAnyStatements(statements []any, version string) []inventory.AssetResourcePolicy { + policies := make([]inventory.AssetResourcePolicy, 0, len(statements)) + for _, statement := range statements { + policies = append(policies, convertStatement(statement.(map[string]any), &version)) + } + return policies +} + +func convertStatements(statements []map[string]any, version string) []inventory.AssetResourcePolicy { + policies := make([]inventory.AssetResourcePolicy, 0, len(statements)) + for _, statement := range statements { + policies = append(policies, convertStatement(statement, &version)) + } + return policies +} + +func convertStatement(statement map[string]any, version *string) inventory.AssetResourcePolicy { + p := inventory.AssetResourcePolicy{} + p.Version = version + + if sid, ok := statement["Sid"]; ok { + p.Id = pointers.Ref(sid.(string)) + } + + if effect, ok := statement["Effect"]; ok { + p.Effect = effect.(string) + } + + if anyPrincipal, ok := statement["Principal"]; ok { + switch principal := anyPrincipal.(type) { + case string: + p.Principal = map[string]any{principal: principal} + case map[string]any: + p.Principal = principal + } + } + + if action, ok := statement["Action"]; ok { + p.Action = anyToSliceString(action) + } + + if notAction, ok := statement["NotAction"]; ok { + p.NotAction = anyToSliceString(notAction) + } + + if resource, ok := statement["Resource"]; ok { + p.Resource = anyToSliceString(resource) + } + + if noResource, ok := statement["NoResource"]; ok { + p.NoResource = anyToSliceString(noResource) + } + + if condition, ok := statement["Condition"]; ok { + p.Condition = condition.(map[string]any) + } + + return p +} + +func anyToSliceString(anyString any) []string { + switch s := anyString.(type) { + case string: + return []string{s} + case []string: + return s + } + + return nil +} diff --git a/internal/inventory/awsfetcher/fetcher_iam_policy_test.go b/internal/inventory/awsfetcher/fetcher_iam_policy_test.go new file mode 100644 index 0000000000..63aec2b4bf --- /dev/null +++ b/internal/inventory/awsfetcher/fetcher_iam_policy_test.go @@ -0,0 +1,174 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package awsfetcher + +import ( + "testing" + "time" + + "github.com/aws/aws-sdk-go-v2/service/iam/types" + "github.com/elastic/elastic-agent-libs/logp" + "github.com/stretchr/testify/mock" + + "github.com/elastic/cloudbeat/internal/dataprovider/providers/cloud" + "github.com/elastic/cloudbeat/internal/inventory" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib/iam" + "github.com/elastic/cloudbeat/internal/resources/utils/pointers" +) + +func TestIAMPolicyFetcher_Fetch(t *testing.T) { + now := time.Now() + + policy1 := iam.Policy{ + Policy: types.Policy{ + Arn: pointers.Ref("arn:aws:iam::0000:policy/policy-1"), + PolicyName: pointers.Ref("policy-1"), + PolicyId: pointers.Ref("178263"), + CreateDate: &now, + UpdateDate: &now, + Description: pointers.Ref("test"), + IsAttachable: true, + Path: pointers.Ref("/"), + Tags: []types.Tag{ + {Key: pointers.Ref("key-1"), Value: pointers.Ref("value-1")}, + {Key: pointers.Ref("key-2"), Value: pointers.Ref("value-2")}, + }, + }, + Document: map[string]any{ + "Version": "2012-10-17", + "Statement": []map[string]any{ + { + "Effect": "Allow", + "Action": []string{"read", "update", "delete"}, + "Resource": []string{"s3/bucket", "s3/bucket/*"}, + }, + { + "Effect": "Deny", + "Action": []string{"delete"}, + "Resource": []string{"s3/bucket"}, + }, + }, + }, + Roles: []types.PolicyRole{ + {RoleId: pointers.Ref("roleId-1"), RoleName: pointers.Ref("roleName-1")}, + {RoleId: pointers.Ref("roleId-2"), RoleName: pointers.Ref("roleName-2")}, + }, + } + + policy2 := iam.Policy{ + Policy: types.Policy{ + Arn: pointers.Ref("arn:aws:iam::0000:policy/policy-2"), + PolicyName: pointers.Ref("policy-2"), + Tags: []types.Tag{ + {Key: pointers.Ref("key-1"), Value: pointers.Ref("value-1")}, + }, + }, + Document: map[string]any{ + "Version": "2012-10-17", + "Statement": map[string]any{ + "Effect": "Allow", + "Action": "read", + "Resource": "*", + }, + }, + Roles: []types.PolicyRole{ + {RoleId: pointers.Ref("roleId-1"), RoleName: pointers.Ref("roleName-1")}, + }, + } + + policy3 := iam.Policy{ + Policy: types.Policy{ + Arn: pointers.Ref("arn:aws:iam::0000:policy/policy-3"), + PolicyName: pointers.Ref("policy-3"), + }, + } + + in := []awslib.AwsResource{policy1, nil, policy2, policy3} + + cloudField := inventory.AssetCloud{ + Provider: inventory.AwsCloudProvider, + Region: "global", + Account: inventory.AssetCloudAccount{ + Id: "123", + Name: "alias", + }, + Service: &inventory.AssetCloudService{ + Name: "AWS IAM", + }, + } + + expected := []inventory.AssetEvent{ + inventory.NewAssetEvent( + iamPolicyClassification, + "arn:aws:iam::0000:policy/policy-1", + "policy-1", + inventory.WithRawAsset(policy1), + inventory.WithCloud(cloudField), + inventory.WithTags(map[string]string{ + "key-1": "value-1", + "key-2": "value-2", + }), + inventory.WithResourcePolicies(inventory.AssetResourcePolicy{ + Version: pointers.Ref("2012-10-17"), + Effect: "Allow", + Action: []string{"read", "update", "delete"}, + Resource: []string{"s3/bucket", "s3/bucket/*"}, + }, inventory.AssetResourcePolicy{ + Version: pointers.Ref("2012-10-17"), + Effect: "Deny", + Action: []string{"delete"}, + Resource: []string{"s3/bucket"}, + }), + ), + + inventory.NewAssetEvent( + iamPolicyClassification, + "arn:aws:iam::0000:policy/policy-2", + "policy-2", + inventory.WithRawAsset(policy2), + inventory.WithCloud(cloudField), + inventory.WithTags(map[string]string{ + "key-1": "value-1", + }), + inventory.WithResourcePolicies(inventory.AssetResourcePolicy{ + Version: pointers.Ref("2012-10-17"), + Effect: "Allow", + Action: []string{"read"}, + Resource: []string{"*"}, + }), + ), + + inventory.NewAssetEvent( + iamPolicyClassification, + "arn:aws:iam::0000:policy/policy-3", + "policy-3", + inventory.WithRawAsset(policy3), + inventory.WithCloud(cloudField), + ), + } + + logger := logp.NewLogger("test_fetcher_iam_role") + provider := newMockIamPolicyProvider(t) + provider.EXPECT().GetPolicies(mock.Anything).Return(in, nil) + + identity := &cloud.Identity{Account: "123", AccountAlias: "alias"} + fetcher := newIamPolicyFetcher(logger, identity, provider) + + collectResourcesAndMatch(t, fetcher, expected) +} diff --git a/internal/inventory/awsfetcher/fetcher_iam_role.go b/internal/inventory/awsfetcher/fetcher_iam_role.go new file mode 100644 index 0000000000..d601af8f4b --- /dev/null +++ b/internal/inventory/awsfetcher/fetcher_iam_role.go @@ -0,0 +1,95 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package awsfetcher + +import ( + "context" + + "github.com/elastic/elastic-agent-libs/logp" + + "github.com/elastic/cloudbeat/internal/dataprovider/providers/cloud" + "github.com/elastic/cloudbeat/internal/inventory" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib/iam" + "github.com/elastic/cloudbeat/internal/resources/utils/pointers" +) + +type iamRoleFetcher struct { + logger *logp.Logger + provider iamRoleProvider + AccountId string + AccountName string +} + +type iamRoleProvider interface { + ListRoles(ctx context.Context) ([]*iam.Role, error) +} + +var iamRoleClassification = inventory.AssetClassification{ + Category: inventory.CategoryIdentity, + SubCategory: inventory.SubCategoryCloudProviderAccount, + Type: inventory.TypeServiceAccount, + SubType: inventory.SubTypeIAM, +} + +func newIamRoleFetcher(logger *logp.Logger, identity *cloud.Identity, provider iamRoleProvider) inventory.AssetFetcher { + return &iamRoleFetcher{ + logger: logger, + provider: provider, + AccountId: identity.Account, + AccountName: identity.AccountAlias, + } +} + +func (i *iamRoleFetcher) Fetch(ctx context.Context, assetChannel chan<- inventory.AssetEvent) { + i.logger.Info("Fetching IAM Roles") + defer i.logger.Info("Fetching IAM Roles - Finished") + + roles, err := i.provider.ListRoles(ctx) + if err != nil { + i.logger.Errorf("Could not list roles: %v", err) + if len(roles) == 0 { + return + } + } + + for _, role := range roles { + if role == nil { + continue + } + + assetChannel <- inventory.NewAssetEvent( + iamRoleClassification, + pointers.Deref(role.Arn), + pointers.Deref(role.RoleName), + + inventory.WithRawAsset(*role), + inventory.WithCloud(inventory.AssetCloud{ + Provider: inventory.AwsCloudProvider, + Region: awslib.GlobalRegion, + Account: inventory.AssetCloudAccount{ + Id: i.AccountId, + Name: i.AccountName, + }, + Service: &inventory.AssetCloudService{ + Name: "AWS IAM", + }, + }), + ) + } +} diff --git a/internal/inventory/awsfetcher/fetcher_iam_role_test.go b/internal/inventory/awsfetcher/fetcher_iam_role_test.go new file mode 100644 index 0000000000..a67350a979 --- /dev/null +++ b/internal/inventory/awsfetcher/fetcher_iam_role_test.go @@ -0,0 +1,117 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package awsfetcher + +import ( + "testing" + "time" + + "github.com/aws/aws-sdk-go-v2/service/iam/types" + "github.com/elastic/elastic-agent-libs/logp" + "github.com/stretchr/testify/mock" + + "github.com/elastic/cloudbeat/internal/dataprovider/providers/cloud" + "github.com/elastic/cloudbeat/internal/inventory" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib/iam" + "github.com/elastic/cloudbeat/internal/resources/utils/pointers" +) + +func TestIAMRoleFetcher_Fetch(t *testing.T) { + now := time.Now() + + role1 := iam.Role{ + Role: types.Role{ + RoleName: pointers.Ref("role-name-1"), + Arn: pointers.Ref("arn:aws:iam::0000:role/role-name-1"), + RoleLastUsed: nil, + Tags: nil, + CreateDate: &now, + MaxSessionDuration: pointers.Ref(int32(3600)), + PermissionsBoundary: nil, + AssumeRolePolicyDocument: pointers.Ref("document"), + Description: pointers.Ref("EKS managed node group IAM role"), + Path: pointers.Ref("/"), + RoleId: pointers.Ref("17823618723"), + }, + } + + role2 := iam.Role{ + Role: types.Role{ + RoleName: pointers.Ref("role-name-2"), + Arn: pointers.Ref("arn:aws:iam::0000:role/role-name-2"), + RoleLastUsed: nil, + Tags: nil, + CreateDate: &now, + MaxSessionDuration: pointers.Ref(int32(3600)), + PermissionsBoundary: nil, + AssumeRolePolicyDocument: pointers.Ref("document"), + Description: pointers.Ref("EKS managed node group IAM role"), + Path: pointers.Ref("/"), + RoleId: pointers.Ref("17823618723"), + }, + } + + in := []*iam.Role{&role1, nil, &role2} + + expected := []inventory.AssetEvent{ + inventory.NewAssetEvent( + iamRoleClassification, + "arn:aws:iam::0000:role/role-name-1", + "role-name-1", + inventory.WithRawAsset(role1), + inventory.WithCloud(inventory.AssetCloud{ + Provider: inventory.AwsCloudProvider, + Region: "global", + Account: inventory.AssetCloudAccount{ + Id: "123", + Name: "alias", + }, + Service: &inventory.AssetCloudService{ + Name: "AWS IAM", + }, + }), + ), + + inventory.NewAssetEvent( + iamRoleClassification, + "arn:aws:iam::0000:role/role-name-2", + "role-name-2", + inventory.WithRawAsset(role2), + inventory.WithCloud(inventory.AssetCloud{ + Provider: inventory.AwsCloudProvider, + Region: "global", + Account: inventory.AssetCloudAccount{ + Id: "123", + Name: "alias", + }, + Service: &inventory.AssetCloudService{ + Name: "AWS IAM", + }, + }), + ), + } + + logger := logp.NewLogger("test_fetcher_iam_role") + provider := newMockIamRoleProvider(t) + provider.EXPECT().ListRoles(mock.Anything).Return(in, nil) + + identity := &cloud.Identity{Account: "123", AccountAlias: "alias"} + fetcher := newIamRoleFetcher(logger, identity, provider) + + collectResourcesAndMatch(t, fetcher, expected) +} diff --git a/internal/inventory/awsfetcher/fetcher_iam_user.go b/internal/inventory/awsfetcher/fetcher_iam_user.go new file mode 100644 index 0000000000..38d28731cd --- /dev/null +++ b/internal/inventory/awsfetcher/fetcher_iam_user.go @@ -0,0 +1,100 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package awsfetcher + +import ( + "context" + + "github.com/elastic/elastic-agent-libs/logp" + + "github.com/elastic/cloudbeat/internal/dataprovider/providers/cloud" + "github.com/elastic/cloudbeat/internal/inventory" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib/iam" +) + +type iamUserFetcher struct { + logger *logp.Logger + provider iamUserProvider + AccountId string + AccountName string +} + +type iamUserProvider interface { + GetUsers(ctx context.Context) ([]awslib.AwsResource, error) +} + +var iamUserClassification = inventory.AssetClassification{ + Category: inventory.CategoryIdentity, + SubCategory: inventory.SubCategoryCloudProviderAccount, + Type: inventory.TypeUser, + SubType: inventory.SubTypeIAM, +} + +func newIamUserFetcher(logger *logp.Logger, identity *cloud.Identity, provider iamUserProvider) inventory.AssetFetcher { + return &iamUserFetcher{ + logger: logger, + provider: provider, + AccountId: identity.Account, + AccountName: identity.AccountAlias, + } +} + +func (i *iamUserFetcher) Fetch(ctx context.Context, assetChannel chan<- inventory.AssetEvent) { + i.logger.Info("Fetching IAM Users") + defer i.logger.Info("Fetching IAM Users - Finished") + + users, err := i.provider.GetUsers(ctx) + if err != nil { + i.logger.Errorf("Could not list users: %v", err) + if len(users) == 0 { + return + } + } + + for _, resource := range users { + if resource == nil { + continue + } + + user, ok := resource.(iam.User) + if !ok { + i.logger.Errorf("Could not get info about user: %s", resource.GetResourceArn()) + continue + } + + assetChannel <- inventory.NewAssetEvent( + iamUserClassification, + user.GetResourceArn(), + user.GetResourceName(), + + inventory.WithRawAsset(user), + inventory.WithCloud(inventory.AssetCloud{ + Provider: inventory.AwsCloudProvider, + Region: user.GetRegion(), + Account: inventory.AssetCloudAccount{ + Id: i.AccountId, + Name: i.AccountName, + }, + Service: &inventory.AssetCloudService{ + Name: "AWS IAM", + }, + }), + ) + } +} diff --git a/internal/inventory/awsfetcher/fetcher_iam_user_test.go b/internal/inventory/awsfetcher/fetcher_iam_user_test.go new file mode 100644 index 0000000000..87b1ef2905 --- /dev/null +++ b/internal/inventory/awsfetcher/fetcher_iam_user_test.go @@ -0,0 +1,131 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package awsfetcher + +import ( + "testing" + "time" + + "github.com/aws/aws-sdk-go-v2/service/iam/types" + "github.com/elastic/elastic-agent-libs/logp" + "github.com/stretchr/testify/mock" + + "github.com/elastic/cloudbeat/internal/dataprovider/providers/cloud" + "github.com/elastic/cloudbeat/internal/inventory" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib/iam" + "github.com/elastic/cloudbeat/internal/resources/utils/pointers" +) + +func TestIAMUserFetcher_Fetch(t *testing.T) { + now := time.Now() + + user1 := iam.User{ + Name: "user-1", + Arn: "arn:aws:iam::000:user/user-1", + LastAccess: "2023-03-28T12:27:26+00:00", + PasswordEnabled: true, + MfaActive: true, + PasswordLastChanged: "2023-03-28T12:27:26+00:00", + AccessKeys: []iam.AccessKey{ + { + Active: true, + HasUsed: true, + LastAccess: "2023-03-28T12:27:26+00:00", + RotationDate: "2023-03-28T12:27:26+00:00", + }, + }, + MFADevices: []iam.AuthDevice{ + { + IsVirtual: true, + MFADevice: types.MFADevice{ + EnableDate: &now, + SerialNumber: pointers.Ref("123"), + UserName: pointers.Ref("user-1"), + }, + }, + }, + InlinePolicies: []iam.PolicyDocument{ + { + PolicyName: "inline-policy", + Policy: "policy", + }, + }, + AttachedPolicies: []types.AttachedPolicy{ + { + PolicyArn: pointers.Ref("arn:aws:iam:1321312:policy/att-policy"), + PolicyName: pointers.Ref("att-policy"), + }, + }, + } + + user2 := iam.User{ + Name: "user-2", + Arn: "arn:aws:iam::000:user/user-2", + LastAccess: "2023-03-28T12:27:26+00:00", + } + + in := []awslib.AwsResource{user1, user2} + + expected := []inventory.AssetEvent{ + inventory.NewAssetEvent( + iamUserClassification, + "arn:aws:iam::000:user/user-1", + "user-1", + inventory.WithRawAsset(user1), + inventory.WithCloud(inventory.AssetCloud{ + Provider: inventory.AwsCloudProvider, + Region: "global", + Account: inventory.AssetCloudAccount{ + Id: "123", + Name: "alias", + }, + Service: &inventory.AssetCloudService{ + Name: "AWS IAM", + }, + }), + ), + + inventory.NewAssetEvent( + iamUserClassification, + "arn:aws:iam::000:user/user-2", + "user-2", + inventory.WithRawAsset(user2), + inventory.WithCloud(inventory.AssetCloud{ + Provider: inventory.AwsCloudProvider, + Region: "global", + Account: inventory.AssetCloudAccount{ + Id: "123", + Name: "alias", + }, + Service: &inventory.AssetCloudService{ + Name: "AWS IAM", + }, + }), + ), + } + + logger := logp.NewLogger("test_fetcher_iam_user") + provider := newMockIamUserProvider(t) + provider.EXPECT().GetUsers(mock.Anything).Return(in, nil) + + identity := &cloud.Identity{Account: "123", AccountAlias: "alias"} + fetcher := newIamUserFetcher(logger, identity, provider) + + collectResourcesAndMatch(t, fetcher, expected) +} diff --git a/internal/inventory/awsfetcher/fetcher_s3_bucket.go b/internal/inventory/awsfetcher/fetcher_s3_bucket.go new file mode 100644 index 0000000000..78fe69cefb --- /dev/null +++ b/internal/inventory/awsfetcher/fetcher_s3_bucket.go @@ -0,0 +1,96 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package awsfetcher + +import ( + "context" + + "github.com/elastic/elastic-agent-libs/logp" + "github.com/samber/lo" + + "github.com/elastic/cloudbeat/internal/dataprovider/providers/cloud" + "github.com/elastic/cloudbeat/internal/inventory" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib/s3" +) + +type s3BucketFetcher struct { + logger *logp.Logger + provider s3BucketProvider + AccountId string + AccountName string +} + +var s3BucketClassification = inventory.AssetClassification{ + Category: inventory.CategoryInfrastructure, + SubCategory: inventory.SubCategoryStorage, + Type: inventory.TypeObjectStorage, + SubType: inventory.SubTypeS3, +} + +type s3BucketProvider interface { + DescribeBuckets(ctx context.Context) ([]awslib.AwsResource, error) +} + +func newS3BucketFetcher(logger *logp.Logger, identity *cloud.Identity, provider s3BucketProvider) inventory.AssetFetcher { + return &s3BucketFetcher{ + logger: logger, + provider: provider, + AccountId: identity.Account, + AccountName: identity.AccountAlias, + } +} + +func (s *s3BucketFetcher) Fetch(ctx context.Context, assetChannel chan<- inventory.AssetEvent) { + s.logger.Info("Fetching S3 Bucket") + defer s.logger.Info("Fetching S3 Bucket - Finished") + + awsBuckets, err := s.provider.DescribeBuckets(ctx) + if err != nil { + s.logger.Errorf("Could not list s3 buckets: %v", err) + if len(awsBuckets) == 0 { + return + } + } + + buckets := lo.Map(awsBuckets, func(item awslib.AwsResource, _ int) s3.BucketDescription { + return item.(s3.BucketDescription) + }) + + for _, bucket := range buckets { + assetChannel <- inventory.NewAssetEvent( + s3BucketClassification, + bucket.GetResourceArn(), + bucket.GetResourceName(), + + inventory.WithRawAsset(bucket), + inventory.WithCloud(inventory.AssetCloud{ + Provider: inventory.AwsCloudProvider, + Region: bucket.Region, + Account: inventory.AssetCloudAccount{ + Id: s.AccountId, + Name: s.AccountName, + }, + Service: &inventory.AssetCloudService{ + Name: "AWS S3", + }, + }), + inventory.WithResourcePolicies(convertPolicy(bucket.BucketPolicy)...), + ) + } +} diff --git a/internal/inventory/awsfetcher/fetcher_s3_bucket_test.go b/internal/inventory/awsfetcher/fetcher_s3_bucket_test.go new file mode 100644 index 0000000000..6e6a48e593 --- /dev/null +++ b/internal/inventory/awsfetcher/fetcher_s3_bucket_test.go @@ -0,0 +1,174 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package awsfetcher + +import ( + "testing" + + s3types "github.com/aws/aws-sdk-go-v2/service/s3/types" + s3ctrltypes "github.com/aws/aws-sdk-go-v2/service/s3control/types" + "github.com/elastic/elastic-agent-libs/logp" + "github.com/stretchr/testify/mock" + + "github.com/elastic/cloudbeat/internal/dataprovider/providers/cloud" + "github.com/elastic/cloudbeat/internal/inventory" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib" + "github.com/elastic/cloudbeat/internal/resources/providers/awslib/s3" + "github.com/elastic/cloudbeat/internal/resources/utils/pointers" +) + +func TestS3BucketFetcher_Fetch(t *testing.T) { + bucket1 := s3.BucketDescription{ + Name: "bucket-1", + SSEAlgorithm: nil, + BucketPolicy: map[string]any{ + "Version": "2012-10-17", + "Statement": []map[string]any{ + { + "Sid": "Test 1", + "Effect": "Allow", + "Principal": map[string]any{ + "AWS": "dima", + "service": "aws.com", + }, + "Action": []string{"read", "update", "delete"}, + "Resource": []string{"s3/bucket", "s3/bucket/*"}, + }, + { + "Sid": "Test 2", + "Effect": "Deny", + "Principal": map[string]any{ + "AWS": "romulo", + }, + "Action": []string{"delete"}, + "Resource": []string{"s3/bucket"}, + }, + }, + }, + BucketVersioning: &s3.BucketVersioning{ + Enabled: true, + MfaDelete: true, + }, + PublicAccessBlockConfiguration: &s3types.PublicAccessBlockConfiguration{ + BlockPublicAcls: pointers.Ref(true), + }, + AccountPublicAccessBlockConfiguration: &s3ctrltypes.PublicAccessBlockConfiguration{ + BlockPublicAcls: pointers.Ref(true), + }, + Region: "europe-west-1", + } + + bucket2 := s3.BucketDescription{ + Name: "bucket-2", + SSEAlgorithm: nil, + BucketPolicy: map[string]any{ + "Version": "2012-10-17", + "Statement": map[string]any{ + "Sid": "Test 1", + "Effect": "Allow", + "Principal": "*", + "Action": "read", + "Resource": "s3/bucket", + }, + }, + BucketVersioning: &s3.BucketVersioning{ + Enabled: false, + MfaDelete: false, + }, + PublicAccessBlockConfiguration: &s3types.PublicAccessBlockConfiguration{ + BlockPublicAcls: pointers.Ref(false), + }, + AccountPublicAccessBlockConfiguration: &s3ctrltypes.PublicAccessBlockConfiguration{ + BlockPublicAcls: pointers.Ref(false), + }, + Region: "europe-west-1", + } + in := []awslib.AwsResource{bucket1, bucket2} + + expected := []inventory.AssetEvent{ + inventory.NewAssetEvent( + s3BucketClassification, + "arn:aws:s3:::bucket-1", + "bucket-1", + inventory.WithRawAsset(bucket1), + inventory.WithCloud(inventory.AssetCloud{ + Provider: inventory.AwsCloudProvider, + Region: "europe-west-1", + Account: inventory.AssetCloudAccount{ + Id: "123", + Name: "alias", + }, + Service: &inventory.AssetCloudService{ + Name: "AWS S3", + }, + }), + inventory.WithResourcePolicies(inventory.AssetResourcePolicy{ + Version: pointers.Ref("2012-10-17"), + Id: pointers.Ref("Test 1"), + Effect: "Allow", + Principal: map[string]any{ + "AWS": "dima", + "service": "aws.com", + }, + Action: []string{"read", "update", "delete"}, + Resource: []string{"s3/bucket", "s3/bucket/*"}, + }, inventory.AssetResourcePolicy{ + Version: pointers.Ref("2012-10-17"), + Id: pointers.Ref("Test 2"), + Effect: "Deny", + Principal: map[string]any{"AWS": "romulo"}, + Action: []string{"delete"}, + Resource: []string{"s3/bucket"}, + }), + ), + inventory.NewAssetEvent( + s3BucketClassification, + "arn:aws:s3:::bucket-2", + "bucket-2", + inventory.WithRawAsset(bucket2), + inventory.WithCloud(inventory.AssetCloud{ + Provider: inventory.AwsCloudProvider, + Region: "europe-west-1", + Account: inventory.AssetCloudAccount{ + Id: "123", + Name: "alias", + }, + Service: &inventory.AssetCloudService{ + Name: "AWS S3", + }, + }), + inventory.WithResourcePolicies(inventory.AssetResourcePolicy{ + Version: pointers.Ref("2012-10-17"), + Id: pointers.Ref("Test 1"), + Effect: "Allow", + Principal: map[string]any{"*": "*"}, + Action: []string{"read"}, + Resource: []string{"s3/bucket"}, + }), + ), + } + + logger := logp.NewLogger("test_fetcher_s3_bucket") + provider := newMockS3BucketProvider(t) + provider.EXPECT().DescribeBuckets(mock.Anything).Return(in, nil) + + identity := &cloud.Identity{Account: "123", AccountAlias: "alias"} + fetcher := newS3BucketFetcher(logger, identity, provider) + + collectResourcesAndMatch(t, fetcher, expected) +} diff --git a/internal/inventory/aws/mock_instances_provider.go b/internal/inventory/awsfetcher/mock_ec2_instances_provider.go similarity index 54% rename from internal/inventory/aws/mock_instances_provider.go rename to internal/inventory/awsfetcher/mock_ec2_instances_provider.go index 8344e5c6b0..ce01192c77 100644 --- a/internal/inventory/aws/mock_instances_provider.go +++ b/internal/inventory/awsfetcher/mock_ec2_instances_provider.go @@ -17,7 +17,7 @@ // Code generated by mockery v2.37.1. DO NOT EDIT. -package aws +package awsfetcher import ( context "context" @@ -26,21 +26,21 @@ import ( mock "github.com/stretchr/testify/mock" ) -// mockInstancesProvider is an autogenerated mock type for the instancesProvider type -type mockInstancesProvider struct { +// mockEc2InstancesProvider is an autogenerated mock type for the ec2InstancesProvider type +type mockEc2InstancesProvider struct { mock.Mock } -type mockInstancesProvider_Expecter struct { +type mockEc2InstancesProvider_Expecter struct { mock *mock.Mock } -func (_m *mockInstancesProvider) EXPECT() *mockInstancesProvider_Expecter { - return &mockInstancesProvider_Expecter{mock: &_m.Mock} +func (_m *mockEc2InstancesProvider) EXPECT() *mockEc2InstancesProvider_Expecter { + return &mockEc2InstancesProvider_Expecter{mock: &_m.Mock} } // DescribeInstances provides a mock function with given fields: ctx -func (_m *mockInstancesProvider) DescribeInstances(ctx context.Context) ([]*ec2.Ec2Instance, error) { +func (_m *mockEc2InstancesProvider) DescribeInstances(ctx context.Context) ([]*ec2.Ec2Instance, error) { ret := _m.Called(ctx) var r0 []*ec2.Ec2Instance @@ -65,41 +65,41 @@ func (_m *mockInstancesProvider) DescribeInstances(ctx context.Context) ([]*ec2. return r0, r1 } -// mockInstancesProvider_DescribeInstances_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DescribeInstances' -type mockInstancesProvider_DescribeInstances_Call struct { +// mockEc2InstancesProvider_DescribeInstances_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DescribeInstances' +type mockEc2InstancesProvider_DescribeInstances_Call struct { *mock.Call } // DescribeInstances is a helper method to define mock.On call // - ctx context.Context -func (_e *mockInstancesProvider_Expecter) DescribeInstances(ctx interface{}) *mockInstancesProvider_DescribeInstances_Call { - return &mockInstancesProvider_DescribeInstances_Call{Call: _e.mock.On("DescribeInstances", ctx)} +func (_e *mockEc2InstancesProvider_Expecter) DescribeInstances(ctx interface{}) *mockEc2InstancesProvider_DescribeInstances_Call { + return &mockEc2InstancesProvider_DescribeInstances_Call{Call: _e.mock.On("DescribeInstances", ctx)} } -func (_c *mockInstancesProvider_DescribeInstances_Call) Run(run func(ctx context.Context)) *mockInstancesProvider_DescribeInstances_Call { +func (_c *mockEc2InstancesProvider_DescribeInstances_Call) Run(run func(ctx context.Context)) *mockEc2InstancesProvider_DescribeInstances_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context)) }) return _c } -func (_c *mockInstancesProvider_DescribeInstances_Call) Return(_a0 []*ec2.Ec2Instance, _a1 error) *mockInstancesProvider_DescribeInstances_Call { +func (_c *mockEc2InstancesProvider_DescribeInstances_Call) Return(_a0 []*ec2.Ec2Instance, _a1 error) *mockEc2InstancesProvider_DescribeInstances_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *mockInstancesProvider_DescribeInstances_Call) RunAndReturn(run func(context.Context) ([]*ec2.Ec2Instance, error)) *mockInstancesProvider_DescribeInstances_Call { +func (_c *mockEc2InstancesProvider_DescribeInstances_Call) RunAndReturn(run func(context.Context) ([]*ec2.Ec2Instance, error)) *mockEc2InstancesProvider_DescribeInstances_Call { _c.Call.Return(run) return _c } -// newMockInstancesProvider creates a new instance of mockInstancesProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// newMockEc2InstancesProvider creates a new instance of mockEc2InstancesProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. -func newMockInstancesProvider(t interface { +func newMockEc2InstancesProvider(t interface { mock.TestingT Cleanup(func()) -}) *mockInstancesProvider { - mock := &mockInstancesProvider{} +}) *mockEc2InstancesProvider { + mock := &mockEc2InstancesProvider{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/internal/inventory/awsfetcher/mock_iam_policy_provider.go b/internal/inventory/awsfetcher/mock_iam_policy_provider.go new file mode 100644 index 0000000000..8dfa99a89d --- /dev/null +++ b/internal/inventory/awsfetcher/mock_iam_policy_provider.go @@ -0,0 +1,109 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package awsfetcher + +import ( + context "context" + + awslib "github.com/elastic/cloudbeat/internal/resources/providers/awslib" + + mock "github.com/stretchr/testify/mock" +) + +// mockIamPolicyProvider is an autogenerated mock type for the iamPolicyProvider type +type mockIamPolicyProvider struct { + mock.Mock +} + +type mockIamPolicyProvider_Expecter struct { + mock *mock.Mock +} + +func (_m *mockIamPolicyProvider) EXPECT() *mockIamPolicyProvider_Expecter { + return &mockIamPolicyProvider_Expecter{mock: &_m.Mock} +} + +// GetPolicies provides a mock function with given fields: ctx +func (_m *mockIamPolicyProvider) GetPolicies(ctx context.Context) ([]awslib.AwsResource, error) { + ret := _m.Called(ctx) + + var r0 []awslib.AwsResource + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]awslib.AwsResource, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) []awslib.AwsResource); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]awslib.AwsResource) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockIamPolicyProvider_GetPolicies_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPolicies' +type mockIamPolicyProvider_GetPolicies_Call struct { + *mock.Call +} + +// GetPolicies is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockIamPolicyProvider_Expecter) GetPolicies(ctx interface{}) *mockIamPolicyProvider_GetPolicies_Call { + return &mockIamPolicyProvider_GetPolicies_Call{Call: _e.mock.On("GetPolicies", ctx)} +} + +func (_c *mockIamPolicyProvider_GetPolicies_Call) Run(run func(ctx context.Context)) *mockIamPolicyProvider_GetPolicies_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockIamPolicyProvider_GetPolicies_Call) Return(_a0 []awslib.AwsResource, _a1 error) *mockIamPolicyProvider_GetPolicies_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockIamPolicyProvider_GetPolicies_Call) RunAndReturn(run func(context.Context) ([]awslib.AwsResource, error)) *mockIamPolicyProvider_GetPolicies_Call { + _c.Call.Return(run) + return _c +} + +// newMockIamPolicyProvider creates a new instance of mockIamPolicyProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockIamPolicyProvider(t interface { + mock.TestingT + Cleanup(func()) +}) *mockIamPolicyProvider { + mock := &mockIamPolicyProvider{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/inventory/awsfetcher/mock_iam_role_provider.go b/internal/inventory/awsfetcher/mock_iam_role_provider.go new file mode 100644 index 0000000000..b3e94c44f0 --- /dev/null +++ b/internal/inventory/awsfetcher/mock_iam_role_provider.go @@ -0,0 +1,108 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package awsfetcher + +import ( + context "context" + + iam "github.com/elastic/cloudbeat/internal/resources/providers/awslib/iam" + mock "github.com/stretchr/testify/mock" +) + +// mockIamRoleProvider is an autogenerated mock type for the iamRoleProvider type +type mockIamRoleProvider struct { + mock.Mock +} + +type mockIamRoleProvider_Expecter struct { + mock *mock.Mock +} + +func (_m *mockIamRoleProvider) EXPECT() *mockIamRoleProvider_Expecter { + return &mockIamRoleProvider_Expecter{mock: &_m.Mock} +} + +// ListRoles provides a mock function with given fields: ctx +func (_m *mockIamRoleProvider) ListRoles(ctx context.Context) ([]*iam.Role, error) { + ret := _m.Called(ctx) + + var r0 []*iam.Role + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]*iam.Role, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) []*iam.Role); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*iam.Role) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockIamRoleProvider_ListRoles_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRoles' +type mockIamRoleProvider_ListRoles_Call struct { + *mock.Call +} + +// ListRoles is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockIamRoleProvider_Expecter) ListRoles(ctx interface{}) *mockIamRoleProvider_ListRoles_Call { + return &mockIamRoleProvider_ListRoles_Call{Call: _e.mock.On("ListRoles", ctx)} +} + +func (_c *mockIamRoleProvider_ListRoles_Call) Run(run func(ctx context.Context)) *mockIamRoleProvider_ListRoles_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockIamRoleProvider_ListRoles_Call) Return(_a0 []*iam.Role, _a1 error) *mockIamRoleProvider_ListRoles_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockIamRoleProvider_ListRoles_Call) RunAndReturn(run func(context.Context) ([]*iam.Role, error)) *mockIamRoleProvider_ListRoles_Call { + _c.Call.Return(run) + return _c +} + +// newMockIamRoleProvider creates a new instance of mockIamRoleProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockIamRoleProvider(t interface { + mock.TestingT + Cleanup(func()) +}) *mockIamRoleProvider { + mock := &mockIamRoleProvider{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/inventory/awsfetcher/mock_iam_user_provider.go b/internal/inventory/awsfetcher/mock_iam_user_provider.go new file mode 100644 index 0000000000..891099c164 --- /dev/null +++ b/internal/inventory/awsfetcher/mock_iam_user_provider.go @@ -0,0 +1,109 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package awsfetcher + +import ( + context "context" + + awslib "github.com/elastic/cloudbeat/internal/resources/providers/awslib" + + mock "github.com/stretchr/testify/mock" +) + +// mockIamUserProvider is an autogenerated mock type for the iamUserProvider type +type mockIamUserProvider struct { + mock.Mock +} + +type mockIamUserProvider_Expecter struct { + mock *mock.Mock +} + +func (_m *mockIamUserProvider) EXPECT() *mockIamUserProvider_Expecter { + return &mockIamUserProvider_Expecter{mock: &_m.Mock} +} + +// GetUsers provides a mock function with given fields: ctx +func (_m *mockIamUserProvider) GetUsers(ctx context.Context) ([]awslib.AwsResource, error) { + ret := _m.Called(ctx) + + var r0 []awslib.AwsResource + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]awslib.AwsResource, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) []awslib.AwsResource); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]awslib.AwsResource) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockIamUserProvider_GetUsers_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUsers' +type mockIamUserProvider_GetUsers_Call struct { + *mock.Call +} + +// GetUsers is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockIamUserProvider_Expecter) GetUsers(ctx interface{}) *mockIamUserProvider_GetUsers_Call { + return &mockIamUserProvider_GetUsers_Call{Call: _e.mock.On("GetUsers", ctx)} +} + +func (_c *mockIamUserProvider_GetUsers_Call) Run(run func(ctx context.Context)) *mockIamUserProvider_GetUsers_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockIamUserProvider_GetUsers_Call) Return(_a0 []awslib.AwsResource, _a1 error) *mockIamUserProvider_GetUsers_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockIamUserProvider_GetUsers_Call) RunAndReturn(run func(context.Context) ([]awslib.AwsResource, error)) *mockIamUserProvider_GetUsers_Call { + _c.Call.Return(run) + return _c +} + +// newMockIamUserProvider creates a new instance of mockIamUserProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockIamUserProvider(t interface { + mock.TestingT + Cleanup(func()) +}) *mockIamUserProvider { + mock := &mockIamUserProvider{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/inventory/awsfetcher/mock_s3_bucket_provider.go b/internal/inventory/awsfetcher/mock_s3_bucket_provider.go new file mode 100644 index 0000000000..d917494433 --- /dev/null +++ b/internal/inventory/awsfetcher/mock_s3_bucket_provider.go @@ -0,0 +1,109 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package awsfetcher + +import ( + context "context" + + awslib "github.com/elastic/cloudbeat/internal/resources/providers/awslib" + + mock "github.com/stretchr/testify/mock" +) + +// mockS3BucketProvider is an autogenerated mock type for the s3BucketProvider type +type mockS3BucketProvider struct { + mock.Mock +} + +type mockS3BucketProvider_Expecter struct { + mock *mock.Mock +} + +func (_m *mockS3BucketProvider) EXPECT() *mockS3BucketProvider_Expecter { + return &mockS3BucketProvider_Expecter{mock: &_m.Mock} +} + +// DescribeBuckets provides a mock function with given fields: ctx +func (_m *mockS3BucketProvider) DescribeBuckets(ctx context.Context) ([]awslib.AwsResource, error) { + ret := _m.Called(ctx) + + var r0 []awslib.AwsResource + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]awslib.AwsResource, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) []awslib.AwsResource); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]awslib.AwsResource) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockS3BucketProvider_DescribeBuckets_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DescribeBuckets' +type mockS3BucketProvider_DescribeBuckets_Call struct { + *mock.Call +} + +// DescribeBuckets is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockS3BucketProvider_Expecter) DescribeBuckets(ctx interface{}) *mockS3BucketProvider_DescribeBuckets_Call { + return &mockS3BucketProvider_DescribeBuckets_Call{Call: _e.mock.On("DescribeBuckets", ctx)} +} + +func (_c *mockS3BucketProvider_DescribeBuckets_Call) Run(run func(ctx context.Context)) *mockS3BucketProvider_DescribeBuckets_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockS3BucketProvider_DescribeBuckets_Call) Return(_a0 []awslib.AwsResource, _a1 error) *mockS3BucketProvider_DescribeBuckets_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockS3BucketProvider_DescribeBuckets_Call) RunAndReturn(run func(context.Context) ([]awslib.AwsResource, error)) *mockS3BucketProvider_DescribeBuckets_Call { + _c.Call.Return(run) + return _c +} + +// newMockS3BucketProvider creates a new instance of mockS3BucketProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockS3BucketProvider(t interface { + mock.TestingT + Cleanup(func()) +}) *mockS3BucketProvider { + mock := &mockS3BucketProvider{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/inventory/inventory.go b/internal/inventory/inventory.go index 6ed8742a0d..721c8e80e8 100644 --- a/internal/inventory/inventory.go +++ b/internal/inventory/inventory.go @@ -54,8 +54,8 @@ func NewAssetInventory(logger *logp.Logger, fetchers []AssetFetcher, publisher A fetchers: fetchers, publisher: publisher, // move to a configuration parameter - bufferFlushInterval: 15 * time.Second, - bufferMaxSize: 100, + bufferFlushInterval: 10 * time.Second, + bufferMaxSize: 1600, assetCh: make(chan AssetEvent), now: now, } @@ -105,11 +105,12 @@ func (a *AssetInventory) publish(assets []AssetEvent) { Meta: mapstr.M{libevents.FieldMetaIndex: generateIndex(e.Asset)}, Timestamp: a.now(), Fields: mapstr.M{ - "asset": e.Asset, - "cloud": e.Cloud, - "host": e.Host, - "network": e.Network, - "iam": e.IAM, + "asset": e.Asset, + "cloud": e.Cloud, + "host": e.Host, + "network": e.Network, + "iam": e.IAM, + "resource_policies": e.ResourcePolicies, }, } }) @@ -118,7 +119,7 @@ func (a *AssetInventory) publish(assets []AssetEvent) { } func generateIndex(a Asset) string { - return fmt.Sprintf("asset_inventory_%s_%s_%s_%s", a.Category, a.SubCategory, a.Type, a.SubStype) + return fmt.Sprintf("asset_inventory_%s_%s_%s_%s", a.Category, a.SubCategory, a.Type, a.SubType) } func (a *AssetInventory) Stop() { diff --git a/internal/inventory/inventory_test.go b/internal/inventory/inventory_test.go index 1d5ea6ddf4..70fe6f2fa2 100644 --- a/internal/inventory/inventory_test.go +++ b/internal/inventory/inventory_test.go @@ -48,7 +48,7 @@ func TestAssetInventory_Run(t *testing.T) { Category: CategoryInfrastructure, SubCategory: SubCategoryCompute, Type: TypeVirtualMachine, - SubStype: SubTypeEC2, + SubType: SubTypeEC2, }, Tags: map[string]string{"Name": "test-server", "key": "value"}, }, @@ -76,6 +76,16 @@ func TestAssetInventory_Run(t *testing.T) { Id: pointers.Ref("a123123"), Arn: pointers.Ref("123123:123123:123123"), }, + "resource_policies": []AssetResourcePolicy{ + { + Version: pointers.Ref("2012-10-17"), + Id: pointers.Ref("Test 1"), + Effect: "Allow", + Principal: map[string]any{"*": "*"}, + Action: []string{"read"}, + Resource: []string{"s3/bucket"}, + }, + }, }, }, } @@ -93,7 +103,7 @@ func TestAssetInventory_Run(t *testing.T) { Category: CategoryInfrastructure, SubCategory: SubCategoryCompute, Type: TypeVirtualMachine, - SubStype: SubTypeEC2, + SubType: SubTypeEC2, }, "arn:aws:ec2:us-east::ec2/234567890", "test-server", @@ -122,6 +132,14 @@ func TestAssetInventory_Run(t *testing.T) { PublicDnsName: pointers.Ref("public-dns"), PrivateDnsName: pointers.Ref("private-dns"), }), + WithResourcePolicies(AssetResourcePolicy{ + Version: pointers.Ref("2012-10-17"), + Id: pointers.Ref("Test 1"), + Effect: "Allow", + Principal: map[string]any{"*": "*"}, + Action: []string{"read"}, + Resource: []string{"s3/bucket"}, + }), ) }) diff --git a/internal/resources/providers/awslib/iam/iam.go b/internal/resources/providers/awslib/iam/iam.go index 5bbcb164f2..6beb7ff509 100644 --- a/internal/resources/providers/awslib/iam/iam.go +++ b/internal/resources/providers/awslib/iam/iam.go @@ -49,6 +49,7 @@ type Client interface { GetAccessKeyLastUsed(ctx context.Context, params *iamsdk.GetAccessKeyLastUsedInput, optFns ...func(*iamsdk.Options)) (*iamsdk.GetAccessKeyLastUsedOutput, error) GetAccountPasswordPolicy(ctx context.Context, params *iamsdk.GetAccountPasswordPolicyInput, optFns ...func(*iamsdk.Options)) (*iamsdk.GetAccountPasswordPolicyOutput, error) GetRole(ctx context.Context, params *iamsdk.GetRoleInput, optFns ...func(*iamsdk.Options)) (*iamsdk.GetRoleOutput, error) + ListRoles(ctx context.Context, params *iamsdk.ListRolesInput, optFns ...func(*iamsdk.Options)) (*iamsdk.ListRolesOutput, error) GetRolePolicy(ctx context.Context, params *iamsdk.GetRolePolicyInput, optFns ...func(*iamsdk.Options)) (*iamsdk.GetRolePolicyOutput, error) GetCredentialReport(ctx context.Context, params *iamsdk.GetCredentialReportInput, optFns ...func(*iamsdk.Options)) (*iamsdk.GetCredentialReportOutput, error) GetUserPolicy(ctx context.Context, params *iamsdk.GetUserPolicyInput, optFns ...func(*iamsdk.Options)) (*iamsdk.GetUserPolicyOutput, error) @@ -85,8 +86,8 @@ type User struct { LastAccess string `json:"last_access,omitempty"` Arn string `json:"arn,omitempty"` PasswordLastChanged string `json:"password_last_changed,omitempty"` - PasswordEnabled bool `json:"password_enabled"` MfaActive bool `json:"mfa_active"` + PasswordEnabled bool `json:"password_enabled"` } type AuthDevice struct { diff --git a/internal/resources/providers/awslib/iam/mock_client.go b/internal/resources/providers/awslib/iam/mock_client.go index 66f4d63f6d..538985a843 100644 --- a/internal/resources/providers/awslib/iam/mock_client.go +++ b/internal/resources/providers/awslib/iam/mock_client.go @@ -1089,6 +1089,76 @@ func (_c *MockClient_ListPolicies_Call) RunAndReturn(run func(context.Context, * return _c } +// ListRoles provides a mock function with given fields: ctx, params, optFns +func (_m *MockClient) ListRoles(ctx context.Context, params *serviceiam.ListRolesInput, optFns ...func(*serviceiam.Options)) (*serviceiam.ListRolesOutput, error) { + _va := make([]interface{}, len(optFns)) + for _i := range optFns { + _va[_i] = optFns[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, params) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *serviceiam.ListRolesOutput + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *serviceiam.ListRolesInput, ...func(*serviceiam.Options)) (*serviceiam.ListRolesOutput, error)); ok { + return rf(ctx, params, optFns...) + } + if rf, ok := ret.Get(0).(func(context.Context, *serviceiam.ListRolesInput, ...func(*serviceiam.Options)) *serviceiam.ListRolesOutput); ok { + r0 = rf(ctx, params, optFns...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*serviceiam.ListRolesOutput) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *serviceiam.ListRolesInput, ...func(*serviceiam.Options)) error); ok { + r1 = rf(ctx, params, optFns...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClient_ListRoles_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRoles' +type MockClient_ListRoles_Call struct { + *mock.Call +} + +// ListRoles is a helper method to define mock.On call +// - ctx context.Context +// - params *serviceiam.ListRolesInput +// - optFns ...func(*serviceiam.Options) +func (_e *MockClient_Expecter) ListRoles(ctx interface{}, params interface{}, optFns ...interface{}) *MockClient_ListRoles_Call { + return &MockClient_ListRoles_Call{Call: _e.mock.On("ListRoles", + append([]interface{}{ctx, params}, optFns...)...)} +} + +func (_c *MockClient_ListRoles_Call) Run(run func(ctx context.Context, params *serviceiam.ListRolesInput, optFns ...func(*serviceiam.Options))) *MockClient_ListRoles_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]func(*serviceiam.Options), len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(func(*serviceiam.Options)) + } + } + run(args[0].(context.Context), args[1].(*serviceiam.ListRolesInput), variadicArgs...) + }) + return _c +} + +func (_c *MockClient_ListRoles_Call) Return(_a0 *serviceiam.ListRolesOutput, _a1 error) *MockClient_ListRoles_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockClient_ListRoles_Call) RunAndReturn(run func(context.Context, *serviceiam.ListRolesInput, ...func(*serviceiam.Options)) (*serviceiam.ListRolesOutput, error)) *MockClient_ListRoles_Call { + _c.Call.Return(run) + return _c +} + // ListServerCertificates provides a mock function with given fields: ctx, params, optFns func (_m *MockClient) ListServerCertificates(ctx context.Context, params *serviceiam.ListServerCertificatesInput, optFns ...func(*serviceiam.Options)) (*serviceiam.ListServerCertificatesOutput, error) { _va := make([]interface{}, len(optFns)) diff --git a/internal/resources/providers/awslib/iam/role.go b/internal/resources/providers/awslib/iam/role.go index ebd61d14e1..bc901b8e17 100644 --- a/internal/resources/providers/awslib/iam/role.go +++ b/internal/resources/providers/awslib/iam/role.go @@ -22,6 +22,8 @@ import ( "fmt" "github.com/aws/aws-sdk-go-v2/service/iam" + "github.com/aws/aws-sdk-go-v2/service/iam/types" + "github.com/samber/lo" ) type RoleGetter interface { @@ -44,3 +46,29 @@ func (p Provider) GetRole(ctx context.Context, roleName string) (*Role, error) { return r, nil } + +func (p Provider) ListRoles(ctx context.Context) ([]*Role, error) { + input := &iam.ListRolesInput{} + + roles := make([]types.Role, 0) + for { + nativeRoles, err := p.client.ListRoles(ctx, input) + if err != nil { + return nil, err + } + + roles = append(roles, nativeRoles.Roles...) + + if !nativeRoles.IsTruncated { + break + } + + input.Marker = nativeRoles.Marker + } + + return lo.Map(roles, func(role types.Role, _ int) *Role { + return &Role{ + Role: role, + } + }), nil +} diff --git a/internal/resources/providers/awslib/kms/provider.go b/internal/resources/providers/awslib/kms/provider.go index 96c8d4afc5..f208b24696 100644 --- a/internal/resources/providers/awslib/kms/provider.go +++ b/internal/resources/providers/awslib/kms/provider.go @@ -70,6 +70,10 @@ func (p *Provider) DescribeSymmetricKeys(ctx context.Context) ([]awslib.AwsResou continue } + if keyInfo.KeyMetadata.KeyManager != types.KeyManagerTypeCustomer { + continue + } + rotationStatus, err := c.GetKeyRotationStatus(ctx, &kmsClient.GetKeyRotationStatusInput{ KeyId: keyEntry.KeyId, }) diff --git a/internal/resources/providers/awslib/kms/provider_test.go b/internal/resources/providers/awslib/kms/provider_test.go index 820de35a69..06751a4433 100644 --- a/internal/resources/providers/awslib/kms/provider_test.go +++ b/internal/resources/providers/awslib/kms/provider_test.go @@ -113,6 +113,19 @@ func (s *ProviderTestSuite) TestProvider_DescribeSymmetricKeys() { expectError: false, regions: []string{awslib.DefaultRegion}, }, + { + name: "Should not return a resource when key is managed by AWS", + kmsClientMockReturnVals: kmsClientMockReturnVals{ + "ListKeys": {{{mock.Anything, mock.Anything}, {&kmsClient.ListKeysOutput{Keys: []types.KeyListEntry{ + {KeyId: &keyId1}, + }}, nil}}}, + "DescribeKey": {{{mock.Anything, mock.Anything}, {&kmsClient.DescribeKeyOutput{KeyMetadata: &types.KeyMetadata{KeyId: &keyId1, KeySpec: types.KeySpecSymmetricDefault, KeyManager: types.KeyManagerTypeAws}}, nil}}}, + "GetKeyRotationStatus": {{{mock.Anything, mock.Anything}, {nil, errors.New("some error")}}}, + }, + expected: []awslib.AwsResource(nil), + expectError: false, + regions: []string{awslib.DefaultRegion}, + }, { name: "Should return a resource for a symmetric key", kmsClientMockReturnVals: kmsClientMockReturnVals{ @@ -125,8 +138,8 @@ func (s *ProviderTestSuite) TestProvider_DescribeSymmetricKeys() { }}, nil}}, }, "DescribeKey": { - {{mock.Anything, mock.MatchedBy(MatchDescribeKeyInput(keyId1))}, {&kmsClient.DescribeKeyOutput{KeyMetadata: &types.KeyMetadata{KeyId: &keyId1, KeySpec: types.KeySpecSymmetricDefault}}, nil}}, - {{mock.Anything, mock.MatchedBy(MatchDescribeKeyInput(keyId2))}, {&kmsClient.DescribeKeyOutput{KeyMetadata: &types.KeyMetadata{KeyId: &keyId2, KeySpec: types.KeySpecSymmetricDefault}}, nil}}, + {{mock.Anything, mock.MatchedBy(MatchDescribeKeyInput(keyId1))}, {&kmsClient.DescribeKeyOutput{KeyMetadata: &types.KeyMetadata{KeyId: &keyId1, KeySpec: types.KeySpecSymmetricDefault, KeyManager: types.KeyManagerTypeCustomer}}, nil}}, + {{mock.Anything, mock.MatchedBy(MatchDescribeKeyInput(keyId2))}, {&kmsClient.DescribeKeyOutput{KeyMetadata: &types.KeyMetadata{KeyId: &keyId2, KeySpec: types.KeySpecSymmetricDefault, KeyManager: types.KeyManagerTypeCustomer}}, nil}}, }, "GetKeyRotationStatus": { {{mock.Anything, mock.MatchedBy(MatchGetKeyRotationStatusInput(keyId1))}, {&kmsClient.GetKeyRotationStatusOutput{KeyRotationEnabled: true}, nil}}, @@ -134,8 +147,8 @@ func (s *ProviderTestSuite) TestProvider_DescribeSymmetricKeys() { }, }, expected: []awslib.AwsResource{ - KmsInfo{KeyMetadata: types.KeyMetadata{KeyId: &keyId1, KeySpec: types.KeySpecSymmetricDefault}, KeyRotationEnabled: true, region: "us-east-1"}, - KmsInfo{KeyMetadata: types.KeyMetadata{KeyId: &keyId2, KeySpec: types.KeySpecSymmetricDefault}, KeyRotationEnabled: true, region: "us-east-2"}, + KmsInfo{KeyMetadata: types.KeyMetadata{KeyId: &keyId1, KeySpec: types.KeySpecSymmetricDefault, KeyManager: types.KeyManagerTypeCustomer}, KeyRotationEnabled: true, region: "us-east-1"}, + KmsInfo{KeyMetadata: types.KeyMetadata{KeyId: &keyId2, KeySpec: types.KeySpecSymmetricDefault, KeyManager: types.KeyManagerTypeCustomer}, KeyRotationEnabled: true, region: "us-east-2"}, }, expectError: false, regions: []string{"us-east-1", "us-east-2"}, diff --git a/internal/resources/providers/awslib/s3/provider.go b/internal/resources/providers/awslib/s3/provider.go index 50d1240aef..67f64c0f82 100644 --- a/internal/resources/providers/awslib/s3/provider.go +++ b/internal/resources/providers/awslib/s3/provider.go @@ -108,7 +108,7 @@ func (p Provider) DescribeBuckets(ctx context.Context) ([]awslib.AwsResource, er BucketVersioning: bucketVersioning, PublicAccessBlockConfiguration: publicAccessBlockConfiguration, AccountPublicAccessBlockConfiguration: accountPublicAccessBlockConfig, - region: region, + Region: region, }) } } @@ -184,7 +184,7 @@ func (p Provider) getBucketsRegionMapping(ctx context.Context, buckets []types.B bucketsRegionMap := make(map[string][]types.Bucket, 0) for _, clientBucket := range buckets { region, regionErr := p.getBucketRegion(ctx, clientBucket.Name) - // If we could not get the region for a bucket, additional API calls for resources will probably fail, we should + // If we could not get the Region for a bucket, additional API calls for resources will probably fail, we should // not describe this bucket. if regionErr != nil { p.log.Errorf("Could not get bucket location for bucket %s. Not describing this bucket. Error: %v", *clientBucket.Name, regionErr) @@ -323,5 +323,5 @@ func (b BucketDescription) GetResourceType() string { } func (b BucketDescription) GetRegion() string { - return b.region + return b.Region } diff --git a/internal/resources/providers/awslib/s3/provider_test.go b/internal/resources/providers/awslib/s3/provider_test.go index 7bb850a2f3..0624ff3b9c 100644 --- a/internal/resources/providers/awslib/s3/provider_test.go +++ b/internal/resources/providers/awslib/s3/provider_test.go @@ -88,7 +88,7 @@ func (s *ProviderTestSuite) TestProvider_DescribeBuckets() { regions: []string{awslib.DefaultRegion}, }, { - name: "Should not return any S3 buckets when the region can not be fetched", + name: "Should not return any S3 buckets when the Region can not be fetched", s3ClientMockReturnVals: s3ClientMockReturnVals{ "ListBuckets": {{{mock.Anything, mock.Anything}, {&s3Client.ListBucketsOutput{Buckets: []types.Bucket{{Name: &bucketName}}}, nil}}}, "GetBucketEncryption": {{{mock.Anything, mock.Anything}, {nil, errors.New("bla")}}}, @@ -120,7 +120,7 @@ func (s *ProviderTestSuite) TestProvider_DescribeBuckets() { BucketVersioning: nil, PublicAccessBlockConfiguration: nil, AccountPublicAccessBlockConfiguration: nil, - region: awslib.DefaultRegion, + Region: awslib.DefaultRegion, }}, expectError: false, regions: []string{awslib.DefaultRegion}, @@ -139,7 +139,7 @@ func (s *ProviderTestSuite) TestProvider_DescribeBuckets() { BucketVersioning: nil, PublicAccessBlockConfiguration: nil, AccountPublicAccessBlockConfiguration: nil, - region: string(region), + Region: string(region), }}, expectError: false, regions: []string{awslib.DefaultRegion}, @@ -168,7 +168,7 @@ func (s *ProviderTestSuite) TestProvider_DescribeBuckets() { BucketVersioning: nil, PublicAccessBlockConfiguration: nil, AccountPublicAccessBlockConfiguration: nil, - region: string(region), + Region: string(region), }}, expectError: false, regions: []string{awslib.DefaultRegion, string(region)}, @@ -191,7 +191,7 @@ func (s *ProviderTestSuite) TestProvider_DescribeBuckets() { BucketVersioning: nil, PublicAccessBlockConfiguration: nil, AccountPublicAccessBlockConfiguration: nil, - region: string(region), + Region: string(region), }}, expectError: false, regions: []string{awslib.DefaultRegion, string(region)}, @@ -214,7 +214,7 @@ func (s *ProviderTestSuite) TestProvider_DescribeBuckets() { BucketVersioning: &BucketVersioning{true, true}, PublicAccessBlockConfiguration: nil, AccountPublicAccessBlockConfiguration: nil, - region: string(region), + Region: string(region), }}, expectError: false, regions: []string{awslib.DefaultRegion, string(region)}, @@ -249,7 +249,7 @@ func (s *ProviderTestSuite) TestProvider_DescribeBuckets() { RestrictPublicBuckets: aws.Bool(false), }, AccountPublicAccessBlockConfiguration: nil, - region: string(region), + Region: string(region), }}, expectError: false, regions: []string{awslib.DefaultRegion, string(region)}, @@ -284,7 +284,7 @@ func (s *ProviderTestSuite) TestProvider_DescribeBuckets() { IgnorePublicAcls: aws.Bool(false), RestrictPublicBuckets: aws.Bool(false), }, - region: string(region), + Region: string(region), }}, expectError: false, regions: []string{awslib.DefaultRegion, string(region)}, @@ -366,7 +366,7 @@ func (s *ProviderTestSuite) TestProvider_DescribeBuckets() { IgnorePublicAcls: aws.Bool(false), RestrictPublicBuckets: aws.Bool(false), }, - region: string(region), + Region: string(region), }, BucketDescription{ Name: secondBucketName, @@ -385,14 +385,14 @@ func (s *ProviderTestSuite) TestProvider_DescribeBuckets() { IgnorePublicAcls: aws.Bool(false), RestrictPublicBuckets: aws.Bool(false), }, - region: awslib.DefaultRegion, + Region: awslib.DefaultRegion, }, }, expectError: false, regions: []string{awslib.DefaultRegion, string(region)}, }, { - name: "Should return two S3 buckets from the same region", + name: "Should return two S3 buckets from the same Region", s3ClientMockReturnVals: s3ClientMockReturnVals{ "ListBuckets": {{{mock.Anything, mock.Anything}, {&s3Client.ListBucketsOutput{Buckets: []types.Bucket{{Name: &bucketName}, {Name: &secondBucketName}}}, nil}}}, "GetBucketEncryption": { @@ -468,7 +468,7 @@ func (s *ProviderTestSuite) TestProvider_DescribeBuckets() { IgnorePublicAcls: aws.Bool(false), RestrictPublicBuckets: aws.Bool(false), }, - region: awslib.DefaultRegion, + Region: awslib.DefaultRegion, }, BucketDescription{ Name: secondBucketName, @@ -487,7 +487,7 @@ func (s *ProviderTestSuite) TestProvider_DescribeBuckets() { IgnorePublicAcls: aws.Bool(false), RestrictPublicBuckets: aws.Bool(false), }, - region: awslib.DefaultRegion, + Region: awslib.DefaultRegion, }, }, expectError: false, diff --git a/internal/resources/providers/awslib/s3/s3.go b/internal/resources/providers/awslib/s3/s3.go index 034355872a..c725ffdfa3 100644 --- a/internal/resources/providers/awslib/s3/s3.go +++ b/internal/resources/providers/awslib/s3/s3.go @@ -36,7 +36,7 @@ type BucketDescription struct { BucketVersioning *BucketVersioning `json:"bucket_versioning,omitempty"` PublicAccessBlockConfiguration *types.PublicAccessBlockConfiguration `json:"public_access_block_configuration"` AccountPublicAccessBlockConfiguration *s3ContorlTypes.PublicAccessBlockConfiguration `json:"account_public_access_block_configuration"` - region string + Region string } // TODO: This can be better typed, but this is a complex object. See this library for example: https://github.com/liamg/iamgo/