[Week6] AWS EKS Security (2/2)

오태경·2024년 4월 13일

본 게시물은 CloudNet@팀 Gasida(서종호) 님이 진행하시는
AWS EKS Workshop Study 내용을 기반으로 작성되었습니다.

EKS IRSA & Pod Identitty

동작 개요

k8s파드 → AWS 서비스 사용 시 ⇒ AWS STS/IAM ↔ IAM OIDC Identity Provider(EKS IdP) 인증/인가

필요 지식

Service Account Token Volume Projection, Admission Control, JWT(JSON Web Token), OIDC

IRSA

  • AWS SDK는 AWS_ROLE_ARN 및 AWS_WEB_IDENTITY_TOKEN_FILE 이름의 환경변수를 읽어들여 Web Identity 토큰으로 AssumeRoleWithWebIdentify를 호출함으로써 Assume Role을 시도하여 임시 자격 증명을 획득하고, 특정 IAM Role 역할을 사용할 수 있게 됨.
  • 이때 Assume Role 동작을 위한 인증은 AWS가 아닌 외부 Web IdP(EKS IdP)에 위임하여 처리
  • EKS IdP를 identity provider로 등록하고, 파드가 Web Identify 토큰을 통해 IAM 역할을 Assume 할 수 있게 Trust Relationship 설정이 필요


실습 1

# 파드1 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: eks-iam-test1
spec:
  containers:
    - name: my-aws-cli
      image: amazon/aws-cli:latest
      args: ['s3', 'ls']
  restartPolicy: Never
  automountServiceAccountToken: false
  terminationGracePeriodSeconds: 0
EOF

# 확인
kubectl get pod
kubectl describe pod

# 로그 확인
kubectl logs eks-iam-test1
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied

# 파드1 삭제
kubectl delete pod eks-iam-test1

CloudTrail 이벤트 ListBuckets 확인 → 기록 표시까지 약간의 시간 필요
https://ap-northeast-2.console.aws.amazon.com/cloudtrail/home?region=ap-northeast-2#/events?EventName=ListBuckets

{
    "eventVersion": "1.09",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "AROA47CRYV27JJKCDZFX5:i-0d695b153aad2fdfc",
        "arn": "arn:aws:sts::891377200830:assumed-role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-5rxDjOwEYYoV/i-0d695b153aad2fdfc",
        "accountId": "891377200830",
        "accessKeyId": "ASIA47CRYV27KEM5VRWO",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": "AROA47CRYV27JJKCDZFX5",
                "arn": "arn:aws:iam::891377200830:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-5rxDjOwEYYoV",
                "accountId": "891377200830",
                "userName": "eksctl-myeks-nodegroup-ng1-NodeInstanceRole-5rxDjOwEYYoV"
            },
            "attributes": {
                "creationDate": "2024-04-13T14:03:16Z",
                "mfaAuthenticated": "false"
            },
            "ec2RoleDelivery": "2.0"
        }
    },
    "eventTime": "2024-04-13T14:52:38Z",
    "eventSource": "s3.amazonaws.com",
    "eventName": "ListBuckets",
    "awsRegion": "ap-northeast-2",
    "sourceIPAddress": "15.164.203.182",
    "userAgent": "[aws-cli/2.15.38 Python/3.11.8 Linux/5.10.213-201.855.amzn2.x86_64 docker/x86_64.amzn.2 prompt/off command/s3.ls]",
    "errorCode": "AccessDenied",
    "errorMessage": "Access Denied",
    "requestParameters": {
        "Host": "s3.ap-northeast-2.amazonaws.com"
    },
    "responseElements": null,
    "additionalEventData": {
        "SignatureVersion": "SigV4",
        "CipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
        "bytesTransferredIn": 0,
        "AuthenticationMethod": "AuthHeader",
        "x-amz-id-2": "rTSI9UKrKYeRt94FsU4l97oMeIe7jAxEfD7IsBUoMRiTjxjyfGL53JoxVuC0y2TTQkJz+4kiPEg=",
        "bytesTransferredOut": 243
    },
    "requestID": "2AZTPJTBDYY83GEZ",
    "eventID": "da20f3ed-65fe-4785-880c-f6da41846d6c",
    "readOnly": true,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "891377200830",
    "eventCategory": "Management",
    "tlsDetails": {
        "tlsVersion": "TLSv1.2",
        "cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
        "clientProvidedHostHeader": "s3.ap-northeast-2.amazonaws.com"
    }
}



