티스토리 뷰

보안

Instance MetaData Service (IMDS):

IMDS의 토큰 기반 인증 과정을 제거한다면?

$ kops edit ig nodes-ap-northeast-2a
>> 
spec: 
  # instanceMetadata: 
  # httpPutResponseHopLimit: 1 
  # httpTokens: required

$ kops update cluster --yes 

# 파드 배포 후 호스트 크레덴셜 확인 
$ k exec -it -- netshoot-pod-7757d5dd99-8pfd4 \
curl 169.254.169.254/latest/meta-data/iam/security-credentials/nodes.$KOPS_CLUSTER_NAME | jq
{
  "Code": "Success",
  "Type": "AWS-HMAC", # Hash-based message authentication code
  "AccessKeyId": "holyMoly",
  "SecretAccessKey": "It'sreal..",
  "Token": "Mamma Mia!!",

}

번외 - boto3 인증 과정

https://tech.cloud.nongshim.co.kr/2021/03/12/boto3%EA%B0%80-aws%EC%9D%98-%EC%9E%90%EA%B2%A9%EC%A6%9D%EB%AA%85credentials%EC%9D%84-%ED%99%95%EC%9D%B8%ED%95%98%EB%8A%94-%EC%88%9C%EC%84%9C-from-python/
boto3로 리소스 확인하는 코드 짤 때 참고한 글이었는데, AWS 크레덴셜에 대해 상세히 정리돼있다. 코드는 다음과 같은 우선순위로 자격증명을 확인, 인증 요청한다.

  1. boto3.client() 함수에 전달된 자격증명
  2. 환경 변수
  3. 공유 자격증명 ~/.aws/credentials
  4. AWS 구성파일 ~/.aws/config
  5. AssumeRole
  6. boto 구성파일 /etc/boto.cfg
  7. 인스턴스 메타데이터

IAM Role Service Account (IRSA)

쿠버네티스 클러스터에는 다양한 서비스가 올라갈 것이고, 이때 각 파드가 접근해야 하는 자원은 상이하기 때문에 파드 수준 인가가 필요하다. 이를 위해 EKS에서는 IRSA를 이용해 Service Account(SA)별 IAM Role을 부여할 수 있다.

 

# SA 생성 
kubectl create sa foo_sa

# SA ~ 클러스터롤 바인딩 
kubectl create clusterrolebinding foo_service_rbind --clusterrole=cluster_role --serviceaccount=foo_sa 
data "aws_eks_cluster" "foo_cluster" {
  name = "foo_cluster"
}

# 사용할 EKS 클러스터의 OIDC URL 가져오기 
data "aws_iam_openid_connect_provider" "foo_oidc" {
  url = data.aws_eks_cluster.my_eks.identity[0].oidc[0].issuer
}

data "aws_iam_policy_document" "foo_irsa_policy" {
  statement {
    actions = ["sts:AssumeRoleWithWebIdentity"]

    # 위에서 확인한 EKS OIDC와 연결
    principals {
      type        = "federated"
      identifiers = [aws_iam_openid_connect_provider.foo_oidc.arn]
    }

    condition {
      test     = "StringEquals"
      variable = trimprefix(aws_iam_openid_connect_provider.foo_oidc.url, "https://")
      values = [
        "system:serviceaccount:default:foo_sa"" 
      ]
    }
  }
}

resource "aws_iam_role" "foo_irsa" {
  name = "foo_irsa"
  assume_role_policy = data.aws_iam_policy_document.foo_irsa_policy.json
}

Security Context

파드에게 허용되는 보안 행동의 폭을 정의

  • Privileged: 파드가 할 수 있는 내용 (Pod can do X with privilege escalation)
  • Restricted: 파드가 할 수 없는 내용 (Pod can't do X due to hardening limit)
  • Baseline: 최소한으로 지정된, 파드 기본 설정

주로 다음과 컨텍스트를 설정할 수 있다.

  • runAsNonRoot: 루트가 아닌 다른 사용자로 실행돼야 함
  • Capabilities: 파드에 linux capabilities 명령어를 통해 기능 제어
  • readOnlyRootFilesystem: root FS를 RO로 할지 결정
  • seLinux: SETENFORCE!
  • privileged: 해당 파드가 privileged 모드로 실행되도록 설정, 노드의 모든 리소스에 접근 가능하도록 power overwhelming
  • runAsUser / runAsGroup: 실행유저/그룹
kubectl get pod -n kube-system -o jsonpath={.items[*].spec.containers[*].securityContext} | jq
>>
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "privileged": true
}
...


