Istio Sidecar 설정을 통한 Pod 의 egress 제어
Network Policy
기본적으로 쿠버네티스에서 파드 - 파드 , 파드 - 서비스 등의 모든 통신은 기본적으로 허용이 되어있다.
이 때 Kubernetes 에서 네이티브하게 지원하는 Network Policy
로 아래와 같은 네트워크 정책을 잡아 파드-파드 등의 호출을 불가능하도록 막을 수 있다.
- 다른 파드 의 요청을 허용할지 여부 (파드 자체의 트래픽, 즉 파드 안의 컨테이너간의 요청을 제어 할 수는 없음)
- 다른 네임스페이스에 존재하는 워크로드로부터의 요청을 허용할지 여부
- IP 블록 기반의 허용 여부 (이 설정과 관계없이 파드가 실행중인 노드와의 트래픽은 항상 허용됨)
아래는 Network Policy 오브젝트의 예시이다.
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
ingress 와 egress 로 들어가고 나가는 트래픽에 대해서 파드를 선택하거나, 네임스페이스를 선택하거나, ip 대역을 지정해서 허용 여부를 결정 할 수 있다.
다만 Network Policy 로는 다음과 같은 것을 할 수 없다.
- 내부 클러스터 트래픽이 특정 게이트웨이를 통과하도록 강제 (서비스 메시나 기타 프록시를 사용하는것을 권장)
- TLS 와 관련된 모든것 (서비스 메시나 Ingress Controller 사용 권장)
- 노드 별 정책
- 서비스 네임(도메인 네임)을 타게팅한 정책
- 모든 네임스페이스 또는 파드에 반영되는 "기본" 정책
기본적으로 쿠버네티스 환경에서 내부 서비스간 호출은 "서비스" 오브젝트의 도메인을 호출해서 사용하는것이 보편적이다. (e.g example-service.example.svc.cluster.local
)
하지만 위에서 알 수 있듯이 서비스 네임
기반의 네트워크 트래픽 정책을 Network Policy
로는 구성 할 수 없다 이 때 Istio
를 쓰고있다면 이런 구성을 할 수 있다
Istio Sidecar
예를들어서 A 애플리케이션은 오직 B 데이터베이스와, C 서비스(애플리케이션)과만 통신을 하면 되고, 그 외에 것들과는 통신을 할 필요가 없다고 했을 때. Istio Sidecar 를 별도로 설정하지 않은 경우 B 데이터베이스, C서비스를 포함해서 그 외 D,E,F,G,…. 등의 같은 쿠버네티스 클러스터 안에 있는 다른 서비스들을 호출 할 수 있는 상태가 된다.
따라서 Sidecar 설정을 통해서 Egress 트래픽을 제한함으로서 특정 서비스가 허용된 서비스만 호출 할 수 있도록 강제 할 수 있다.
어떻게 설정 할 수 있나요?
일단 Istio 의 MeshConfig 부터 손을 봐야하는데.
Istio 의 서비스 메시 글로벌 옵션인 outboundTrafficPolicy
가 기본적으로는 ALLOW_ANY
로 되어있다. 이것은 대상 포트에 대한 서비스 또는 서비스 엔트리가 없는 경우 무조건적으로 아웃바운드 트래픽을 허용한다는 의미이다.
쉽게 생각해서 내부 서비스를 포함해서 외부 엔드포인트(e.g www.google.com)에 대해서 모두 다 아웃바운드 트래픽을 허용한다는 의미이다.
REGISTRY_ONLY
옵션의 경우 아웃바운트 트래픽으로 서비스 엔트리로 관리하고있거나, 서비스 레지스트리에 등록된 서비스만 아웃바운드 트래픽을 허용한다는 의미이다.
여기서 Sidecar 옵션을 활용하기 위해서는 outboundTrafficPolicy
를 REGISTRY_ONLY
로 수정하고 진행해야 한다.
meshConfig:
outboundTrafficPolicy:
mode: REGISTRY_ONLY
위와 같이 Istio 의 meshConfig 의 outboundTrafficPolicy
mode 를 수정한다. (디폴트는 ALLOW_ANY
)
apiVersion: v1
kind: Namespace
metadata:
name: red
labels:
istio-injection: enabled
---
apiVersion: v1
kind: Namespace
metadata:
name: blue
labels:
istio-injection: enabled
---
apiVersion: v1
kind: Namespace
metadata:
name: green
labels:
istio-injection: enabled
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-red-1
namespace: red
labels:
name: nginx-red-1
app: nginx-red-1
spec:
containers:
- name: nginx
image: nginx:latest
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-blue-1
namespace: blue
labels:
name: nginx-blue-1
app: nginx-blue-1
spec:
containers:
- name: nginx
image: nginx:latest
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-green-1
namespace: green
labels:
name: nginx-green-1
app: nginx-green-1
spec:
containers:
- name: nginx
image: nginx:latest
테스트를위해 red, green, blue 라는 세 네임스페이스에, nginx 파드를 각각 띄우도록 위 yaml 을 반영한다. 이 때 sidecar 는 모두 inject 되도록 Namespace 레벨에서 istio-injection: enabled
레이블이 붙어있다.
apiVersion: v1
kind: Service
metadata:
name: nginx-red-svc
namespace: red
spec:
selector:
name: nginx-red-1
ports:
- port: 80
targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-blue-svc
namespace: blue
spec:
selector:
app: nginx-blue-1
ports:
- port: 80
targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-green-svc
namespace: green
spec:
selector:
app: nginx-green-1
ports:
- port: 80
targetPort: 80
이후 각 파드에 대한 서비스 오브젝트를 생성한다.
이 상태에서 red
네임스페이스의 파드에서 green
네임스페이스의 파드로, red
네임스페이스 파드에서 blue
네임스페이스의 파드로 HTTP 요청을 아래와 같이 해본다.
$ kubectl exec -it -n red nginx-red-1 -- curl -XGET -v nginx-green-svc.green.svc.cluster.local
$ kubectl exec -it -n red nginx-red-1 -- curl -XGET -v nginx-blue-svc.blue.svc.cluster.local
Istio Sidecar
리소스를 생성해서 egress 를 강제하지 않았기 때문에 요청에 대한 응답이 잘 들어오게 된다.
apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
name: red-sidecar
namespace: red
spec:
egress:
- hosts:
- "istio-system/*"
이 때 red
네임스페이스에 대해서 egress
항목에 hosts
를 istio-system/*
로 지정하여 Sidecar 리소스를 생성해보자.
이것은 istio-system
을 제외한 나머지 네임스페이스에 존재하는 서비스 로 트래픽을 outbound 하지 않도록 하겠다는 설정을 의미한다.
hosts
내의 항목은 namespace/dnsName
포멧으로 작성한다. 위 케이스는 istio-system
네임스페이스의 모든 호스트에 대한 egress 를 허용한다는 의미이다. 이후
$ kubectl exec -it -n red nginx-red-1 -- curl -XGET -v nginx-green-svc.green.svc.cluster.local
$ kubectl exec -it -n red nginx-red-1 -- curl -XGET -v nginx-blue-svc.blue.svc.cluster.local
을 통해 red
네임스페이스에서 green
, blue
네임스페이스의 서비스를 호출 해보면 502 BadGateway 가 뜨면서 트래픽이 넘어가지 않는 것을 확인 할 수 있다.
apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
name: red-sidecar
namespace: red
spec:
egress:
- hosts:
- "istio-system/*"
- "green/*"
- "blue/*"
위와 같이 사이드카 오브젝트를 수정하고 반영한뒤 요청하면, 이 때는 응답이 잘 들어온다.
REGISTRY_ONLY 로 meshConfig 내 설정을 변경했더니 외부 도메인에 대한 트래픽 처리가 안되요!
위와 같이 Sidecar 리소스를 설정하고, outboundTrafficPolicy.mode 가 REGISTRY_ONLY 인 경우 egress 에 등록된 호스트(서비스) 외에는 트래픽이 넘어가지 않게 된다.
만약 red
nginx 앱에서 www.google.com
을 호출하고 싶다면 아래와 같이 설정해야한다.
apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
name: red-sidecar
namespace: red
spec:
egress:
- hosts:
- "istio-system/*"
- "green/*" # green 네임스페이스로 넘어가는 outbound 트래픽 허용
- "blue/*" # blue 네임스페이스로 넘어가는 outbound 트래픽 허용
- "red/*" # 자기 자신의 네임스페이스에 대한 모든 호스트 허용 -> service entry 추가 항목 반영 (ServiceEntry 를 red 네임스페이스에 생성했기 때문에)
---
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: svc-entry
namespace: red
spec:
hosts:
- www.google.com
ports:
- number: 443
name: https
protocol: TLS
- number: 80
name: http
protocol: HTTP
location: MESH_EXTERNAL
resolution: DNS
위와 같이 ServiceEntry
오브젝트를 생성하고, Sidecar 오브젝트 또한 자기 자신의 네임스페이스를 지정하면 www.google.com
에 대한 egress 트래픽도 허용되게 된다.
ServiceEntry
오브젝트에 대한 네임스페이스가 red
이기 때문에 Sidecar 에서도 red/*
을 지정했다. 만약 해당 오브젝트가 다른 네임스페이스에 있다면 다른 네임스페이스를 지정하면 된다. apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: google
namespace: entries
spec:
hosts:
- www.google.com
ports:
- number: 443
name: https
protocol: TLS
- number: 80
name: http
protocol: HTTP
location: MESH_EXTERNAL
resolution: DNS
위와 같이 entries
라는 네임스페이스에 ServiceEntry
를 생성했다면.
apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
...
spec:
egress:
- hosts:
- "istio-system/*"
- "entries/www.google.com"
과 같은 형태로 네임스페이스/FQDN 으로 지정해서 www.google.com
에 대한 egress 트래픽을 허용 할 수 있다.