반응형

Public Cloud의 Kubernetes(예: AWS AKS, Azure AKS, Oracle OKE 등)을 사용하다보면,

Public Network에서 들어오는 UDP 트래픽을 Network LB(NLB)가 처리하지 못하는 것처럼 보일 때가 있다.

 

예를 들어 아래와 같은 Service Manifest를 적용했다고 가정해보자.

 

apiVersion: v1
kind: Service
metadata:
  name: almighty
  annotations:
    oci.oraclecloud.com/load-balancer-type: "nlb"  # <- 이 내용을 추가해야 한다.
spec:
  selector:
    app: almighty
  ports:
    - name: myweb
      protocol: TCP
      port: 8080
      targetPort: 8080
    - name: yourweb
      protocol: TCP
      port: 80
      targetPort: 80
    - name: myudp
      protocol: UDP       # <- 테스트를 위해 UDP를 추가한다.
      port: 9090
      targetPort: 9090
  type: LoadBalancer      # <- Service Type을 LoadBalancer로 설정한다.

 

위 Service Manifest를 적용하면, External-IP 항목이 Public IP 값으로 설정되지만 실제로 Pod로 UDP 트래픽이 전달되지 못하는 현상이 생길 것이다.

 

왜 이런 문제가 생길까?

 

Public Network에서 유입되는 UDP 트래픽이 Pod에 전달되지 못하는 정확한 원인은 아래와 같다.

Public Cloud Service 제공사가 제공하는 NLB(Network Load Balancer)는 제대로 UDP 트래픽을 처리한 것이 맞다.

단지, Cloud Infra가 제공하는 K8S Node의 Security Rule에 의해서 UDP 트래픽이 Block되는 것이다.

따라서 Security Rule에 UDP 트래픽이 Pass 되도록 Rule을 추가하기만 하면 된다. (즉, Linux Firewall 설정 하듯이~)

 

AWS AKS, Azure AKS, Oracle OKE 모두 비슷하게 NLB가 UDP 트래픽을 처리하고 Security Rule도 비슷하기 때문에 Oracle의 OCI OKE만 이용해서 설명하면 아래와 같다.

 

아래의 순서로 메뉴/화면을 찾아서 들어간다.

[ Kubernetes Clusters (OKE) ]

-> Clusters 화면에서 특정 Cluster의 VCN을 선택/클릭한다.

-> VCN을 클릭하면, 여러개의 Subnets이 목록이 보여지는데 oke-svclbsubnet-xxxxoke-nodesubnet-yyyy을 선택하여 Security Lists를 확인한다.

-> [ Security Lists ] 화면에 있는 Rule 이름을 선택한다.

-> [ Ingress Rules ]와 [ Egress Rules ] 화면에서 UDP, TCP 트래픽을 모두 허용하도록 Rule을 추가한다.

     예를 들어, 115.33.55.0/24, 특정 UDP 포트(예: 9090), 특정 TCP 포트(예: 8080) 허용으로 설정하면 된다.

 

아래 화면을 참고~~~

 

 

 

이렇게 Security Rule을 추가하고, 다시 Public IP와 UDP 포트를 이용하여 트래픽을 보내보면, Pod 내부까지 UDP 트래픽이 잘 전달되는 것을 확인할 수 있다.

 

 


조심해야 할 사항:
  Security Rule 설정하고, 40초 후에 Traffic Test해야 한다.
  Node Security Rule이 적용되기 까지 40초가 걸리기 때문에 그 이후에 Traffic Test를 해야 제대로 Traffic이 Pod까지 전달된다.
  

 


생각해볼 내용:
  Node Security List에 TCP Rule을 추가할 때, 왜 Egress Rule도 추가해야 하는지 잘 이해가 안 된다.
  Cluster 외부에서 Ingress Rule에 따라 TCP Traffic을 허용해주면, ConnTrack DB에 Pin Hole 같은게 생기면서
  TCP의 응답 패킷도 알아서 Pass(Forward)시켜주는게 일반적인 Traffic 처리가 아닐까라고 생각했는데... 이게 아닌가 보다.
  아무튼 귀찮지만 Ingress에 추가한 Rule을 Egress에도 꼭 추가해야 한다. (잊지 말자 !!!)

 

 

 

 

참고하면 좋은 Web Docs

Oracle OCI Network Load Balancer 사용하기

https://thekoguryo.github.io/release-notes/20220315-support-for-oci-network-load-balancers/

 

Support for OCI Network Load Balancers

OKE에서 Service Type을 Load Balancer를 사용할때 이제는 OCI Network Load Balancer을 추가적으로 지원합니다.

thekoguryo.github.io

 

위 문서는 간단하게 사용법 위주로 설명되어 있고, 아래 문서는 내부 동작에 관해 자세하게 내용을 다루고 있다.

