LabHub
배우기 러닝패스 코스

Istio Service Mesh

Narrowing From Deny-All to Least Privilege

LabHub 에서 이어서 보기

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

목표

기본 거부에서 출발해 호출 그래프대로만 권한을 여는 최소 권한 인가를 만들고, 각 호출의 결과를 매트릭스로 설명할 수 있게 됩니다.

왜 중요한가

인가 설계에서 가장 많이 하는 실수는 필요한 것을 하나씩 막는 방향으로 접근하는 것입니다. 그러면 빠뜨린 경로가 영원히 열려 있고, 그 사실을 아무도 모릅니다. 반대로 전면 거부에서 출발하면 빠뜨린 경로는 즉시 403 으로 드러나므로 설계가 완결됩니다. Istio 는 이 뒤집기를 spec: {} 한 줄로 표현합니다 — ALLOW 정책은 존재하는데 매칭 규칙이 없으니 아무도 통과하지 못합니다.

평가 순서도 반드시 몸에 익혀야 합니다. CUSTOM → DENY → ALLOW 순이고, ALLOW 는 "정책이 하나라도 있으면 매칭돼야 통과"입니다. 즉 ALLOW 정책을 처음 추가하는 순간 그 워크로드가 화이트리스트 모드로 바뀝니다. 이 사실을 모르고 정책 하나를 추가했다가 기존 트래픽이 전부 막히는 사고가 흔합니다. DENY 가 ALLOW 보다 먼저 평가된다는 성질은 반대로 안전망이 됩니다 — 관리 경로처럼 "무슨 일이 있어도 막아야 하는 것"은 DENY 로 한 겹 더 깔아 둡니다.

마지막으로, 인가 정책의 principals 는 mTLS 인증서의 신원에서 나옵니다. 평문으로 들어온 요청은 principal 이 비어 있어 절대 매칭되지 않으므로, STRICT 전환이 인가의 전제 조건입니다.

단계

