EKS Security #1

kimchigood·2023년 6월 1일
0

AEWS Study

목록 보기
8/11
post-thumbnail

이번 포스팅은 EKS Secutity에 대한 내용이다. 인증/인가와 IRSA 2가지 주제에 대해 다뤄보도록 하겠다.

EKS 인증/인가

항상 헷갈리는 영단어인 Authentication과 Authorizaiton이다. 먼저 인증을 통해 신원확인을 하고, 인가를 통해서 역할을 확인해서 허용된 범위 만큼의 행동을 허가해주는 개념이다.

포스팅에서는 EKS에서 인증/인가는 어떻게 진행되는 지 알아보고, 신규입사자가 왔을 때를 가정해서 인증/인가를 실습해보겠다.

먼저 환경세팅은 가시다님이 제공해주신 원클릭배포를 참조하자.

curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/eks-oneclick5.yaml

CF만 위에걸로 바꿔서 환경세팅을 하면 된다.

추가로 인증관련된 플러그인을 설치해주면 실습 때 편하게 관련정보 조회가 가능하다.
(설치를 하면 뭔가 보안 경고가 뜨긴한다.)

kubectl krew install access-matrix rbac-tool rbac-view rolesum

그럼 아래 그림을 보면서 EKS 인증/인가의 방법에 대해 알아보자.

간략히 정리하면,

  1. 먼저, 사용자가 kubectl 커맨드를 날리면 사용자PC의 kubeconfig에 세팅된 내용으로 token을 AWS STS를 통해 발급 받는다.
  2. 토큰을 EKS API서버로 보내면 다시 aws-iam-autheticator-server를 통해서 토큰에 맵핑되어 있는 UserID, User, Role에 대한 ARN을 반환받는다.
  3. aws-auth configMap에서 User, Group을 EKS API서버로 전달하고, EKS에서는 RBAC을 통해서 인가를한다.

그럼 실제 실습에서 어떻게 되는 지 확인해보자.

1. 인증

1-1 kubectl 명령

aws sts get-caller-identity --query Arn
"arn:aws:iam::<자신의 Account ID>:user/admin"

# kubeconfig 정보 확인
cat ~/.kube/config | yh
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLSasdasdasdasUZJQ0FURS0tLSsadasd0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFsadasadaQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
    server: https://2sadasdasdasasdasdas25A95.gr7.ap-northeast-2.eks.amazonaws.com
  name: myeks.ap-northeast-2.eksctl.io
contexts:
- context:
    cluster: myeks.ap-northeast-2.eksctl.io
    namespace: default
    user: pkos-admin@myeks.ap-northeast-2.eksctl.io
  name: kimchigood
current-context: kimchigood
kind: Config
preferences: {}
users:
- name: pkos-admin@myeks.ap-northeast-2.eksctl.io
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      args:
      - eks
      - get-token
      - --output
      - json
      - --cluster-name
      - myeks
      - --region
      - ap-northeast-2
      command: aws
      env:
      - name: AWS_STS_REGIONAL_ENDPOINTS
        value: regional
      interactiveMode: IfAvailable
      provideClusterInfo: false

args 의 커맨드가 토큰을 발급하는 내용이다.

aws eks get-token --cluster-name $CLUSTER_NAME | jq -r '.status.token'

1-2. Bearer Token -> EKS API Endpoint

토큰 값을 https://jwt.io/ 에서 HS384 -> HS256 순으로 바꿔보면, Payload 항목에서 디코딩 정보를 확인할 수 있다.

위의 Payload 값을 https://url-decode.com/ 에서 변환해보자.

호출 URL, Action, Version, Credential, Expires 등의 정보를 확인할 수 있다.

1-3. Token Review 요청

EKS API가 Token Review를 Webhook token authenticator에 요청을 한다.(STS getCallerIdentity 호출)
https://ap-northeast-2.console.aws.amazon.com/cloudtrail/home?region=ap-northeast-2#/events?EventName=GetCallerIdentity

