EKS RBAC 및 AWS IAM Authenticator

이재호·2023년 5월 9일
0

EKS RBAC

rbac.authorization.k8s.io API Group을 사용합니다.

참고자료

  1. k8s RBAC

Role and ClusterRole

  • Role은 특정한 namespace에 퍼미션을 주는 역할이기 때문에 항상 namespace를 명시해 주어야 합니다.
  • ClusterRole은 non-namespaced 리소스입니다. 다음 세 가지 use-case가 있습니다.
    • 네임스페이스 리소스에 권한을 부여하고 특정 네임스페이스에서 접근 권한을 설정
    • 네임스페이스 리소스에 권한을 부여하고 전체 네임스페이스에서 접근 권한 설정 (like Pods)
      • 특정 유저가 kubectl get pods --all-namespaces 와 같은 커맨드를 실행할 때 필요한 권한
    • 클러스터 범위의 리소스 (like nodes)에 권한 부여
      apiVersion: rbac.authorization.k8s.io/v1
      kind: Role
      metadata:
        namespace: default
        name: pod-reader
      rules:
      - apiGroups: [""] # "" indicates the core API group
        resources: ["pods"]
        verbs: ["get", "watch", "list"]
      apiVersion: rbac.authorization.k8s.io/v1
      kind: ClusterRole
      metadata:
        # "namespace" omitted since ClusterRoles are not namespaced
        name: secret-reader
      rules:
      - apiGroups: [""]
        #
        # at the HTTP level, the name of the resource for accessing Secret
        # objects is "secrets"
        resources: ["secrets"]
        verbs: ["get", "watch", "list"]

RoleBinding and ClusterRoleBinding

  • RoleBinding은 Role에 작성한 권한을 users, groups, or service accounts에 부여합니다. RoleBinding은 정해진 네임스페이스에 권한을 부여합니다.
    apiVersion: rbac.authorization.k8s.io/v1
    # This role binding allows "jane" to read pods in the "default" namespace.
    # You need to already have a Role named "pod-reader" in that namespace.
    kind: RoleBinding
    metadata:
      name: read-pods
      namespace: default
    subjects:
    # You can specify more than one "subject"
    - kind: User
      name: jane # "name" is case sensitive
      apiGroup: rbac.authorization.k8s.io
    roleRef:
      # "roleRef" specifies the binding to a Role / ClusterRole
      kind: Role #this must be Role or ClusterRole
      name: pod-reader # this must match the name of the Role or ClusterRole you wish to bind to
      apiGroup: rbac.authorization.k8s.io
  • RoleBinding은 동일한 네임스페이스 내부에 있는 Role을 참조할 수 있습니다. 또한 ClusterRole을 참조할수도 있습니다. 이 경우에 ClusterRole은 특정 네임스페이스에 제한됩니다.
  • 아래 예제는 “dave”에게 ClusterRole권한을 부여했지만, “dave”는 “deployment”라는 네임스페이스 내부에 있는 Secrets만 접근 가능합니다.
    apiVersion: rbac.authorization.k8s.io/v1
    # This role binding allows "dave" to read secrets in the "development" namespace.
    # You need to already have a ClusterRole named "secret-reader".
    kind: RoleBinding
    metadata:
      name: read-secrets
      #
      # The namespace of the RoleBinding determines where the permissions are granted.
      # This only grants permissions within the "development" namespace.
      namespace: development
    subjects:
    - kind: User
      name: dave # Name is case sensitive
      apiGroup: rbac.authorization.k8s.io
    roleRef:
      kind: ClusterRole
      name: secret-reader
      apiGroup: rbac.authorization.k8s.io
  • 전체 클러스터에 있는 리소스에 대한 접근권한을 부여하려면 ClusterRoleBinding을 사용합니다.
    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: read-secrets-global
    subjects:
    - kind: Group
      name: manager # Name is case sensitive
      apiGroup: rbac.authorization.k8s.io
    roleRef:
      kind: ClusterRole
      name: secret-reader
      apiGroup: rbac.authorization.k8s.io
  • binding은 한 번 생성되면, 변경할 수 없습니다. 참조한 롤을 변경하고 싶다면 binding 오브젝트를 새롭게 생성하는것이 좋습니다.