# 샘플 파드 배포
nano capabilities.yaml
>> 
apiVersion: v1
kind: Pod
metadata:
  name: sample-capabilities
spec:
  containers:
  - name: nginx-container
    image: masayaaoyama/nginx:capsh
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
kubectl create -f capabilities.yaml

# 파드 권한 확인
kubectl exec -it sample-capabilities -- cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm:    00000000a80425fb
CapEff:    00000000a80425fb

혹은

kubectl exec -it sample-capabilities -- capsh --print | grep Current
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+ep


# 파드 권한 추가
nano capabilities.yaml
>>
 securityContext:
      capabilities:
        add: ["NET_ADMIN", "SYS_TIME"]
        drop: ["AUDIT_WRITE"]

# 파드 권한 재확인
kubectl exec -it sample-capabilities -- capsh --print | grep Current
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_admin,cap_net_raw,cap_sys_chroot,cap_sys_time,cap_mknod,cap_setfcap+ep

혹은

kubectl exec -it sample-capabilities -- cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm:    000000008a0435fb
CapEff:    000000008a0435fb


sysctl

개인적으로 흥미로운 주제라고 생각한다. 파드에서 sysctl, 즉 커널 파라미터를 수정할 수 있으려면? https://kubernetes.io/ko/docs/tasks/administer-cluster/sysctl-cluster/

  • safe parameter
    • 오직 해당 파드에만 영향을 미치는 파라미터여야 한다. (다른 파드 및 노드에 영향 X)
    • 파드 리소스 제한을 벗어나는 것을 허용하지 않아야 한다.
    • safe sysctl은 기본적으로 활성화된다.
    • net.ipv4.ip_local_port_range, net.ipv4.ping_group_range 등이 존재
  • unsafe parameter
    • safe parameter를 제외한 대부분의 커널 파라미터
    • kubelet에서 사용 허용해줘야 함 kubelet --allowed-unsafe-sysctls 'kernel.msg*,net.core.somaxconn'
    • sysctl 파라미터도 네임스페이스를 따라가므로, 각 파드는 소속 네임스페이스의 sysctl을 구성할 수 있다.
    • 따라서 소속 네임스페이스가 없는 파드는 노드 레벨 -> 노드 OS에서 sysctl 구성 혹은 privilege 파드 데몬셋으로 구성

파라미터 수정으로 동작 패턴이 달라지거나 제약 사항이 변경될 수 있기 때문에, 파라미터 튜닝이 필요한 파드의 경우 taint 명령을 통해 특정 노드에 스케일링 되도록 구성하는 것을 권장한다.

도전과제 - Polaris scan 및 조치

# 레디스 배포 
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update 
helm pull bitnami/redis && tar -xzvf redis-17.9.3.tgz

kubectl create ns redis && kubectl ns redis 
helm install redis -f values.yaml . 


helm repo add fairwinds-stable https://charts.fairwinds.com/stable
helm repo update
helm pull fairwinds-stable/polaris && tar -xzvf polaris-5.7.3.tgz
nano polaris/values.yaml
>> 
  service:
    # dashboard.service.type -- Service Type
    type: LoadBalancer  # replace 'ClusterIP' with 'LoadBalancer'


kubectl create ns polaris && kubectl ns polaris
helm install polaris -f values.yaml . 

kubectl annotate service polaris-dashboard "external-dns.alpha.kubernetes.io/hostname=polaris.$KOPS_CLUSTER_NAME" -n polaris

 

 

nano redis/my-values.yaml
>
master:
  resources:
    limits: 
      cpu: 150m
      memory: 512Mi
  containerSecurityContext:
    enabled: true
    allowPrivilegeEscalation: false
    capabilities:
      drop:
        - ALL
...

kubectl ns redis 
helm upgrade redis -f values.yaml . 

 

댓글