특히, Service Manifest의 externalTrafficPolicy: local 방식에 관해 궁금하다면 아래 문서를 보는 것이 좋다.

 

https://blogs.oracle.com/cloud-infrastructure/post/network-load-balancer-support-on-oracle-kubernetes-engine

 

Using a network load balancer for Kubernetes services

Announcing network load balancer support on Oracle Container Engine for Kubernetes.

orasites-prodapp.cec.ocp.oraclecloud.com

 

더 자세히 파고 들어가고 싶다면, Oracle 공식 Web Docs를 보길~~~

 

https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengcreatingloadbalancer.htm

 

Defining Kubernetes Services of Type LoadBalancer

Include in the backend set:

docs.oracle.com

 

반응형

Kubernetes, OCP(Openshift Container Platform)을 사용하다 보면, Resource에 대한 접근 권한 때문에 답답한 경우가 많다.

SCC(SecurityContext)를 잘 관리할 줄 알면, 제일 좋지만 급하게.. 그리고 간단하게 테스트만 하는 상황이라면,

아래와 같이 시스템의 모든 자원에 접근할 수 있도록 권한을 최고 수준(privileged)으로 올려주고 테스트하는 것이 정신 건강에 좋다.

(상용 서비스에서는 절대 이렇게 하면 안 된다.  Test bed 같은 곳에서 잠깐 Feasibility check만 하고 Pod를 종료시킨다는 가정하에 사용할 것)

 

apiVersion: v1
kind: Pod
metadata:
  name: my-example-pod
spec:
  hostNetwork: true    ## <-- 이 설정이 있으면, Pod 내부에서 Host Network이 보인다
                       ##     예를 들어, Host OS의 eno3 network port를 보기 위해
                       ##     ifconfit eno3  명령 실행이 가능해진다.
  containers:
    ...
    securityContext:
      privileged: true   ## <-- 이렇게 하면 모든 자원에 접근 가능해진다.
    ...
  securityContext: {}
  ...

 

 

만약, 극단적으로 Pod 내부에 있는 Process가 Host OS의 자원에 제약 없이 접근하고 싶다면 아래 예제 YAML처럼 권한 설정을 하면 된다.  이렇게 하면, Pod 내부에서 Pod 밖(즉 Host OS)의 IP network 자원, TCP Stack, IPC, ProcessID(PID) 등에 자유롭게 접근할 수 있다.

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: privileged
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
spec:
  privileged: true
  allowPrivilegeEscalation: true
  allowedCapabilities:
  - '*'
  volumes:
  - '*'
  hostNetwork: true
  hostPorts:
  - min: 0
    max: 65535
  hostIPC: true
  hostPID: true
  runAsUser:
    rule: 'RunAsAny'
  seLinux:
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'RunAsAny'
  fsGroup:
    rule: 'RunAsAny'

 

위 YAML에 대해 자세히 알고 싶다면, 아래 kubernetes.io 문서를 참고하세요.

 

 

Pod Security Policies

FEATURE STATE: Kubernetes v1.21 [deprecated] Caution: PodSecurityPolicy is deprecated as of Kubernetes v1.21, and will be removed in v1.25. We recommend migrating to Pod Security Admission, or a 3rd party admission plugin. For a migration guide, see Migrat

kubernetes.io

 

 

 

 

다시 한번 강조하지만, 위와 같이 securityContext.privileged: true 설정된 Pod를 상용 서비스를 제공하는 클러스터에서 구동한 상태로 오래 방치하면 결국 해커의 먹이감이 된다.  꼭 10분~20분 정도 테스트만 하고 바로 Pod를 종료(삭제)해야 한다.

 

 

 


 

 

 


아래 내용은 참고용으로만 볼 것!!!

 

DaemonSet으로 구동하는 Pod들은 securityContext 값이 privileged 으로 설정된 경우가 많다.

왜냐하면 이런 DaemonSet으로 구동된 Pod들은 각 노드에서 Host 자원에 접근해야 하는 경우가 있기 때문이다.

아래 예시를 보면 바로 이해가 될것이다.

 

Multus 배포 예시

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: multus
  namespace: openshift-multus
  ... 중간 생략 ...
  
  spec:
    template:
    ... 중간 생략 ...
    
    spec:
      containers:
      ... 중간 생략 ...
      
        securityContext:
          privileged: true      ## <-- 이 부분
... 중간 생략 ...

 

 

Openshift SDN 배포 예시

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: sdn
  ... 중간 생략 ...
spec:
  template:
  ... 중간 생략 ...
  
    spec:
      containers:
      ... 중간 생략 ...
      
        securityContext:
          privileged: true   ## <-- 이 부분
      ... 중간 생략 ...

 

 

게시물 작성자: sejong.jeonjo@gmail.com

 

반응형

 

