클라우드 컴퓨팅 & NoSQL/도커 & 쿠버네티스

쿠버네티스 #17 - 보안 (2/4) 네트워크 정책을 이용한 트래픽 통제

Terry Cho 2018. 8. 24. 18:22


쿠버네티스 #17

보안 2/4 - 네트워크 정책

조대협 (http://bcho.tistory.com)

네트워크 정책 (Network Policy)

쿠버네티스의 보안 기능중의 하나가 네트워크 정책을 정의함으로써 Pod로 부터 들어오거나 나가는 트래픽을 통제할 수 있다. Network Policy라는 기능인데, 일종의 Pod용 방화벽정도의 개념으로 이해하면 된다.

특정 IP나 포트로 부터만 트래픽이 들어오게 하거나 반대로, 특정 IP나 포트로만 트래픽을 내보내게할 수 있는 등의 설정이 가능한데, 이 외에도 다음과 같은 방법으로 Pod에 대한 Network Policy를 설정할 수 있다.

Ingress 트래픽 컨트롤 정의

어디서 들어오는 트래픽을 허용할것인지를 정의하는 방법은 여러가지가 있다.

  • ipBlock
    CIDR IP 대역으로, 특정 IP 대역에서만 트래픽이 들어오도록 지정할 수 있다.

  • podSelector
    label을 이용하여, 특정 label을 가지고 있는 Pod들에서 들어오는 트래픽만 받을 수 있다. 예를 들어 DB Pod의 경우에는 apiserver 로 부터 들어오는 트래픽만 받는것과 같은 정책 정의가 가능하다.

  • namespaceSelector
    재미있는 기능중 하나인데, 특정 namespace로 부터 들어오는 트래픽만을 받을 수 있다. 운영 로깅 서버의 경우에는 운영 환경 namespace에서만 들어오는 트래픽을 받거나, 특정 서비스 컴포넌트의 namespace에서의 트래픽만 들어오게 컨트롤이 가능하다. 내부적으로 새로운 서비스 컴포넌트를 오픈했을때, 베타 서비스를 위해서 특정 서비스나 팀에게만 서비스를 오픈하고자 할때 유용하게 사용할 수 있다.

  • Protocol & Port
    받을 수 있는 프로토콜과 허용되는 포트를 정의할 수 있다.

Egress 트래픽 컨트롤 정의

Egress 트래픽 컨트롤은 ipBlock과 Protocol & Port 두가지만을 지원한다.

  • ipBlock
    트래픽이 나갈 수 있는 IP 대역을 정의한다. 지정된 IP 대역으로만 outbound 호출을할 수 있다.

  • Protocol & Port
    트래픽을 내보낼 수 있는 프로토콜과, 포트를 정의한다.

예제

예제를 살펴보자. 아래 네트워크 정책은 app:apiserver 라는 라벨을 가지고 있는 Pod들의 ingress 네트워크 정책을 정의하는 설정파일로, 5000번 포트만을 통해서 트래픽을 받을 수 있으며, role:monitoring이라는 라벨을 가지고 있는 Pod에서 들어오는 트래픽만 허용한다.


kind: NetworkPolicy

apiVersion: networking.k8s.io/v1

metadata:

 name: api-allow-5000

spec:

 podSelector:

   matchLabels:

     app: apiserver

 ingress:

 - ports:

   - port: 5000

   from:

   - podSelector:

       matchLabels:

         role: monitoring




네트워크 정책을 정의하기 위한 전체 스키마는 다음과 같다.

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


자 그럼, 간단하게 네트워크 정책을 정의해서 적용하는 테스트를 해보자

app:shell 이라는 라벨을 가지는 pod와 app:apiserver 라는 라벨을 가지는 pod 를 만든후에, app:shell pod에서 app:apiserver pod로 HTTP 호출을 하는 것을 테스트 한다.

다음 app:apiserver pod에 label이 app:loadbalancer 인 Pod만 호출을 받을 수 있도록 네트워크 정책을 적용한 후에, app:shell pod에서 app:apiserver로 호출이 되지 않는 것을 확인해보도록 하겠다.


테스트 환경은 구글 클라우드 쿠버네티스 엔진 ( GKE : Google cloud Kubernetes engine) 를 사용하였다.

GKE의 경우에는 NetworkPolicy가 Default로 Disable 상태이기 때문에, GKE 클러스터를 만들때 또는 만든 후에, 이 기능을 Enabled 로 활성화 해줘야 한다.

아래는 GKE 클러스터 생성시, 이 기능을 활성화 하는 부분이다.


클러스터 설정이 끝났으면, 이제 테스트에 사용할 Pod 를 준비해보자.

apiserver는 아래와 같이 server.js 의 node.js 파일을 가지고 8080 포트를 통해서 서비스하는 pod가 된다.

var os = require('os');


var http = require('http');

var handleRequest = function(request, response) {

 response.writeHead(200);

 response.end("Hello World! I'm API Server  "+os.hostname() +" \n");


 //log

 console.log("["+

Date(Date.now()).toLocaleString()+

"] "+os.hostname());

}

var www = http.createServer(handleRequest);

www.listen(8080);

이 서버로 컨테이너 이미지를 만들어서 등록한후에, 그 이미지로 아래와 같이 app:apiserver 라벨을 가지는

Pod를 생성해보자.


apiVersion: v1

kind: Pod

metadata:

 name: apiserver

 labels:

   app: apiserver

spec:

 containers:

 - name: apiserver

   image: gcr.io/terrycho-sandbox/apiserver:v1

   ports:

   - containerPort: 8080

마찬가지로, app:shell 라벨을 가진 Pod도 같은  server.js 파일로 생성한다.

app:apiserver와 app:shell 라벨을 가진 pod를 생성하기 위한 코드와 yaml 파일은 https://github.com/bwcho75/kube101/tree/master/10.security/3.%20networkpolicy 를 참고하기 바란다.


두 개의 Pod를 생성하였으면 shell pod 에 kubectl exec -it {shell pod 명} -- /bin/bash를 이용해서 로그인한후에

apiserver의 URL인 10.20.3.4:8080으로 curl 로 요청을 보내보면 아래와 같이 호출되는 것을 확인할 수 있다.



이번에는 네트워크 정책을 정의하여, app:apiserver pod에 대해서 app:secure-shell 라벨을 가진 pod로 부터만 접근이 가능하도록 정책을 정해서 정의해보자


아래는 네트워크 정책을 정의한 accept-secureshell.yaml 파일이다.

kind: NetworkPolicy

apiVersion: networking.k8s.io/v1

metadata:

 name: accept-secureshell

spec:

 policyTypes:

 - Ingress

 podSelector:

   matchLabels:

     app: apiserver

 ingress:

 - from:

   - podSelector:

       matchLabels:

         app: secureshell


이 설덩은 app:apiserver 라벨이 설정된 Pod로의 트래픽은 라벨이 app:secureshell에서 보내는 트래픽만 받도록 설정한 정책이다.

%kubectl create -f accept-secureshell.yaml

명령어를 이용해서 앞에서 만든 정책을 적용한후에, 앞에서와 같이 app:shell → app:apiserver로 curl 호출을 실행하면 다음과 같이 연결이 막히는 것을 확인할 수 있다.



이외에도 다양한 정책으로, 트래픽을 컨트롤할 수 있는데, 이에 대한 레시피는 https://github.com/ahmetb/kubernetes-network-policy-recipes 문서를 참고하면 좋다.

그리드형