Kubernetes 이야기

Network Policy 본문

Kubernetes/보안

Network Policy

kmaster 2022. 5. 8. 22:37
반응형

Kubernetes에서는 다양한 Resource들 ( Deployment, Service, Secret, Configmap, PVC ... ) 존재한다. 이러한 Resource들은 Namespace에 속하고, RBAC 권한에 따라 다른 Namespace의 Resource들을 참조하지 못하도록 엄격하게 통제할 수 있다.

하지만, 네트워크의 경우 동일한 Cluster에서는 기본적으로 Network 격리가 안되고 모든 소스에서 트래픽을 받아들이도록 구성된다.

 

즉, 아래와 같이 Namespace 별로 배포된 앱에서 다른 Namespace의 앱을 직접 호출할 수 있다.

 

 

Kubernetes에서는 사용자 애플리케이션이 작동하는 컨테이너를 보호하기 위해 네트워크 폴리시 ( NetworkPolicy ) 를 이용해 네트워크에서의 접근을 제한할 수 있다.

네트워크 폴리지 규칙은 Kubernetes API에서 Query 되고 CNI ( Container Networking Interface ) Plugin ( 예: Calico, Cillium 등 )에 의해 시행된다.

네트워크 폴리시는 OSI 3 또는 4 계층 수준에서 트래픽 흐름을 제어한다. 또한 3가지 유형에 대해 접근 제한이 가능하며 그 유형은 아래와 같다.

 

1. 허용되는 파드

2. 허용되는 네임스페이스

3. IP 블록

 

클러스터 내의 데이터 트래픽은 네트워크 정책 없이는 완전히 제한되지 않으므로 모든 연결을 방지하는 네트워크 정책을 먼저 적용해야 한다. 그런 다음 추가 네트워크 정책(화이트리스트)을 통해 원하는 연결을 허용할 수 있다.

 

네트워크 폴리시는 podSelectorpolicyTypes 그리고 ingress, egress 를 이용해 설정한다.

 

podSelector 네트워크 폴리시가 적용되는 파드를 설정한다. (필수)
policyTypes 네트워크 폴리시는 인그레스와 이그레스 각각에 트래픽 정책을 설정할 수 있다. 미 설정시 인그레스에 대해 정 책을 적용한다. 만약 policyTypes 에 Ingress 와 Egress 둘 다 설정이 되어 있으면 인그레스와 이그레스 모두에 대해 정책을 적용한다. ( 선택사항이지만 명시적으로 지정하는 것이 좋다. )
ingress 화이트 리스트 기반의 ingress 규칙을 구성한다. ingress 규칙은 from 과 ports 로 설정하며 두 조건을 만족 하는 트래픽을 허용한다. (선택사항)
egress 화이트 리스트 기반의 egress 규칙을 구성한다. egress 규칙은 to 와 ports 로 설정하며 두 조건을 만족하는 트래픽을 허용한다. (선택사항)

 

NetworkPolicy의 일반적은 구문은 아래와 같다.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978
설치된 네트워킹 플러그인이 네트워킹 정책을 지원하지 않으면 클러스터의 API 서버에 적용하더라도 효과가 없다.

 

예시

예시1) Namespace 들간의 네트워크 허용

  • Namespace db에 Postgresql 배포
  • Namespace app1에 nginx 배포
  • Namespace app2에 nginx 배포
  • db namespace에 app1 namespace만 허용하고 다른 namespace에서는 접근 금지

그럼 먼저 db namespace에 postgres를 설치해 보자. ( 테스트를 위한 것으로 별도 PVC는 없음 )

# kubectl create ns db
# cat <<EOF | kubectl create -n db -f -
apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app: postgres
  name: postgres
spec:
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: postgres
  serviceName: postgres-svc
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - env:
        - name: PGDATA
          value: /var/lib/postgresql/data/pgdata
        - name: POSTGRES_USER
          value: test
        - name: POSTGRES_PASSWORD
          value: test
        - name: POSTGRES_DB
          value: test
        image: postgres:12.4-alpine
        imagePullPolicy: IfNotPresent
        name: postgres
        ports:
        - containerPort: 5432
          protocol: TCP
EOF
statefulset.apps/postgres created

# cat <<EOF | kubectl create -n db -f -
apiVersion: v1
kind: Service
metadata:
  name: postgres
spec:
  type: ClusterIP
  clusterIP: None
  ports:
  - name: postgres
    port: 5432
    targetPort: 5432
    protocol: TCP 
  selector:
    app: postgres
EOF

 

이제 app1과 app2 namespace에 앱을 배포해 보자.

# kubectl create ns app1
# kubectl create ns app2
# cat <<EOF | kubectl create -n app1 -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

# cat <<EOF | kubectl create -n app2 -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

 

그럼 app1과 app2 namespace에서 db namespace의 postgres 접근이 되는지 확인해 보자.