잠깐 !!!
만약, 단순히 Container, Pod 내부에서 Root 권한 또는 Host OS 자원에 대한 접근 권한이 필요한 경우라면
이 블로그를 읽고 따라하기 보다는 아래 블로그를 읽고 간단하게 테스트하기를 권장한다.

https://andrewpage.tistory.com/191

 

 


 

 

Pod 내부의 Application이 Host OS의 Root User 권한으로 OS 자원에 접근하거나 명령을 수행해야 한다면, 해당 'namespace'에

privileged 속성 또는 privilieged와 동등한 수준의 자체 정의한 SecurityContextConstraints(SCC)를 추가해야 한다.

 

SCC 생성

나만의 SCC를 생성해보자.

$  cat << EOF > my-scc.yaml

kind: SecurityContextConstraints
metadata:
  name: andrew-scc
allowHostDirVolumePlugin: false
allowHostIPC: false
allowHostNetwork: false
allowHostPID: false
allowHostPorts: false
allowPrivilegeEscalation: true
allowPrivilegedContainer: false
allowedCapabilities:
- NET_ADMIN
- NET_RAW
- NET_BIND_SERVICE
apiVersion: security.openshift.io/v1
defaultAddCapabilities: null
fsGroup:
  type: RunAsAny
groups:
- system:cluster-admins
priority: 10
readOnlyRootFilesystem: false
requiredDropCapabilities:
- MKNOD
runAsUser:
  type: RunAsAny
seLinuxContext:
  type: MustRunAs
supplementalGroups:
  type: RunAsAny
users: null
volumes:
- configMap
- downwardAPI
- emptyDir
- persistentVolumeClaim
- projected
- secret

EOF

 

위와 같이 my-scc.yaml을 작성했다면, 이 파일을 OCP에 적용한다.

## 참고로, scc는 namespace마다 있는 것이 아니다. 
## 따라서 namespace를 지정하지 않고, 아래 명령처럼 scc를 생성한다.

$  oc apply -f my-scc.yaml

 

이렇게 생성한 SCC를 내가 사용할 ServiceAccount와 binding한다.

$ oc create ns andrew-project
$ oc adm policy add-scc-to-group andrew-scc system:serviceaccounts:andrew
$ oc adm policy add-scc-to-user andrew-scc  -z andrew  -n andrew-project

# SCC 정보를 확인하는 명령
$ oc describe scc andrew-scc

# 'andrew' ServiceAccount로부터 'andrew-scc' SCC를 삭제하는 명령
$ oc adm policy remove-scc-from-user andrew-scc  -z andrew -n andrew-project

 

 

추가로 Pod Deploy할 때, 아래와 같이 'securityContext' 항목을 설정해야 한다.

 

# File name: my-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ubuntu-pv
  labels:
    app: ubuntu-pv
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ubuntu-pv
  template:
    metadata:
      labels:
        app: ubuntu-pv
    spec:
    ## NOTE: 이 부분을 추가 !!!
      securityContext:
        runAsUser: 0
      containers:
      - name: ubuntu-pv
        image: myharbor.andrew.space:8080/my-ubuntu:0.1.2
        ## NOTE: 이 부분을 추가 !!!
        securityContext:
          capabilities:
            add:
              - "NET_ADMIN"
              - "NET_RAW"
              - "NET_BIND_SERVICE"

 

 


 

 

여기까지는 그냥 SecurityContextConstraints에 관한 이해없이 따라해도 잘 동작한다.

그런데 만약 SecurityContextConstraints에 대해서 깊이 있는 지식을 얻고 싶다면, 아래의 Red Hat Blog를 읽어볼 것을 추천한다.

SecurityContextConstraints 방식으로 Process의 실행 권한을 제어하는 방법 외에도 Linux에서 전통적으로 사용하는 Stick Bit(SETUID)와 File Capability 방식으로 Binary File을 만들고, Container Image를 미리 제작하는 방법도 같이 소개하고 있다.

어떤 방식이 더 편하고, 보안(Security)이 우수한지는 사용하는 사람이 판단할 일이다.

 

 

 

추천 Blog - Capabilities in OpenShift (OCP)

 

https://cloud.redhat.com/blog/linux-capabilities-in-openshift

 

Linux Capabilities in OpenShift

We know that Linux has two types of users: Privileged and Unprivileged.

content.cloud.redhat.com

 

 

추천 Web Docs

아래 Web Docs에서 Chapter 15.에 가장 확실한 설명이 있다.

https://access.redhat.com/documentation/ko-kr/openshift_container_platform/4.10/html-single/authentication_and_authorization/index

 

인증 및 권한 부여 OpenShift Container Platform 4.10 | Red Hat Customer Portal

이 문서에서는 OpenShift Container Platform에서 ID 공급자를 정의하는 방법을 설명합니다. 또한 클러스터를 보호하기 위해 역할 기반 액세스 제어를 구성하는 방법도 알아봅니다.

access.redhat.com

 

 

 

+ Recent posts