실습 2

  • Kubernetes Pod에는 Kubernetes Service Account이라는 Kubernetes 개념을 통해 ID가 부여된다.
  • Service Account가 생성되면 JWT 토큰이 자동으로 Kubernetes Secret으로 생성된다.
  • 이 Secret을 Pod에 마운트하고 Service Account를 사용하여 Kubernetes API Server에 인증할 수 있다.
# 파드2 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: eks-iam-test2
spec:
  containers:
    - name: my-aws-cli
      image: amazon/aws-cli:latest
      command: ['sleep', '36000']
  restartPolicy: Never
  terminationGracePeriodSeconds: 0
EOF

# 확인
kubectl get pod
kubectl describe pod
kubectl get pod eks-iam-test2 -o yaml | kubectl neat | yh
kubectl exec -it eks-iam-test2 -- ls /var/run/secrets/kubernetes.io/serviceaccount
kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token ;echo

# aws 서비스 사용 시도
kubectl exec -it eks-iam-test2 -- aws s3 ls
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied
command terminated with exit code 254

# 서비스 어카운트 토큰 확인
SA_TOKEN=$(kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)
echo $SA_TOKEN

# jwt 혹은 아래 JWT 웹 사이트 이용 https://jwt.io/
jwt decode $SA_TOKEN --json --iso8601
...

#헤더
{
  "alg": "RS256",
  "kid": "572cc930954fafce0f45c796ebfc5af9e813efe4"
}

# 페이로드 : OAuth2에서 쓰이는 aud, exp 속성 확인! > projectedServiceAccountToken 기능으로 토큰에 audience,exp 항목을 덧붙힘
## iss 속성 : EKS OpenID Connect Provider(EKS IdP) 주소 > 이 EKS IdP를 통해 쿠버네티스가 발급한 토큰이 유요한지 검증
{
  "aud": [
    "https://kubernetes.default.svc"
  ],
  "exp": 1744556558,
  "iat": 1713020558,
  "iss": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/2D3EF5FBF387DEF75B12F3C46DFC57C0",
  "kubernetes.io": {
    "namespace": "default",
    "pod": {
      "name": "eks-iam-test2",
      "uid": "f1990c27-69fa-46d0-a576-873bda3b3a0f"
    },
    "serviceaccount": {
      "name": "default",
      "uid": "edc5298f-5aff-4b96-92ea-92c391447be8"
    },
    "warnafter": 1713024165
  },
  "nbf": 1713020558,
  "sub": "system:serviceaccount:default:default"
}

# 파드2 삭제
kubectl delete pod eks-iam-test2



Pod/Container 보안 컨텍스트

Container 보안 컨텍스트 - SecurityContext

각 컨테이너에 대한 보안 설정 → 침해사고 발생 시 침해사고를 당한 권한을 최대한 축소하여 그 사고에 대한 확대를 방치

0. 컨테이너 보안 컨텍스트 확인 : kube-system 파드 내 컨테이너 대상

kubectl get pod -n kube-system -o jsonpath={.items[*].spec.containers[*].securityContext} | jq
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true,
  "runAsNonRoot": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true,
  "runAsNonRoot": true
}
{
  "capabilities": {
    "add": [
      "NET_ADMIN",
      "NET_RAW"
    ]
  }
}
{
  "capabilities": {
    "add": [
      "NET_ADMIN"
    ]
  },
  "privileged": true
}
{
  "capabilities": {
    "add": [
      "NET_ADMIN",
      "NET_RAW"
    ]
  }
}
{
  "capabilities": {
    "add": [
      "NET_ADMIN"
    ]
  },
  "privileged": true
}
{
  "capabilities": {
    "add": [
      "NET_ADMIN",
      "NET_RAW"
    ]
  }
}
{
  "capabilities": {
    "add": [
      "NET_ADMIN"
    ]
  },
  "privileged": true
}
{
  "allowPrivilegeEscalation": false,
  "capabilities": {
    "add": [
      "NET_BIND_SERVICE"
    ],
    "drop": [
      "ALL"
    ]
  },
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "capabilities": {
    "add": [
      "NET_BIND_SERVICE"
    ],
    "drop": [
      "ALL"
    ]
  },
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "privileged": true,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "privileged": true,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "privileged": true,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true
}
{
  "readOnlyRootFilesystem": true,
  "runAsNonRoot": true,
  "runAsUser": 1000
}
{
  "privileged": true
}
{
  "privileged": true
}
{
  "privileged": true
}
{
  "allowPrivilegeEscalation": false,
  "capabilities": {
    "drop": [
      "ALL"
    ]
  },
  "readOnlyRootFilesystem": true,
  "runAsNonRoot": true,
  "runAsUser": 1000,
  "seccompProfile": {
    "type": "RuntimeDefault"
  }
}