Resources

  • 다음과 같이 resource와 subresource를 같이 명시해줄 수 있습니다.
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      namespace: default
      name: pod-and-pod-logs-reader
    rules:
    - apiGroups: [""]
      resources: ["pods", "pods/log"]
      verbs: ["get", "list"]
  • 다음과 같이 *를 사용해 모든 리소스에 모든 권한을 줄 수 있습니다.
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      namespace: default
      name: example.com-superuser  # DO NOT USE THIS ROLE, IT IS JUST AN EXAMPLE
    rules:
    - apiGroups: ["example.com"]
      resources: ["*"]
      verbs: ["*"]

Aggregated ClusterRoles

  • 다음과 같이 여러 ClusterRole을 참조하는 ClusterRole을 만들 수 있습니다.
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: monitoring
    aggregationRule:
      clusterRoleSelectors:
      - matchLabels:
          rbac.example.com/aggregate-to-monitoring: "true"
    rules: [] # The control plane automatically fills in the rules

subjects 참조

  • RoleBinding이나 ClusterRoleBinding은 롤을 subjects에 바인딩 시켜줍니다.
  • subjects에는 groups, users 혹은 serviceAccounts가 될 수 있습니다.

IAM과 RBAC가 동작하는 원리

Subjects

K8s API에 접근 가능한 방법은 UsersService Accounts 를 이용하는 방법이 있습니다.

  • Users는 certificates로 검증되는 API에 접근하는 주체입니다.
  • Service Account는 반면에 pods가 다른 resource에 접근하기 위해 사용합니다.
  • Group은 특정 유저에 대한 organization 이름이며, 유저가 생성될 때 같이 생성됩니다.

Authentication

  • K8s는 client certificates, bearer tokens, authenticating proxy 중 하나를 이용해 API요청을 인증합니다. 그 과정에서 authentication plugin을 사용합니다.
  • authentication plugin은 간단히 말해 유저에 대한 정보를 지니고 있으며, K8s에 요청이 적절한지 여부를 알려주는 역할을 합니다. 플러그인은 요청마다 다음과 같은 정보를 연결해 K8s가 인증을 할 수 있도록 도와줍니다.
    • Username: AWS에서는 이메일 같은 것을 사용합니다.
    • UID: 조금 더 유니크한 식별자입니다.
    • Groups: 논리적인 user를 묶는 단위이며 자주 사용하는 그룹에는 sytem:masters 가 있습니다.

Workflow

  • K8s API가 AWS IAM Authenticator와 연결되어 있다고 가정합니다.
  • 클라이언트가 bearer token을 사용해 API 서버에 인증을 시도하면, authenticator는 TokenReview 오브젝트에 토큰을 담아 원격 서비스에 전달합니다.
  • 원격 서비스는 인증 여부에 대한 내용을 상태에 담아서 응답해야 합니다.
    {
      "apiVersion": "authentication.k8s.io/v1",
      "kind": "TokenReview",
      "status": {
        "authenticated": true,
        "user": {
          # Required
          "username": "janedoe@example.com",
          # Optional
          "uid": "42",
          # Optional group memberships
          "groups": ["developers", "qa"],
          # Optional additional information provided by the authenticator.
          # This should not contain confidential data, as it can be recorded in logs
          # or API objects, and is made available to admission webhooks.
          "extra": {
            "extrafield1": [
              "extravalue1",
              "extravalue2"
            ]
          }
        },
        # Optional list audience-aware token authenticators can return,
        # containing the audiences from the `spec.audiences` list for which the provided token was valid.
        # If this is omitted, the token is considered to be valid to authenticate to the Kubernetes API server.
        "audiences": ["https://myserver.example.com"]
      }
    }

Authorization

  • EKS클러스터에 AWS IAM 리소스로 접근할 수 있는것도 위에서 설명했듯이 AWS IAM Authenticator가 있기 때문입니다.
  • 인증서버가 유저가 어떤 그룹에 속해있는 지 알기 위해서는 aws-auth라는 ConfigMap에 정의합니다.
  • ConfigMap은
    • K8s groups을 IAM roles에 매핑 mapRoles
    • K8s groups을 IAM users에 매핑 mapUsers
  • 그래서 결국 서버는 aws-auth로 부터 정보를 받아 TokenReview 오브젝트에 담아서 보내줍니다. 그러면 K8s API는 요청자가 어떤 그룹에 속해있고, 어떤 Role이 바인딩 되어있는지 알게되어 요청에 대한 허가를 내릴지 결정합니다.

