LabHub
배우기 러닝패스 코스

ICA — Istio Certified Associate

Building Up a Zero-Trust Policy

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

mTLS 강제와 인가 정책을 순서대로 쌓아 올리면서, 평가 순서(CUSTOM → DENY → ALLOW)와 "ALLOW 가 하나라도 있으면 기본 거부로 뒤집힌다"는 성질을 매니페스트로 체득합니다.

왜 중요한가

메시 보안 도입이 실패하는 이유는 거의 항상 순서를 건너뛰기 때문입니다. 평문 출발지를 정리하지 않고 STRICT 를 켜면 사이드카 없는 클라이언트가 즉시 끊기고, 호출 그래프를 모른 채 기본 거부를 깔면 무엇을 열어야 할지 모른 채 장애가 납니다.

특히 잊기 쉬운 사실이 하나 있습니다. principals 는 mTLS 인증서에서 나오므로 PERMISSIVE 상태의 평문 요청에는 principal 이 비어 있고, 그러면 어떤 principals 조건에도 매칭되지 않습니다. "정책은 분명히 맞는데 403"의 가장 흔한 원인입니다. 그래서 이 실습은 mTLS → 기본 거부 → 최소 권한 → 안전망 → 최종 사용자 인증 순서를 그대로 따라갑니다.

이 실습은 kwok 기반의 API 객체·매니페스트 설계 실습입니다. Kubernetes 객체는 API에 저장되지만 실제 payments 컨테이너·Envoy·JWT 검증 서버가 실행되는 것은 아닙니다. Istio 정책은 /root/ica-security/ 아래 파일로 작성하며 HTTP 응답이나 암호화 동작을 채점하지 않습니다. 예시 IdP 주소에 접속하지 않습니다.

8단계는 기존 ALLOW를 유지하고 8080 포트의 JWT 없는 요청에 DENY를 적용하는 설계입니다. 별도 JWT ALLOW는 두 조건의 AND가 아니라 허용 경로의 OR를 만들므로 쓰지 않습니다. 다른 포트의 JWT 필수화나 메트릭 접근 허용을 보장하는 정책은 아닙니다.