1. readOnlyRootFilesystem : root 파일 시스템을 읽기 전용으로 사용

#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: rootfile-readonly
spec:
  containers:
  - name: netshoot
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
    securityContext:
      readOnlyRootFilesystem: true
  terminationGracePeriodSeconds: 0
EOF

# 파일 생성 시도
kubectl exec -it rootfile-readonly -- touch /tmp/text.txt
touch: /tmp/text.txt: Read-only file system
command terminated with exit code 1

# 기존 파일 수정 시도 : 아래 /etc/hosts파일 말고 다른 파일로 예제 만들어 두자
## 기본적으로  mount 옵션이 ro 이긴 한데. 특정 파일이나 폴더가 rw로 mount가 되어서 그곳에서는 파일 생성, 삭제등이 가능하네요.
## 특히 /etc/hosts 파일은 HostAliases로 항목 추가가 가능한데, 해당 파링은 kubelet에 의해 관리되고, 파드 생성/재시작 중 덮었여질 수 있다.
## /dev 라던가 /sys/fs/cgroup 폴더 안에서도 가능하네요.
## /etc/hostname 같은 경우는 호스트와 별도의 파일이지만 mount가 / (ro)에 속하게 되어 제한이 걸리네요.
kubectl exec -it rootfile-readonly -- cat /etc/hosts

kubectl exec -it rootfile-readonly -- sh -c "echo write > /etc/hosts"

kubectl exec -it rootfile-readonly -- cat /etc/hosts
write

# 특정 파티션, 파일의 ro/rw 확인
kubectl exec -it rootfile-readonly -- mount | grep hosts
/dev/nvme0n1p1 on /etc/hosts type xfs (rw,noatime,attr2,inode64,logbufs=8,logbsize=32k,noquota)

