Ssoon
[5주차] 마이크로서비스 통신 보안 : 서비스 간 트래픽 권한 부여 본문
🔐 Istio로 서비스 간 인가 쉽게 이해하기
🔒 9.3 서비스 간 트래픽 인가
인가(Authorization)는 인증된 주체(사용자 또는 서비스)가 특정 작업(예: 리소스 접근, 수정, 삭제)을 수행할 수 있는지 결정하는 과정입니다. 인가 정책은 누가(Who) 어떤 **작업(What)**을 할 수 있는지를 정의하며, 이를 통해 보안을 강화합니다.
Istio는 AuthorizationPolicy 리소스를 제공해 서비스 메시에서 접근 정책을 선언적으로 설정할 수 있습니다. 이 정책은 다음 세 가지 범위로 적용됩니다:
- Mesh-wide: 서비스 메시 전체에 적용.
- Namespace-wide: 특정 네임스페이스에 적용.
- Workload-specific: 특정 워크로드에 적용.
인가 정책은 신원이 도용되었을 때 접근 범위(blast radius)를 제한해 보안 위협을 최소화합니다.
AuthorizationPolicy는 인증된 주체의 작업을 제어해 서비스 간 접근을 안전하게 관리하며,
신원 도용 시 피해를 최소화합니다.
📌 핵심 요약
- 인가(Authorization)는 인증된 주체가 수행할 수 있는 작업을 정의합니다.
- Istio의 AuthorizationPolicy 리소스는 Mesh-wide, Namespace-wide, Workload-specific 범위로 접근 정책을 설정합니다.
- 인가 정책은 신원 도용 시 접근 범위를 제한해 보안을 강화합니다.
🔐 Istio 인가(Authorization) 쉽게 이해하기
🔒 9.3.1 Istio에서 인가 이해하기
- Istio에서 인가는 서비스와 함께 배포된 sidecar proxy가 수행합니다. 이 프록시는 요청을 허용하거나 거부할지 결정하는 모든 정책을 포함한 인가 엔진 역할을 합니다. 따라서 Istio의 접근 제어는 프록시 내부에서 즉시 처리되어 매우 효율적입니다.
- 인가 정책은 AuthorizationPolicy 리소스를 통해 정의됩니다. 다음은 간단한 AuthorizationPolicy 예제입니다:
$ cat ch9/allow-catalog-requests-in-web-app.yaml
apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
name: "allow-catalog-requests-in-web-app"
namespace: istioinaction
spec:
selector:
matchLabels:
app: webapp
rules:
- to:
- operation:
paths: ["/api/catalog*"] #경로가 "/api/catalog"로 시작하는 모든 요청에 이 규칙이 적용됨
action: ALLOW #규칙에 맞는 요청을 허용
#webapp 서비스는 "/api/catalog*" 경로로의 요청만 허용하고 다른 모든 경로에 대한 요청은 거부
- 이 정책이 클러스터에 적용되면, istiod는 이를 처리해 데이터 플레인 프록시에 설정을 업데이트합니다.
Istio의 인가는 sidecar proxy에서 효율적으로 처리되며,
AuthorizationPolicy 리소스로 정책을 정의합니다.
🛠️ AuthorizationPolicy의 속성
AuthorizationPolicy 리소스는 정책을 정의하는 세 가지 주요 필드로 구성됩니다:
- selector: 정책이 적용될 워크로드를 지정합니다. 예를 들어, 특정 레이블(예: app: webapp)을 가진 워크로드에만 정책을 적용할 수 있습니다.
- action: 정책의 동작을 정의합니다. 가능한 값은 다음과 같습니다:
- ALLOW: 요청이 규칙과 일치하면 허용.
- DENY: 요청이 규칙과 일치하면 거부.
- CUSTOM: 사용자 정의 정책(고급 사용 사례).
- rules: 요청이 정책을 활성화할 조건을 정의하는 규칙 목록입니다.
이 중 rules 필드가 가장 복잡하며, 요청의 출처와 작업을 기반으로 정책을 활성화합니다.
AuthorizationPolicy는 selector, action, rules 필드로 구성되며,
rules는 요청의 출처와 작업을 기반으로 정책을 활성화합니다.
📋 AuthorizationPolicy 규칙 이해하기
rules는 요청의 출처(source) 와 작업(operation) 을 기준으로 정책을 활성화합니다. 규칙이 요청과 일치하면 action(ALLOW 또는 DENY)이 적용됩니다. 규칙은 다음 필드로 구성됩니다:
- from: 요청의 출처를 지정합니다. 가능한 값은 다음과 같습니다:
- principals: 요청 출처의 신원(SPIFFE ID, mTLS로 인증된 경우 사용). notPrincipals로 반대 조건 설정 가능.
- namespaces: 요청 출처의 네임스페이스(SVID에서 추출, mTLS 필요).
- ipBlocks: 요청 출처의 IP 주소 또는 CIDR 범위.
- to: 요청의 작업을 지정합니다. 예: 요청의 호스트, HTTP 메서드, 경로 등.
- when: 규칙이 일치한 후 추가로 만족해야 하는 조건.
rules는 요청의 출처(from)와 작업(to)을 기반으로 정책을 활성화하며,
mTLS가 활성화된 경우 SPIFFE ID나 네임스페이스를 활용합니다.
📌 핵심 요약
- Istio의 인가는 sidecar proxy에서 처리되며, AuthorizationPolicy 리소스로 정책을 정의합니다.
- AuthorizationPolicy는 selector, action, rules 필드로 구성되며, 요청의 출처와 작업을 기반으로 접근을 제어합니다.
- rules는 from(출처), to(작업), when(조건) 필드를 사용해 정책을 세밀하게 설정하며, mTLS를 통해 SPIFFE ID와 네임스페이스를 활용합니다.
- Istio 공식 문서에서 AuthorizationPolicy의 모든 속성을 확인할 수 있습니다.
🔐 Istio 인가 워크스페이스 설정 쉽게 이해하기
🛠️ 9.3.2 워크스페이스 설정하기
- 이 실습은 이전 섹션의 환경을 기반으로 진행됩니다.
PeerAuthentication 정책 적용
- 이제 이전에 설정한 PeerAuthentication 정책을 적용해 서비스 간 인증을 관리합니다:
# Mesh-wide STRICT 정책 적용 => 앞에서 이미 설정함
$ kubectl -n istio-system apply -f ch9/meshwide-strict-peer-authn.yaml
peerauthentication.security.istio.io/default unchanged
# webapp 워크로드에 PERMISSIVE 정책 적용 => 앞에서 이미 설정함
$ kubectl -n istioinaction apply -f ch9/workload-permissive-peer-authn.yaml
peerauthentication.security.istio.io/webapp unchanged
catalog, webapp, sleep 서비스를 설치한 후( => 앞에서 이미 설정함),
Mesh-wide STRICT와 webapp-specific PERMISSIVE 정책을 적용합니다.
📊 현재 환경 상태 요약
현재 환경의 상태는 다음과 같습니다:
- sleep 워크로드: default 네임스페이스에 배포되며, 암호화되지 않은(clear-text) HTTP 요청을 보내는 데 사용됩니다. sidecar proxy가 없어 mTLS를 지원하지 않습니다.
- webapp 워크로드: istioinaction 네임스페이스에 배포되며, PERMISSIVE 모드로 설정되어 default 네임스페이스의 비인증 요청을 수락합니다.
- catalog 워크로드: istioinaction 네임스페이스에 배포되며, STRICT 모드로 설정되어 동일 네임스페이스의 인증된 워크로드 요청만 수락합니다.
sleep은 비인증 요청을 보내고,
webapp은 비인증 요청을 수락하며,
catalog는 인증된 요청만 수락하는 환경이 구성되었습니다.
📌 핵심 요약
- 환경 초기화를 위해 istioinaction 네임스페이스에서 기존 리소스를 삭제하고, catalog, webapp, sleep 서비스를 설치했습니다.
- Mesh-wide PeerAuthentication은 STRICT 모드로 모든 트래픽에 mTLS를 강제하며, webapp 워크로드에는 PERMISSIVE 모드를 적용해 비인증 트래픽을 허용했습니다.
- 현재 환경은 sleep(비인증 요청), webapp(비인증 수락), catalog(인증 요청만 수락) 워크로드로 구성되어 인가 실습을 준비했습니다.
🔐 Istio 인가 정책 적용 시 동작 변화 쉽게 이해하기
🔒 9.3.3 정책 적용 시 워크로드 동작 변화
- Istio에서 AuthorizationPolicy를 워크로드에 적용할 때, 한 가지 중요한 점을 미리 알아두어야 합니다.
ALLOW 정책이 하나 이상 적용된 워크로드는 기본적으로 모든 트래픽을 거부(deny-by-default) 합니다.
트래픽이 허용되려면 적어도 하나의 ALLOW 정책과 요청이 일치해야 합니다.
AuthorizationPolicy 적용
- 다음은 webapp 워크로드에 /api/catalog* 경로의 요청만 허용하는 AuthorizationPolicy 예제입니다:
$ cat ch9/allow-catalog-requests-in-web-app.yaml
apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
name: "allow-catalog-requests-in-web-app"
namespace: istioinaction
spec:
selector:
matchLabels:
app: webapp
rules:
- to:
- operation:
paths: ["/api/catalog*"]
action: ALLOW
# AuthorizationPolicy 리소스 적용
$ kubectl apply -f ch9/allow-catalog-requests-in-web-app.yaml
authorizationpolicy.security.istio.io/allow-catalog-requests-in-web-app created
- 허용되는 요청:
- 이 요청은 /api/catalog 경로와 일치하므로 정책의 ALLOW 동작에 따라 허용됩니다.
$ kubectl -n default exec deploy/sleep -c sleep -- curl -sSL webapp.istioinaction/api/catalog | jq
[
{
"id": 1,
"color": "amber",
"department": "Eyewear",
"name": "Elinor Glasses",
"price": "282.00"
},
...
- 거부되는 요청:
- 이 요청은 /hello/world 경로로, 정책에 명시된 /api/catalog*와 일치하지 않습니다. 따라서 deny-by-default 동작에 의해 거부됩니다.
$ kubectl -n default exec deploy/sleep -c sleep -- \
curl -sSL webapp.istioinaction/hello/world
RBAC: access denied
- 다음 실습을 위해 정책 삭제
$ kubectl delete -f ch9/allow-catalog-requests-in-web-app.yaml
authorizationpolicy.security.istio.io "allow-catalog-requests-in-web-app" deleted
Den-reinforced Behavior Explained
- 왜 두 번째 요청이 거부되었을까요? ALLOW 정책이 워크로드에 적용되면, 명시적으로 허용된 트래픽 외에는 모두 거부됩니다. 즉, 정책이 요청을 명시적으로 허용하거나 거부하지 않더라도, ALLOW 정책이 존재하면 기본적으로 거부됩니다.
- 모든 서비스에 대해 "이 요청이 허용될까?"를 매번 고민하는 대신, deny catch-all 정책을 추가해 관리 부담을 줄이는 것이 좋습니다. 이 정책은 다른 정책이 적용되지 않을 때 모든 트래픽을 거부하도록 설정합니다.
- deny catch-all 정책을 사용하면, 우리가 허용하고 싶은 트래픽만 신경 쓰면 됩니다. 이렇게 하면 사고 과정이 단순화되고, 보안 정책을 더 명확하게 관리할 수 있습니다.
ALLOW 정책이 적용된 워크로드는 deny-by-default 동작을 따르며,
deny catch-all 정책을 추가해 허용 트래픽만 관리하면 편리합니다.
📌 핵심 요약
- AuthorizationPolicy에 ALLOW 정책이 적용된 워크로드는 deny-by-default 동작을 따르며, 요청이 허용되려면 적어도 하나의 ALLOW 정책과 일치해야 합니다.
- 예를 들어, /api/catalog* 경로만 허용하는 정책은 해당 경로와 일치하지 않는 요청(예: /hello/world)을 거부합니다.
- deny catch-all 정책을 추가하면 허용하고 싶은 트래픽만 정의해 정책 관리를 단순화할 수 있습니다.
🔐 Istio Catch-All Deny 정책 쉽게 이해하기.
🔒 9.3.4 Catch-All 정책으로 모든 요청 기본 거부
Istio에서 보안을 강화하고 정책 관리를 쉽게 하려면, ALLOW 정책이 명시적으로 허용하지 않은 모든 요청을 거부하는 catch-all deny-all 정책을 설정하는 것이 좋습니다. 이 정책은 서비스 메시 전체에 적용되며, 허용하고 싶은 트래픽만 생각하면 되므로 관리 부담을 줄여줍니다.
Deny-All 정책 정의
- 다음은 mesh-wide deny-all 정책의 예제입니다.
- 이 정책은 spec이 비어 있어 모든 요청을 거부합니다. istio-system 네임스페이스에 적용되므로 서비스 메시 전체에 영향을 미칩니다.
$ cat ch9/policy-deny-all-mesh.yaml
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: deny-all
namespace: istio-system
spec: {}
- 이 정책을 클러스터에 적용합니다:
$ kubectl apply -f ch9/policy-deny-all-mesh.yaml
authorizationpolicy.security.istio.io/deny-all created
- 프록시가 새 설정을 반영할 때까지 잠시 기다린 후, sleep 서비스에서 webapp으로 요청을 보냅니다:
- 이 결과는 deny-all 정책이 적용되어 요청이 거부되었음을 보여줍니다. 어떤 ALLOW 정책도 이 요청을 허용하지 않았기 때문입니다.
$ kubectl -n default exec deploy/sleep -c sleep -- curl -sSL webapp.istioinaction/api/catalog
RBAC: access denied
Mesh-wide deny-all 정책은 비어 있는 spec으로 모든 요청을 거부하며,
보안을 강화하고 정책 관리를 단순화합니다.
📋 Catch-All 정책의 반대: Allow-All 정책
- deny-all 정책이 모든 요청을 거부하는 반면, allow-all 정책은 비어 있는 규칙(rules: [{}])을 사용해 모든 요청을 허용합니다. 다음은 allow-all 정책 예제입니다:
$ cat ch9/policy-allow-all-mesh.yamcat ch9/policy-allow-all-mesh.yaml
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-all
namespace: istio-system
spec:
rules:
- {}
- 이 정책은 모든 요청을 허용하므로 보안이 취약해질 수 있습니다. 따라서 실제 환경에서는 deny-all 정책을 기본으로 사용하고, 필요한 경우 특정 요청만 허용하는 ALLOW 정책을 추가하는 것이 안전합니다.
Allow-all 정책은 모든 요청을 허용하지만,
보안 강화를 위해 deny-all 정책과 함께 특정 ALLOW 정책을 사용하는 것이 좋습니다.
📌 핵심 요약
- Catch-all deny-all 정책은 istio-system 네임스페이스에 비어 있는 spec으로 정의되어 모든 요청을 기본적으로 거부합니다.
- 이 정책은 보안을 강화하고, 허용하고 싶은 트래픽만 정의하면 되므로 정책 관리를 단순화합니다.
- 반대로, allow-all 정책은 비어 있는 규칙(rules: [{}])으로 모든 요청을 허용하지만 보안에 취약합니다.
- 실습에서 deny-all 정책을 적용해 sleep 서비스의 요청이 거부됨을 확인했습니다.
🔐 Istio로 네임스페이스 기반 인가 쉽게 이해하기
🔒 9.3.5 단일 네임스페이스에서 오는 요청 허용
- Istio에서는 특정 네임스페이스에서 오는 트래픽을 허용하도록 AuthorizationPolicy를 설정할 수 있습니다.
이는 source.namespace 속성을 사용해 구현하며, 요청의 출처 네임스페이스를 기반으로 접근을 제어합니다.
네임스페이스 기반 AuthorizationPolicy 예제
- 다음은 istioinaction 네임스페이스의 워크로드가 default 네임스페이스에서 오는 HTTP GET 요청을 허용하는 정책입니다:
$ cat << EOF | kubectl apply -f -
apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
name: "webapp-allow-view-default-ns"
namespace: istioinaction # istioinaction의 워크로드
spec:
rules:
- from: # default 네임스페이스에서 시작한
- source:
namespaces: ["default"]
to: # HTTP GET 요청에만 적용
- operation:
methods: ["GET"]
EOF
authorizationpolicy.security.istio.io/webapp-allow-view-default-ns created
- 이 정책은 다음과 같이 동작합니다:
- istioinaction 네임스페이스의 워크로드에 적용.
- default 네임스페이스에서 오는 요청 중 HTTP GET 메서드만 허용.
이 정책을 적용하면, default 네임스페이스의 sleep 서비스에서 webapp으로 보내는 GET 요청이 허용될 것 같지만, 실제로는 그렇지 않습니다. 왜일까요?
$ kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/api/catalog
RBAC: access denied
레거시 워크로드의 제약
- sleep 서비스는 sidecar proxy가 없는 레거시 워크로드입니다. 따라서 mTLS를 지원하지 않고, Istio가 요청의 출처 네임스페이스를 확인할 수 있는 신원(SPIFFE ID)을 제공하지 않습니다.
이로 인해 webapp 프록시는 요청이 default 네임스페이스에서 왔는지 검증할 수 없어 정책이 작동하지 않습니다. - 이 문제를 해결하려면 다음 두 가지 방법 중 하나를 선택할 수 있습니다:
- sleep 서비스에 sidecar proxy 주입: 권장되는 방법으로, sleep 서비스에 프록시를 추가해 mTLS를 활성화하고 신원을 부여합니다. 이를 통해 요청의 출처를 검증할 수 있습니다.
- webapp에서 비인증 요청 허용: 덜 안전한 방법으로, webapp이 비인증 요청을 수락하도록 설정합니다.
- 실제 환경에서는 첫 번째 방법(sidecar proxy 주입)이 바람직하지만, 데모를 위해 두 번째 방법(비인증 요청 허용)을 사용한다고 가정하겠습니다. 이는 팀이 sidecar를 주입할 수 없는 상황(예: 팀 전체가 휴가 중)을 가정한 예제입니다.
source.namespace를 사용해 네임스페이스 기반 요청을 허용할 수 있지만,
레거시 워크로드(sleep)는 신원이 없어 정책 적용에 제약이 있습니다.
📌 핵심 요약
- AuthorizationPolicy의 source.namespace 속성은 특정 네임스페이스(default)에서 오는 HTTP GET 요청을 허용하도록 설정할 수 있습니다.
- sleep과 같은 레거시 워크로드는 sidecar proxy가 없어 mTLS와 신원을 제공하지 않으므로, 네임스페이스 기반 정책이 작동하지 않습니다.
- 해결 방법으로는 sidecar proxy 주입(권장) 또는 비인증 요청 허용(덜 안전)을 선택할 수 있습니다.
🔐 Istio로 비인증 레거시 워크로드 요청 허용하기
🔒 9.3.6 비인증 레거시 워크로드의 요청 허용
- 레거시 워크로드(예: sleep)는 sidecar proxy가 없어 mTLS를 지원하지 않으므로, 요청의 신원(SPIFFE ID)이나 네임스페이스를 확인할 수 없습니다. 이런 경우, AuthorizationPolicy에서 from 필드를 생략해 비인증 요청을 허용할 수 있습니다.
비인증 요청 허용 정책
- 다음은 webapp 워크로드가 비인증 워크로드의 HTTP GET 요청을 허용하도록 설정한 정책입니다:
$ cat ch9/allow-unauthenticated-viecat ch9/allow-unauthenticated-view-default-ns.yaml
apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
name: "webapp-allow-unauthenticated-view-default-ns"
namespace: istioinaction
spec:
selector:
matchLabels:
app: webapp
rules:
- to:
- operation:
methods: ["GET"] # GET 메소드를 사용하는 요청만 이 규칙이 적용됨
#이 정책은 GET 요청만 허용하고 다른 HTTP 메소드(POST, PUT, DELETE 등)는 허용하지 않습니다.
- selector: app: webapp 레이블을 가진 워크로드에만 적용되어, catalog 워크로드는 여전히 mTLS를 요구합니다.
- from 필드 생략: 출처(신원, 네임스페이스 등)를 확인하지 않으므로 비인증 요청을 허용.
- operation: HTTP GET 메서드 요청만 허용.
- 이 정책을 적용합니다:
$ kubectl apply -f ch9/allow-unauthenticated-view-default-ns.yaml
authorizationpolicy.security.istio.io/webapp-allow-unauthenticated-view-default-ns created
- 이제 sleep 서비스에서 webapp으로 요청을 다시 보냅니다:
$ kubectl -n default exec deploy/sleep -c sleep -- curl -sSL webapp.istioinaction/api/catalog
error calling Catalog service
- 요청이 실패했습니다! 하지만 이는 Istio의 오류가 아니라 애플리케이션 오류입니다. 무슨 일이 일어난 걸까요?
오류 분석
- webapp은 비인증 요청을 수락했지만, 요청을 처리하기 위해 catalog 서비스를 호출했습니다. 그런데 catalog 워크로드는 Mesh-wide deny-all 정책과 STRICT mTLS 설정 때문에 요청을 거부했습니다. 이전에 설정한 deny-all 정책은 명시적으로 허용된 트래픽 외에는 모두 거부하도록 설계되었기 때문에, webapp에서 catalog로의 요청에 대한 ALLOW 정책이 없어 실패한 것입니다.
- 이 문제는 우리가 deny-all 정책을 추가한 이유를 상기시킵니다: 허용하고 싶은 트래픽만 신경 쓰기 위해! 다음 섹션에서 catalog로의 요청을 허용하는 정책을 추가해 이 문제를 해결하겠습니다.
비인증 요청을 허용하려면 from 필드를 생략한 AuthorizationPolicy를 사용하지만,
후속 요청(catalog)이 deny-all 정책으로 거부될 수 있습니다.
📌 핵심 요약
- AuthorizationPolicy에서 from 필드를 생략해 webapp이 비인증 워크로드(sleep)의 HTTP GET 요청을 허용하도록 설정했습니다.
- selector를 사용해 정책을 webapp에만 적용, catalog는 mTLS를 유지했습니다.
- 요청은 webapp에서 수락되었지만, catalog로의 후속 요청이 deny-all 정책으로 거부되어 애플리케이션 오류가 발생했습니다.
- 다음 단계는 catalog로의 요청을 허용하는 ALLOW 정책을 추가하는 것입니다.
🔒 Istio를 활용한 서비스 인증 및 권한 부여
🛡️ 서비스 계정 기반 요청 인증
Istio는 서비스 계정 정보를 SVID(SPIFFE Verifiable Identity Document)에 인코딩하여, 상호 인증(mutual authentication) 과정에서 이를 검증합니다. 검증된 정보는 filter metadata에 저장되며, 이를 활용해 특정 서비스 계정에서 오는 요청만 허용하는 정책을 설정할 수 있습니다.
예를 들어, catalog 서비스가 webapp 서비스 계정에서 오는 요청만 허용하도록 설정하려면, 아래와 같은 Authorization Policy를 작성합니다. 이 정책은 catalog 서비스로 들어오는 요청을 필터링하여 webapp 서비스 계정의 principal(주체)만 허용합니다.
Authorization Policy는 SVID에 포함된 서비스 계정 정보를 기반으로
요청을 필터링하여 보안을 강화합니다.
📜 Authorization Policy 설정
- 아래는 catalog 서비스가 webapp 서비스 계정에서 오는 GET 요청만 허용하도록 설정한 Authorization Policy의 예입니다. 이 정책은 istioinaction 네임스페이스에 적용되며, catalog 앱에만 영향을 미칩니다.
$ cat ch9/catalog-viewer-cat ch9/catalog-viewer-policy.yaml
apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
name: "catalog-viewer"
namespace: istioinaction
spec:
selector:
matchLabels:
app: catalog
rules:
- from:
- source:
principals: ["cluster.local/ns/istioinaction/sa/webapp"] #istioinaction 네임스페이스의 webapp 서비스 계정으로부터 오는 요청만 이 규칙이 적용
to:
- operation:
methods: ["GET"] #GET 메소드를 사용하는 요청만 이 규칙이 적용됨
- selector: 정책이 적용될 워크로드를 지정합니다. 여기서는 app: catalog 레이블을 가진 워크로드에 적용됩니다.
- principals: 요청을 보내는 주체의 신원을 정의합니다. 여기서는 webapp 서비스 계정만 허용합니다.
- methods: 허용할 HTTP 메서드를 지정합니다. 이 경우 GET 요청만 허용됩니다.
- 이 정책은 webapp 서비스 계정에서 오는 GET 요청만 catalog 서비스로 전달되도록 보장합니다.
Authorization Policy는 catalog 서비스가 webapp 서비스 계정의
GET 요청만 허용하도록 설정합니다.
🚀 정책 적용
- 작성한 Authorization Policy를 클러스터에 적용합니다.
$ kubectl apply -f ch9/catalog-viewer-policy.yaml
authorizationpolicy.security.istio.io/catalog-viewer created
- 정책이 적용되면, webapp 서비스 계정에서 오는 요청만 catalog 서비스에 도달할 수 있습니다. 다른 신원의 요청은 차단됩니다.
kubectl apply 명령어로 Authorization Policy를
클러스터에 배포하여 정책을 즉시 적용할 수 있습니다.
✅ 요청 테스트
- 정책 적용 후, webapp 서비스에서 catalog 서비스로 요청을 보내 테스트해 봅시다.
아래 명령어는 sleep 파드에서 webapp.istioinaction/api/catalog 엔드포인트로 요청을 보냅니다.
$ kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/api/catalog | jq
[
{
"id": 1,
"color": "amber",
"department": "Eyewear",
"name": "Elinor Glasses",
"price": "282.00"
},
...
- 이 응답은 요청이 성공적으로 catalog 서비스에 도달했음을 보여줍니다. 이 정책 덕분에 다른 신원의 요청은 차단되어 보안이 강화되었다는 점입니다.
정책 적용 후 webapp 서비스 계정에서 보낸 요청만
catalog 서비스에 성공적으로 도달합니다.
🛑 보안 강화의 중요성
- 이 정책은 webapp 서비스 계정에서 오는 요청만 허용함으로써, 워크로드의 신원이 도용되더라도 피해를 최소화합니다. 예를 들어, 다른 서비스 계정이나 신원에서 오는 요청은 차단되므로, 공격자가 시스템에 침투하더라도 catalog 서비스에 접근할 수 없습니다.
- Istio의 Authorization Policy를 활용하면 세밀한 수준에서 접근 제어를 설정할 수 있어, 보안 위협에 효과적으로 대응할 수 있습니다.
Authorization Policy는 신원 도용 시 피해를 최소화하며,
세밀한 접근 제어로 보안을 강화합니다.
📌 핵심 요약
- SVID와 filter metadata: Istio는 SVID를 통해 서비스 계정 정보를 검증하고, 이를 filter metadata에 저장하여 Authorization Policy에서 활용합니다.
- Authorization Policy: catalog 서비스가 webapp 서비스 계정에서 오는 GET 요청만 허용하도록 설정합니다.
- 정책 적용: kubectl apply 명령어로 정책을 클러스터에 배포합니다.
- 테스트: 정책 적용 후 webapp 서비스 계정의 요청만 catalog 서비스에 도달합니다.
- 보안 강화: 신원 도용 시 피해를 최소화하며, 세밀한 접근 제어로 보안을 강화합니다.
🔒 Istio의 조건부 Authorization Policy 활용
🛡️ 조건부 정책의 필요성
- 서비스 보안 정책을 설정할 때, 모든 요청을 동일하게 처리하는 대신 특정 조건이 충족될 때만 허용하고 싶을 때가 있습니다. 예를 들어, admin 권한을 가진 사용자에게만 모든 작업을 허용하려면, Authorization Policy의 when 속성을 사용해 조건을 정의할 수 있습니다. 이 속성은 요청의 특정 속성(예: JWT의 클레임)을 확인하여 정책 적용 여부를 결정합니다.
when 속성을 사용하면 특정 조건(예: admin 권한)이 충족될 때만
요청을 허용하는 정책을 설정할 수 있습니다.
📜 조건부 Authorization Policy 설정
아래는 admin 그룹에 속한 사용자에게만 모든 작업을 허용하는 Authorization Policy의 예입니다. 이 정책은 istio-system 네임스페이스에 적용되며, 두 가지 조건을 확인합니다:
- 요청의 requestPrincipal이 auth@istioinaction.io/*에서 발급된 토큰인지.
- JWT에 포함된 group 클레임의 값이 admin인지.
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-mesh-all-ops-admin
namespace: istio-system
spec:
rules:
- from:
- source:
requestPrincipals: ["auth@istioinaction.io/*"]
when:
- key: request.auth.claims[group]
values: ["admin"]
- requestPrincipals: 요청의 주체가 특정 발급자(issuer)에서 온 JWT 토큰인지 확인합니다. 여기서는 auth@istioinaction.io/*로 시작하는 발급자를 허용합니다.
- when: 조건부 검사를 정의합니다. request.auth.claims[group] 키의 값이 admin이어야 정책이 적용됩니다.
- values: 조건에 부합해야 하는 값의 리스트를 지정합니다. 여기서는 admin만 허용됩니다.
- 이 정책은 위 두 조건이 모두 충족될 때만 요청을 허용합니다.
Authorization Policy는 admin 그룹에 속한 사용자에게만
모든 작업을 허용하도록 설정합니다.
🔄 notValues로 제외 조건 설정
- 때로는 특정 값에 대해 정책을 적용하지 않도록 설정하고 싶을 수 있습니다. 이를 위해 notValues 속성을 사용할 수 있습니다. 예를 들어, request.auth.claims[group] 값이 admin이 아닌 경우에만 정책을 적용하려면, notValues: ["admin"] 로 설정하면 됩니다.
notValues를 사용하면 특정 값을 제외한 경우에 정책을 적용할 수 있으며,
Istio 속성 문서에서 조건으로 사용할 수 있는 속성을 확인할 수 있습니다.
🆚 Principals vs. RequestPrincipals
Authorization Policy의 from 절에서는 요청 주체를 식별하기 위해 두 가지 옵션을 제공합니다: principals와 requestPrincipals. 이 둘의 차이점은 다음과 같습니다:
- principals: mTLS(mutual TLS) 연결에서 확인된 피어(peer)의 신원을 나타냅니다. 이는 PeerAuthentication 설정을 통해 관리됩니다.
- requestPrincipals: RequestAuthentication을 통해 확인된 최종 사용자(end-user)의 신원을 나타냅니다. 이 신원은 주로 JWT(JSON Web Token)에서 추출됩니다.
principals는 mTLS 기반의 서비스 신원을,
requestPrincipals는 JWT 기반의 사용자 신원을 나타냅니다.
📌 핵심 요약
- 조건부 정책: when 속성을 사용해 특정 조건(예: JWT의 group 클레임이 admin)이 충족될 때만 요청을 허용할 수 있습니다.
- Authorization Policy 예시: requestPrincipals와 request.auth.claims[group] 조건을 사용해 admin 사용자에게만 모든 작업을 허용합니다.
- notValues: 특정 값을 제외한 경우에 정책을 적용할 수 있습니다.
- principals vs. requestPrincipals: principals는 mTLS 기반의 서비스 신원, requestPrincipals는 JWT 기반의 사용자 신원을 나타냅니다.
- 참조 자료: Istio 공식 문서에서 조건 속성과 신원 식별 방법을 확인할 수 있습니다.
🔒 Istio의 Value-Match 표현식과 정책 평가 이해
🛡️ Value-Match 표현식의 종류
Istio는 Authorization Policy에서 값을 비교할 때 단순히 정확한 일치를 요구하지 않고, 다양한 value-match 표현식을 지원하여 규칙을 더 유연하게 만듭니다. 지원되는 표현식은 다음과 같습니다:
- Exact Matching: 정확한 값과 일치해야 합니다. 예를 들어, GET은 정확히 GET 메서드와만 일치합니다.
- Prefix Matching: 값이 특정 접두사로 시작하는 경우를 허용합니다. 예를 들어, /api/catalog*는 /api/catalog/1, /api/catalog/list 등과 일치합니다.
- Suffix Matching: 값이 특정 접미사로 끝나는 경우를 허용합니다. 예를 들어, *.istioinaction.io는 login.istioinaction.io, app.istioinaction.io 등과 일치합니다.
- Presence Matching: 필드가 존재하기만 하면 값과 상관없이 허용합니다. 이는 *로 표시하며, 값이 무엇이든 상관없습니다.
Istio는 Exact, Prefix, Suffix, Presence Matching을 통해
유연한 정책 규칙을 정의할 수 있습니다.
📜 복잡한 Authorization Policy 예시
복잡한 Authorization Policy를 통해 value-match 표현식과 정책 평가 방식을 살펴보겠습니다. 아래 정책은 두 개의 규칙을 포함하며, 요청이 이 중 하나라도 일치하면 허용됩니다.
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-mesh-all-ops-admin
namespace: istio-system
spec:
rules:
- from:
- source:
principals: ["cluster.local/ns/istioinaction/sa/webapp"]
- source:
namespaces: ["default"]
to:
- operation:
methods: ["GET"]
paths: ["/users*"]
- operation:
methods: ["POST"]
paths: ["/data"]
when:
- key: request.auth.claims[group]
values: ["beta-tester", "admin", "developer"]
- to:
- operation:
paths: ["*.html", "*.js", "*.png"]
- 첫 번째 규칙: webapp 서비스 계정 또는 default 네임스페이스에서 오는 요청이, /users* 경로에 대한 GET 요청이거나 /data 경로에 대한 POST 요청이어야 하며, JWT의 group 클레임이 beta-tester, admin, developer 중 하나여야 합니다.
- 두 번째 규칙: .html, .js, .png로 끝나는 경로에 대한 요청을 허용합니다.
이 정책은 첫 번째 또는 두 번째 규칙 중 하나라도 일치하면 요청을 허용합니다.
핵심 내용: 정책은 여러 규칙을 포함할 수 있으며, 하나라도 일치하면 요청이 허용됩니다.
🔍 첫 번째 규칙의 평가 방식
첫 번째 규칙이 요청에 적용되려면, from, to, when 속성이 모두 일치해야 합니다. 각 속성의 평가 방식을 자세히 살펴보겠습니다.
from (Sources)
from:
- source:
principals: ["cluster.local/ns/istioinaction/sa/webapp"]
- source:
namespaces: ["default"]
- from은 소스 목록을 정의합니다. 요청이 webapp 서비스 계정에서 오거나 default 네임스페이스에서 와야 합니다.
- 소스 중 하나라도 일치하면 from 조건이 충족됩니다(OR 조건).
to (Operations)
to:
- operation:
methods: ["GET"]
paths: ["/users*"]
- operation:
methods: ["POST"]
paths: ["/data"]
- to는 허용할 작업(operation)을 정의합니다. 요청이 /users* 경로에 대한 GET 요청이거나 /data 경로에 대한 POST 요청이어야 합니다.
- 작업 중 하나라도 일치하면 to 조건이 충족됩니다(OR 조건).
- 각 작업 내의 속성(methods, paths)은 모두 일치해야 합니다(AND 조건).
when (Conditions)
when:
- key: request.auth.claims[group]
values: ["beta-tester", "admin", "developer"]
- when은 추가 조건을 정의합니다. 요청의 JWT에 group 클레임이 beta-tester, admin, developer 중 하나여야 합니다.
- 모든 조건이 일치해야 when 조건이 충족됩니다(AND 조건).
종합 평가
첫 번째 규칙이 적용되려면:
- from의 소스 중 하나가 일치하고,
- to의 작업 중 하나가 일치하고,
- when의 모든 조건이 일치해야 합니다.
즉, from과 to는 각각 내부적으로 OR 조건으로 평가되지만, from, to, when은 서로 AND 조건으로 결합됩니다.
핵심 내용: 첫 번째 규칙은 소스, 작업, 조건이 모두 일치해야 적용되며, 각 속성 내부는 OR, 속성 간은 AND로 평가됩니다.
🔄 두 번째 규칙의 평가 방식
두 번째 규칙은 더 간단합니다:
to:
- operation:
paths: ["*.html", "*.js", "*.png"]
- 이 규칙은 경로가 .html, .js, .png로 끝나는 모든 요청을 허용합니다.
- from이나 when 조건이 없으므로, 경로만 일치하면 됩니다.
핵심 내용: 두 번째 규칙은 특정 파일 확장자를 가진 경로에 대한 요청을 제한 없이 허용합니다.
📌 핵심 요약
- Value-Match 표현식: Istio는 Exact, Prefix, Suffix, Presence Matching을 지원하여 유연한 정책을 정의할 수 있습니다.
- 정책 구조: Authorization Policy는 여러 규칙을 포함하며, 하나라도 일치하면 요청이 허용됩니다.
- 첫 번째 규칙 평가: from (소스 중 하나), to (작업 중 하나), when (모든 조건)이 AND 조건으로 결합되어 평가됩니다.
- 두 번째 규칙 평가: .html, .js, .png 경로에 대한 요청을 제한 없이 허용합니다.
- 평가 로직: 소스와 작업 내부는 OR 조건, 속성 간은 AND 조건으로 평가됩니다.
🔒 Istio의 Authorization Policy 평가 순서 이해
🛡️ 정책 평가 순서의 필요성
워크로드에 여러 Authorization Policy가 적용되면, 어떤 정책이 우선 적용되는지 혼란스러울 수 있습니다. 많은 시스템은 priority 필드를 사용해 순서를 명시하지만, Istio는 고유한 평가 순서를 따릅니다. 이 순서는 CUSTOM, DENY, ALLOW 정책과 catch-all 정책의 유무에 따라 결정됩니다. 아래에서 이 과정을 단계별로 살펴보겠습니다.
Istio는 정책 평가 순서를 정의하여 복잡한 정책 환경에서도 일관된 동작을 보장합니다.
📜 Istio의 정책 평가 순서
Istio는 다음과 같은 순서로 Authorization Policy를 평가합니다:
- CUSTOM 정책: 외부 인증 서버와 통합된 CUSTOM 정책이 가장 먼저 평가됩니다. (이 글에서는 나중에 다룹니다.)
- DENY 정책: DENY 정책이 다음으로 평가됩니다. 만약 DENY 정책이 요청을 차단하면, 이후 단계는 진행되지 않습니다.
- ALLOW 정책: DENY 정책에 의해 차단되지 않았다면, ALLOW 정책이 평가됩니다. 하나라도 일치하면 요청이 허용됩니다.
- Catch-all 정책의 유무에 따른 결과:
- Catch-all 정책이 있는 경우: 이 정책이 요청의 최종 승인 여부를 결정합니다.
- Catch-all 정책이 없는 경우:
- ALLOW 정책이 하나도 없는 경우: 요청이 허용됩니다.
- ALLOW 정책이 있지만 일치하는 것이 없는 경우: 요청이 거부됩니다.
이 흐름은 복잡할 수 있지만, catch-all DENY 정책을 정의하면 훨씬 간단해집니다. 이 경우, CUSTOM과 DENY 정책에 의해 차단되지 않은 요청은 ALLOW 정책이 하나라도 일치해야 허용됩니다.
Istio는 CUSTOM, DENY, ALLOW, Catch-all 정책 순으로 평가하며,
catch-all 정책 유무에 따라 결과가 달라집니다.
🔄 Catch-all 정책의 역할
Catch-all 정책은 특정 조건 없이 모든 요청을 처리하는 정책으로, 평가 흐름의 마지막 단계에서 중요한 역할을 합니다. 예를 들어, catch-all DENY 정책을 설정하면, 명시적으로 허용되지 않은 모든 요청을 차단할 수 있습니다. 이는 보안을 강화하는 데 유용합니다.
반대로, catch-all 정책이 없고 ALLOW 정책도 없으면 요청이 기본적으로 허용되므로, 보안이 취약해질 수 있습니다. 따라서 catch-all DENY 정책을 사용하는 것이 권장됩니다.
Catch-all DENY 정책은 명시적으로 허용되지 않은 요청을 차단하여 보안을 강화합니다.
📊 평가 흐름의 시각화
Istio의 정책 평가 흐름은 조건에 따라 복잡할 수 있습니다. 이를 시각적으로 이해하기 위해 flow diagram을 참고하면 도움이 됩니다(책의 Figure 9.11 참조). 흐름은 다음과 같이 요약됩니다:
- CUSTOM 정책 확인 → 차단 여부 결정.
- DENY 정책 확인 → 차단 여부 결정.
- ALLOW 정책 확인 → 일치 시 허용.
- Catch-all 정책 또는 기본 동작(허용/거부) 적용.
catch-all DENY 정책을 사용하면, ALLOW 정책이 없으면 모든 요청이 차단되므로 흐름이 단순해집니다.
Flow diagram은 정책 평가 흐름을 시각적으로 이해하는 데 유용하며,
catch-all DENY 정책은 흐름을 간소화합니다.
📌 핵심 요약
- 정책 평가 순서: Istio는 CUSTOM → DENY → ALLOW → Catch-all 순으로 정책을 평가합니다.
- CUSTOM 정책: 외부 인증 서버와 통합된 정책이 가장 먼저 평가됩니다.
- DENY와 ALLOW: DENY가 차단하지 않으면 ALLOW 정책이 요청을 허용합니다.
- Catch-all 정책: Catch-all DENY는 보안을 강화하며, 없으면 기본 동작(허용/거부)에 따라 결과가 달라집니다.
- 시각화: Flow diagram은 평가 흐름을 이해하는 데 유용하며, catch-all DENY 정책은 흐름을 단순화합니다.
'Istio Hands-on Study [1기]' 카테고리의 다른 글
[5주차] 마이크로서비스 통신 보안 : 맞춤형 외부 인증 서비스와의 통합 (0) | 2025.04.22 |
---|---|
[5주차] 마이크로서비스 통신 보안 : 최종 사용자 인증 및 권한 부여 (0) | 2025.04.22 |
[5주차] 마이크로서비스 통신 보안 : 자동 mTLS (0) | 2025.04.22 |
[5주차] 마이크로서비스 통신 보안 : 애플리케이션-네트워킹 보안의 필요성 (0) | 2025.04.22 |
[4주차] 관찰 가능성(네트워크 동작 시각화하기) : Kiali로 시각화하기 (0) | 2025.04.22 |