데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      AWS(ALB)에서 Istio를 활용한 Kubernetes Pod별 방화벽 적용

      주재현 23.05.16
      6,984 8 0

      들어가며

      가상화를 이용한 Micro Service 들이 많아지면서 그만큼 Network Mesh 또한 복잡해지고 있습니다.

      이번 블로그는 제가 운영하고 있는 모바일지갑 플랫폼에서 Pod별 방화벽 설정을 적용하면서 좌충우돌 했던 내용을 적어 봅니다.


      Istio에 익숙하신 분들은 바로 공식 문서를 참고 하셔도 됩니다.

      https://istio.io/latest/docs/ops/configuration/traffic-management/network-topologies/

      https://istio.io/latest/docs/tasks/security/authorization/authz-ingress/#source-ip-address-of-the-original-client

      https://istio.io/latest/docs/tasks/security/authorization/authz-ingress/#ip-based-allow-list-and-deny-list


      플랫폼의 네트워크 흐름도

      image.png

      현재 모바일지갑에서 사용하는 대략적인 네트워크 흐름도 입니다.

      전체 아키텍처 참고

      https://devocean.sk.com/blog/techBoardDetail.do?ID=163545

      AWS의 ALB(Application Load Balancer)를 사용하고 있고, Istio gateway를 sidecar 방식으로 사용하고 있습니다.

      Istio와 ALB 연결 참고

      https://devocean.sk.com/blog/techBoardDetail.do?ID=163656

      기본적인 방화벽은 ALB의 Security Group에서 제어 합니다.

      하지만 아키텍처에 따라 하나의 ALB에서 여러 서비스들이 사용한다면 제약이 따르게 됩니다.

      위 그림과 같이 하나의 ALB 아래에 Any Open 서비스와 방화벽 설정이 필요한 경우를 가정했습니다.


      물론 방화벽 설정에 있어 다양한 방법이 가능합니다. 오늘은 Pod에 방화벽 설정을 목표로 합니다.

      • Load Balancer를 분리하여 Security Group을 설정하는 방법이 가장 확실합니다.

      • 동일한 ALB내에서 아래와 같이 Admin Service의 port를 8443과 같은 방법으로 분리하여 별도의 Listeners 설정도 가능합니다.

        image.png


      테스트를 위한 환경 준비

      테스트를 위해 Istio 공식 sample pod인 httpbin을 deploy 합니다.

      Istio sample pod

      https://github.com/istio/istio/blob/master/samples/httpbin/httpbin.yaml

      그리고 Gateway를 붙여야 하는데, 저는 아래와 같이 istio에서 제공하는 gateway를 사용하지 않고 저희 환경과 맞추기 위해 실제 저희 환경에서 사용하는 Gateway와 Virtual Service에 세팅했습니다.

      Istio sample gateway

      https://github.com/istio/istio/blob/master/samples/httpbin/httpbin-gateway.yaml

      정상 설치 되었다면 아래와 같이 pod가 보입니다. (저희는 ArgoCD를 사용합니다.)

      image.png

      ArgoCD 설치 및 사용법

      https://devocean.sk.com/blog/techBoardDetail.do?ID=163661

      기본 Curl을 보내봅니다.

      % curl --location 'https://domain.com/httpbin/get?show_env=true'

      아래와 같이 응답이 옵니다.

      {
        "args": {
          "show_env": "true"
        },
        "headers": {
          "Accept": "*/*",
          "Host": "domain.com",
          "User-Agent": "curl/7.87.0",
          "X-Amzn-Trace-Id": "Root=1-642f69b9-491858ca6438aaf71c08dcb6",
          "X-B3-Parentspanid": "d55053c40dac8f68",
          "X-B3-Sampled": "0",
          "X-B3-Spanid": "f3de16cd08c6d4a1",
          "X-B3-Traceid": "e92bc2ec07d92c74d55053c40dac8f68",
          "X-Envoy-Attempt-Count": "1",
          "X-Envoy-External-Address": "192.168.1.20",
          "X-Envoy-Original-Path": "/httpbin/get?show_env=true",
          "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/httpbin;Hash=c10549f62e61cff75232ce46e4dc0980837b1d7d457ca76dfaaf26f8969aceab;Subject=\"\";URI=spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account",
          "X-Forwarded-For": "20.1.1.1,192.168.1.20",
          "X-Forwarded-Port": "443",
          "X-Forwarded-Proto": "http",
          "X-Request-Id": "3ca5d2dd-c2fa-4439-8fff-4f7365d026d7"
        },
        "origin": "20.1.1.1,192.168.1.20",
        "url": "http://domain.com/get?show_env=true"
      }

      여기서 주목할 부분은 X-Forwarded-For 와 X-Envoy-External-Address 두개 입니다.


      Istio 설정 및 테스트

      Istio 공식 문서에 있는 IP기반 화이트 리스트 작성하기를 참고합니다.

      IP-based allow list and deny list

      https://istio.io/latest/docs/tasks/security/authorization/authz-ingress/#ip-based-allow-list-and-deny-list

      image.png

      ipBlocks 과 remoteIpBlocks 두가지를 지원하는데, 저희는 HTTP/HTTPS 기반의 ALB 이므로 remoteIpBlocks를 사용합니다.


      아래와 같이 20.1.1.1 만 허용하도록 config를 작성하여 배포합니다.

      apiVersion: security.istio.io/v1beta1
      kind: AuthorizationPolicy
      metadata:
        name: httpbin-firewall
      spec:
        selector:
          matchLabels:
            app: httpbin
        action: ALLOW
        rules:
          - from:
              - source:
                  remoteIpBlocks: ["20.1.1.1"]

      테스트를 위해 20.1.1.1 client에서 curl을 보내봅니다.

      % curl --location 'https://domain.com/httpbin/get?show_env=true'

      분명히 제대로 세팅했는데 403 Forbidden 응답이 옵니다.

      RBAC: access denied

      istio proxy의 Log를 보면 분명 "20.1.1.1,192.168.1.20" 가 제대로 들어왔는데, 안되고 있습니다.

      "GET /httpbin/get?show_env=true HTTP/1.1" 403 - rbac_access_denied_matched_policy[none] - "-" 0 19 0 - "20.1.1.1,192.168.1.20" "PostmanRuntime/7.31.3" "4df34c0d-4f55-454c-a9aa-edf17.....

      혹시나 싶어 화이트리스트 방화벽을 192.168.1.20 으로 수정했더니 잘됩니다.

      즉 현재 istio는 RemoteIP 를 제일 마지막 ip인 192.168.1.20를 바라보고 있습니다.

      하지만 또 이상한건 192.168.1.20 는 EC2 의 Private IP이고 istio proxy container의 IP는 아니라는 점입니다.


      제일 첫 그림에서 나와있지만 192.168.2.20가 istio proxy 주소이기 때문에 이론적으로 3개의 ip가 있어야 합니다.

      image.png

      이부분은 X-Forwarded-For 관련 문서를 참고하셔야 합니다. 기본적으로 아래와 같은 형식으로 Client IP를 포함해서 Proxy의 모든 IP가 찍혀야 합니다.

      X-Forwarded-For: client, proxy1, proxy2

      클라이언트 IP ⟶ Proxy 서버 및 장비 ⟶ 웹 서버

      X-Forwarded-For: 203.0.113.195, 70.41.3.18, 150.172.238.178

      https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/X-Forwarded-For

      믿을건 구글신뿐(이제 ChatGPT 갓님인가??), 검색에 들어갑니다.

      1. sidecar.istio.io/interceptionMode: TPROXY 세팅하기

      2. numTrustedProxies 적용하기

      계속 검색에 들어갔더니 저와 같은 istio 공식 issue에 동일한 사례가 있습니다.

      Incorrect RemoteIP when Authorization Policy is applied to Injected Istio Proxy #30166

      https://github.com/istio/istio/issues/30166

      여기서 단서를 발견합니다.


      use_remote_address


      Envoy의 공식 문서를 보면 use_remote_address의 세팅을 true로 해야지 client ip를 사용할 수 있다고 합니다.

      https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto

      use_remote_address

      (BoolValue) If set to true, the connection manager will **use the real remote address of the client connection **when determining internal versus external origin and manipulating various headers. If set to false or absent, the connection manager will use the x-forwarded-for HTTP header. See the documentation for x-forwarded-for, x-envoy-internal, and x-envoy-external-address for more information.

      문서에 나와 있는 EnvoyFilter를 적용하는 예제를 참고하여 아래와 같이 적용했습니다.

      apiVersion: networking.istio.io/v1alpha3
      kind: EnvoyFilter
      metadata:
        name: fix-xff
        namespace: istio-system
      spec:
        #workloadSelector:
        #labels:
        #app: istio-ingressgateway
        configPatches:
          - applyTo: NETWORK_FILTER
            match:
              context: SIDECAR_INBOUND
              listener:
                filterChain:
                  filter:
                    name: envoy.filters.network.http_connection_manager
            patch:
              operation: MERGE
              value:
                typed_config:
                  "@type": >-
                    type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                  xff_num_trusted_hops: 0
                  use_remote_address: true

      디시 테스트를 위해 20.1.1.1 client에서 curl을 보내봅니다.

      % curl --location 'https://domain.com/httpbin/get?show_env=true'

      하지만 여전히 403 Forbidden 응답이 옵니다.

      RBAC: access denied

      방화벽을 해제하고 다시 확인해 봅니다.

      {
          "args": {
              "show_env": "true"
          },
          "headers": {
              "Accept": "*/*",
              "Accept-Encoding": "gzip, deflate, br",
              "Host": "domain.com",
              "Postman-Token": "b63b4e94-89a8-4a01-88c7-89b3ba589c51",
              "User-Agent": "PostmanRuntime/7.31.3",
              "X-Amzn-Trace-Id": "Root=1-642f77b5-7d524fb530a4a5b7496c9980",
              "X-B3-Parentspanid": "37a6030f41ef91af",
              "X-B3-Sampled": "0",
              "X-B3-Spanid": "6d6123e03af86ece",
              "X-B3-Traceid": "b965f742ce381a4f37a6030f41ef91af",
              "X-Envoy-Attempt-Count": "1",
              "X-Envoy-External-Address": "192.168.2.20",
              "X-Envoy-Original-Path": "/httpbin/get?show_env=true",
              "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/httpbin;Hash=a7d5080606efe88f485e9cad795ddf7a23ab2676691f8e61a20fdb143f00af6a;Subject=\"\";URI=spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account",
              "X-Forwarded-For": "20.1.1.1,192.168.1.20,192.168.2.20",
              "X-Forwarded-Port": "443",
              "X-Forwarded-Proto": "https",
              "X-Request-Id": "6286cef0-0b1c-43bd-b017-8981a8001920"
          },
          "origin": "20.1.1.1,192.168.1.20,192.168.2.20",
          "url": "https://domain.com/get?show_env=true"
      }

      이전과 분명 달라졌습니다.

      "X-Forwarded-For": "20.1.1.1,192.168.1.20,192.168.2.20" 에 istio proxy의 주소가 제대로 찍히기 시작했습니다.

      다시 한번 공부를 해봅니다. 위에서 추가한 설정 중 xff_num_trusted_hops : 0 이 있습니다.

      xff_num_trusted_hops

      (uint32) *The number of additional ingress proxy hops from the right side of the x-forwarded-for HTTP header to trust *when determining the origin client’s IP address. The default is zero if this option is not specified. See the documentation for x-forwarded-for for more information.

      실제 remote IP가 어디에 있는지 지정해 주는 옵션입니다. 현재는 0으로 세팅했기 때문에 제일 마지막값인 192.168.2.20 를 바라보고 있습니다.

      "X-Envoy-External-Address": "192.168.2.20",

      우리는 실제 IP가 2개의 proxy를 지나서 있습니다.

      "20.1.1.1,192.168.1.20,192.168.2.20"

      즉 위와 같이 X-Forwarded-For의 값에서 오른쪽에서 2번째에 있는 20.1.1.1 가 진짜 remote ip 임을 세팅해야 합니다.


      아래와 같이 세팅하고 배포합니다.

                  xff_num_trusted_hops: 2

      다세 테스트를 했더니 제대로 동합니다.

      {
          "args": {
              "show_env": "true"
          },
          "headers": {
              "Accept": "*/*",
              "Accept-Encoding": "gzip, deflate, br",
              "Host": "domain.com",
              "Postman-Token": "b63b4e94-89a8-4a01-88c7-89b3ba589c51",
              "User-Agent": "PostmanRuntime/7.31.3",
              "X-Amzn-Trace-Id": "Root=1-642f77b5-7d524fb530a4a5b7496c9980",
              "X-B3-Parentspanid": "37a6030f41ef91af",
              "X-B3-Sampled": "0",
              "X-B3-Spanid": "6d6123e03af86ece",
              "X-B3-Traceid": "b965f742ce381a4f37a6030f41ef91af",
              "X-Envoy-Attempt-Count": "1",
              "X-Envoy-External-Address": "20.1.1.1",
              "X-Envoy-Original-Path": "/httpbin/get?show_env=true",
              "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/httpbin;Hash=a7d5080606efe88f485e9cad795ddf7a23ab2676691f8e61a20fdb143f00af6a;Subject=\"\";URI=spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account",
              "X-Forwarded-For": "20.1.1.1,192.168.1.20,192.168.2.20",
              "X-Forwarded-Port": "443",
              "X-Forwarded-Proto": "https",
              "X-Request-Id": "6286cef0-0b1c-43bd-b017-8981a8001920"
          },
          "origin": "20.1.1.1,192.168.1.20,192.168.2.20",
          "url": "https://domain.com/get?show_env=true"
      }

      이제 Pod에 적용한 화이트 리스트 IP 기반으로 정상 동작합니다.

      네트워크의 세계는 참으로 어렵고 넓습니다.


      그외 Istio Authorization Policy 설정

      참고

      https://istio.io/latest/docs/reference/config/security/authorization-policy/

      위와 같이 IP 기반 설정 말고도 많은 옵션이 있습니다.

      image.png

      아래와 같이 When 옵션을 이용하면 header 값을 검사하여 방화벽 설정도 가능합니다.

          - when:
            - key: request.headers[x-forwarded-for]
              values:
              - "220.100.50.170*"
              - "203.236.*"
              - "10.20.30.4*"


      Istio egress Outbound 방화벽 설정

      저희는 서비스 오픈 초기부터 사용하고 있지만 Outbound 방화벽도 istio를 통해 적용 가능합니다.

      기존 LB(Load Balancer)기반 L4 outbound 방화벽은 IP 적용만 가능한데, 문제점은 AWS등의 ALB에 cname mapping된 요즘 도메인의 IP는 동적으로 변경이 됩니다.

      그래서 IP를 특정할 수 없는 문제점이 있습니다. 이때 유용하게 사용할 수 있습니다.


      아래와 같이 ServiceEntry 를 통해 hosts 도메인 방화벽 적용도 가능해집니다.

      apiVersion: networking.istio.io/v1beta1
      kind: ServiceEntry
      metadata:
        name: pgadmin-ext
      spec:
        hosts:
        - security.ubuntu.com                 # ubuntu apt update Server
        - archive.ubuntu.com                  # ubuntu apt update Server
        - www.pgadmin.org                     # pgadmin Server
        - ftp.postgresql.org
        ports:
        - number: 80
          name: http
          protocol: HTTP
        - number: 443
          name: https
          protocol: HTTPS
        resolution: DNS
        location: MESH_EXTERNAL

      물론 IP를 적용할 수도 있습니다.

      apiVersion: networking.istio.io/v1beta1
      kind: ServiceEntry
      metadata:
        name: validator-ext-ip
      spec:
        hosts:
          - dummy.com
        addresses:
          - 1.1.1.1
        ports:
          - number: 1
            name: dummy
            protocol: TCP
        resolution: NONE
        location: MESH_EXTERNAL

      inbound/outbound 모두 Istio에서 적용 하시면 편리한 DevOps 가 가능합니다.

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      주재현 님의 최신 블로그

      더보기
      동영상 기고하기