kubectl exec -it rootfile-readonly -- mount | grep ro
overlay on / type overlay (ro,relatime,lowerdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/136/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/135/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/134/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/133/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/132/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/131/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/130/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/129/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/128/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/127/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/126/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/125/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/124/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/123/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/122/fs,upperdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/137/fs,workdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/137/work)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
sysfs on /sys type sysfs (ro,nosuid,nodev,noexec,relatime)
tmpfs on /sys/fs/cgroup type tmpfs (rw,nosuid,nodev,noexec,relatime,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (ro,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (ro,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/perf_event type cgroup (ro,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/freezer type cgroup (ro,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/devices type cgroup (ro,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (ro,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/blkio type cgroup (ro,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (ro,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/cpuset type cgroup (ro,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/pids type cgroup (ro,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/memory type cgroup (ro,nosuid,nodev,noexec,relatime,memory)
/dev/nvme0n1p1 on /etc/hostname type xfs (ro,noatime,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/nvme0n1p1 on /etc/resolv.conf type xfs (ro,noatime,attr2,inode64,logbufs=8,logbsize=32k,noquota)
tmpfs on /run/secrets/kubernetes.io/serviceaccount type tmpfs (ro,relatime,size=3388344k)
proc on /proc/bus type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/fs type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/irq type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/sys type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/sysrq-trigger type proc (ro,nosuid,nodev,noexec,relatime)
tmpfs on /proc/acpi type tmpfs (ro,relatime)
tmpfs on /proc/kcore type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /proc/keys type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /proc/latency_stats type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /proc/timer_list type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /proc/sched_debug type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /sys/firmware type tmpfs (ro,relatime)overlay on / type overlay (ro,relatime,lowerdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/136/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/135/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/134/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/133/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/132/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/131/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/130/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/129/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/128/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/127/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/126/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/125/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/124/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/123/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/122/fs,upperdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/137/fs,workdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/137/work)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
sysfs on /sys type sysfs (ro,nosuid,nodev,noexec,relatime)
tmpfs on /sys/fs/cgroup type tmpfs (rw,nosuid,nodev,noexec,relatime,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (ro,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (ro,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/perf_event type cgroup (ro,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/freezer type cgroup (ro,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/devices type cgroup (ro,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (ro,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/blkio type cgroup (ro,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (ro,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/cpuset type cgroup (ro,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/pids type cgroup (ro,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/memory type cgroup (ro,nosuid,nodev,noexec,relatime,memory)
/dev/nvme0n1p1 on /etc/hostname type xfs (ro,noatime,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/nvme0n1p1 on /etc/resolv.conf type xfs (ro,noatime,attr2,inode64,logbufs=8,logbsize=32k,noquota)
tmpfs on /run/secrets/kubernetes.io/serviceaccount type tmpfs (ro,relatime,size=3388344k)
proc on /proc/bus type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/fs type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/irq type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/sys type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/sysrq-trigger type proc (ro,nosuid,nodev,noexec,relatime)
tmpfs on /proc/acpi type tmpfs (ro,relatime)
tmpfs on /proc/kcore type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /proc/keys type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /proc/latency_stats type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /proc/timer_list type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /proc/sched_debug type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /sys/firmware type tmpfs (ro,relatime)

## /proc, /dev, /sys/fs/cgroup, /etc/hosts, /proc/kcore, /proc/keys, /proc/timer_list
kubectl exec -it rootfile-readonly -- mount | grep rw
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev type tmpfs (rw,nosuid,size=65536k,mode=755)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666)
mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime)
tmpfs on /sys/fs/cgroup type tmpfs (rw,nosuid,nodev,noexec,relatime,mode=755)
/dev/nvme0n1p1 on /etc/hosts type xfs (rw,noatime,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/nvme0n1p1 on /dev/termination-log type xfs (rw,noatime,attr2,inode64,logbufs=8,logbsize=32k,noquota)
shm on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size=65536k)
tmpfs on /proc/kcore type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /proc/keys type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /proc/latency_stats type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /proc/timer_list type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /proc/sched_debug type tmpfs (rw,nosuid,size=65536k,mode=755)

# 파드 상세 정보 확인
kubectl get pod rootfile-readonly -o jsonpath={.spec.containers[0].securityContext} | jq
{
  "readOnlyRootFilesystem": true
}




2. Linux Capabilities : 슈퍼 유저의 힘을 작은 조각으로 나눔, Capabilities are a per-thread attribute

# Linux Capabilities 확인 : 현재 38개
capsh --print
Current: =ep
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read
Ambient set =
Current IAB:
Securebits: 00/0x0/1'b0 (no-new-privs=0)
 secure-noroot: no (unlocked)
 secure-no-suid-fixup: no (unlocked)
 secure-keep-caps: no (unlocked)
 secure-no-ambient-raise: no (unlocked)
uid=0(root) euid=0(root)
gid=0(root)
groups=0(root)
Guessed mode: UNCERTAIN (0)

# proc 에서 확인 : bit 별 Capabilities - 링크
cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm:	0000003fffffffff
CapEff:	0000003fffffffff

# 노드 Linux Capabilities 확인 : 아래 굵은색은 파드 기본 Linux Capabilities(14개)
ssh ec2-user@$N1 sudo yum -y install libcap-ng-utils
ssh ec2-user@$N1 sudo capsh --print
cap_chown                   파일이나 디렉토리의 소유자를 변경할 수 있는 권한
cap_dac_override            파일이나 디렉토리의 접근 권한을 무시하고 파일이나 디렉토리에 대한 접근을 수행할 수 있는 권한 (DAC의 약자는 Discretionary access control이다)
cap_dac_read_search         파일이나 디렉토리를 읽거나 검색할 수 있는 권한
cap_fowner                  파일이나 디렉토리의 소유자를 변경할 수 있는 권한
cap_fsetid                  일이나 디렉토리의 Set-User-ID (SUID) 또는 Set-Group-ID (SGID) 비트를 설정할 수 있는 권한
cap_kill                    다른 프로세스를 종료할 수 있는 권한
cap_setgid                  프로세스가 그룹 ID를 변경할 수 있는 권한
cap_setuid                  프로세스가 사용자 ID를 변경할 수 있는 권한
cap_setpcap                 프로세스가 자신의 프로세스 권한을 변경할 수 있는 권한
cap_linux_immutable         파일의 immutability(불변성) 속성을 변경할 수 있는 권한을 제공
cap_net_bind_service        프로그램이 특정 포트에 바인딩(bind)하여 소켓을 개방할 수 있는 권한
cap_net_broadcast           프로세스가 네트워크 브로드캐스트 메시지를 보낼 수 있는 권한
cap_net_admin               네트워크 인터페이스나 소켓 설정을 변경할 수 있는 권한
cap_net_raw                 네트워크 패킷을 송수신하거나 조작할 수 있는 권한
cap_ipc_lock                메모리 영역을 잠금(lock)하고 언락(unlock)할 수 있는 권한
cap_ipc_owner               IPC 리소스(Inter-Process Communication Resources)를 소유하고, 권한을 변경할 수 있는 권한
cap_sys_module              커널 모듈을 로드하거나 언로드할 수 있는 권한
cap_sys_rawio               입출력(I/O) 포트와 같은 하드웨어 리소스를 직접 접근할 수 있는 권한
cap_sys_chroot              프로세스가 chroot() 시스템 콜을 호출하여 프로세스의 루트 디렉토리를 변경할 수 있는 권한
cap_sys_ptrace              다른 프로세스를 추적(trace)하거나 디버깅할 수 있는 권한
cap_sys_pacct               프로세스 회계(process accounting)를 위한 파일에 접근할 수 있는 권한
cap_sys_admin               시스템 관리자 권한을 제공하는 권한
cap_sys_boot                시스템 부팅과 관련된 작업을 수행할 수 있는 권한
cap_sys_nice                프로세스의 우선순위를 변경할 수 있는 권한
cap_sys_resource            자원 제한(resource limit)과 관련된 작업을 수행할 수 있는 권한
cap_sys_time                시스템 시간을 변경하거나, 시간 관련 시스템 콜을 사용할 수 있는 권한
cap_sys_tty_config          터미널 설정을 변경할 수 있는 권한
cap_mknod                   mknod() 시스템 콜을 사용하여 파일 시스템에 특수 파일을 생성할 수 있는 권한
cap_lease                   파일의 잠금과 관련된 작업을 수행할 수 있는 권한
cap_audit_write             시스템 감사(audit) 로그에 대한 쓰기 권한
cap_audit_control           시스템 감사(audit) 설정과 관련된 작업을 수행할 수 있는 권한
cap_setfcap                 파일 시스템 캡러빌리티(file system capability)을 설정할 수 있는 권한
cap_mac_override            SELinux 또는 AppArmor과 같은 MAC(Mandatory Access Control) 시스템을 우회하고 자신의 프로세스가 접근 가능한 파일, 디바이스, 네트워크 등을 제한 없이 접근할 수 있는 권한
cap_mac_admin               SELinux 또는 AppArmor과 같은 MAC(Mandatory Access Control) 시스템을 관리하고 수정할 수 있는 권한
cap_syslog                  시스템 로그를 읽거나, 쓸 수 있는 권한
cap_wake_alarm              시스템의 RTC(Real-Time Clock)를 사용하여 장치를 깨우거나 슬립 모드를 해제할 수 있는 권한
cap_block_suspend           시스템의 전원 관리 기능 중 하나인 Suspend(절전 모드)를 방지하는 권한
cap_audit_read              시스템 감사(audit) 로그를 읽을 수 있는 권한
  • 파드의 Linux Capabilities 기본 확인

    # 샘플 파드 생성
    cat <<EOF | kubectl create -f -
    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
    EOF
    
    # 파드의 Linux Capabilities 기본 확인
    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
    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
    
    # proc 에서 확인 : bit 별 Capabilities - 링크
    kubectl exec -it sample-capabilities -- cat /proc/1/status | egrep 'CapPrm|CapEff'
    CapPrm:	00000000a80425fb
    CapEff:	00000000a80425fb
    
    # 파드에서 시간 변경 시도
    kubectl exec -it sample-capabilities -- date
    Sat Apr 13 15:22:43 UTC 2024
    
    # 파드에서 시간 변경 시도
    kubectl exec -it sample-capabilities -- date -s "12:00:00"
    
    kubectl exec -it sample-capabilities -- date
    Sat Apr 13 15:23:03 UTC 2024
    
  • 파드에 Linux Capabilities 부여 및 삭제

    #
    cat <<EOF | kubectl create -f -
    apiVersion: v1
    kind: Pod
    metadata:
     name: sample-capabilities2
    spec:
     containers:
     - name: nginx-container
       image: masayaaoyama/nginx:capsh
       command: ["tail"]
       args: ["-f", "/dev/null"]
       securityContext:
         capabilities:
           add: ["NET_ADMIN", "SYS_TIME"]
           drop: ["AUDIT_WRITE"]
     terminationGracePeriodSeconds: 0
    EOF
    
    # 파드의 Linux Capabilities 기본 확인
    kubectl exec -it sample-capabilities2 -- 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
    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
    # 제거됨 cap_audit_write
    
    # 파드 상세 정보 확인
    kubectl get pod sample-capabilities2 -o jsonpath={.spec.containers[0].securityContext} | jq
    {
     "capabilities": {
       "add": [
         "NET_ADMIN",
         "SYS_TIME"
       ],
       "drop": [
         "AUDIT_WRITE"
       ]
     }
    }
    
    # proc 에서 확인 : bit 별 Capabilities
    kubectl exec -it sample-capabilities2 -- cat /proc/1/status | egrep 'CapPrm|CapEff'
    CapPrm:	000000008a0435fb
    CapEff:	000000008a0435fb
    
    kubectl exec -it sample-capabilities2 -- date
    Sat Apr 13 15:31:25 UTC 2024
    
    kubectl exec -it sample-capabilities2 -- date -s "12:00:00"
    Sat Apr 13 12:00:00 UTC 2024
    
    kubectl exec -it sample-capabilities2 -- date
    Sat Apr 13 12:00:14 UTC 2024
  • 특수 권한 컨테이너 생성 : 호스트와 동등한 권한 부여됨

    #
    cat <<EOF | kubectl create -f -
    apiVersion: v1
    kind: Pod
    metadata:
     name: sample-capabilities3
    spec:
     containers:
     - name: nginx-container
       image: masayaaoyama/nginx:capsh
       command: ["tail"]
       args: ["-f", "/dev/null"]
       securityContext:
         privileged: true
     terminationGracePeriodSeconds: 0
    EOF
    
    # 파드의 Linux Capabilities 기본 확인
    kubectl exec -it sample-capabilities3 -- capsh --print | grep Current
    Current: = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read,38,39,40+ep
    
    # 파드 상세 정보 확인
    kubectl get pod sample-capabilities3 -o jsonpath={.spec.containers[0].securityContext} | jq
    {
     "privileged": true
    }
    
    # proc 에서 확인 : bit 별 Capabilities - 링크
    kubectl exec -it sample-capabilities3 -- cat /proc/1/status | egrep 'CapPrm|CapEff'
    CapPrm:	000001ffffffffff
    CapEff:	000001ffffffffff



Pod 보안 컨텍스트

  • 파드 레벨에서 보안 컨텍스트를 적용 : 파드에 포함된 모든 컨테이너가 영향을 받음
  • 파드와 컨테이너 정책 중복 시, 컨테이너 정책이 우선 적용됨




0. 컨테이너 보안 컨텍스트 확인 : kube-system 파드 내 컨테이너 대상

kubectl get pod -n kube-system -o jsonpath={.items[*].spec.securityContext} | jq
{
  "fsGroup": 65534
}
{
  "fsGroup": 65534
}
{}
{}
{}
{}
{}
{
  "fsGroup": 1000,
  "runAsGroup": 1000,
  "runAsNonRoot": true,
  "runAsUser": 1000
}
{
  "fsGroup": 1000,
  "runAsGroup": 1000,
  "runAsNonRoot": true,
  "runAsUser": 1000
}
{
  "fsGroup": 0,
  "runAsGroup": 0,
  "runAsNonRoot": false,
  "runAsUser": 0
}
{
  "fsGroup": 0,
  "runAsGroup": 0,
  "runAsNonRoot": false,
  "runAsUser": 0
}
{
  "fsGroup": 0,
  "runAsGroup": 0,
  "runAsNonRoot": false,
  "runAsUser": 0
}
{}
{}
{}
{}
{}
{}




1. 실행 사용자 변경
runuser 파드는 실행 사용자를 nobody(UID:65534) 사용자로 실행, 실행권한에 서브그룹 1001/1002 추가

#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: rundefault
spec:
  containers:
  - name: centos
    image: centos:7
    command: ["tail"]
    args: ["-f", "/dev/null"]
    securityContext:
      readOnlyRootFilesystem: true
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: runuser
spec:
  securityContext:
    runAsUser: 65534
    runAsGroup: 65534
    supplementalGroups:
    - 1001
    - 1002
  containers:
  - name: centos
    image: centos:7
    command: ["tail"]
    args: ["-f", "/dev/null"]
    securityContext:
      readOnlyRootFilesystem: true
  terminationGracePeriodSeconds: 0
EOF


kubectl get pod rundefault -o jsonpath={.spec.securityContext} | jq
{}

kubectl get pod runuser -o jsonpath={.spec.securityContext} | jq
{
  "runAsGroup": 65534,
  "runAsUser": 65534,
  "supplementalGroups": [
    1001,
    1002
  ]
}

# 실행 사용자 정보 확인
kubectl exec -it rundefault -- id
uid=0(root) gid=0(root) groups=0(root)

kubectl exec -it runuser    -- id
uid=65534 gid=65534 groups=65534,1001,1002

# 프로세스 정보 확인
kubectl exec -it rundefault -- ps -axo uid,user,gid,group,pid,comm
  UID USER       GID GROUP        PID COMMAND
    0 root         0 root           1 tail
    0 root         0 root          14 ps
    
kubectl exec -it runuser    -- ps -axo uid,user,gid,group,pid,comm
  UID USER       GID GROUP        PID COMMAND
65534 65534    65534 65534          1 tail
65534 65534    65534 65534         13 ps




2. root 사용자로 실행 제한

#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: nonroot
spec:
  securityContext:
    runAsNonRoot: true
  containers:
  - name: netshoot
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

# 이벤트 확인
kubectl events --for pod/nonroot
LAST SEEN   TYPE     REASON      OBJECT        MESSAGE
3h30m       Normal   Pulling     Pod/nonroot   Pulling image "nicolaka/netshoot"
14s         Normal   Scheduled   Pod/nonroot   Successfully assigned default/nonroot to ip-192-168-1-23.ap-northeast-2.compute.internal




3. 파일 시스템 그룹 지정

  • 일반적으로 마운트한 볼륨의 소유자와 그룹은 root:root로 되어 있다. 실행 사용자를 변경한 경우에는 마운트한 볼륨에 권한이 없는 경우가 있다. 따라서 마운트하는 볼륨의 그룹을 변경할 수 있도록 되어 있다. (setdig도 설정된다) - 예) emptyDir 혹은 PV 등 volumeMounts
#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: fsgoup1
spec:
  volumes:
  - name: vol1
    emptyDir: {}
  containers:
  - name: centos
    image: centos:7
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: vol1
      mountPath: /data/demo
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: fsgoup2
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  volumes:
  - name: vol2
    emptyDir: {}
  containers:
  - name: centos
    image: centos:7
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: vol2
      mountPath: /data/demo
  terminationGracePeriodSeconds: 0
EOF

#
kubectl get pod fsgoup1 -o jsonpath={.spec.securityContext} | jq
{}

kubectl get pod fsgoup2 -o jsonpath={.spec.securityContext} | jq
{
  "fsGroup": 2000,
  "runAsGroup": 3000,
  "runAsUser": 1000
}

# 실행 사용자 정보 확인
kubectl exec -it fsgoup1 -- id
uid=0(root) gid=0(root) groups=0(root)

kubectl exec -it fsgoup2 -- id
uid=1000 gid=3000 groups=3000,2000

# 프로세스 정보 확인
kubectl exec -it fsgoup1 -- ps -axo uid,user,gid,group,pid,comm
UID USER       GID GROUP        PID COMMAND
    0 root         0 root           1 sleep
    0 root         0 root          14 ps

kubectl exec -it fsgoup2 -- ps -axo uid,user,gid,group,pid,comm
  UID USER       GID GROUP        PID COMMAND
 1000 1000      3000 3000           1 sleep
 1000 1000      3000 3000          14 ps

# 디렉터리 정보 확인 : fsgoup2파드의 마운트 볼륨 그룹의 GID가 2000 (fsGroup: 2000)
kubectl exec -it fsgoup1 -- ls -l /data
drwxrwxrwx 2 root root 6 Apr 13 15:51 demo

kubectl exec -it fsgoup2 -- ls -l /data
drwxrwsrwx 2 root 2000 6 Apr 13 15:51 demo

# fsgoup2파드에서 파일 생성 및 확인
kubectl exec -it fsgoup2 -- sh -c "echo write > /data/demo/sample.txt"

kubectl exec -it fsgoup2 -- cat /data/demo/sample.txt
write

kubectl exec -it fsgoup2 -- ls -l /data/demo/sample.txt
-rw-r--r-- 1 1000 2000 6 Apr 13 15:54 /data/demo/sample.txt

# fsgoup2파드에서 다른 디렉토리에 파일 생성 시도
kubectl exec -it fsgoup2 -- sh -c "echo write > /data/sample.txt"
sh: /data/sample.txt: Permission denied
command terminated with exit code 1




4. sysctl을 사용한 커널 파라미터 설정 : 커널 파라미터 변경 적용을 위해서는 컨테이너에서도 설정 필요, 파드 수준 적용으로 컨테이너 간에 공유됨

  • 커널 파라미터는 안전한 것(safe)과 안전하지 않은 것(unsafe)으로 분류된다.
    • 안전한 것 safe : 호스트의 커널과 적절하게 분리되어 있으며 다른 파드에 영향이 없는 것, 파드가 예상치 못한 리소스를 소비하지 않는 것
      • kernel.shm_rmid_forced
      • net.ipv4.ip_local_port_range
      • net.ipv4.tcp_syncookies
      • net.ipv4.ping_group_range (since Kubernetes 1.18),
      • net.ipv4.ip_unprivileged_port_start (since Kubernetes 1.22).
    • 안전하지 않은 것 unsafe : 사실상 대부분의 커널 파라미터 ⇒ 적용을 위해서는 kubelet 설정 필요
      # Unsafe sysctls are enabled on a node-by-node basis with a flag of the kubelet
      kubelet --allowed-unsafe-sysctls 'kernel.msg*,net.core.somaxconn' ...




unsafe 파라미터를 변경 시도

#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: unsafe
spec:
  securityContext:
    sysctls:
    - name: net.core.somaxconn
      value: "12345"
  containers:
    - name: centos-container
      image: centos:7
      command: ["/bin/sleep", "3600"]
  terminationGracePeriodSeconds: 0
EOF

# 
kubectl events --for pod/unsafe
LAST SEEN   TYPE      REASON            OBJECT       MESSAGE
18s         Normal    Scheduled         Pod/unsafe   Successfully assigned default/unsafe to ip-192-168-2-241.ap-northeast-2.compute.internal
18s         Warning   SysctlForbidden   Pod/unsafe   forbidden sysctl: "net.core.somaxconn" not allowlisted

safe 파라미터 수정

#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: sysctl1
spec:
  containers:
    - name: centos-container
      image: centos:7
      command: ["/bin/sleep", "3600"]
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: sysctl2
spec:
  securityContext:
    sysctls:
    - name: net.ipv4.ip_local_port_range
      value: "1025 61000"
  containers:
    - name: centos-container
      image: centos:7
      command: ["/bin/sleep", "3600"]
  terminationGracePeriodSeconds: 0
EOF

#
kubectl get pod sysctl1 -o jsonpath={.spec.securityContext} | jq
{}

kubectl get pod sysctl2 -o jsonpath={.spec.securityContext} | jq
{
  "sysctls": [
    {
      "name": "net.ipv4.ip_local_port_range",
      "value": "1025 61000"
    }
  ]
}

kubectl exec -it sysctl1 -- sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768 60999

kubectl exec -it sysctl2 -- sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 1025	61000




initContainer 와 privileged 를 활용하여 unsafe 커널 파라미터를 강제로 변경

#
curl -s -O https://raw.githubusercontent.com/MasayaAoyama/kubernetes-perfect-guide/ko/2nd-edition/samples/chapter13/sample-sysctl-initcontainer.yaml

cat sample-sysctl-initcontainer.yaml| yh
apiVersion: v1
kind: Pod
metadata:
  name: sample-sysctl-initcontainer
spec:
  initContainers:
  - name: initialize-sysctl
    image: busybox:1.27
    command:
    - /bin/sh
    - -c
    - |
      sysctl -w net.core.somaxconn=12345
    securityContext:
      privileged: true
  containers:
  - name: tools-container
    image: amsy810/tools:v2.0

kubectl apply -f sample-sysctl-initcontainer.yaml

kubectl describe pod sample-sysctl-initcontainer
kubectl get pod sample-sysctl-initcontainer -o json | jq

# 확인
kubectl exec -it sample-sysctl-initcontainer -c tools-container -- sysctl net.core.somaxconn
net.core.somaxconn = 12345

0개의 댓글