시작 전 준비. 실습 파드는 실습마다 새로 뜨므로 앞 실습의 메시 설정은 남아 있지 않습니다. kubectl get crd authorizationpolicies.security.istio.io 가 비어 있으면 istioctl manifest generate --set profile=minimal > /root/istio/manifest.yamlkubectl apply -f /root/istio/manifest.yaml두 번 실행하고, kubectl create ns mesh-lab && kubectl label ns mesh-lab istio-injection=enabled 로 네임스페이스를 준비하세요. 2번과 4번에서 selector 가 가리킬 payments·ratings 워크로드도 이 실습 안에서 새로 만들어야 합니다.

  1. mesh-lab 에 AuthorizationPolicy deny-all 을 만드세요 — spec: {} 만 두어 selector 도 rules 도 없이(action 은 생략해 기본값 ALLOW) 적용합니다. 그리고 /root/istio/authz/out/denyall-note.txt 에 규칙이 없으면 아무도 통과하지 못하는 이유를 적으세요.
  2. mesh-lab 에 ServiceAccount frontend 를 만들고(앞 실습의 payments 워크로드가 없다면 함께 만드세요), AuthorizationPolicy allow-frontend 를 만드세요: selector.matchLabels.apppayments, actionALLOW, rules[0].from[0].source.principals 에는 cluster.local/ns/mesh-lab/sa/frontend 를 적으세요. spiffe:// 접두어를 붙이면 안 됩니다.
  3. 같은 allow-frontend 정책에 rules[0].to[0].operation 을 추가하세요. methods["GET"] 하나만, paths 에는 /api/* 를 포함하세요. 그리고 /root/istio/authz/out/path-note.txt 에 경로 매칭이 정규식이 아니라 접두어/접미어 와일드카드만 지원한다는 점을 적으세요.
  4. mesh-lab 에 ratings 워크로드(Service ratings, Deployment ratings-v1, 파드 라벨 app: ratings)를 만들고, AuthorizationPolicy allow-mesh-lab 을 만드세요: selector.matchLabels.appratings, rules[0].from[0].source.namespaces["mesh-lab"] 입니다. 그리고 /root/istio/authz/out/ns-note.txt 에 네임스페이스 단위가 서비스 어카운트(principal) 단위보다 거친 통제라는 점을 적으세요.
  5. mesh-lab 에 AuthorizationPolicy deny-admin 을 만드세요: actionDENY, rules[0].to[0].operation.paths/admin/* 을 포함합니다. 그리고 /root/istio/authz/order.txt 에 평가 순서를 한 줄에 하나씩, 세 줄로 적으세요 — 첫 줄에 CUSTOM, 둘째 줄에 DENY, 셋째 줄에 ALLOW 가 들어가야 합니다(한 줄에 몰아 쓰면 안 됩니다).
  6. mesh-lab 에 AuthorizationPolicy allow-v2-clients 를 만드세요. rules[0].when 에 조건을 두 개 두되, 하나는 key: request.headers[x-api-version]values: ["v2"] 로, 다른 하나는 key: source.namespacenotValues: ["legacy"] 로 적으세요. notValues 를 쓰는 조건의 key 에는 request.headers 가 들어가면 안 됩니다.
  7. mesh-lab 에 AuthorizationPolicy audit-sensitive 를 만드세요: actionAUDIT, rules[0].to[0].operation.paths 에 감사할 경로(예: /internal/*)를 지정합니다. 그리고 /root/istio/authz/out/audit-note.txt 에 AUDIT 이 트래픽을 막지 않고 기록만 한다는 점과, 정책을 실제로 켜기 전에 영향을 미리 보는 용도라는 점을 함께 적으세요.
  8. /root/istio/authz/out/authz-matrix.json 을 만드세요. cases 배열에 6건 이상을 담되 각 항목에는 expected("allow" 또는 "deny" 둘 중 하나)와 policy(그 결과를 만든 정책 이름 — mesh-lab 에 실제로 존재해야 하고 공백 없는 한 단어)가 있어야 합니다. 허용 예상 2건 이상, 거부 예상 2건 이상이 필요합니다. mesh-lab 의 AuthorizationPolicy 는 5개 이상이어야 하고, istioctl analyze -n mesh-lab 결과를 /root/istio/authz/out/analyze.txt 에 저장하되 Error [ 가 없어야 합니다.

참고

빈 규칙으로 전면 거부 깔기

mesh-lab 에 AuthorizationPolicy deny-all 을 만드세요 — spec: {} 만 두어 selector 도 rules 도 없이(action 은 생략해 기본값 ALLOW) 적용합니다. 그리고 /root/istio/authz/out/denyall-note.txt 에 규칙이 없으면 아무도 통과하지 못하는 이유를 적으세요.

규칙이 하나도 없는 허용 정책이 곧 전면 거부입니다. 왜 그런지는 평가 순서의 마지막 줄에 답이 있습니다. 네임스페이스 전체에 걸어야 하므로 selector 는 두지 마세요.

호출자 신원으로 한 경로만 열기

mesh-lab 에 ServiceAccount frontend 를 만들고(앞 실습의 payments 워크로드가 없다면 함께 만드세요), AuthorizationPolicy allow-frontend 를 만드세요: selector.matchLabels.apppayments, actionALLOW, rules[0].from[0].source.principals 에는 cluster.local/ns/mesh-lab/sa/frontend 를 적으세요. spiffe:// 접두어를 붙이면 안 됩니다.

정책은 받는 쪽 워크로드에 붙습니다. 신원은 IP 가 아니라 서비스 어카운트이고, 이 필드에는 접두어를 붙이지 않는다는 점에 주의하세요.

메서드와 경로까지 좁히기

같은 allow-frontend 정책에 rules[0].to[0].operation 을 추가하세요. methods["GET"] 하나만, paths 에는 /api/* 를 포함하세요. 그리고 /root/istio/authz/out/path-note.txt 에 경로 매칭이 정규식이 아니라 접두어/접미어 와일드카드만 지원한다는 점을 적으세요.

읽기만 허용하는 단계입니다. 경로 매칭은 정규식이 아니라는 점을 확인하고 메모에 적으세요.

네임스페이스 단위로 허용하기

mesh-lab 에 ratings 워크로드(Service ratings, Deployment ratings-v1, 파드 라벨 app: ratings)를 만들고, AuthorizationPolicy allow-mesh-lab 을 만드세요: selector.matchLabels.appratings, rules[0].from[0].source.namespaces["mesh-lab"] 입니다. 그리고 /root/istio/authz/out/ns-note.txt 에 네임스페이스 단위가 서비스 어카운트(principal) 단위보다 거친 통제라는 점을 적으세요.

신원 단위보다 거친 통제입니다. 언제 이 정도로 충분하고 언제 부족한지 메모에 남기세요.

거부 안전망 깔고 평가 순서 적기

mesh-lab 에 AuthorizationPolicy deny-admin 을 만드세요: actionDENY, rules[0].to[0].operation.paths/admin/* 을 포함합니다. 그리고 /root/istio/authz/order.txt 에 평가 순서를 한 줄에 하나씩, 세 줄로 적으세요 — 첫 줄에 CUSTOM, 둘째 줄에 DENY, 셋째 줄에 ALLOW 가 들어가야 합니다(한 줄에 몰아 쓰면 안 됩니다).

거부는 허용보다 먼저 평가되므로 허용 정책의 실수를 방어합니다. 순서 정리는 한 줄에 하나씩 적어야 합니다.

조건으로 더 세밀하게 좁히기

mesh-lab 에 AuthorizationPolicy allow-v2-clients 를 만드세요. rules[0].when 에 조건을 두 개 두되, 하나는 key: request.headers[x-api-version]values: ["v2"] 로, 다른 하나는 key: source.namespacenotValues: ["legacy"] 로 적으세요. notValues 를 쓰는 조건의 key 에는 request.headers 가 들어가면 안 됩니다.

조건은 여러 개를 걸 수 있고, 제외 조건도 표현할 수 있습니다. 헤더 조건과 제외 조건을 서로 다른 항목으로 나누세요.

막지 않고 기록만 하는 정책 만들기

mesh-lab 에 AuthorizationPolicy audit-sensitive 를 만드세요: actionAUDIT, rules[0].to[0].operation.paths 에 감사할 경로(예: /internal/*)를 지정합니다. 그리고 /root/istio/authz/out/audit-note.txt 에 AUDIT 이 트래픽을 막지 않고 기록만 한다는 점과, 정책을 실제로 켜기 전에 영향을 미리 보는 용도라는 점을 함께 적으세요.

정책을 진짜로 켜기 전에 영향을 먼저 보는 장치입니다. 트래픽에 어떤 영향을 주는지 정확히 적으세요.

호출별 허용·거부 매트릭스 만들기

/root/istio/authz/out/authz-matrix.json 을 만드세요. cases 배열에 6건 이상을 담되 각 항목에는 expected("allow" 또는 "deny" 둘 중 하나)와 policy(그 결과를 만든 정책 이름 — mesh-lab 에 실제로 존재해야 하고 공백 없는 한 단어)가 있어야 합니다. 허용 예상 2건 이상, 거부 예상 2건 이상이 필요합니다. mesh-lab 의 AuthorizationPolicy 는 5개 이상이어야 하고, istioctl analyze -n mesh-lab 결과를 /root/istio/authz/out/analyze.txt 에 저장하되 Error [ 가 없어야 합니다.

각 호출이 어느 정책 때문에 그렇게 결정되는지까지 적어야 합니다. 정책 이름은 클러스터에 실제로 있는 것이어야 합니다.