단계

  1. 네임스페이스 ica-security 를 만들고 라벨 istio-injection=enabled 를 붙이세요.
  2. 네임스페이스 ica-security 에 서비스어카운트 payments-apiorders-api 를 만드세요.
  3. 같은 네임스페이스에 디플로이먼트 payments-api 를 배포하세요. 파드 라벨은 app=payments-api, serviceAccountNamepayments-api, 이미지는 nginx:1.27-alpine 입니다. 그리고 서비스 payments-api 를 포트 8080(이름 http)과 9090(이름 http-metrics) 두 개로 만드세요.
  4. /root/ica-security/pa-mesh.yaml 에 PeerAuthentication 을 작성하세요. 이름 default, 네임스페이스 istio-system, mtls.mode: STRICT 이며 selector 는 두지 않습니다. 이어서 /root/ica-security/pa-workload.yaml 에 네임스페이스 ica-security, selector app: payments-api, mtls.mode: STRICT 이면서 portLevelMtls 로 포트 9090PERMISSIVE 인 정책을 작성하세요.
  5. /root/ica-security/ap-deny-all.yaml 에 AuthorizationPolicy 를 작성하세요. 이름 default-deny, 네임스페이스 ica-security, spec 은 비어 있는 객체입니다.
  6. /root/ica-security/ap-allow-orders.yaml 에 AuthorizationPolicy 를 작성하세요. selector app: payments-api, action: ALLOW 이고 규칙 하나에 from.source.principalscluster.local/ns/ica-security/sa/orders-api, to.operation.methodsPOST, to.operation.paths/v1/charges 입니다.
  7. /root/ica-security/ap-deny-admin.yaml 에 AuthorizationPolicy 를 작성하세요. selector app: payments-api, action: DENY, 규칙은 to.operation.paths/admin/* 하나뿐이며 from 은 두지 않습니다. metadata.annotationsistio.io/dry-run: "true" 를 붙이세요.
  8. /root/ica-security/ra-jwt.yaml에 RequestAuthentication을 작성하세요. 이름 payments-api-jwt, 네임스페이스 ica-security, selector app: payments-api, jwtRules는 하나이며 issuer https://idp.example.com/, jwksUri https://idp.example.com/.well-known/jwks.json, audiences [payments-api]입니다. 이어서 /root/ica-security/ap-require-jwt.yaml에 이름 require-jwt, 같은 네임스페이스·selector, action: DENY인 AuthorizationPolicy를 작성하세요. 규칙 하나의 from.source.notRequestPrincipals["*"], 같은 규칙의 to.operation.ports["8080"]입니다. 추가 from·to·rules 조건과 dry-run은 두지 않습니다. 6단계의 최소 권한 ALLOW는 그대로 유지하세요.

참고

보안 실습 네임스페이스 만들기

네임스페이스 ica-security 를 만들고 라벨 istio-injection=enabled 를 붙이세요.

사이드카가 없으면 mTLS 도 인가도 걸 수 없습니다. 자동 주입 라벨을 붙이세요.

워크로드별 전용 서비스어카운트 만들기

네임스페이스 ica-security 에 서비스어카운트 payments-apiorders-api 를 만드세요.

SPIFFE ID 의 마지막 칸이 서비스어카운트입니다. 여러 워크로드가 default 를 공유하면 정책으로 서로를 구분할 수 없습니다.

서비스어카운트를 붙인 워크로드 배포

같은 네임스페이스에 디플로이먼트 payments-api 를 배포하세요. 파드 라벨은 app=payments-api, serviceAccountNamepayments-api, 이미지는 nginx:1.27-alpine 입니다. 그리고 서비스 payments-api 를 포트 8080(이름 http)과 9090(이름 http-metrics) 두 개로 만드세요.

파드 스펙에 serviceAccountName 을 명시해야 그 신원으로 인증서가 발급됩니다. 서비스에는 애플리케이션 포트와 메트릭 포트를 함께 여세요.

메시 전역 STRICT 와 포트 예외

/root/ica-security/pa-mesh.yaml 에 PeerAuthentication 을 작성하세요. 이름 default, 네임스페이스 istio-system, mtls.mode: STRICT 이며 selector 는 두지 않습니다. 이어서 /root/ica-security/pa-workload.yaml 에 네임스페이스 ica-security, selector app: payments-api, mtls.mode: STRICT 이면서 portLevelMtls 로 포트 9090PERMISSIVE 인 정책을 작성하세요.

메시 전역 정책은 루트 네임스페이스에 정해진 이름으로 두고 selector 를 붙이지 않습니다. selector 가 붙는 순간 워크로드 정책이 됩니다. 예외 포트는 portLevelMtls 로 좁게 지정하세요.

네임스페이스 기본 거부 만들기

/root/ica-security/ap-deny-all.yaml 에 AuthorizationPolicy 를 작성하세요. 이름 default-deny, 네임스페이스 ica-security, spec 은 비어 있는 객체입니다.

규칙이 하나도 없는 ALLOW 정책이 곧 전면 거부입니다. spec 필드 자체를 빼면 안 되고, 비어 있는 상태로 두어야 합니다.

호출 그래프대로만 열어 주기

/root/ica-security/ap-allow-orders.yaml 에 AuthorizationPolicy 를 작성하세요. selector app: payments-api, action: ALLOW 이고 규칙 하나에 from.source.principalscluster.local/ns/ica-security/sa/orders-api, to.operation.methodsPOST, to.operation.paths/v1/charges 입니다.

principals 는 트러스트 도메인부터 시작하는 경로 형식입니다. 메서드와 경로는 to.operation 아래에 들어갑니다. 이 단계에서는 서비스어카운트 조건만 쓰세요.

관리 경로를 DENY 로 한 겹 더 막기

/root/ica-security/ap-deny-admin.yaml 에 AuthorizationPolicy 를 작성하세요. selector app: payments-api, action: DENY, 규칙은 to.operation.paths/admin/* 하나뿐이며 from 은 두지 않습니다. metadata.annotationsistio.io/dry-run: "true" 를 붙이세요.

DENY 는 ALLOW 보다 먼저 평가되므로 ALLOW 실수를 방어하는 안전망이 됩니다. 출발지와 무관하게 막아야 하니 from 은 두지 마세요. 운영에 올리기 전 단계이므로 그림자 평가 애너테이션도 붙입니다.

최종 사용자 토큰 필수화

/root/ica-security/ra-jwt.yaml에 RequestAuthentication을 작성하세요. 이름 payments-api-jwt, 네임스페이스 ica-security, selector app: payments-api, jwtRules는 하나이며 issuer https://idp.example.com/, jwksUri https://idp.example.com/.well-known/jwks.json, audiences [payments-api]입니다. 이어서 /root/ica-security/ap-require-jwt.yaml에 이름 require-jwt, 같은 네임스페이스·selector, action: DENY인 AuthorizationPolicy를 작성하세요. 규칙 하나의 from.source.notRequestPrincipals["*"], 같은 규칙의 to.operation.ports["8080"]입니다. 추가 from·to·rules 조건과 dry-run은 두지 않습니다. 6단계의 최소 권한 ALLOW는 그대로 유지하세요.

RequestAuthentication은 토큰 검증입니다. 별도 ALLOW는 기존 허용과 합쳐지므로 필수화가 아닙니다. JWT 주체가 없는 요청을 DENY로 먼저 거부하고, 대상 포트는 문자열 8080으로 제한하세요.