Istio 심화 — 왜 그렇게 흐르는가 · AuthorizationPolicy 가 RBAC 필터 두 개가 되는 방식 · 실습
두 정책을 RBAC 필터로 옮겨 평가 순서를 확인한다
목표
DENY·ALLOW 두 AuthorizationPolicy 를 쓰고, 평가 순서로 결과를 먼저 예측한 뒤, 두 정책을 Envoy 의 RBAC 필터 두 개로 옮겨 띄워 예측과 대조한다. 순서 바꾸기·ALLOW 빼기·dry-run 으로 규칙을 하나씩 확인한다.
왜 중요한가
인가 사고의 대부분은 평가 순서를 잘못 읽은 데서 온다 — ALLOW 하나를 더했더니 전부 막히거나, 경로만 적은 DENY 가 TCP 서비스를 끊는다. 정책이 필터 사슬로 어떻게 옮겨지는지 알면 403 하나를 보고 어느 정책의 어느 줄이 막았는지 짚을 수 있다.
단계
1. /root/ist2-authz/deny.yaml 에 AuthorizationPolicy deny-admin(네임스페이스 default, selector.matchLabels.app: payments, action: DENY, 규칙 하나 — to.operation.paths: ["/admin*"])을, /root/ist2-authz/allow.yaml 에 allow-get(같은 네임스페이스·selector, action: ALLOW, to.operation.methods: ["GET"])을 쓰세요. /root/ist2-authz 에서 istioctl validate -f deny.yaml -f allow.yaml 의 출력과 종료 코드를 /root/ist2-authz/01-validate.txt 에 담으세요(마지막 줄 rc=).
2. 두 정책이 payments 에 함께 걸려 있을 때 네 요청 — GET /, GET /admin/x, POST /, POST /admin — 의 판정을 평가 순서만으로 예측해 /root/ist2-authz/02-predict.txt 에 <메서드> <경로>=allow|deny 꼴 네 줄로 적으세요(예: HEAD /=deny). 아직 아무것도 띄우지 않습니다.
3. /root/ist2-authz/authz.yaml 에 Envoy 설정을 쓰세요 — 관리 포트 9987; 리스너 virtualInbound 는 127.0.0.1:10087, HCM 의 stat_prefix 는 inbound_0.0.0.0_8110, 모든 경로를 클러스터 inbound|8110||(127.0.0.1:8110)로 보냅니다. http_filters 는 정확히 세 개를 이 순서로 둡니다 — (1) istio_authz_deny: RBAC, rules.action: DENY, 정책 이름 ns[default]-policy[deny-admin]-rule[0], 권한은 url_path 접두사 /admin; (2) envoy.filters.http.rbac: RBAC, rules.action: ALLOW, 정책 이름 ns[default]-policy[allow-get]-rule[0], 권한은 헤더 :method 가 정확히 GET; 두 정책 모두 principals: [{any: true}]; (3) 라우터. 그다음 yq 로 http_filters 를 읽어 /root/ist2-authz/03-chain.txt 에 필터마다 한 줄씩 <이름> <rules.action>(라우터는 -)을 적으세요.
4. python3 /opt/lab/envoy/upstream.py 8110 ok 로 업스트림을 띄우고 /root/ist2-authz/authz.yaml 로 Envoy 를 띄우세요. 2단계의 네 요청을 localhost:10087 로 보내 /root/ist2-authz/04-result.txt 에 <메서드> <경로>=<HTTP 코드> 네 줄을 적고, 다섯째 줄 body_403= 에는 GET /admin/x 가 받은 본문 첫 줄을 적으세요.
5. /root/ist2-authz/authz.yaml 을 /root/ist2-authz/authz-swapped.yaml 로 복사한 뒤 두 RBAC 필터의 순서만 바꾸세요(ALLOW 필터가 앞, DENY 필터가 뒤, 라우터는 맨 끝, 나머지는 그대로). 이 파일로 Envoy 를 다시 띄워 같은 네 요청을 보내고 /root/ist2-authz/05-swapped.txt 에 네 줄(<메서드> <경로>=<코드>)과 same_as_04=(네 코드가 4단계와 모두 같으면 yes, 아니면 no)을 적으세요.
6. /root/ist2-authz/authz.yaml 에서 ALLOW 필터(envoy.filters.http.rbac)만 뺀 /root/ist2-authz/authz-denyonly.yaml 을 만들어 Envoy 를 다시 띄우세요. POST / 와 GET /admin/x 를 보내 /root/ist2-authz/06-denyonly.txt 에 두 줄(<메서드> <경로>=<코드>)을 적고, 셋째 줄 evaluation_rule= 에는 POST / 를 통과시킨 것이 평가 순서 다섯 줄 중 몇 번째 줄인지 숫자로 적으세요.
7. /root/ist2-authz/authz.yaml 을 바탕으로 /root/ist2-authz/authz-dryrun.yaml 을 만드세요 — DENY 필터(istio_authz_deny)의 정책을 rules 에서 shadow_rules 로 옮기고(rules 는 두지 않음) 같은 필터에 shadow_rules_stat_prefix: istio_dry_run_deny_ 를 더합니다. ALLOW 필터와 라우터는 그대로입니다. 이 파일로 Envoy 를 다시 띄워 GET /admin/x 와 POST / 를 한 번씩 보내고, /root/ist2-authz/07-dryrun.txt 에 네 줄을 적으세요 — 두 요청의 <메서드> <경로>=<코드>, stat=(관리 포트 /stats 에서 shadow_denied 로 끝나는 통계 중 접두사가 붙은 것의 전체 이름), shadow_denied=(그 값).
8. /root/ist2-authz/08-report.md 에 다섯 줄을 적으세요 — chain=(3단계 http_filters 의 이름을 순서대로 쉼표로), deny_body=(RBAC 가 거부할 때의 본문), swapped_same=(5단계의 same_as_04 값), dry_run_field=(dry-run 정책이 들어가는 RBAC 필드 이름), deny_tcp_fix=(1단계 경고가 권하는, DENY 규칙의 operation 에 더할 필드 이름). 그 아래 - 로 시작하는 설명을 네 줄 이상 적으세요.
참고
- 이 파드에는 진짜 istiod 도 진짜 사이드카도 없습니다. 그래서
istioctl proxy-config로 실제 생성물을 볼 수 없고, 번역 규칙을 알고 손으로 등가 Envoy 설정을 만들어 동작을 확인합니다. 같은 규칙이 운영 클러스터의proxy-config출력에 그대로 보입니다. - 업스트림 흉내 서버는 GET 만 압니다. 다른 메서드가 프록시를 통과하면 앱이 501 을 돌려줍니다. 프록시가 막은 요청은 403 과 본문
RBAC: access denied로 구분합니다. - 4~7단계는 설정 파일을 바꿀 때마다 Envoy 를 다시 띄웁니다. 앞 단계의 파일은 고치지 말고 새 이름으로 만드세요 — 채점기는 떠 있는 Envoy 가 아니라 파일을 봅니다.
- Envoy 를 띄울 때는
setsid --fork nohup envoy -c <파일> --log-level warn > <로그> 2>&1 </dev/null로 셸에서 완전히 떼어 놓으세요. 다시 띄우기 전에는pkill -x envoy로 정리합니다 (pkill -f 'envoy -c'는 그 문자열이 든 셸 자신까지 죽입니다). - 업스트림 흉내용 서버가 이미지에 있습니다:
python3 /opt/lab/envoy/upstream.py <포트> ok|fail|slow. 응답 본문은<모드>:<포트> <경로>입니다. - 설정을 고친 뒤에는 띄우기 전에
envoy --mode validate -c <파일>로 먼저 거르세요. 클러스터 이름에|가 들어가므로 YAML 에서는 반드시 따옴표로 감쌉니다.
8단계
- DENY 와 ALLOW 두 정책을 쓰고 istioctl 로 거른다
- 띄우기 전에 평가 순서로 결과를 예측한다
- 두 정책을 RBAC 필터 두 개로 옮긴다
- 띄워서 예측과 대조한다
- 필터 순서를 바꿔도 판정은 같다
- ALLOW 정책이 하나도 없으면 DENY 에 안 걸린 것은 다 통과한다
- dry-run 은 같은 정책을 shadow_rules 에 넣는다
- AuthorizationPolicy 가 Envoy 에서 되는 것을 정리한다