Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AWS-EKS: Access Denied when installing a helm chart using a KubernetesManifest or HelmChart or EKS Blueprints #28881

Closed
srispl opened this issue Jan 26, 2024 · 10 comments
Labels
@aws-cdk/aws-eks Related to Amazon Elastic Kubernetes Service bug This issue is a bug. p2

Comments

@srispl
Copy link

srispl commented Jan 26, 2024

Describe the bug

I have created an EKS cluster using AWS CDK and wanted to install few tools.

Note: The EKS cluster is created in separate CloudFormation stack created through CDK and I am trying to install the tool in separate stack by importing the cluster created in another stack.

I tried to install the tool using EksBlueprint Addons , KubernetesManifest, HelmChart, but all 3 are ending up with Access Denied error.

If I move the same code to the stack where the EKS cluster is created or run the output YAML through kubectl, it is working as expected. It looks like the problem is due to the role created by the Constructs cannot assume the role of kubectl to apply the yaml on EKS cluster.

Expected Behavior

CDK should apply the resources to the EKS cluster.

Current Behavior

An error is thrown and the stack fails to deploy.

❌ Deployment failed: Error: The stack named BuildkitStack failed creation, it may need to be manually deleted from the AWS console: ROLLBACK_COMPLETE: Received response status [FAILED] from custom resource. Message returned: Error: b'\nAn error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:sts::XXXXXXXXXX:assumed-role/XXXXXX (This is the lambda role) is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::XXXXXXXXXX:role/XXXX (This is Kubectl role) \nUnable to connect to the server: getting credentials: exec: executable aws failed with exit code 255\n'

Reproduction Steps

  1. Create an EKS cluster stack either with Blueprints or Helm in Stack1
  2. Get the cluster name, endpoint, kubectlArn, version, clusterSecurityGroupId, openIdConnectProvider created in Stack1 and import the EKS cluster using ImportClusterProvider or aws_eks.Cluster.fromClusterAttributes construct in Stack2.
  3. Create a simple configmap as Manifest file.
  4. Pass the manifest file created in Step#3 as property to KubernetesManifest construct.
  5. Build and deploy stack2.

Note: You can reproduce above steps by replacing step#3 with helmchart using HelmChart construct or any EKS blueprint addon using blueprints.addons construct.

Possible Solution

Solution#1:
The lambda role created should be added to the kubectl role as trust relationship, so it can assume kubectl role to apply the manifest.
Solution#2:
Output the Lambda role Arn from the construct with standard naming convention, so, the arn can be added in Stack#2.

Additional Information/Context

No response

CDK CLI Version

2.61.0

Framework Version

No response

Node.js Version

18.19.0

OS

Mac OS 14.2.1

Language

TypeScript

Language Version

TypeScript (5.0.4)

Other information

No response

@srispl srispl added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Jan 26, 2024
@github-actions github-actions bot added the @aws-cdk/aws-eks Related to Amazon Elastic Kubernetes Service label Jan 26, 2024
@pahud
Copy link
Contributor

pahud commented Jan 29, 2024

Can you provide a minimal reproducible code snippet? Please note for all EKS BluePrints issue, you will need to report to https://github.com/aws-quickstart/cdk-eks-blueprints/issues instead.

@pahud pahud added p2 response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. and removed needs-triage This issue or PR still needs to be triaged. labels Jan 29, 2024
Copy link

This issue has not received a response in a while. If you want to keep this issue open, please leave a comment below and auto-close will be canceled.

@github-actions github-actions bot added the closing-soon This issue will automatically close in 4 days unless further comments are made. label Jan 31, 2024
@srispl
Copy link
Author

srispl commented Jan 31, 2024

Here is what the sample code.

Stack 1:

const cluster = new eks.Cluster(this, 'hello-eks', {
  version: eks.KubernetesVersion.V1_28,
  kubectlLayer: new KubectlV28Layer(this, 'kubectl'),
});

Stack 2:

