Search

Spire Spiffe Integration

CLUSTER_NAME="<EKS_CLUSTER_NAME>" CLUSTER_OIDC=$(aws eks describe-cluster --name $CLUSTER_NAME --query "cluster.identity.oidc.issuer" --output text | cut -c 9-100) ACCOUNT=$(aws sts get-caller-identity --query "Account" --output text)
Shell
복사
cat << EOF > aws-ebs-csi-driver-trust-policy.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/OIDC" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "OIDC:aud": "sts.amazonaws.com" } } } ] } EOF
Shell
복사
sed -i "s|ACCOUNT_ID|$ACCOUNT|g" aws-ebs-csi-driver-trust-policy.json sed -i "s|OIDC|$CLUSTER_OIDC|g" aws-ebs-csi-driver-trust-policy.json
Shell
복사
aws iam create-role --role-name AmazonEKS_EBS_CSI_DriverRole --assume-role-policy-document file:///home/ec2-user/aws-ebs-csi-driver-trust-policy.json
Shell
복사
aws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy --role-name AmazonEKS_EBS_CSI_DriverRole
Shell
복사
eksctl create addon --name aws-ebs-csi-driver --cluster $CLUSTER_NAME --service-account-role-arn arn:aws:iam::$ACCOUNT:role/AmazonEKS_EBS_CSI_DriverRole --force
Shell
복사
kubectl patch storageclass gp2 -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
Shell
복사
helm으로 spire-crds, spire를 설치
helm upgrade --install -n spire-server spire-crds spire-crds --repo https://spiffe.github.io/helm-charts-hardened/ --create-namespace helm upgrade --install -n spire-server spire spire --repo https://spiffe.github.io/helm-charts-hardened/ --wait --set global.spire.trustDomain="example.org"
Shell
복사
SPIRE에 workload를 등록하는 방법은 두 가지가 있다
ClusterSPIFFEID CR을 활용해 필터링 된 pod를 자동으로 등록하거나,
spire-server pod에 접근하여 spire-server 명령어로 수동 등록할 수 있다.
자동으로 등록하려면 Istio gateway, Istio sidecar를 위한 ClusterSPIFFEID 리소스를 각각 생성한다.
istio-ingressgateway-reg-cluster-spiffeid.yaml
apiVersion: spire.spiffe.io/v1alpha1 kind: ClusterSPIFFEID metadata: name: istio-ingressgateway-reg spec: spiffeIDTemplate: "spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}" workloadSelectorTemplates: - "k8s:ns:istio-system" - "k8s:sa:istio-ingressgateway-service-account"
YAML
복사
istio-sidecar-reg-cluster-spiffeid.yaml
apiVersion: spire.spiffe.io/v1alpha1 kind: ClusterSPIFFEID metadata: name: istio-sidecar-reg spec: spiffeIDTemplate: "spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}" podSelector: matchLabels: spiffe.io/spire-managed-identity: "true" workloadSelectorTemplates: - "k8s:ns:default"
YAML
복사
수동으로 등록하려면, spire-server pod에 exec로 접근하여 /opt/spire/bin/spire-server 명령어를 사용한다
SPIRE_SERVER_POD=$(kubectl get pod -l statefulset.kubernetes.io/pod-name=spire-server-0 -n spire-server -o jsonpath="{.items[0].metadata.name}")
Shell
복사
Istio 공식 문서에서는 spire ns를 사용하고 socketPath를 /run/spire/sockets/server.sock으로 지정하는데,
namespace가 존재하지 않는다는 것과 실행 시 폴더를 찾을 수 없다는 문제가 있었다.
따라서 spire-server로 ns를 변경하고,
kubectl exec -n spire-server "$SPIRE_SERVER_POD" -- \ /opt/spire/bin/spire-server entry create \ -spiffeID spiffe://example.org/ns/istio-system/sa/istio-ingressgateway-service-account \ -parentID spiffe://example.org/ns/spire/sa/spire-agent \ -selector k8s:sa:istio-ingressgateway-service-account \ -selector k8s:ns:istio-system
Shell
복사

Istio

Istio 설치 전, Gateway API crds를 설치
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml
Shell
복사
spiffe/spire 설정을 위한 IstioOperator manifest를 작성한다
문서 예제를 그대로 사용
Code
istioctl로 설치
curl -L https://istio.io/downloadIstio | sh - cd istio-1.27.0 export PATH=$PWD/bin:$PATH istioctl version
Shell
복사
istioctl install --skip-confirmation -f ./istio.yaml
Shell
복사
$ kubectl get po -n istio-system NAME READY STATUS RESTARTS AGE istio-ingressgateway-5f447d94f8-h6qdd 1/1 Running 0 56s istiod-566955fbd5-5sjdx 1/1 Running 0 70s
Shell
복사

