https://garden.io/blog/aws-security-issue 1 Purple white and red flowers. Our garden is growing. We've raised a Series A funding round. Read more here [62c1474ef4][62c144a7f9] [62c155d932] V Product [6] Get Started How Garden works One blueprint for your entire system, from soup to nuts Features Ephemeral environments Spin up production-like environments with one command End-to-end testing Catch errors fast with testing during development and elegant test caching Plans V Company [6] About us Click here for office pet photos Newsroom What people are saying about us Careers We've been looking for someone like you V Resources [6] Blog The latest and greatest in DevOps Developer productivity survey Find out why devs report spending 16h a week on non-development tasks Improve developer productivity How do you accelerate developer productivity in a sustainable way? Community newsletter Developer advocate Tao Hansen on DevOps, FOSS, and other neat things DocsGitHub[6418ee8b8a] Book a DemoGet STARTED [62c1458cef] Product How Garden worksEphemeral environmentsEnd-to-end testing Plans Company About usNewsroomCareers Resources BlogDeveloper productivity surveyImprove developer productivity Community newsletter Docs GitHub Github [6418ee8b8a] Discord Book A DemoGet Started [62c144a7f9] We reported a security issue in AWS CDK's eks.Cluster component [62c527bdc6] Anna Mager [637ec6c407] Steffen Neubauer June 6, 2023 SHARE We recently discovered a security issue in AWS CDK, the open-source cloud development kit that can be used to deploy and manage infrastructure as code using different programming languages. After reporting the issue to the AWS Security Team, the AWS CDK team swiftly started working on a solution and published fixed versions of AWS CDK, v2.80.0 and v1.202.0, on May 20th. You are affected if you are using older versions of the AWS CDK to create EKS (Kubernetes) clusters. AWS CDK brings a lot to the table as it allows engineers to write infrastructure as code in any of 5 popular programming languages, using a vast library of abstractions called "Constructs". Constructs provide functionality on different levels of abstraction, some only represent mere AWS resources like S3 buckets or IAM roles while others can be entire solutions with well-architected defaults. The output of a CDK program is a CloudFormation template, which in turn can be used to actually create or update the AWS resources on your AWS accounts. The security issue we discovered was present in the eks.Cluster component since 2020 - around three years ago at the time of writing. If you want to jump to the code straight away, this line of code is the culprit: // the role used to create the cluster. this becomes the administrator role // of the cluster. const creationRole = new iam.Role(this, 'CreationRole', { assumedBy: new iam.AccountRootPrincipal(), }); If this doesn't already give away the entire story for you, in this blog post, we will explain the vulnerability, how and why we found it, why it matters to you and what you need to do to resolve the issue in case you are affected. We will also share our experience working with the AWS Security team to report and resolve the issue. What's going on here Let's zoom out a little bit and first look at the functionality of the eks.Cluster component. The eks.Cluster component performs significant heavy lifting for the user: Not only can it help to create and manage an EKS cluster, which is an AWS-managed Kubernetes cluster, it can also be used to install custom Kubernetes manifests on the EKS cluster. This functionality is implemented under the hood using CloudFormation Custom Resources. Custom resources are Lambda functions that receive events like "Create", "Update" or "Delete" from the CloudFormation service, and in turn execute appropriate infrastructure actions. For a Lambda function to be able to install a Kubernetes Manifest in an EKS cluster, it will need permission to assume a role that grants it permission to access the Kubernetes API. To facilitate creating the EKS cluster and then deploying resources on top of it, the Cluster component will create an IAM role called creationRole which has full access to the created EKS cluster and the permission to update or delete it. That's pretty awesome actually, because it makes it really easy to set up an EKS cluster and run complex applications on top of it, all in a few simple lines of Javascript or Python that are easy to read and easy to understand. const cluster = new eks.Cluster(this, 'my-cluster', { clusterName: `my-cluster`, version: eks.KubernetesVersion.V1_23, defaultCapacity: 2 }); cluster.addHelmChart('NginxIngress', { chart: 'nginx-ingress', repository: 'https://helm.nginx.com/stable', namespace: 'kube-system', }); The trust policy of this creationRole was set to allow the AWS account root principal to assume it. This is the entire problem, as it means that every identity with sts:assumeRole permission in the AWS account is allowed assume this role, and not just the Lambda functions part of the CloudFormation template that actually need the permission. Even worse, AWS CDK for EKS offers means to manage access to the created EKS cluster, but the undocumented creationRole undermined all the access management facilities altogether as it allows any identity with the sts:assumeRole permission in the account to assume it. Why we dug so deep [?][?] At Garden we are committed to transforming your developer experience by making it feel as if your entire stack of services (we call it the Stack Graph!) runs on your local laptop, while actually running on a production-like remote Kubernetes cluster. As Site Reliability Engineers here at Garden, we were working on an AWS quickstart solution to simplify the process of setting up the remote development cluster. The goal is to boil down the entire setup process to one click. Our development cluster solution aims to enable you to use all major Garden features right away: * Autoscaling to support any Engineering team size and workload requirements * Fast builds and quiet fans using Garden's in-cluster building and AWS ECR repositories * The ability to deploy a separate instance of your app for every developer, branch, CI run, staging environment (whatever you need!) using a wildcard Route53 record, all with SSL using AWS Certificate Manager, a Load Balancer, as well as an Ingress Controller. If we made you curious about our solution, or you think this might be exactly what you were looking for, feel free to give it a spin at https://github.com/garden-io/garden-aws-quickstart. Because many of our users also use AWS, and it's possible to deploy CloudFormation templates using one click, we decided to use CloudFormation as a means to distribute this solution. Due to the complexity of AWS EKS, writing CloudFormation code manually to do all this is not easily achievable. That's why we decided to use the AWS CDK and the AWS EKS blueprint as a foundation for our custom solution. We wanted to make sure we created a well architected and secure solution to you, so we took extra care when it came to access management when we added the ability to specify IAM users and roles to the CloudFormation stack. So we double checked all the IAM roles and their policies. The discovery We realized that our CloudFormation stack was not only creating the roles we specified, but also additional ones that are required for its internal functioning. This in and of itself is completely fine, but while we were optimizing the privileges, we stumbled upon the so called creationRole. The creationRole has the privileges to edit or delete the EKS cluster, to create service linked roles, describe several properties of EC2 instances and also to perform encrypt and decrypt operations on a KMS key. The Kubernetes aws-auth ConfigMap , which is used to map IAM identities to Kubernetes RBAC roles, grants it the system:masters role. The system:masters role in Kubernetes can access all namespaces and resources in the cluster. It basically grants super-admin privileges to the created EKS cluster. All of this makes sense, because CloudFormation will need to do all of these things including managing the aws-auth ConfigMap and deploying services to the cluster. However, what struck us as odd was the trust policy of the creationRole. A trust-policy determines who can assume a given role and in this case it was set to the account root principal: { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111122223333:root" }, "Action": "sts:AssumeRole" } ] } This has the effect that every identity in the account with sts:assumeRole permission can assume this role. At first we assumed this issue was part of the EKS blueprints pattern, but upon digging a bit deeper we realized that it can be traced back all the way to the AWS CDK code. And here, again, the culprit of the issue, a single line of code, hopefully now making a little bit more sense to you: // the role used to create the cluster. this becomes the administrator role // of the cluster. const creationRole = new iam.Role(this, 'CreationRole', { assumedBy: new iam.AccountRootPrincipal(), // <= this is the culprit }); Why this matters The existence of the creationRole together with this overly permissive trust policy undermines all the access controls put in place when carefully managing the aws-auth ConfigMap, as any identity (e.g user or another role) with the sts:assumeRole permission in the AWS account will be able to access that cluster with system:master permissions. This has far-reaching implications. Any identity in the account would be able to add more users or roles to the cluster, delete users or roles' access to the cluster, deploy any kind of application and decrypt secrets in the cluster. Decrypting secrets in the EKS cluster could potentially broaden the blast radius of this issue to other services in or outside AWS. Since this is part of AWS CDKs EKS construct implementation this affects every EKS cluster created using the AWS CDK. We believe that the fact that it is completely hidden to the user increases the severity. The permissions and security guide on the CDK EKS documentation outlines several mechanisms to securing the cluster and granting permissions to IAM users and roles. The creationRole and its capabilities are not mentioned. If you are using the CDK to create EKS clusters you are probably not aware that this role exists. Once you have become aware of its existence, there is no way to avoid its creation since it is deep in the machinery of the EKS construct and not configurable. The role exists for the whole lifecycle of your CDK stack. What happened next After going back and forth trying to secure the trust policy of the creationRole from our code, we came to the conclusion that this is not an issue with our code, but with the CDK itself. So we wrote an e-mail to aws-security@amazon.com. Amazon is taking security seriously and offers means for security researchers to report vulnerabilities. The SLA from Amazon's side were well-kept and we received an answer within 24 hours. They soon confirmed that this was indeed treated as a valid security issue by AWS and that someone would be working on a fix. They were transparent, immediately worked on a fix and kept us posted. We stayed in close contact with AWS security to make sure that this blog post would not be released before the fix has been released. Overall, we think it was a pleasure to work with the AWS Security team to solve this issue and collaborate on this blog post. Full timeline Date & summary Apr 18, 2023, 7:38 PM: Initial Report sent to AWS Security [1] Apr 19, 2023, 12:44 PM: Confirmation received from AWS Security: Will investigate Apr 21, 2023, 4:18 PM: Update received from AWS Security: Still investigating Apr 28, 2023, 4:15 PM: Update received from AWS Security: Working on a fix May 11, 2023, 7:04 PM: Update received from AWS Security: Working on a fix May 17, 2023, 5:48 PM: Update received from AWS Security: Planning to notify affected customers May 24, 2023, 7:06 PM: Report sent to AWS Security that the issue has not yet been resolved in EKS Blueprints [2] May 24, 2023, 7:49 PM: Confirmation received from AWS Security: Service Team has been engaged on the issue May 26, 2023, 7:26 PM: Update received from AWS Security: Working on updating AWS CDK library in EKS Blueprints May 30, 2023, 10:24 PM: Update received from AWS Security: Issues have been resolved in latest versions of both aws-cdk and cdk-eks-blueprints as of 2023-05-26 The solution While the essence of the problem is one line of code, solving it in such a foundational component was much more complicated and required serious engineering efforts on AWS's side. On first glance, the solution looks quite straightforward by changing the trust policy to only allow the creationRole to be assumed by the specific roles that need to: // the role used to create the cluster. this becomes the administrator role // of the cluster. const creationRole = new iam.Role(this, 'CreationRole', { // the role would be assumed by the provider handlers, as they are the ones making // the requests. assumedBy: new iam.CompositePrincipal( provider.provider.onEventHandler.role!, provider.provider.isCompleteHandler!.role! ), }); What apparently complicated the implementation is the fact that the kubectl Lambda function is implemented within a cdk.NestedStack: // note that 'scope' is used here intentionally because we have to create the role // outside of the nested stack, so that it can be used in the trust policy of the // creation role. const handlerRole = cluster.kubectlLambdaRole ?? new iam.Role(scope, 'KubectlHandlerRole', { assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), managedPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole')], }); And then, integration testing required importing thousands of lines of updated code from @aws-cdk-testing/ framework-integ/test. The fix has been released with the AWS CDK versions v2.80.0 and v1.202.0 on May 20, 2023. Amazon not only improved the creationRole, but also the mastersRole: If not specified by the user, the mastersRole also used a too permissive trust policy, with the difference that it was documented and the user could override the behaviour. With these versions an overly permissive trust policy is not the default anymore, and we are really happy that this makes eks.Cluster now safe by default. If you are using AWS CDK versions prior to v2.80.0 or v1.202.0 and the eks.Cluster component (directly or indirectly), we strongly recommend updating your CDK version and deploying the new version of your stack with the updated component. Conclusion Overall this was a really interesting and exciting process for us and we were very happy with the communication with AWS about this issue, and we feel that AWS resolved the issue real quickly. Just in case you've gotten curious about the Garden AWS Quickstart solution, feel free to give it a spin on GitHub at https://github.com /garden-io/garden-aws-quickstart - and also stay tuned as we will soon accompany it with another blog post. If you have any feedback regarding this post or would like to join the conversation in our community, you are very welcome to join us on discord at https://go.garden.io/discord. Footnotes [1] Initial report sent to AWS From: Steffen Neubauer *@garden.io To: AWS Security CC: Anna Mager *@garden.io Dear AWS Security Team, My colleague Anna Mager and I discovered the following security vulnerability. We kindly bring this to your attention in this Email. Quick summary CDK: EKS cluster adminRole grants excessive permissions to any principal in the account Affected Open Source projects * aws-cdk: https://github.com/aws/aws-cdk * cdk-eks-blueprints https://github.com/aws-quickstart/ cdk-eks-blueprints Steps to reproduce the issue When creating an EKS cluster using the AWS CDK libraries, as following: const clusterAdmin=iam.Role.fromRoleArn(this,'arn:aws:iam::xxx:role/ aws-reserved/sso.amazonaws.com/eu-central-1/ AWSReservedSSO_AdministratorAccess_xxx');const cluster=neweks.Cluster (this,'demogo-cluster',{ clusterName:`demogo`, mastersRole: clusterAdmin, version: eks.KubernetesVersion.V1_18, defaultCapacity:2}); cluster.addAutoScalingGroupCapacity('spot-group',{ instanceType:newec2.InstanceType('m5.xlarge'), spotPrice: cdk.Stack.of(this).region==primaryRegion? '0.248':'0.192'}); For an exhaustive step-by-step guide, please refer to the official EKS CDK workshop at https://catalog.us-east-1.prod.workshops.aws/ workshops/c15012ac-d05d-46b1-8a4a-205e7c9d93c9/en-US/ 40-deploy-clusters/200-cluster/210-cluster Observed behaviour * The mastersRole will be added to the EKS cluster's auth ConfigMap, as expected * Roles associated with Lambda functions that are providers for EKS CustomResources have access to the AWS resources related to the EKS cluster, as expected * Any principal in the same account with the sts.AssumeRole has permission to assume the "CreationRole" - see also cluster.adminRole - This is unexpected! This indirectly grants excessive permissions to all principals with sts.AssumeRole - for example they all can delete the EKS cluster, and use the KMS key to decrypt or encrypt. See also the screenshots attached to this Email[2]. See also the culprit lines of code on GitHub:- https://github.com/aws /aws-cdk/blob/0d156a810a7a049e03f2d84582f12b7a231dea2e/packages/ aws-cdk-lib/aws-eks/lib/cluster-resource.ts#L122- https://github.com/ aws/aws-cdk/blob/0d156a810a7a049e03f2d84582f12b7a231dea2e/packages/ aws-cdk-lib/aws-eks/lib/cluster-resource.ts#L60 Expected behaviour * The mastersRole will be added to the EKS cluster's auth ConfigMap * Roles associated with Lambda functions that are providers for EKS CustomResources have access to the resources related to the EKS cluster * Other untrusted principals in the account cannot assume a role created by the CDK code above do not have excessive permissions (e.g. modify or delete) to the AWS resources related to the EKS cluster Steps to remediate the issue The trust relationship of the "CreationRole" should only grant permission to assume it to the role associated with the Lambda function that provides the custom resources that need access to the EKS cluster, e.g. to perform kubectl operations. Workarounds We did not find a working workaround yet. We also did not find documentation that makes users aware of this behaviour. If you are aware of a workaround other than not using the CDK eks.Cluster library, please let us know. Severity The cdk eks blueprint library (affected by this issue as well) advertises that the project can be used to deploy well-architected EKS clusters and manage access permissions to the clusters[1]. Because * Any principals in the account have access to critical EKS resources like the KMS key and API endpoints like update EKS cluster, delete EKS cluster etc. * This contradicts with the advertised features I personally feel this is a high severity issue, and it should be resolved within a reasonable time frame of 90 days. Responsible disclosure We reserve the right to disclose this issue at some point in the future (responsible disclosure). We will attempt to coordinate the date with you. We hope that this issue will be resolved until then. Please let us know if there was any missing information, or if there were any errors in this report. We would like to stay in touch about this issue. Thank you for the great work of keeping AWS secure in advance! References [1] According to https://github.com/aws-quickstart/cdk-eks-blueprints [...] Customers can use this QuickStart to easily architect and deploy a multi-team Blueprints built on EKS. Specifically, customers can leverage the eks-blueprints module to: * Deploy Well-Architected EKS clusters across any number of accounts and regions. [...] * Define teams, namespaces, and their associated access permissions for your clusters. [...] [2] Screenshots of the "CreationRole": [2] Report sent to AWS Security that the issue has not yet been resolved in EKS Blueprints From: Steffen Neubauer *@garden.io To: AWS Security CC: Anna Mager *@garden.io Hi R., We would like to inform you that the original issue reported in the email on April 18th has not been fully resolved. Issue description it has been resolved in the aws-cdk ( https://github.com/aws/aws-cdk ) - But not downstream in cdk-eks-blueprints ( https://github.com/aws-quickstart/cdk-eks-blueprints ) The blueprints depend on "aws-cdk": "^2.78.0", (See package json file at https://github.com/aws-quickstart/cdk-eks-blueprints/blob/main/ package.json#LL24C28-L24C28)mm) - but the version fixed is 2.80.0 or 1.202.0 Workarounds The consumers of the aws-quickstart cdk-eks-blueprint cannot update the cdk themselves, as it would result in type errors down the line (At least that is what we found so far in our experiments). Steps to remediate the issue It's important to update cdk-eks-blueprints to a fixed version and inform users of the blueprints of this issue as well. There might be more downstream users of the CDK eks.Cluster component, but the cdk-eks-blueprints seem to be an open-source repository that is being maintained by AWS so it should be in scope for reports via aws-security@amazon.com as well. Responsible disclosure Because this information was already present in the original report from April 18th we expect it to be fixed in the same 90 day window (until 18th of June). Thank you for keeping AWS and your customers secure! Best regards, Steffen [62c1474eee] Previous Liked this article? why not share it. Next [62c1474eee] Stay in the loop Subscribe to our newsletter for all the updates. garden Copyright (c) Garden. All rights reserved. HomePricingHow Garden Works DocumentationBlogCommunity newsletter Contact UsAbout usNewsroomCareers Privacy PolicyTerms of ServiceImpressum Follow Us [62c1458d1f][62c1458cab][62c1458b08][62c1458da3]