今更ながら、Security Groups for Podsを試すメモ。
クラスターの準備
クラスターを作成する。
cat << EOF > cluster.yaml apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: sgp-cluster region: ap-northeast-1 iam: withOIDC: true managedNodeGroups: - name: sample-ng instanceType: m5.xlarge desiredCapacity: 1 privateNetworking: true EOF
作成されたVPCを取得する。
VPCID=$(aws eks describe-cluster --name sgp-cluster \ --query "cluster.resourcesVpcConfig.vpcId" \ --output text)
クラスターロールにAmazonEKSVPCResourceController
ポリシーが必要だが、eksctlが勝手につけてくれる。
RDSとセキュリティグループの準備
アプリケーション用のセキュリティグループを作成する。
RDSSG=$(aws ec2 create-security-group --group-name RDSDbAccessSG \ --description "Security group to apply to apps that need access to RDS" --vpc-id $VPCID \ --query "GroupId" --output text)
データベース用のセキュリティグループを作成する。
PostgreSQLデータベースを作成する。
IAM認証のためのポリシーを作成する。
DBインスタンスの識別子を確認する。
$ aws rds describe-db-instances --query "DBInstances[*].[DBInstanceIdentifier,DbiResourceId]" [ [ "database-1", "db-VPBTU3X2JLLL4CFDAV4JQ2KAGY" ], [ "production-database", "db-YFON4JI4SDJ53PIDJ43JCQN5DU" ], [ "staging-database", "db-B7LXVCUYPHCIP4TYSALTKMTVYA" ] ]
ポリシーを作成する。
DB_ID="db-VPBTU3X2JLLL4CFDAV4JQ2KAGY" DB_USER="db_userx" AWS_ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account) AWS_REGION=$(aws configure get region) cat << EOF > db-connect-policy.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "rds-db:connect" ], "Resource": [ "arn:aws:rds-db:${AWS_REGION}:${AWS_ACCOUNT_ID}:dbuser:${DB_ID}/${DB_USER}" ] } ] } EOF
aws iam create-policy \ --policy-name db-connect-policy \ --policy-document file://db-connect-policy.json
データベースアカウントを作成する。
Cloud9を同じVPCに立てて作業する。
データベースに接続したら以下を実行。
CREATE USER db_userx; GRANT rds_iam TO db_userx;
AWS CNIの設定
aws-nodeのバージョンを確認する。
- https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/managing-vpc-cni.html#updating-vpc-cni-add-on
$ kubectl describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2 amazon-k8s-cni-init:v1.7.5-eksbuild.1 amazon-k8s-cni:v1.7.5-eksbuild.1
1.8のマニフェストをダウンロードして適用する。
curl -o aws-k8s-cni.yaml https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/release-1.8/config/v1.8/aws-k8s-cni.yaml sed -i.bak -e 's/us-west-2/ap-northeast-1/' aws-k8s-cni.yaml kubectl apply -f aws-k8s-cni.yaml
1.9のマニフェストをダウンロードして適用する。
curl -o aws-k8s-cni.yaml https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/release-1.9/config/v1.9/aws-k8s-cni.yaml sed -i.bak -e 's/us-west-2/ap-northeast-1/' aws-k8s-cni.yaml kubectl apply -f aws-k8s-cni.yaml
バージョンを確認する。
$ kubectl describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2 amazon-k8s-cni-init:v1.9.3 amazon-k8s-cni:v1.9.3
この段階でENIは2つになっている。
環境変数を設定する。
kubectl set env daemonset -n kube-system aws-node ENABLE_POD_ENI=true
この時点でENIが増えた。
インターフェースのタイプがtrunkになっている(普通はinterface)。
(その後気がついたらaws-k8s-iのほうは消えてENIは2つだけになっていた)
initContainerの環境変数を変更する。
kubectl edit daemonset aws-node -n kube-system initContainers: - env: - name: DISABLE_TCP_EARLY_DEMUX value: "true" # falseをtrueに変更
IRSAの設定
IRSAのための設定ファイルを作成する。
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account) cat << EOF > serviceaccount.yaml apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: sgp-cluster region: ap-northeast-1 iam: withOIDC: true serviceAccounts: - metadata: name: rds-db-access namespace: default labels: {role: "backend"} attachPolicyARNs: - "arn:aws:iam::${AWS_ACCOUNT_ID}:policy/db-connect-policy" EOF
ServiceAccountを作成する。
eksctl create iamserviceaccount --config-file=serviceaccount.yaml --approve
SecurityGroupPolicyの作成
SecurityGroupPolicyに記載するセキュリティグループを確認する。
CLUSTERSG=$(aws eks describe-cluster --name sgp-cluster \ --query "cluster.resourcesVpcConfig.clusterSecurityGroupId" \ --output text) echo $CLUSTERSG $RDSSG
sg-0178264f1bec3ac39 sg-0900d843812be0618
SecurityGroupPolicyの定義ファイルを作成する。セレクターとしてServiceAccountが使われている。
cat << EOF > sgp-policy.yaml apiVersion: vpcresources.k8s.aws/v1beta1 kind: SecurityGroupPolicy metadata: name: my-sg-policy spec: serviceAccountSelector: matchLabels: role: backend securityGroups: groupIds: - $CLUSTERSG - $RDSSG EOF
SecurityGroupPolicyを作成する。
$ kubectl apply -f sgp-policy.yaml securitygrouppolicy.vpcresources.k8s.aws/my-sg-policy created
これでセキュリティグループが増える訳ではなく、新たに作られるPodがセレクターにマッチした場合に指定のセキュリティグループが割り当てられる。
セレクターとしては、PodセレクターとServiceAccountセレクターがある。
$ k explain SecurityGroupPolicy.spec KIND: SecurityGroupPolicy VERSION: vpcresources.k8s.aws/v1beta1 RESOURCE: spec <Object> DESCRIPTION: SecurityGroupPolicySpec defines the desired state of SecurityGroupPolicy FIELDS: podSelector <Object> A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects. securityGroups <Object> GroupIds contains the list of security groups that will be applied to the network interface of the pod matching the criteria. serviceAccountSelector <Object> A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.
イメージのビルド
Pythonのプログラムをpostgres_test_iam.py
として作成する。
import os import boto3 import psycopg2 HOST = os.getenv('HOST') PORT = "5432" USER = os.getenv('USER') REGION = "ap-northeast-1" DBNAME = os.getenv('DATABASE') session = boto3.Session() client = boto3.client('rds', region_name=REGION) token = client.generate_db_auth_token(DBHostname=HOST, Port=PORT, DBUsername=USER, Region=REGION) conn = None try: conn = psycopg2.connect(host=HOST, port=PORT, database=DBNAME, user=USER, password=token, 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()
Dockerfile
を作成する。
FROM python:3.8.5-slim-buster ADD postgres_test_iam.py / RUN pip install psycopg2-binary boto3 CMD [ "python", "-u", "./postgres_test_iam.py" ]
イメージをビルドしてECRにプッシュする。
docker build -t postgres-test . aws ecr create-repository --repository-name postgres-test-demo AWS_ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account) aws ecr get-login-password | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com docker tag postgres-test ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/postgres-test-demo:latest docker push ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/postgres-test-demo:latest
デプロイ
アプリをデプロイする。
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account) DB_HOST="database-1.coxgqjvvcbta.ap-northeast-1.rds.amazonaws.com" DB_NAME="postgres" DB_USER="db_userx" cat << EOF > postgres-test.yaml apiVersion: v1 kind: Pod metadata: name: postgres-test spec: serviceAccountName: rds-db-access containers: - name: postgres-test image: ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/postgres-test-demo:latest env: - name: HOST value: ${DB_HOST} - name: DATABASE value: ${DB_NAME} - name: USER value: ${DB_USER} EOF
$ kubectl apply -f postgres-test.yaml pod/postgres-test created
ログを確認する。
$ k get po NAME READY STATUS RESTARTS AGE postgres-test 0/1 Completed 1 16s $ kubectl logs postgres-test ('PostgreSQL 13.3 on aarch64-unknown-linux-gnu, compiled by gcc (GCC) 7.3.1 20180712 (Red Hat 7.3.1-6), 64-bit',)
ブランチENIが作成されている。
Podを削除する。
$ k delete pod postgres-test pod "postgres-test" deleted
ブランチENIが消えた。
今度はServiceAccountを指定しないPodを作成する。これによって、ネットワーク的にも、IAM権限的にも繋がらなくなるはず。
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account) DB_HOST="database-1.coxgqjvvcbta.ap-northeast-1.rds.amazonaws.com" DB_NAME="postgres" DB_USER="db_userx" cat << EOF > postgres-test-no-sa.yaml apiVersion: v1 kind: Pod metadata: name: postgres-test-no-sa spec: containers: - name: postgres-test image: ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/postgres-test-demo:latest env: - name: HOST value: ${DB_HOST} - name: DATABASE value: ${DB_NAME} - name: USER value: ${DB_USER} EOF
kubectl apply -f postgres-test-no-sa.yaml
確認する。
$ k get po NAME READY STATUS RESTARTS AGE postgres-test-no-sa 0/1 Completed 0 5s $ k logs postgres-test-no-sa Database connection failed due to connection to server at "database-1.coxgqjvvcbta.ap-northeast-1.rds.amazonaws.com" (192.168.113.177), port 5432 failed: timeout expired
もちろんブランチENIは作成されていない。また、このログはネットワーク的に繋がらなかったエラーであり、generate_db_auth_token
は成功しているので、このAPIはIAM権限が不要(ローカルで機械的にトークンが生成されるだけ?)と思われる。