为了启用Security Group for Pod功能,EKS 集群在控制平面上运行了两个新组件:
为了实现此功能,每个worker node将与一个主干网络接口(trunk ENI)
和多个分支网络接口(branch ENI)
相关联。trunk ENI
充当连接到实例的标准网络接口。然后,VPC resource controller
将branch ENI
关联到trunk ENI
。这增加了每个实例可以连接的网络接口的数量。由于安全组是通过ENI指定的,因此我们现在可以将需要特定安全组的 Pod 调度到分配给工作节点的这些附加网络接口上。
首先,需要将新的 IAM 策略附加到nodegroup Role,以允许 EC2 实例管理ENI、私有 IP 地址以及与实例的绑定和分离。
添加AmazonEKSVPCResourceController
policy到节点组的Role:
aws iam attach-role-policy \
--policy-arn arn:aws:iam::aws:policy/AmazonEKSVPCResourceController \
--role-name ${ROLE_NAME}
aws-node 中将变量ENABLE_POD_ENI
设置为 true,启用 CNI 插件来管理 pod 的网络接口。
kubectl -n kube-system set env daemonset aws-node ENABLE_POD_ENI=true
# let's wait for the rolling update of the daemonset
kubectl -n kube-system rollout status ds aws-node
一旦此设置设置为 true,对于集群中的每个节点,插件都会添加一个带有vpc.amazonaws.com/has-trunk-attached=true
的标签。 VPC resource controller
创建并附加一个称为trunk ENI
的特殊网络接口,其描述为 aws-k8s-trunk-eni
。
kubectl get nodes --show-labels
创建集群时还会自动添加新的CRD, 集群管理员可以通过SecurityGroupPolicy
CRD 指定将哪些安全组分配给 pod 。在命名空间内,可以根据 pod 标签或与 pod 关联的service account的标签来选择 pod。对于任何匹配的 Pod,还可以定义要应用的安全组 ID。
验证 CRD 是否存在:
kubectl get crd securitygrouppolicies.vpcresources.k8s.aws
Webhook会监视SecurityGroupPolicy
CRD的任何更改,以便将 Pod 调度到具有可用branch ENI
的节点上。一旦 Pod 被调度,resource controller
将创建一个branch ENI
并将其附加到trunk ENI
。成功附加后,控制器会向 pod 对象添加一个带有branch ENI
详细信息的注释。
现在让我们创建我们的策略。
cat << EoF > sg-policy.yaml
apiVersion: vpcresources.k8s.aws/v1beta1
kind: SecurityGroupPolicy
metadata:
name: allow-rds-access
spec:
podSelector:
matchLabels:
app: green-pod
securityGroups:
groupIds:
- ${POD_SG}
EoF
如果 pod 具有标签app: green-pod
,则会将安全组附加到它。
部署在特定的namespace
:
kubectl create namespace sg-per-pod
kubectl -n sg-per-pod apply -f sg-policy.yaml
kubectl -n sg-per-pod describe securitygrouppolicy
接下来创建一个python应用,该应用连接到我们的 Postgres 数据库并在成功时输出信息,否则打印错误消息:
cat << EoF > postgres_test_iam.py
import os
import psycopg2
HOST = os.getenv('HOST')
PORT = "5432"
USER = os.getenv('USER')
REGION = "us-west-2"
DBNAME = os.getenv('DATABASE')
DBPASS = os.getenv('PASSWORD')
print(HOST,USER,DBNAME,DBPASS)
conn = None
try:
conn = psycopg2.connect(host=HOST, port=PORT, database=DBNAME, user=USER, password=DBPASS, connect_timeout=3)
cur = conn.cursor()
cur.execute("""SELECT version()""")
query_results = cur.fetchone()
print(query_results)
cur.close()
except Exception as e:
print("Database connection failed due to {}".format(e))
finally:
if conn is not None:
conn.close()
EoF
将以下内容另存为Dockerfile
:
cat << EoF > Dockerfile
FROM python:3.8.5-slim-buster
ADD postgres_test_iam.py /
RUN pip install psycopg2-binary
CMD [ "python", "-u", "./postgres_test_iam.py" ]
EoF
构建容器并将其推送到ECR 。使用自己的帐户 ID:
docker build -t postgres-test .
aws ecr create-repository --repository-name postgres-test-demo
aws ecr get-login-password | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.us-west-2.amazonaws.com
docker tag postgres-test ${AWS_ACCOUNT_ID}.dkr.ecr.us-west-2.amazonaws.com/postgres-test-demo:latest
docker push ${AWS_ACCOUNT_ID}.dkr.ecr.us-west-2.amazonaws.com/postgres-test-demo:latest
现在让我们部署应用程序并测试只有所需的 Pod 才能访问我们的 RDS 数据库。将以下内容保存为 postgres-test.yaml。将 HOST、DATABASE 和 USER 环境变量替换为您在上面创建 RDS 数据库的步骤中的值。
cat << EoF > postgres-test.yaml
apiVersion: v1
kind: Pod
metadata:
name: postgres-test
labels:
app: green-pod
namespace: sg-per-pod
spec:
containers:
- name: postgres-test
image: ${AWS_ACCOUNT_ID}.dkr.ecr.us-west-2.amazonaws.com/postgres-test-demo:latest
env:
- name: HOST
value: ${RDS_ENDPOINT}
- name: DATABASE
value: eksworkshop
- name: USER
value: eksworkshop
- name: PASSWORD
value: ${RDS_PASSWORD}
EoF
kubectl apply -f postgres-test.yaml
让我们检查日志以确认该 pod 确实可以访问我们的 RDS 数据库。
kubectl logs postgres-test -n sg-per-pod
现在我们来验证一下:
我们可以使用此命令在 podAnnotations
部分找到 ENI ID。
kubectl -n sg-per-pod describe pod $GREEN_POD_NAME | head -11
输出:
在控制台上,可以看到这个ENI关联到了POD_SG,并且属于branch ENI:
我们稍微修改一下 Pod 配置, 更改下它的label。该 Pod 将不再与我们的Security group policy
匹配,并且不应能够访问数据库。将以下内容保存为 postgres-test-wrong-label.yaml
:
cat << EoF > postgres-test-wrong-label.yaml
apiVersion: v1
kind: Pod
metadata:
name: postgres-test-wrong-label
labels:
app: red-pod
namespace: sg-per-pod
spec:
containers:
- name: postgres-test
image: ${AWS_ACCOUNT_ID}.dkr.ecr.us-west-2.amazonaws.com/postgres-test-demo:latest
env:
- name: HOST
value: ${RDS_ENDPOINT}
- name: DATABASE
value: eksworkshop
- name: USER
value: eksworkshop
- name: PASSWORD
value: ${RDS_PASSWORD}
EoF
kubectl apply -f postgres-test-wrong-label.yaml
kubectl logs postgres-test-wrong-label -n sg-per-pod
最后让我们验证 pod 有没有 annotation
:
kubectl describe po postgres-test-wrong-label -n sg-per-pod
输出:
在本模块中,我们在EKS上启用了Pod 的安全组功能。
我们创建了一个SecurityGroup
策略并部署了 2 个 Pod(使用相同的 docker 映像)和一个受安全组保护的 RDS 数据库。
根据这一策略,两个 Pod 中只有一个能够连接到数据库。
最后,使用 CLI 和 AWS 控制台,我们能够找到 Pod 的 ENI 并验证安全组是否已附加上去。
实验完成后记得删除数据库:
# delete database
aws rds delete-db-instance \
--db-instance-identifier rds-eksworkshop \
--delete-automated-backups \
--skip-final-snapshot
Security group for pod的具体原理参考: https://aws.amazon.com/cn/blogs/containers/introducing-security-groups-for-pods/