(kimchigood:default) [root@myeks-bastion ~]# kubectl api-resources | grep authentication
tokenreviews                                   authentication.k8s.io/v1               false        TokenReview

STS를 통해서, 명령을 날린 유저가 IAM 유저임이 확인되면, User/Role에 대한 ARN을 반환해준다.

2. 인가

이제 k8s RBAC을 통해 인가(Authorization)을 하게된다.

1.aws-auth configMap에서 mapping 정보 확인
2. aws-auth configMap에서 IAM 사용자, 역할 ARN, k8s 오브젝트로 권한 확인 후 최 종 동작 실행
3. EKS를 생성한 IAM principal은 aws-auth 와 상관없이 kubernetes-admin Username으로 system:masters 그룹에 권한을 가짐

(kimchigood:default) [root@myeks-bastion ~]# kubectl get cm -n kube-system aws-auth -o yaml | kubectl neat | yh
apiVersion: v1
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::033359017116:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-K52WBJDFORCH
      username: system:node:{{EC2PrivateDNSName}}
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system

When you create an Amazon EKS cluster, the IAM principal that creates the cluster is automatically granted system:masters permissions in the cluster's role-based access control (RBAC) configuration in the Amazon EKS control plane. This principal doesn't appear in any visible configuration, so make sure to keep track of which principal originally created the cluster.
https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html

설명대로 최초로 EKS를 생성한 Principal이고 system:masetes 그룹을 부여 받는다. 다만 aws-auth configMap에서는 보이지 않는다.

system:masters, system:authenticated 그룹의 정보와 권한을 확인해보자.

# system:masters , system:authenticated 그룹의 정보 확인

(kimchigood:default) [root@myeks-bastion ~]# kubectl rbac-tool lookup system:masters

W0601 22:16:56.672653    5770 warnings.go:67] policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
  SUBJECT        | SUBJECT TYPE | SCOPE       | NAMESPACE | ROLE
+----------------+--------------+-------------+-----------+---------------+
  system:masters | Group        | ClusterRole |           | cluster-admin
  
(kimchigood:default) [root@myeks-bastion ~]# kubectl rbac-tool lookup system:authenticated
W0601 22:16:57.740343    5823 warnings.go:67] policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
  SUBJECT              | SUBJECT TYPE | SCOPE       | NAMESPACE | ROLE
+----------------------+--------------+-------------+-----------+----------------------------------+
  system:authenticated | Group        | ClusterRole |           | system:discovery
  system:authenticated | Group        | ClusterRole |           | eks:podsecuritypolicy:privileged
  system:authenticated | Group        | ClusterRole |           | system:public-info-viewer
  system:authenticated | Group        | ClusterRole |           | system:basic-user
  
(kimchigood:default) [root@myeks-bastion ~]# kubectl rolesum -k Group system:masters
Group: system:masters

Policies:
• [CRB] */cluster-admin ⟶  [CR] */cluster-admin
  Resource  Name  Exclude  Verbs  G L W C U P D DC
  *.*       [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔

(kimchigood:default) [root@myeks-bastion ~]# kubectl rolesum -k Group system:authenticated
W0601 22:16:59.623340    5929 warnings.go:70] policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
Group: system:authenticated

Policies:
• [CRB] */eks:podsecuritypolicy:authenticated ⟶  [CR] */eks:podsecuritypolicy:privileged

  Name            PRIV  RO-RootFS  Volumes  Caps  SELinux   RunAsUser  FSgroup   SUPgroup
  eks.privileged  True    False      [*]    [*]   RunAsAny  RunAsAny   RunAsAny  RunAsAny

