Emissary를 이용한 Envoy의 JWT SVID 기반 액세스 제어

개시하다


몇 달 전 지아이허브Emissary의 OSS브로드캐스트가 SPIFFT 슬랙에 공개됐는데, 에미스터리를 이용해 뭘 할 수 있을지 신경 쓰여 조사했다.또한 이 기사에서는 SPIFE/SPIRE에 대한 설명이 생략됩니다.

이른바 Emissary



Emissary는 Envoy의 요청에 포함된 JWT SVID에 대한 액세스 제어 기능과 Envoy에서 다른 서비스의 요청에 JWT SVID를 추가하는 기능을 기반으로 한 소프트웨어로, Envoy의 승인 처리를 다른 시스템에 위탁하는 메커니즘External Authorization의 서버다.서버는 TCP 또는 UDS에서 시작할 수 있습니다.
External Authorizaain의 Center인 Network Filter(L4) 및 HTTP Filter(L7)의 extauthz 필터가 있지만 Emissary는 HTTP Filter의 서버로 사용됩니다.기본적으로 External Authorization에서 사용하지만, Lua 스크립트에서 사용하는 방법도 샘플 코드에 소개되어 있습니다.
Emissary에는 Ingress 모드와 Egress 모드 두 가지 모드가 있는데, Envoy의 extauthz 필터에서 Emissary까지의 요청에 포함된 x-emissary-mode 은 페이지의 눈썹에 따라 모드를 결정합니다.모드는 샘플의 Envoy 설정처럼 고정extensions.filters.http.ext_authz.v3.AuthorizationRequestheaders_to_add으로 설정하는 것이 좋다.

Ingress 모드


Ingress 모드는 HTTP 요청x-emissary-auth 제목에서 건너온 JWT SVID의 디지털 서명을 확인한 후aud에 자체 SPIFT ID를 설정했는지 확인한 후 HTTP 방법과 HTTP 경로 등의 요청 정보에 따라 액세스 제어(인정)를 하는 기능이다.
액세스 제어의 정의는 Emissary가 시작될 때EMISSARY_INGRESS_MAP의 환경 변수에서 JSON 형식으로 설정되어야 합니다.다음 예제에서는 SPIFFT IDspiffe://domain.test/app가 있는 통신 상대방이 특정 엔드포인트에 액세스할 수 있도록 허용합니다.
$ EMISSARY_INGRESS_MAP='{"spiffe://domain.test/app": [{"path":"/put","methods":["PUT"]},{"path":"/p","methods":["PATCH"]},{"path":"/g","methods":["GET"]}]}'

$ echo $EMISSARY_INGRESS_MAP | jq .
{
  "spiffe://domain.test/app": [
    {
      "path": "/put",
      "methods": [
        "PUT"
      ]
    },
    {
      "path": "/p",
      "methods": [
        "PATCH"
      ]
    },
    {
      "path": "/g",
      "methods": [
        "GET"
      ]
    }
  ]
}

Egress 모드


Egress 모드는 SPIRE Agent의 Workload API를 통해 JWT SVID를 획득하고 HTTP에서 요청한x-emissary-auth 머리글에 JWT SVID를 설정하는 기능입니다.
JWT SVID의 aud(JWT를 받아들이는 시스템, 즉 통신 상대방)에 포함된 SPIFFT ID나 임의의 문자열은 Emissary가 시작될 때 JSON 형식으로 설정EMISSARY_EGRESS_MAP해야 한다.다음 정의 예제에서는 호스트 이름app.domain.test의 요청audspiffe://domain.test/app의 JWT SVID를 정의했습니다.
$ EMISSARY_EGRESS_MAP='{"app.domain.test": "spiffe://domain.test/app"}'

$ echo $EMISSARY_EGRESS_MAP | jq .
{
  "app.domain.test": "spiffe://domain.test/app"
}

전체 상황을 정리하다.


서비스 간 통신은 Emissary를 통해 설정된 Envoy를 전제로 합니다.

요청을 받았을 때

  • 요청을 받은 Envoy 설정x-emissary-mode:ingress의 제목을 HTTP Filterext_authz 필터를 통해 Emissary에 문의
  • Emissary는 요청한 x-emissary-auth 헤더에 정의된 JWT SVID를 검증한 후 EMISSARY_INGRESS_MAP에서 허가된 요청으로 판정하고 Envoy에 응답
  • Envoy 판단 결과에 따라 응답 제어
  • 요청을 보낼 때

  • Envoy와 동거하는 서비스에서 자신의 Envoy에게 요청
  • 요청을 받은 Envoy 설정x-emissary-mode:egress의 제목을 HTTP Filterext_authz 필터를 통해 Emissary에 요청
  • Emmisary는 동거 서비스 요청에 포함된 Host 제목과 EMISSARY_EGRESS_MAP에 정의된 정보를 대조하여 JWT SVIDaud에 포함된 SPIFFT ID를 판정한 후 Workload API에서 JWT SVID
  • 를 발행한다.
  • 원래 요청한 x-emissary-auth 머리글에 JWT SVID를 불러와 다른 서비스에 요청
  • 손으로 움직여야 되나?


    Kubbernetes 부팅 EmissaryExample를 제공했기 때문에 참고spire-examples로 SPIRE가 설치된 Kubbernetes 클러스터를 준비했다면 손 옆에서도 시도해 볼 수 있습니다.

    감상


    Envoy에서 JWT SVID를 사용하여 액세스 제어를 하는 용례는 매우 유용한 인상을 주지만 서비스 수량이 많은 환경에서 동적 변경EMISSARY_INGRESS_MAPEMISSARY_EGRESS_MAP의 메커니즘이 없으면 불가능하다본격적인 도입을 논의할 때는 기능 개선이나 아이디어를 빌려 새로운 것을 만드는 것이 좋다고 생각한다.
    이후 External Authorization Server에서 Egress 팟캐스트에 제목을 추가하는 사용법은 참신했다.매번 요구에 따라 JWT SVID를 발매하고 설치하면 이런 방법밖에 없나요? 이게 일반적인가요? 해설이 부족해서 자세한 분 있으면 알려주세요.
    마지막으로 단락HiNative 에 대한 기고은 엔비와 에미스토리가 동의어라고 했다.

    좋은 웹페이지 즐겨찾기