const eksCluster = aws_eks.Cluster.fromClusterAttributes(this, `eks-cluster`, {
      clusterName: Fn.importValue('ClusterName_From_Stack1'),
      clusterSecurityGroupId: Fn.importValue('ClusterSecurityGroup_From_Stack1'),
      kubectlRoleArn: Fn.importValue('ClusterKubectlArn_From_Stack1'),
      clusterEndpoint: Fn.importValue('ClusterEndpoint_From_Stack1'),
    });

const installKSM = readYamlDocument(__dirname + '/serviceaccount-ksm.yaml');
//Link for Yaml: https://github.com/kubernetes/kube-state-metrics/blob/main/examples/standard/service-account.yaml

 const manifests = new Map<string, string>([['installKSM', [installKSM]]);

manifests.forEach((yamlDoc: string, itemName: string) => {
      const manifests = yamlDoc.split('---').map((e) => loadYaml(e));
      new KubernetesManifest(this, `${itemName}-manifests`, {
        cluster: eksCluster,
        manifest: manifests,
        overwrite: true,
      });
    });

@pahud
Copy link
Contributor

pahud commented Jan 31, 2024

const eksCluster = aws_eks.Cluster.fromClusterAttributes(this, `eks-cluster`, {
      clusterName: Fn.importValue('ClusterName_From_Stack1'),
      clusterSecurityGroupId: Fn.importValue('ClusterSecurityGroup_From_Stack1'),
      kubectlRoleArn: Fn.importValue('ClusterKubectlArn_From_Stack1'),
      clusterEndpoint: Fn.importValue('ClusterEndpoint_From_Stack1'),
    });

Looks like you are installing helm charts on an imported/existing eks cluster.

What if you create a new eks cluster in the cdk stack and install the charts on it. Will it work?

@srispl
Copy link
Author

srispl commented Jan 31, 2024

I tried new and existing cluster. It failed. It works when I run everything on same stack. But, I want to do in 2 different stacks.

@github-actions github-actions bot removed closing-soon This issue will automatically close in 4 days unless further comments are made. response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. labels Feb 1, 2024
@pahud
Copy link
Contributor

pahud commented Feb 1, 2024

OK I think you will need to reuse the kubectlLambdaRole created from the cluster stack.

check out this sample:

export class EksClusterStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props)

    const cluster = new eks.Cluster(this, 'demo-eks-cluster', {
      vpc: getDefaultVpc(this),
      defaultCapacity: 1,
      version: eks.KubernetesVersion.V1_28,
      kubectlLayer: new KubectlV28Layer(this, 'kubectlLayer'),
    });

    const kubectlProvider = cluster.stack.node.tryFindChild('@aws-cdk--aws-eks.KubectlProvider') as eks.KubectlProvider;

    new CfnOutput(this, 'ClusterName', { value: cluster.clusterName, exportName: 'ClusterName' });
    new CfnOutput(this, 'ClusterSecurityGroupId', { value: cluster.clusterSecurityGroupId, exportName: 'ClusterSecurityGroup' });
    new CfnOutput(this, 'KubectlRoleArn', { value: cluster.kubectlRole?.roleArn!, exportName: 'ClusterKubectlArn' });
    new CfnOutput(this, 'ClusterEndpoint', { value: cluster.clusterEndpoint, exportName: 'ClusterEndpoint' });
    new CfnOutput(this, 'KubectlProviderHandlerRole', { value: kubectlProvider.handlerRole.roleArn, exportName: 'KubectlProviderHandlerRoleArn' });
  }
}

