EKS 的 IRSA(IAM Roles for Service Accounts)
是一种机制,允许在 EKS集群中运行的 Kubernetes Pods 安全地访问 AWS 服务,而不需要管理和分发长期的 AWS 凭证(如访问密钥)。通过 IRSA可以将 AWS IAM 角色与 Kubernetes 的 Service Account 关联,从而为 Pod 提供临时的 AWS 凭证。
IRSA 解决了在 Kubernetes 环境中安全地向 Pod 提供 AWS 服务访问权限的问题。传统上,应用程序通过配置文件、环境变量等获取 AWS 凭证,这种方法存在安全风险。而 IRSA 提供了一种基于 IAM 角色和临时凭证的安全机制,使 Pod 可以在执行特定操作时自动获取到所需的权限。
IRSA 的核心原理是将 Kubernetes Service Account 和 AWS IAM Role 关联在一起,允许 Pod 使用该 IAM Role 来获取临时的 AWS 凭证,进而访问特定的 AWS 服务。
主要步骤如下:
eks.amazonaws.com
,并使用 OIDC 提供者来确定具体的 Service Account。[Kubernetes Service Account] ----> [OIDC Provider (EKS)] ----> [IAM Role] ----> [Temporary AWS Credentials]
\
`--> [Kubernetes Pod]
Pod 使用的 Service Account 通过 OIDC 提供者向 IAM 请求角色的临时凭证,然后 Pod 使用这些凭证访问 AWS 资源。
main.go:
package main
import (
"fmt"
"log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
)
func main() {
// Create a new session using the default credentials and region from environment variables or AWS config file
sess, err := session.NewSession(&aws.Config{
Region: aws.String("us-west-2"), // specify your region
})
if err != nil {
log.Fatalf("Unable to create session: %v", err)
}
// Create a new S3 service client
svc := s3.New(sess)
// Call the ListBuckets API
result, err := svc.ListBuckets(nil)
if err != nil {
log.Fatalf("Unable to list buckets: %v", err)
}
// Print the names of the buckets
fmt.Println("Buckets:")
for _, bucket := range result.Buckets {
fmt.Printf("* %s created on %s\n", aws.StringValue(bucket.Name), bucket.CreationDate)
}
}
go.mod:
module describe-s3
go 1.15
require (
github.com/aws/aws-sdk-go v1.53.12 // indirect
github.com/pkg/errors v0.9.1 // indirect
)
Dockerfile:
# Use the official Golang image as a build stage
FROM golang:1.15-alpine AS builder
# Set the Current Working Directory inside the container
WORKDIR /app
# Copy go mod and sum files
COPY go.mod go.sum ./
# Download all dependencies. Dependencies will be cached if the go.mod and go.sum files are not changed
RUN go mod download
# Copy the source from the current directory to the Working Directory inside the container
COPY . .
# Build the Go app
RUN go build -o main .
# Start a new stage from scratch
FROM alpine:latest
# Set the Current Working Directory inside the container
WORKDIR /root/
# Copy the Pre-built binary file from the previous stage
COPY --from=builder /app/main .
# Command to run the executable
CMD ["./main"]
上传到ECR:
docker build -t my-go-app .
docker tag my-go-app:latest aws_account_id.dkr.ecr.us-west-2.amazonaws.com/my-go-app:latest
aws ecr create-repository --repository-name my-go-app --region us-west-2
aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin aws_account_id.dkr.ecr.us-west-2.amazonaws.com
docker push aws_account_id.dkr.ecr.us-west-2.amazonaws.com/my-go-app:latest
eksctl create iamserviceaccount \
--cluster=eks-cilium \
--namespace=default \
--name=s3-irsa \
--attach-policy-arn=arn:aws:iam::aws:policy/AmazonS3FullAccess \
--override-existing-serviceaccounts \
--approve
如果查看创建出来的Role的trust relationship, 会发现格式如下,在Condition字段里限制了具体的service account:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<account_id>:oidc-provider/<eks_cluster_oidc>"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"<eks_cluster_oidc>:sub": "system:serviceaccount:<namespace>:<service_account>"
}
}
}
]
}
S3.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: s3-access-deployment
spec:
replicas: 1
selector:
matchLabels:
app: s3-access
template:
metadata:
labels:
app: s3-access
spec:
serviceAccountName: s3-irsa
containers:
- name: s3-access-container
image: 145197526627.dkr.ecr.us-west-2.amazonaws.com/my-go-app:latest
ports:
- containerPort: 8080
env:
- name: AWS_REGION
value: "us-west-2" # Replace with your region
kubectl apply -f s3.yaml
查看pod日志:
项目结构有两个文件,一个pom.xml
,另一个是App.java
文件。
pom.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>list-s3-v2</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.21.36</version> <!-- Replace with the latest version -->
</dependency>
</dependencies>
</project>
App.java
:
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.ListBucketsResponse;
import software.amazon.awssdk.services.s3.model.S3Exception;
public class ListS3 {
public static void main(String[] args) {
// Set your AWS region
Region region = Region.US_EAST_1; // Change to your desired region
// Create an S3 client
S3Client s3 = S3Client.builder()
.region(region)
.build();
try {
// List S3 buckets
ListBucketsResponse response = s3.listBuckets();
// Print the bucket names
System.out.println("S3 Buckets:");
response.buckets().forEach(bucket -> System.out.println(bucket.name()));
} catch (S3Exception e) {
System.err.println(e.awsErrorDetails().errorMessage());
System.exit(1);
}
}
}
cat >my-deployment.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
serviceAccountName: s3-irsa
containers:
- name: my-app
image: maven
command: ["sleep", "100000000"]
EOF
kubectl apply -f my-deployment.yaml
这里直接使用了maven镜像,里面安装了最新版本的java,并集成了maven工具。
部署好pod后,登录到上面:
kubectl exec -it xxxx bash
然后参考https://msk.kpingfan.com/09.kafka-java-development/02.cloud9-development/ 的内容进行maven构建,注意最后在执行打包好的文件时:
mvn exec:java -Dexec.mainClass="com.kpingfan.kafka.App"
要改成
mvn exec:java -Dexec.mainClass="App"