Accessing an Amazon EKS Kubernetes cluster from an AWS Lambda function with Go.
This is a Go Lambda function that interacts with an EKS cluster. In particular, the Lambda function creates a new deployment in an existing EKS cluster. The deployment consists of two replicas of an NGINX pod.
The Lambda function code uses the Kubernetes Go client library (client-go) to access the Kubernetes cluster (note that with a client library, you can do everything that you can do with kubectl). Read below why Go is used.
Accessing an EKS cluster requires some additional steps which are summarised in the Requirements section. You have to work through this section before deploying the Lambda function.
Since we use Go, the Lambda function code has to be compiled before deployment (what's deployed is the resulting statically linked binary file). Furthermore, compilation must target the Lambda execution environment platform (which is Linux). Compiling for any target platform can be achieved with Go cross compiling.
Use the following command for compilation (already defined in build-handler.sh):
GOOS=linux go build handler.goThe Lambda application is defined with the AWS Serverless Application Model (SAM) in the file template.yml.
Deploy the Lambda application with the following SAM CLI commands (already defined in deploy.sh):
sam package --template-file template.yml --output-template-file package.yml --s3 <SOME_BUCKET>
sam deploy --template-file package.yml --capabilities CAPABILITY_IAM --stack-name lambda-eks-testSAM allows to execute Lambda functions locally (they will run in a Docker container that simulates the Lambda execution environment).
To run the function locally, use the following SAM CLI command (already defined in local.sh):
sam local invoke --no-event LambdaEksTestFunctionNote that this also requires all the requirements from the next section. The IAM identity that is used in this case is the one configured on your local machine (the one returned by aws sts get-caller-identity).
Authentication against an EKS cluster requires the [aws-iam-authenticator](https://github.com/kubernetes-sigs/aws-iam-authenticator) executable (this executable is referenced in the kubeconfig file, see next section). That means, aws-iam-authenticator must be present in the Lambda execution environment.
-
Install
aws-iam-authenticatoron your machine, if you haven't already:go get -u github.com/kubernetes-sigs/aws-iam-authenticator/cmd/aws-iam-authenticator
-
Build
aws-iam-authenticatorfor the Lambda execution enviroment:GOOS=linux go build github.com/kubernetes-sigs/aws-iam-authenticator/cmd/aws-iam-authenticator
The created aws-iam-authenticator binary must be saved in the project root directory so that it is included in the Lambda function ZIP file.
To make requests to a Kubernetes cluster, a kubeconfig file is required. The kubeconfig file must be present in the execution environment of the Lambda function. The kubeconfig file is read by the Go client library.
-
Create a kubeconfig file for the target cluster:
aws eks update-kubeconfig --name <ClusterName> --kubeconfig kubeconfig
-
In the created file, replace
aws-iam-authenticatorwith./aws-iam-authenticator(in theusers.user.exec.commandfield)- This is necessary to make the command executable in the Lambda execution enviroment, because in the Lambda execution environment
aws-iam-authenticatoris not in thePATH
- This is necessary to make the command executable in the Lambda execution enviroment, because in the Lambda execution environment
The created kubeconfig file must be saved in the project root directory so that is included in the Lambda function ZIP file.
The Go client library uses the IAM role assigned to the Lambda function to authenticate to the EKS cluster (the determination of the role and encoding in the bearer authentication token is done by the aws-iam-authenticator).
To make the cluster recognise and authenticate requests coming from the Lambda function, we must add this role to the aws-auth ConfigMap of the cluster.
-
Open the
aws-authConfigMap for editing:kubectl edit -n kube-system configmap/aws-auth
-
Append the following data to the already existing value of the
data.mapRolesfield in the ConfigMap (replace<LambdaRoleARN>with the ARN of the execution role of the Lambda function):mapRoles: | - rolearn: <LambdaRoleARN> username: lambda
-
Save the file (the changes are automatically applied to the cluster)
At this point, requests from the Lambda function to the cluster get authenticated, but the specific Kubernetes action requested by our program (create deployments) does not yet get authorised.
In the authentication step, we map requests from the Lambda function to an internal Kubernetes user called lambda. This is a custom user that we just invented and it does not have any permissions by default.
We have to assign permission to create deployments to the lambda user.
EKS uses the native Kubernetes RBAC authorisation system. In RBAC, permissions are defined as Role objects and assigned to users with RoleBinding objects. Role and RoleBinding are ordinary Kubernetes API resource objects, and you can define them like other API resource objects.
-
Define the following Role and RoleBinding (already exists in file permissions.yml):
kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: lambda-create-deployments namespace: default rules: - apiGroups: ["apps"] resources: ["deployments"] verbs: ["create"] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: lambda-create-deployments namespace: default subjects: - kind: User name: lambda apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: lambda-create-deployments apiGroup: rbac.authorization.k8s.io
-
Create the Role and RoleBinding:
kubectl apply -f permissions.yml
Now, requests from the Lambda function to create a deployment will be authorised. But any other request, for example, to list deployments, will be denied by the authorisation system. That's exactly what we want to guarantee the principle of least privilege.
If you extend the Lambda function to do other Kubernetes actions, you have to adapt the RBAC permissions through Role and RoleBinding objects accordingly.
The client-side part of the EKS authentication mechanism makes use of a Kubernetes feature called exec provider or credentials plugin, which is defined here. The feature defines an exec section in the kubeconfig file. This section defines an external command that returns the credentials to authenticate to the cluster (in the case of EKS this external command is aws-iam-authenticator and the returned credential is a bearer token that encodes an IAM identity).
Support for this feature must be implemented in the different Kubernetes client libraries (because the client libraries read the kubeconfig file). However, at the moment, most client libraries to not yet support this feature. The Go client library does, and this is the reason that we use Go and a Go Lambda function.