블로그 이미지
평범하게 살고 싶은 월급쟁이 기술적인 토론 환영합니다.같이 이야기 하고 싶으시면 부담 말고 연락주세요:이메일-bwcho75골뱅이지메일 닷컴. 조대협


Archive»


 
 


쿠버네티스 #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 문서를 참고하면 좋다.


도커는 컨테이너로 기동하기 때문에 네트워크를 통해서 도커의 IP를 접근하거나 또는 도커에서 호스트의 IP를 접근하기위해서는 별도의 설정이 필요하다.


시간을 많이 소요한 부분이 MAC 환경이 다름을 인지 못했기 때문인데, 도커에는 네트워크 모드중에 host 모드(docker run --net="host" )로 설정하고 기동하면 된다.라는 것이 있다. 이 경우 도커의 네트워킹이 host 머신의 네트워크를 그대로 사용하기 때문에, host의 ip와 port가 그대로 도커와 연결이 되지만, 이 host 모드는 MAC에서는 작동을 하지 않는다. 


MAC의 경우에는 이 Host 모드가 동작하지 않는다.

다음과 같은 시나리오가 있다고 보자


 -->0.0.0.0:8087 (docker) --> 0.0.0.0:8081 (host)


호스트 머신에서 도커가 돌고 있을때, 이 도커가 8087로 Listen을 하고, 도커에서 호스트 머신의 8081 포트를 억세스 하고자 할때, 도커로 들어오는 8087 트래픽을 받기 위해서는 docker run -p 8087:8087 식으로 정의해서, 도커의 8087 포트와 호스트 머신의 8087 포트를 맵핑해줘야 한다.


도커에서 호스트를 호출할때는 MAC의 경우에는 host.docker.internal 주소로 호출하면 된다. https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds



L2,L3,L4 스위치

클라우드 컴퓨팅 & NoSQL/IaaS 클라우드 | 2011.05.11 23:44 | Posted by 조대협
한동안 블로그 포스팅이 뜸했습니다.
바뻤습니다.
한동안 클라우드 연구하다가 Storage에서 머리가 아퍼오더니.. Storage 산을 넘으니 이번에는 Network이 저를 괴롭힙니다.

L2 - Dummy Hub 개념으로 보면 된다. Mac Address를 보고 라우팅
L3 - Hub + Router = Subnet 이 다른 네트웍도 라우팅 기능을 이용해서 묶어 줄 수 있다. IP를 보고 라우팅
L4 - IP Layer에서 Load Balancing도 가능

Inter Rack 간 통신을 performance degration없이 묶는 법을 아시는분?

* Load Balancing의 성능 측정 단위
L4 - CPS (Connection Per Second)