IAM role/user to K8s user/group mapping

Authenticator가 사용할 수 있도록 매핑을 만들어 주는 방식에는 두 가지가 있습니다.

  1. 다음과 같이 K8s resource 방식으로 만들어 주는 방법

    ---
    apiVersion: iamauthenticator.k8s.aws/v1alpha1
    kind: IAMIdentityMapping
    metadata:
      name: kubernetes-admin
    spec:
      # Arn of the User or Role to be allowed to authenticate
      arn: arn:aws:iam::XXXXXXXXXXXX:user/KubernetesAdmin
      # Username that Kubernetes will see the user as, this is useful for setting
      # up allowed specific permissions for different users
      username: kubernetes-admin
      # Groups to be attached to your users/roles. For example `system:masters` to
      # create cluster admin, or `system:nodes`, `system:bootstrappers` for nodes to
      # access the API server.
      groups:
      - system:masters
  2. kube-system/aws-auth ConfigMap을 사용하는 방법

IAM과 RBAC 테스트

사전준비

  • IAM User/Role 생성

    resource "aws_iam_user" "lee-dev" {
      name = "lee-dev"
      path = "/test/"
    }
    
    resource "aws_iam_access_key" "lee-dev" {
      user = aws_iam_user.lee-dev.name
    }
    
    output "aws_iam_smtp_password_v4" {
      value     = aws_iam_access_key.lee-dev.ses_smtp_password_v4
      sensitive = true
    }
    
    resource "aws_iam_role" "eks-admin-role-test" {
      name = "eks-admin-role-test"
    
      assume_role_policy = <<EOF
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Action": "sts:AssumeRole",
          "Principal": {
            "AWS": "arn:aws:iam::123123123123:root"
          },
          "Effect": "Allow",
          "Sid": ""
        }
      ]
    }
    EOF
    }
    
    resource "aws_iam_policy" "eks-admin-role-test-policy" {
      name        = "eks-admin-role-test-policy"
      description = "eks admin role 테스트 해보기 위한 정책입니다."
    
      policy = <<EOF
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Action": [
            "eks:DescribeCluster"
          ],
          "Effect": "Allow",
          "Resource": "*"
        }
      ]
    }
    EOF
    }
    
    resource "aws_iam_role_policy_attachment" "eks-admin-role-test-attachment" {
      role       = aws_iam_role.eks-admin-role-test.name
      policy_arn = aws_iam_policy.eks-admin-role-test-policy.arn
    }
  • mapRoles 매핑

  • Role trust policy에 Principal 수정

    • Role에 사용할 IAM User를 trust policy에 추가해줍니다.

      resource "aws_iam_role" "eks-admin-role-test" {
        name = "eks-admin-role-test"
      
        assume_role_policy = <<EOF
      {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Action": "sts:AssumeRole",
            "Principal": {
              "AWS": "arn:aws:iam::123123123123:user/test/lee-dev"
            },
            "Effect": "Allow",
            "Sid": ""
          }
        ]
      }
      EOF
      }