export class EksAddOnStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props)

    const eksCluster = eks.Cluster.fromClusterAttributes(this, `eks-cluster`, {
      clusterName: Fn.importValue('ClusterName'),
      clusterSecurityGroupId: Fn.importValue('ClusterSecurityGroup'),
      kubectlRoleArn: Fn.importValue('ClusterKubectlArn'),
      clusterEndpoint: Fn.importValue('ClusterEndpoint'),
      kubectlLambdaRole: iam.Role.fromRoleArn(this, 'kubectlLambdaRole', Fn.importValue('KubectlProviderHandlerRoleArn')),
    });

    //Link for Yaml: https://github.com/kubernetes/kube-state-metrics/blob/main/examples/standard/service-account.yaml
    const parsedYaml = yaml.load(fs.readFileSync(path.join(__dirname + '/serviceaccount-ksm.yaml'), 'utf8')) as Map<string, any>;
  

    new eks.KubernetesManifest(this, 'installKSM', {
      cluster: eksCluster,
      manifest: [parsedYaml],
      overwrite: true,
      skipValidation: true,
    });

  }
}

This works for me.

Read #25835 (comment) for more details.

@pahud pahud added the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Feb 1, 2024
@trondhindenes
Copy link

afaik it's important to provide the kubectl layer in the imported cluster as well

@github-actions github-actions bot removed the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Feb 2, 2024
@srispl
Copy link
Author

srispl commented Feb 2, 2024

@trondhindenes , Its already added since the lambdarole is pre-created while creating cluster.

@pahud ,Tried as suggested and the solution is working. Thanks for quick turn around! Good to close.

@srispl srispl closed this as completed Feb 2, 2024
Copy link

github-actions bot commented Feb 2, 2024

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

@bhatrohit
Copy link

OK I think you will need to reuse the kubectlLambdaRole created from the cluster stack.

check out this sample:

export class EksClusterStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props)

    const cluster = new eks.Cluster(this, 'demo-eks-cluster', {
      vpc: getDefaultVpc(this),
      defaultCapacity: 1,
      version: eks.KubernetesVersion.V1_28,
      kubectlLayer: new KubectlV28Layer(this, 'kubectlLayer'),
    });

    const kubectlProvider = cluster.stack.node.tryFindChild('@aws-cdk--aws-eks.KubectlProvider') as eks.KubectlProvider;

    new CfnOutput(this, 'ClusterName', { value: cluster.clusterName, exportName: 'ClusterName' });
    new CfnOutput(this, 'ClusterSecurityGroupId', { value: cluster.clusterSecurityGroupId, exportName: 'ClusterSecurityGroup' });
    new CfnOutput(this, 'KubectlRoleArn', { value: cluster.kubectlRole?.roleArn!, exportName: 'ClusterKubectlArn' });
    new CfnOutput(this, 'ClusterEndpoint', { value: cluster.clusterEndpoint, exportName: 'ClusterEndpoint' });
    new CfnOutput(this, 'KubectlProviderHandlerRole', { value: kubectlProvider.handlerRole.roleArn, exportName: 'KubectlProviderHandlerRoleArn' });
  }
}

export class EksAddOnStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props)

    const eksCluster = eks.Cluster.fromClusterAttributes(this, `eks-cluster`, {
      clusterName: Fn.importValue('ClusterName'),
      clusterSecurityGroupId: Fn.importValue('ClusterSecurityGroup'),
      kubectlRoleArn: Fn.importValue('ClusterKubectlArn'),
      clusterEndpoint: Fn.importValue('ClusterEndpoint'),
      kubectlLambdaRole: iam.Role.fromRoleArn(this, 'kubectlLambdaRole', Fn.importValue('KubectlProviderHandlerRoleArn')),
    });

    //Link for Yaml: https://github.com/kubernetes/kube-state-metrics/blob/main/examples/standard/service-account.yaml
    const parsedYaml = yaml.load(fs.readFileSync(path.join(__dirname + '/serviceaccount-ksm.yaml'), 'utf8')) as Map<string, any>;
  

    new eks.KubernetesManifest(this, 'installKSM', {
      cluster: eksCluster,
      manifest: [parsedYaml],
      overwrite: true,
      skipValidation: true,
    });

  }
}

This works for me.

Read #25835 (comment) for more details.

Tried this but still it threw same error. @pahud @srispl

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-eks Related to Amazon Elastic Kubernetes Service bug This issue is a bug. p2
Projects
None yet
Development

No branches or pull requests

4 participants