• [CRB] */system:basic-user ⟶  [CR] */system:basic-user
  Resource                                       Name  Exclude  Verbs  G L W C U P D DC
  selfsubjectaccessreviews.authorization.k8s.io  [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖
  selfsubjectrulesreviews.authorization.k8s.io   [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖


• [CRB] */system:discovery ⟶  [CR] */system:discovery


• [CRB] */system:public-info-viewer ⟶  [CR] */system:public-info-viewer

cluster-admin 롤을 가지고 있으니, 모든 조회가 가능하다.이렇게 해서 사용자가 kubectl로 인증/인가를 받게 되는 것이다.

3. 실습

만약 신규 멤버가 입사해서 k8s에 권한을 주는 시나리오를 가정해보자.

3-1. 신규 유저 생성 및 권한 부여하기

# testuser 사용자 생성
aws iam create-user --user-name testuser

(kimchigood:default) [root@myeks-bastion ~]# aws iam create-user --user-name testuser

{
    "User": {
        "Path": "/",
        "UserName": "testuser",
        "UserId": "sadadasdasdasdasd",
        "Arn": "arn:aws:iam::033359017116:user/testuser",
        "CreateDate": "2023-06-01T13:21:28+00:00"
    }
}
(kimchigood:default) [root@myeks-bastion ~]#
(kimchigood:default) [root@myeks-bastion ~]# aws iam create-access-key --user-name testuser
{
    "AccessKey": {
        "UserName": "testuser",
        "AccessKeyId": "asdasdasdasdsad",
        "Status": "Active",
        "SecretAccessKey": "sadasdasdasdasdasddasdasdasd",
        "CreateDate": "2023-06-01T13:21:34+00:00"
    }
}
(kimchigood:default) [root@myeks-bastion ~]# aws iam attach-user-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess --user-name testuser

포스팅 처음 내용의 CF로 만들었다면 Bastion서버가 2개이다. 나머지 Bastion 서버 IP 확인 후 접속해보자

# IP확인
aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output table

위에서 세팅한 AccessKeyId와 SecretAccessKey 입력 후 ap-northeast-2 (서울) 리전을 세팅해준다.

# testuser 자격증명 설정
aws configure

[root@myeks-bastion-2 ~]# aws sts get-caller-identity --query Arn
"arn:aws:iam::033359017116:user/testuser"

지금은 bastion2에서 어떤 명령도 안먹힌다. testuser의 권한이 없기 때문이다.
그럼 다시 원래 bastion에서 몇가지 세팅을 해주다.

(kimchigood:default) [root@myeks-bastion ~]# eksctl create iamidentitymapping --cluster $CLUSTER_NAME --username testuser --group system:masters --arn arn:aws:iam::$ACCOUNT_ID:user/testuser

(kimchigood:default) [root@myeks-bastion ~]# kubectl get cm -n kube-system aws-auth -o yaml | kubectl neat | yh
apiVersion: v1
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::0777777777666:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-K52WBJDFORCH
      username: system:node:{{EC2PrivateDNSName}}
  mapUsers: |
    - groups:
      - system:masters
      userarn: arn:aws:iam::077777777666:user/testuser
      username: testuser
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
  
# iamidentitymapping 확인 
(kimchigood:default) [root@myeks-bastion ~]# eksctl get iamidentitymapping --cluster $CLUSTER_NAME
ARN											USERNAME				GROUPS					ACCOUNT
arn:aws:iam::777777777666:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-K52WBJDFORCH	system:node:{{EC2PrivateDNSName}}	system:bootstrappers,system:nodes
arn:aws:iam::777777777666:user/testuser							testuser				system:masters

이제 testuser도 system:masters 그룹에 속해졌다. cluster-admin 권한을 받은 것이다. 다시 bastion-2로 돌아와서,

# testuser kubeconfig 생성 >> aws eks update-kubeconfig 실행이 가능한 이유는?, 3번 설정 후 약간의 적용 시간 필요
aws eks update-kubeconfig --name $CLUSTER_NAME --user-alias testuser

# 첫번째 bastic ec2의 config와 비교해보자
cat ~/.kube/config | yh

# kubectl 사용 확인
kubectl ns default
kubectl get node -v6

몇가지 작업을 해주면 cluster-admin 롤로 kubectl 명령을 날릴 수 있다.

3-2. 권한변경하기

(testuser:N/A) [root@myeks-bastion-2 ~]# kubectl get cm -n kube-system aws-auth -o yaml | kubectl neat | yh
apiVersion: v1
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::033359017116:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-K52WBJDFORCH
      username: system:node:{{EC2PrivateDNSName}}
  mapUsers: |
    - groups:
      - system:masters
      userarn: arn:aws:iam::033359017116:user/testuser
      username: testuser
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system

aws-auth configMap을 확인해보면, 아까와 다르게 mapUsers가 들어와있다.
여기서 바로 그룹을 바꿔보자

kubectl edit cm -n kube-system aws-auth
system:masters --> system:authenticated

(testuser:N/A) [root@myeks-bastion-2 ~]# k get nodes
Error from server (Forbidden): nodes is forbidden: User "testuser" cannot list resource "nodes" in API group "" at the cluster scope

cluster-admin 권한이 먹히지 않는다!

4. 도전과제

testuser IAM User에 kubernetes 모든 리소스에 대한 읽기 전용 권한(RBAC) 부여 후 kubectl로 확인 해보기

먼저 clusterrole과 clusterrolebinding 을 생성해주고 적용한다.

# clusterrolebinding
(kimchigood:default) [root@myeks-bastion ~]# cat rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
# This cluster role binding allows anyone in the "manager" group to read secrets in any namespace.
kind: ClusterRoleBinding
metadata:
  name: reader
subjects:
- kind: Group
  name: readonly # Name is case sensitive
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: reader
  apiGroup: rbac.authorization.k8s.io
  
 
# readonly 권한을 가진 clusterrole
(kimchigood:default) [root@myeks-bastion ~]# cat readonlyrole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # "namespace" omitted since ClusterRoles are not namespaced
  name: reader
rules:
- apiGroups: ["*"]
  #
  # at the HTTP level, the name of the resource for accessing Secret
  # objects is "secrets"
  resources: ["*"]
  verbs: ["get", "watch", "list"]  

readonly 권한을 가진 clusterrole을 생성한 후, clusterrolebinding에서 readony 라는 그룹을 만들고, readonly role과 맵핑 시킨다.

맵핑확인

(kimchigood:default) [root@myeks-bastion ~]#      kubectl rbac-tool lookup readonly
W0601 23:01:49.435080    9552 warnings.go:67] policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
  SUBJECT  | SUBJECT TYPE | SCOPE       | NAMESPACE | ROLE
+----------+--------------+-------------+-----------+--------+
  readonly | Group        | ClusterRole |           | reader

다시 이제 testuser 에 권한 부여를 해준다.

kubectl edit cm -n kube-system aws-auth

apiVersion: v1
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::033359017116:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-K52WBJDFORCH
      username: system:node:{{EC2PrivateDNSName}}
  mapUsers: |
    - groups:
      - readonly
      userarn: arn:aws:iam::0777777777666:user/testuser
      username: testuser
kind: ConfigMap
metadata:
  creationTimestamp: "2023-06-01T11:48:08Z"
  name: aws-auth
  namespace: kube-system
  resourceVersion: "35686"
  uid: 685ad877-91bc-4e50-8ecf-57a23dbe7a8f

groups를 readonly로 바꿔주면 끝!

(testuser:N/A) [root@myeks-bastion-2 ~]# k get po
No resources found in default namespace.
(testuser:N/A) [root@myeks-bastion-2 ~]# k get ing
No resources found in default namespace.
(testuser:N/A) [root@myeks-bastion-2 ~]# k get all
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.100.0.1   <none>        443/TCP   147m

이번 포스팅에서는 인증/인가에 대해 알아보았다. 평소에 role, rolebinding 정도 적용해서, RBAC 을 사용하긴 했었는데, 이렇게 디테일하게 kubectl이 어떻게 인증/인가를 거치는지 알아보지는 못했다. 특히 mutatingwebhookconfigurations, validatingwebhookconfigurations 이 웹훅은 admission controller 관련된 내용이었던 것 같은데, cert-manager 트러블 슈팅할 때 애 좀 먹었던 녀석들이다.

이번 스터디도도 깊고 깊은 Kubernetes 해저체험을 하고온 느낌이다.

profile
Shout out to Kubernetes⎈

0개의 댓글