mapRoles

  • profile 활용하는 방법
    • aws profile을 만들어 줍니다.

      [profile eks-role]
      source_profile = lee-test
      role_arn = arn:aws:iam::123123123123:role/eks-admin-role-test
      region = ap-northeast-2
      [lee-test]
      aws_access_key_id = {{access-key}}
      aws_secret_access_key = {{secret-key}}
    • 생성한 profile 사용해 K8s context 만들어 줍니다.

      aws eks update-kubeconfig --region ap-northeast-2 --name your-cluster --profile eks-role
    • kube config에서 다음과 같이 eks-role 프로파일 사용하는 컨텍스트가 생성됩니다.

      name: arn:aws:eks:ap-northeast-2:123123123123:cluster/your-cluster
        user:
          exec:
            apiVersion: client.authentication.k8s.io/v1beta1
            args:
            - --region
            - ap-northeast-2
            - eks
            - get-token
            - --cluster-name
            - your-cluster
            command: aws
            env:
            - name: AWS_PROFILE
              value: eks-role
  • assumeRole 활용하는 방법 이번에는 위에서 생성한 아무 권한이 없는 lee-test IAM User에 assume-role을 사용해서 K8s context를 생성해 보겠습니다.
    aws sts assume-role --role-arn "arn:aws:iam::123123123123:role/eks-admin-role-test" --role-session-name AWSCLI-Session --profile lee-test
    위와 같이 생성한 유저에 assume-role을 적용하면 다음과 같은 credential을 얻을 수 있습니다.
    {
        "Credentials": {
            "AccessKeyId": "abcde",
            "SecretAccessKey": "abcde+92dnq2d",
            "SessionToken": "IQoJb3JpZ2luX2VjEC0aDmFwLW5vcnRoZWFzdC0yIkYwRAIgFeiUaQzjsktwHrVJGniPulHfQTy2HVK1tHqCo54NiY8CIAz3xOJJai7oQAjn7DekiZ2yMxcmjiLEGaPliag1WGyrKpsCCFcQAhoMODY2NjExNjI0MDgwIgx2VooXhS5gesQFN0sq+AHJi/EUNb2Jh9/3PIot66GqwW3Ik3fvw37oiyr20iNjw5pjuhgRTkbhTnvhbIV9y/zQfPvoOg+1BHpccV5D0Zlta/UZ0OUAHc+22vj4gmSvtYAlk2XH5pDTHeK9PZO5Qr0xxEIkNt2gTpxtZ6e/lROJtgbGHbK6cSwFcKMlufbBw9fjdt0sMltmkNUTKxZ/DgWhjAc79ONMUIqP0XPeuJCe4tqpi5wG+V0a+4Ezh3DH1ipviA/oAuKq/E2q9Q84djCc/Ev+saDUBbLYO7gNbEJyD4B2vagYBbRskm7xX/jOqFng3Qr4O3H3CIBdMbGQaPpiCZhkShk9oDDxi8ucBjqeAZYAa1yDZRAFWMCzGrd5R/X5fPb2XY49ZnpO/0p8tvT2Y+0iCPAsD4wrAcyCq3Y+G+nNrxk8vNTtWiEDFgDcZulNB4qNoW8vM5hIrFNrjvIV9D94ass0PKz6s3G+fS+10W0tpTun2KrA+DAZA7TyCglt5viElvsDw7gpnLBPMnB0gwz5BIBgNAIJrpjBU1HikLR0e5VYBkg4zSKRcJON",
            "Expiration": "2022-12-09T06:21:53+00:00"
        },
        "AssumedRoleUser": {
            "AssumedRoleId": "AROA4TRQT7CIJ532MPACE:AWSCLI-Session",
            "Arn": "arn:aws:sts::123123123123:assumed-role/eks-admin-role-test/AWSCLI-Session"
        }
    }
    위에서 생성한 temporary security credential을 프로파일에 넣고 (혹은 AWS_ 환경변수에 넣어주어도 상관 없음) K8s 컨텍스트를 만들어 보겠습니다.
    [eks-role]
    aws_access_key_id = abcde
    aws_secret_access_key = abcde+92dnq2d
    aws_session_token = IQoJb3JpZ2luX2VjEC0aDmFwLW5vcnRoZWFzdC0yIkYwRAIgFeiUaQzjsktwHrVJGniPulHfQTy2HVK1tHqCo54NiY8CIAz3xOJJai7oQAjn7DekiZ2yMxcmjiLEGaPliag1WGyrKpsCCFcQAhoMODY2NjExNjI0MDgwIgx2VooXhS5gesQFN0sq+AHJi/EUNb2Jh9/3PIot66GqwW3Ik3fvw37oiyr20iNjw5pjuhgRTkbhTnvhbIV9y/zQfPvoOg+1BHpccV5D0Zlta/UZ0OUAHc+22vj4gmSvtYAlk2XH5pDTHeK9PZO5Qr0xxEIkNt2gTpxtZ6e/lROJtgbGHbK6cSwFcKMlufbBw9fjdt0sMltmkNUTKxZ/DgWhjAc79ONMUIqP0XPeuJCe4tqpi5wG+V0a+4Ezh3DH1ipviA/oAuKq/E2q9Q84djCc/Ev+saDUBbLYO7gNbEJyD4B2vagYBbRskm7xX/jOqFng3Qr4O3H3CIBdMbGQaPpiCZhkShk9oDDxi8ucBjqeAZYAa1yDZRAFWMCzGrd5R/X5fPb2XY49ZnpO/0p8tvT2Y+0iCPAsD4wrAcyCq3Y+G+nNrxk8vNTtWiEDFgDcZulNB4qNoW8vM5hIrFNrjvIV9D94ass0PKz6s3G+fS+10W0tpTun2KrA+DAZA7TyCglt5viElvsDw7gpnLBPMnB0gwz5BIBgNAIJrpjBU1HikLR0e5VYBkg4zSKRcJON
    [profile eks-role]
    region = ap-northeast-2
    update-kubeconfig 커맨드 실행
    aws eks update-kubeconfig --region ap-northeast-2 --name your-cluster --alias your-role --profile eks-role
  • 결론적으로 IAM Role은 aws-auth configMap에서 mapRole에 추가하여 그룹과 유저를 매칭해주는 역할을 해줄 수 있습니다.
  • 단독으로 사용하여서는 K8s 클러스터에 접근할 수 있는 토큰을 얻기는 어렵습니다. 다음과 같은 방법으로 활용할 수 있습니다.
    1. AWS profile에 role arn을 추가하고 eks update-kubeconfig 사용
    2. assumeRole로 임시 credential을 만들어 profile에 추가하고 eks update-kubeconfig 사용
  • 위 방법으로 K8s 접근 토큰을 얻어 접속하게 되면, 해당 IAM/role에 매핑된 Groups에 binding 되어 있는 ClusterRole / Role 에 부여된 권한을 사용할 수 있게 됩니다.