# k exec -it -n app1 nginx-deployment-7848d4b86f-55t89 -- /usr/bin/curl postgres.db.svc.cluster.local:5432
curl: (52) Empty reply from server
command terminated with exit code 52

# k exec -it -n app2 nginx-deployment-7848d4b86f-krzwt -- /usr/bin/curl postgres.db.svc.cluster.local:5432
curl: (52) Empty reply from server
command terminated with exit code 52

참고로 간단히 테스트를 위해 curl로 postgres port인 5432 접근 여부만 확인하였다.

 

이제 아래와 같이 NetworkPolicy를 설정해 보자.

# cat <<EOF | kubectl create -n db -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db.postgres
  namespace: db
spec:
  podSelector:
    matchLabels:
      app: postgres
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: app1
  policyTypes:
  - Ingress
EOF

 

이제 다시, nginx에서 postgres에 접근해 보자.

# k exec -it -n app1 nginx-deployment-7848d4b86f-55t89 -- /usr/bin/curl postgres.db.svc.cluster.local:5432
curl: (52) Empty reply from server
command terminated with exit code 52

# # k exec -it -n app2 nginx-deployment-7848d4b86f-krzwt -- /usr/bin/curl postgres.db.svc.cluster.local:5432
curl: (28) Failed to connect to postgres.db.svc.cluster.local port 5432: Connection timed out
command terminated with exit code 28

 

위와 같이 app2 namespace에서는 접근이 안되는것을 확인할 수 있다.

 

 

예시2) 특정 서비스에 대한 여러 조건에 대한 인그레스 트래픽 거부

 

아래와 같은 조건으로 테스트 해보자.

  • db namespace 있는 app=postgres 인 label을 가진 pod에 대한 ingress 접근 제한 ( 외부에서 접근이 가능하도록 NodePort로 변경한다. )
  • 10.10.0.0/16 ip 대역의 접근 허용
  • namespace: app1에서 app=nginx label을 가진 pod의 접근 허용 (AND 조건임)

예시1에서 사용한 앱을 그대로 사용하고 아래와 같은 NetworkPolicy로 생성해 보자.

 

먼저 postgres service를 아래와 같이 수정한다.

cat <<EOF | kubectl create -n db -f -
apiVersion: v1
kind: Service
metadata:
  name: postgres
spec:
  type: NodePort
  ports:
  - name: postgres
    port: 5432
    targetPort: 5432
    protocol: TCP 
  selector:
    app: postgres
EOF
참고로, 외부 PC에서 접속하기 위해 externalTrafficPolicy를 Local로 설정해야 인식하는 경우가 있다.

 

Network Policy는 아래와 같다.

cat <<EOF | kubectl create -n db -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: network-policy
  namespace: db
spec:
  podSelector:
    matchLabels:
      app: postgres
  policyTypes:
  - Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 10.10.0.0/16
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: app1 
      podSelector:
        matchLabels:
          app: nginx
EOF

 

테스트결과

 

1) 10.10.x.x ip를 가진 PC에서 테스트 (성공)

# curl 10.60.200.122:30726
curl: (52) Empty reply from server

 

2) 10.60.x.x ip를 가진 PC에서 테스트 (실패)

# curl 10.60.200.122:30726
curl: (7) Failed connect to 10.60.200.122:30726; 연결 시간 초과

 

3) 같은 cluster상에 다른 pod에서 접근시 (실패)

# k exec -it sampleapp-67447b449f-v8jf2 -- /usr/bin/curl postgres.db.svc.cluster.local:5432
curl: (28) Failed to connect to postgres.db.svc.cluster.local port 5432: Connection timed out
command terminated with exit code 28

 

4) namespace2의 app: nginx label을 가진 pod에서 접근시 (실패)

# k exec -it -n app2 nginx-deployment-7848d4b86f-krzwt -- /usr/bin/curl 10.107.93.117:5432
curl: (28) Failed to connect to 10.107.93.117 port 5432: Connection timed out
command terminated with exit code 28

 

5) namespace1의 app: nginx2 label을 가진 pod에서 접근시 (실패)

# cat <<EOF | kubectl create -n app1 -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment2
  labels:
    app: nginx2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx2
  template:
    metadata:
      labels:
        app: nginx2
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
EOF
# # k exec -it -n app1 nginx-deployment2-577dd96cd7-fzzjd -- /usr/bin/curl 10.107.93.117:5432
curl: (28) Failed to connect to 10.107.93.117 port 5432: Connection timed out
command terminated with exit code 28

 

6) namespace1의 app: nginx label을 가진 pod에서 접근시 (성공)

]# k exec -it -n app1 nginx-deployment-7848d4b86f-55t89 -- /usr/bin/curl 10.107.93.117:5432
curl: (52) Empty reply from server
command terminated with exit code 52

 

참고자료

https://kubernetes.io/docs/concepts/services-networking/network-policies/

반응형
Comments