Application

테스트를 위해 간단하게 서로 통신이 가능한 애플리케이션을 구성해보았다
GET / 요청 시, 내부에서 APP_URL env 주소로 GET /api 요청을 보낸다
Code
애플리케이션을 Docker 컨테이너로 registry에 build/push 한다
이제 k8s에 istio 및 spire를 연결하여 애플리케이션을 배포하기 위해 manifest를 작성한다
inject annotation으로 sidecar와 spire을 설정하고, spiffe CSI volume을 설정해야 한다
conn-a, conn-b 와 같이 2개를 배포했다
apiVersion: v1 kind: Pod metadata: name: conn-a labels: app: conn-a # Injects custom sidecar template annotations: inject.istio.io/templates: "sidecar,spire" spec: containers: - name: app image: ghcr.io/dya-only/conn:latest ports: - containerPort: 8080 env: # ENV for requests to other API - name: APP_URL value: conn-b volumes: # CSI volume - name: workload-socket csi: driver: "csi.spiffe.io" readOnly: true --- apiVersion: v1 kind: Service metadata: name: conn-a spec: selector: app: conn-a ports: - protocol: TCP port: 80 targetPort: 8080
YAML
복사
apiVersion: v1 kind: Pod metadata: name: conn-b labels: app: conn-b # Injects custom sidecar template annotations: inject.istio.io/templates: "sidecar,spire" spec: containers: - name: app image: ghcr.io/dya-only/conn:latest ports: - containerPort: 8080 env: # ENV for requests to other API - name: APP_URL value: conn-a volumes: # CSI volume - name: workload-socket csi: driver: "csi.spiffe.io" readOnly: true --- apiVersion: v1 kind: Service metadata: name: conn-b spec: selector: app: conn-b ports: - protocol: TCP port: 80 targetPort: 8080
YAML
복사
이제 conn-a, conn-b를 모두 istioctl을 사용해서 istio-proxy 등을 주입해 실행한다
istioctl kube-inject --filename ./conn-a.yaml | kubectl apply -f - istioctl kube-inject --filename ./conn-b.yaml | kubectl apply -f -
Shell
복사
Pod가 istio-proxy 컨테이너와 함께 실행되는 것을 확인할 수 있다

Check

먼저 아래 명령어로 workload를 위한 identity들이 생성된 것을 확인한다
SPIRE_SERVER_POD=$(kubectl get pod -l statefulset.kubernetes.io/pod-name=spire-server-0 -n spire-server -o jsonpath="{.items[0].metadata.name}") kubectl exec -t "$SPIRE_SERVER_POD" -n spire-server -c spire-server -- ./bin/spire-server entry show
Shell
복사
Ingress-gateway가 여전히 Running 상태인지 확인한다
$ kubectl get po -n istio-system NAME READY STATUS RESTARTS AGE istio-ingressgateway-5f447d94f8-h6qdd 1/1 Running 0 3h19m istiod-566955fbd5-5sjdx 1/1 Running 0 3h20m
Shell
복사
conn app pod가 가지고 있는 TLS cert를 가져와서 SPIRE로 인증되었는지 확인한다.
APP_POD=$(kubectl get pod -l app=conn-a -o jsonpath="{.items[0].metadata.name}") istioctl proxy-config secret "$APP_POD" -o json | jq -r '.dynamicActiveSecrets[0].secret.tlsCertificate.certificateChain.inlineBytes' | base64 --decode > chain.pem
Shell
복사
$ openssl x509 -in chain.pem -text | grep SPIRE Subject: C=US, O=SPIRE
Shell
복사
애플리케이션 컨테이너에 접근해서 다른 서비스로 HTTP 요청이 가능한지 확인한다.
/app # curl conn-b/api app page
Shell
복사

Kiali + Prometheus

Kiali와 Prometheus Istio addon을 설치해서 Graph로 확인해볼 수도 있다
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/addons/kiali.yaml kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/addons/prometheus.yaml
Shell
복사
설치 후 conn-a → conn-b 요청을 몇 번 시도한 후, Kiali dashboard에 접근하여 Traffic Graph를 확인하자
kubectl exec conn-a -- curl http://conn-b/api
Shell
복사
kubectl port-forward svc/kiali 20001:20001 -n istio-system --address 0.0.0.0
Shell
복사
kubectl patch svc kiali -n istio-system -p '{"spec": {"type": "LoadBalancer"}}'
Shell
복사
conn-a Pod에서 conn-b Service, Pod로 접근할 때 mTLS가 활성화되어 있음을 확인할 수 있다