Readonly best practice

리서치한 내용을 바탕으로 Readonly 계정을 관리하는 best practice를 작성해 보았습니다.

IAM User/Role 생성

resource "aws_iam_user" "lee-dev" {
  name = "lee-dev"
  path = "/test/"
}

resource "aws_iam_access_key" "lee-dev" {
  user = aws_iam_user.lee-dev.name
}

output "aws_iam_smtp_password_v4" {
  value     = aws_iam_access_key.lee-dev.ses_smtp_password_v4
  sensitive = true
}

resource "aws_iam_role" "eks-admin-role-test" {
  name = "eks-admin-role-test"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "AWS": "arn:aws:iam::123123123123:root"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_policy" "eks-admin-role-test-policy" {
  name        = "eks-admin-role-test-policy"
  description = "eks admin role 테스트 해보기 위한 정책입니다."

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "eks:DescribeCluster"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "eks-admin-role-test-attachment" {
  role       = aws_iam_role.eks-admin-role-test.name
  policy_arn = aws_iam_policy.eks-admin-role-test-policy.arn
}

Role trust policy에 Principal 수정

  • Role에 사용할 IAM User를 trust policy에 추가해줍니다. 혹은 *를 사용해 다수의 유저에게 권한을 부여할 수도 있습니다.
resource "aws_iam_role" "eks-admin-role-test" {
  name = "eks-admin-role-test"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "AWS": "arn:aws:iam::123123123123:user/test/lee-dev"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

Role과 RoleBinding 생성

resource "kubernetes_cluster_role" "eks-readonly-role-test" {
  metadata {
    name = "eks-readonly-role-test"
  }

  rule {
    api_groups = ["*"]
    resources  = ["*"]
    verbs      = ["get", "list", "watch"]
  }
}

resource "kubernetes_cluster_role_binding" "eks-readonly-role-test" {
  metadata {
    name = "eks-readonly-role-test"
  }
  role_ref {
    api_group = "rbac.authorization.k8s.io"
    kind      = "ClusterRole"
    name      = "cluster-admin"
  }
  subject {
    kind      = "Group"
    name      = "eks-readonly-role-test"
    api_group = "rbac.authorization.k8s.io"
  }
}

mapRoles 매핑

Role을 적절하게 AWS credential에 부여하는 방법을 사용해 K8s Context 생성

적용 후 Role로 생성한 credential로 접속한 유저는 readonly만 가능합니다.

profile
복세편살

0개의 댓글