The Source the Partner's Log Saw
한국어 원문으로 표시합니다.
목표
진짜 Istio 메시에서 Sidecar 범위·REGISTRY_ONLY·ServiceEntry·이그레스 게이트웨이·게이트웨이 인가를 차례로 걸고, 각각이 무엇을 막고 무엇을 못 막는지 받는 쪽의 로그와 상태 코드로 확인한다.
왜 중요한가
나가는 트래픽은 기본값이 '어디로든 통과' 라서 아무것도 하지 않으면 메시는 누가 어디로 나가는지 모른다. 장치를 걸어도 그것이 네임스페이스 설정인지, 게이트웨이의 강제인지 구분하지 않으면 '막았다' 고 믿는 빈틈이 남는다. 받는 쪽(협력사)이 본 출발지로 확인하는 습관이 그 빈틈을 보여 준다.
단계
kubectl apply -f /opt/fixtures/istlab/egress-app.yaml로 재료(메시 밖outside의 partner, 메시 안shop의 client,other의 intruder)를 올리고 파드가 준비될 때까지 기다리세요. 그다음/root/istlab-egress/01-baseline.txt에 세 줄을 적으세요 — client 에서http://partner.outside/의 상태 코드svc=, partner 파드 IP 로 직접 부른 상태 코드podip=, client 사이드카가 아는 클러스터 수clusters=(istioctl proxy-config clusters client.shop -o json | jq length)./root/istlab-egress/sidecar.yaml에 네임스페이스shop의Sidecardefault를 적어 적용하세요 — egress hosts 는./*(같은 네임스페이스)와istio-system/*둘뿐입니다. 적용 뒤 client 의 클러스터 수를/root/istlab-egress/02-scope.txt에clusters=로 적으세요./root/istlab-egress/sidecar.yaml의 Sidecar 에outboundTrafficPolicy.mode: REGISTRY_ONLY를 더해 다시 적용하세요. 그다음 client 에서 partner 파드 IP 로x-request-id를 붙여 부르고, 그 요청이 남긴 client 사이드카의 접근 로그 한 줄을 그대로/root/istlab-egress/03-blocked.log에 저장하세요./root/istlab-egress/serviceentry.yaml에 네임스페이스shop의ServiceEntrypartner-api를 적어 적용하세요 — 호스트api.partner.test,location: MESH_EXTERNAL, 포트 80 HTTP,resolution: DNS, 엔드포인트 주소partner.outside.svc.cluster.local. 적용 뒤 client 에서api.partner.test를 이름 풀이한 주소를/root/istlab-egress/04-se.txt에vip=로,http://api.partner.test/의 상태 코드를code=로 적으세요./root/istlab-egress/egress.yaml에 세 리소스를 적어 적용하세요 — (1) 네임스페이스shop의 IstioGatewaypartner-egress(셀렉터istio: egressgateway, 포트 80 · 프로토콜 HTTPS ·tls.mode: ISTIO_MUTUAL, 호스트api.partner.test), (2)istio-system의DestinationRuleegressgateway-for-partner(호스트istio-egressgateway.istio-system.svc.cluster.local, 부분집합partner의 포트 80 에ISTIO_MUTUAL·sni: api.partner.test), (3)shop의VirtualServicepartner-via-egress(호스트api.partner.test, gatewayspartner-egress·mesh—mesh에서 온 것은 이그레스 게이트웨이의 부분집합partner80 으로, 게이트웨이에서 온 것은api.partner.test80 으로). 적용 뒤/root/istlab-egress/05-path.txt에 이그레스 게이트웨이 파드 IPegress_pod_ip=와, 경로에 표식을 붙여 보낸 요청에 대해 partner 로그가 남긴 출발지partner_saw=를 적으세요.other네임스페이스의 intruder 에서 두 번 부르고/root/istlab-egress/06-bypass.txt에 적으세요 —http://partner.outside/를 직접 부른 상태 코드intruder_direct=,http://api.partner.test/의 상태 코드intruder_via_egress=. 같은 직접 호출을 client 에서 한 결과도client_direct=로 덧붙입니다./root/istlab-egress/egress-authz.yaml에istio-system의AuthorizationPolicypartner-only-shop을 적어 적용하세요 — 셀렉터istio: egressgateway,action: ALLOW, 출발지 주체cluster.local/ns/shop/sa/client, 목적지 호스트api.partner.test. 적용 뒤 client 의api.partner.test는 200, intruder 의 같은 요청은 403 이어야 합니다./root/istlab-egress/08-report.md에 여섯 줄 —clusters_before=·clusters_after=(1·2단계),blocked_code=(3단계 로그 줄의 상태 코드),partner_saw=(5단계에서 partner 가 본 출발지가 이그레스 게이트웨이면egressgateway, client 면client),intruder_direct=·intruder_via_egress=(6단계) — 을 적고 그 아래 배운 것을 네 줄 이상 적으세요.
참고
- 이 VM 은 준비에 2~4분 걸립니다. 사이드카의 DNS 프록시가 켜져 있어 ServiceEntry 호스트를 이름으로 부를 수 있습니다.
- client 에서 명령을 칠 때는
kubectl -n shop exec client -c curl -- curl …처럼 컨테이너를 지정하세요. - partner 의 로그는
kubectl -n outside logs partner로, 게이트웨이 로그는kubectl -n istio-system logs deploy/istio-egressgateway로 봅니다. 사이드카 로그에서는 요청 id 를 붙여 찾고, 게이트웨이를 지나는 요청은 게이트웨이가 id 를 새로 매기므로 경로에 표식을 붙여 찾습니다. - 설정을 바꾼 직후에는 프록시로 퍼지는 데 몇 초가 걸립니다.
- 흔한 실수 — VirtualService 에
mesh게이트웨이를 빼는 것. 그러면 사이드카가 이그레스 게이트웨이를 거치지 않고 바로 나가고, 협력사 로그에 client 파드 IP 가 찍힙니다.
기본값에서는 어디로든 나간다
kubectl apply -f /opt/fixtures/istlab/egress-app.yaml 로 재료(메시 밖 outside 의 partner, 메시 안 shop 의 client, other 의 intruder)를 올리고 파드가 준비될 때까지 기다리세요. 그다음 /root/istlab-egress/01-baseline.txt 에 세 줄을 적으세요 — client 에서 http://partner.outside/ 의 상태 코드 svc=, partner 파드 IP 로 직접 부른 상태 코드 podip=, client 사이드카가 아는 클러스터 수 clusters=(istioctl proxy-config clusters client.shop -o json | jq length).
메시의 기본 바깥 정책은 ALLOW_ANY 입니다. 사이드카가 모르는 목적지(서비스가 아닌 파드 IP, 외부 주소)로 가는 요청은 PassthroughCluster 로 그대로 흘려보냅니다. 그리고 사이드카는 기본으로 클러스터의 모든 서비스를 알고 있어서, 서비스가 늘면 모든 프록시의 설정이 함께 커집니다. 두 사실을 숫자로 남겨 두면 뒤 단계에서 무엇이 바뀌었는지 비교할 수 있습니다.
Sidecar 리소스로 프록시가 아는 범위를 줄인다
/root/istlab-egress/sidecar.yaml 에 네임스페이스 shop 의 Sidecar default 를 적어 적용하세요 — egress hosts 는 ./*(같은 네임스페이스)와 istio-system/* 둘뿐입니다. 적용 뒤 client 의 클러스터 수를 /root/istlab-egress/02-scope.txt 에 clusters= 로 적으세요.
이름이 default 이고 셀렉터가 없는 Sidecar 는 그 네임스페이스의 모든 사이드카에 걸립니다. egress hosts 는 '이 프록시가 알아야 할 서비스' 목록이라, 여기 없는 네임스페이스의 서비스는 설정에서 빠집니다 — 수천 개 서비스가 있는 메시에서 프록시 메모리와 istiod 의 밀어 넣기 부담을 줄이는 주된 방법입니다. 줄어든 클러스터 목록에 partner.outside 가 없는지도 보세요.
모르는 목적지는 막는다 — REGISTRY_ONLY
/root/istlab-egress/sidecar.yaml 의 Sidecar 에 outboundTrafficPolicy.mode: REGISTRY_ONLY 를 더해 다시 적용하세요. 그다음 client 에서 partner 파드 IP 로 x-request-id 를 붙여 부르고, 그 요청이 남긴 client 사이드카의 접근 로그 한 줄을 그대로 /root/istlab-egress/03-blocked.log 에 저장하세요.
REGISTRY_ONLY 는 '서비스 레지스트리에 있는 곳으로만' 이라는 뜻입니다. 사이드카가 모르는 목적지는 PassthroughCluster 대신 BlackHoleCluster 로 가고 502 가 납니다. 이 네임스페이스의 레지스트리는 2단계에서 줄였으므로 partner.outside 서비스도 이제 '모르는 곳' 입니다. 로그에서 요청을 찾으려면 요청 id 를 직접 붙이세요 — kubectl -n shop logs client -c istio-proxy | grep <id>.
협력사를 레지스트리에 올린다 — ServiceEntry
/root/istlab-egress/serviceentry.yaml 에 네임스페이스 shop 의 ServiceEntry partner-api 를 적어 적용하세요 — 호스트 api.partner.test, location: MESH_EXTERNAL, 포트 80 HTTP, resolution: DNS, 엔드포인트 주소 partner.outside.svc.cluster.local. 적용 뒤 client 에서 api.partner.test 를 이름 풀이한 주소를 /root/istlab-egress/04-se.txt 에 vip= 로, http://api.partner.test/ 의 상태 코드를 code= 로 적으세요.
ServiceEntry 는 메시 밖 목적지를 레지스트리에 올려 REGISTRY_ONLY 아래에서도 나갈 수 있게 합니다. 그런데 api.partner.test 라는 이름은 클러스터 DNS 에 없습니다. 이 메시는 사이드카의 DNS 프록시를 켜 두어서, 사이드카가 이 이름에 자동으로 할당한 가상 IP(240.240.x.x)를 답합니다. 그 값은 ServiceEntry 의 status.addresses 에도 있습니다. 파드 안에서 nslookup api.partner.test 로 확인하세요.
나가는 길을 이그레스 게이트웨이 하나로 모은다
/root/istlab-egress/egress.yaml 에 세 리소스를 적어 적용하세요 — (1) 네임스페이스 shop 의 Istio Gateway partner-egress(셀렉터 istio: egressgateway, 포트 80 · 프로토콜 HTTPS · tls.mode: ISTIO_MUTUAL, 호스트 api.partner.test), (2) istio-system 의 DestinationRule egressgateway-for-partner(호스트 istio-egressgateway.istio-system.svc.cluster.local, 부분집합 partner 의 포트 80 에 ISTIO_MUTUAL·sni: api.partner.test), (3) shop 의 VirtualService partner-via-egress(호스트 api.partner.test, gateways partner-egress·mesh — mesh 에서 온 것은 이그레스 게이트웨이의 부분집합 partner 80 으로, 게이트웨이에서 온 것은 api.partner.test 80 으로). 적용 뒤 /root/istlab-egress/05-path.txt 에 이그레스 게이트웨이 파드 IP egress_pod_ip= 와, 경로에 표식을 붙여 보낸 요청에 대해 partner 로그가 남긴 출발지 partner_saw= 를 적으세요.
VirtualService 하나가 두 구간을 맡습니다. 사이드카(mesh)에서는 목적지를 이그레스 게이트웨이로 바꾸고, 게이트웨이에서는 진짜 목적지로 보냅니다. 사이드카 → 게이트웨이 구간의 mTLS 는 저절로 걸리지 않습니다 — Gateway 를 평문 HTTP 로 두면 요청은 지나가도 게이트웨이가 보낸 쪽의 신원을 모릅니다(7단계에서 필요합니다). DestinationRule 을 istio-system 에 두는 것은 다른 네임스페이스의 사이드카도 부분집합 partner 를 찾게 하려는 것입니다 — shop 에 두면 shop 의 사이드카만 봅니다. 거쳤는지는 받는 쪽에서 보세요. 게이트웨이는 x-request-id 를 새로 매기므로 http://api.partner.test/<표식> 처럼 경로에 표식을 붙여 partner 로그에서 찾고, 그 줄의 출발지가 이그레스 게이트웨이 파드 IP 인지 봅니다.
게이트웨이를 거치지 않는 길이 아직 남아 있다
other 네임스페이스의 intruder 에서 두 번 부르고 /root/istlab-egress/06-bypass.txt 에 적으세요 — http://partner.outside/ 를 직접 부른 상태 코드 intruder_direct=, http://api.partner.test/ 의 상태 코드 intruder_via_egress=. 같은 직접 호출을 client 에서 한 결과도 client_direct= 로 덧붙입니다.
Sidecar 리소스와 REGISTRY_ONLY 는 shop 의 프록시 설정일 뿐입니다. 다른 네임스페이스의 프록시는 여전히 ALLOW_ANY 이고, 사이드카가 없는 파드라면 아예 이 설정을 모릅니다. Istio 문서도 이그레스 게이트웨이만으로는 '모든 바깥 트래픽이 게이트웨이를 지난다' 를 강제할 수 없고, 네트워크 정책 같은 다른 장치를 함께 써야 한다고 밝힙니다. 그 빈틈을 숫자로 남기세요.
게이트웨이에서 누가 나갈 수 있는지 정한다
/root/istlab-egress/egress-authz.yaml 에 istio-system 의 AuthorizationPolicy partner-only-shop 을 적어 적용하세요 — 셀렉터 istio: egressgateway, action: ALLOW, 출발지 주체 cluster.local/ns/shop/sa/client, 목적지 호스트 api.partner.test. 적용 뒤 client 의 api.partner.test 는 200, intruder 의 같은 요청은 403 이어야 합니다.
사이드카에서 이그레스 게이트웨이까지는 mTLS 라서, 게이트웨이는 요청을 보낸 워크로드의 신원(SPIFFE)을 압니다. 그래서 '어느 네임스페이스·서비스어카운트가 이 협력사로 나갈 수 있는가' 를 게이트웨이 한 곳에서 정할 수 있습니다 — 이그레스 게이트웨이를 두는 진짜 이유가 이것입니다. ALLOW 정책이 하나라도 있으면 맞지 않는 요청은 모두 거절됩니다.
나가는 길을 어디서 막았는지 정리한다
/root/istlab-egress/08-report.md 에 여섯 줄 — clusters_before=·clusters_after=(1·2단계), blocked_code=(3단계 로그 줄의 상태 코드), partner_saw=(5단계에서 partner 가 본 출발지가 이그레스 게이트웨이면 egressgateway, client 면 client), intruder_direct=·intruder_via_egress=(6단계) — 을 적고 그 아래 배운 것을 네 줄 이상 적으세요.
값은 앞 단계 파일에서 옮기세요. 설명 줄에는 'Sidecar·REGISTRY_ONLY·이그레스 게이트웨이·인가 정책이 각각 무엇을 막고 무엇을 못 막는가' 를 자기 말로 적어 두세요.