Egress Gateways

Задача Доступ до зовнішніх сервісів показує, як налаштувати Istio для дозволу доступу до зовнішніх HTTP і HTTPS сервісів з застосунків всередині мережі. Там зовнішні сервіси викликаються безпосередньо з клієнтського sidecar. Цей приклад також демонструє, як налаштувати Istio для виклику зовнішніх сервісів, хоча цього разу непрямо через спеціалізований egress gateway сервіс.

Istio використовує ingress та egress gateways для налаштування балансувальників навантаження, які працюють на краю сервісної мережі. Ingress gateway дозволяє визначити точки входу в мережу, через які проходить весь вхідний трафік. Egress gateway є симетричною концепцією; він визначає точки виходу з мережі. Egress gateways дозволяють застосовувати можливості Istio, наприклад, моніторинг та правила маршрутизації, до трафіку, що виходить з мережі.

Використання

Розгляньте організацію, яка має суворі вимоги до безпеки, що весь трафік, який покидає сервісну мережу, повинен проходити через набір спеціалізованих вузлів. Ці вузли будуть працювати на окремих машинах, відокремлених від решти вузлів, які запускають застосунки в кластері. Ці спеціальні вузли будуть служити для застосування політики до вихідного трафіку і будуть моніторитися ретельніше, ніж інші вузли.

Інший випадок використання — кластер, де вузли застосунків не мають публічних IP-адрес, тому сервіси всередині мережі, які на них працюють, не можуть отримати доступ до Інтернету. Визначення egress gateway, що спрямовує весь вихідний трафік через себе, і виділення публічних IP-адрес для вузлів egress gateway дозволяє вузлам застосунків отримувати контрольований доступ до зовнішніх сервісів.

Перш ніж почати

  • Налаштуйте Istio, дотримуючись інструкцій з Посібника з встановлення.

  • Розгорніть демонстраційний застосунок curl, щоб використовувати його як джерело для надсилання тестових запитів.

    Zip
    $ kubectl apply -f @samples/curl/curl.yaml@
  • Встановіть змінну оточення SOURCE_POD на імʼя вашого вихідного podʼа:

    $ export SOURCE_POD=$(kubectl get pod -l app=curl -o jsonpath={.items..metadata.name})
  • Увімкніть ведення журналу доступу Envoy якщо його ще не ввімкнено. Наприклад, за допомогою istioctl:

    $ istioctl install <flags-you-used-to-install-Istio> --set meshConfig.accessLogFile=/dev/stdout

Розгортання egress gateway Istio

  1. Перевірте чи розгорнтуо Istio egress gateway:

    $ kubectl get pod -l istio=egressgateway -n istio-system

    Якщо жодного з podʼів не було повернено, розгорніть Istio egress gateway, виконавши наступний крок.

  2. Якщо ви використовували конфігурацію IstioOperator для встановлення Istio, додайте наступні поля до конфігурації:

    spec:
      components:
        egressGateways:
        - name: istio-egressgateway
          enabled: true

    В іншому випадку, додайте еквівалентні параметри, наприклад, до вашої оригінальної команди istioctl install:

    $ istioctl install <flags-you-used-to-install-Istio> \
                       --set "components.egressGateways[0].name=istio-egressgateway" \
                       --set "components.egressGateways[0].enabled=true"

Egress gateway для трафіку HTTP

Спочатку створіть ServiceEntry, щоб дозволити прямий трафік до зовнішнього сервісу.

  1. Визначте ServiceEntry для edition.cnn.com.

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    kind: ServiceEntry
    metadata:
      name: cnn
    spec:
      hosts:
      - edition.cnn.com
      ports:
      - number: 80
        name: http-port
        protocol: HTTP
      - number: 443
        name: https
        protocol: HTTPS
      resolution: DNS
    EOF
  2. Переконайтеся, що ваш ServiceEntry було застосовано правильно, надіславши HTTP-запит на http://edition.cnn.com/politics.

    $ kubectl exec "$SOURCE_POD" -c curl -- curl -sSL -o /dev/null -D - http://edition.cnn.com/politics
    ...
    HTTP/1.1 301 Moved Permanently
    ...
    location: https://edition.cnn.com/politics
    ...
    
    HTTP/2 200
    Content-Type: text/html; charset=utf-8
    ...

    Результат має бути таким самим, як у прикладі Створення TLS для вихідного трафіку, без TLS origination.

  3. Створіть Gateway для вихідного трафіку до edition.cnn.com на порті 80.

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: istio-egressgateway
spec:
  selector:
    istio: egressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - edition.cnn.com
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: egressgateway-for-cnn
spec:
  host: istio-egressgateway.istio-system.svc.cluster.local
  subsets:
  - name: cnn
EOF
  1. Налаштуйте правила маршрутизації, щоб спрямувати трафік від sidecars до egress gateway та від egress gateway до зовнішнього сервісу:
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: direct-cnn-through-egress-gateway
spec:
  hosts:
  - edition.cnn.com
  gateways:
  - istio-egressgateway
  - mesh
  http:
  - match:
    - gateways:
      - mesh
      port: 80
    route:
    - destination:
        host: istio-egressgateway.istio-system.svc.cluster.local
        subset: cnn
        port:
          number: 80
      weight: 100
  - match:
    - gateways:
      - istio-egressgateway
      port: 80
    route:
    - destination:
        host: edition.cnn.com
        port:
          number: 80
      weight: 100
EOF
  1. Повторно надішліть HTTP-запит до http://edition.cnn.com/politics.

    $ kubectl exec "$SOURCE_POD" -c curl -- curl -sSL -o /dev/null -D - http://edition.cnn.com/politics
    ...
    HTTP/1.1 301 Moved Permanently
    ...
    location: https://edition.cnn.com/politics
    ...
    
    HTTP/2 200
    Content-Type: text/html; charset=utf-8
    ...

    The output should be the same as in the step 2.

  2. Перевірте журнал podʼа egress gateway на наявність рядка, що відповідає нашому запиту.

Якщо Istio розгорнуто у просторі імен istio-system, команда для друку журналу буде такою:

$ kubectl logs -l istio=egressgateway -c istio-proxy -n istio-system | tail

Ви повинні побачити рядок, схожий на наступний:

[2019-09-03T20:57:49.103Z] "GET /politics HTTP/2" 301 - "-" "-" 0 0 90 89 "10.244.2.10" "curl/7.64.0" "ea379962-9b5c-4431-ab66-f01994f5a5a5" "edition.cnn.com" "151.101.65.67:80" outbound|80||edition.cnn.com - 10.244.1.5:80 10.244.2.10:50482 edition.cnn.com -

Зверніть увагу, що ви перенаправили тільки HTTP-трафік з порту 80 через egress gateway. HTTPS-трафік на порт 443 йшов безпосередньо на edition.cnn.com.

Видалення HTTP-шлюзу

Видаліть попередні визначення, перш ніж переходити до наступного кроку:

$ kubectl delete serviceentry cnn
$ kubectl delete gateway istio-egressgateway
$ kubectl delete virtualservice direct-cnn-through-egress-gateway
$ kubectl delete destinationrule egressgateway-for-cnn

Egress gateway для трафіку HTTPS

У цьому розділі ви направляєте HTTPS-трафік (TLS, ініційований застосунком) через egress gateway. Вам потрібно вказати порт 443 з протоколом TLS у відповідному ServiceEntry і egress Gateway.

  1. Визначте ServiceEntry для edition.cnn.com:

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    kind: ServiceEntry
    metadata:
      name: cnn
    spec:
      hosts:
      - edition.cnn.com
      ports:
      - number: 443
        name: tls
        protocol: TLS
      resolution: DNS
    EOF
  2. Переконайтеся, що ваш ServiceEntry було застосовано правильно, надіславши HTTPS-запит на https://edition.cnn.com/politics.

    $ kubectl exec "$SOURCE_POD" -c curl -- curl -sSL -o /dev/null -D - https://edition.cnn.com/politics
    ...
    HTTP/2 200
    Content-Type: text/html; charset=utf-8
    ...
  3. Створіть вихідний Gateway для edition.cnn.com і правила маршрутизації, щоб спрямувати трафік через egress gateway і від egress gateway до зовнішнього сервісу.

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: istio-egressgateway
spec:
  selector:
    istio: egressgateway
  servers:
  - port:
      number: 443
      name: tls
      protocol: TLS
    hosts:
    - edition.cnn.com
    tls:
      mode: PASSTHROUGH
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: egressgateway-for-cnn
spec:
  host: istio-egressgateway.istio-system.svc.cluster.local
  subsets:
  - name: cnn
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: direct-cnn-through-egress-gateway
spec:
  hosts:
  - edition.cnn.com
  gateways:
  - mesh
  - istio-egressgateway
  tls:
  - match:
    - gateways:
      - mesh
      port: 443
      sniHosts:
      - edition.cnn.com
    route:
    - destination:
        host: istio-egressgateway.istio-system.svc.cluster.local
        subset: cnn
        port:
          number: 443
  - match:
    - gateways:
      - istio-egressgateway
      port: 443
      sniHosts:
      - edition.cnn.com
    route:
    - destination:
        host: edition.cnn.com
        port:
          number: 443
      weight: 100
EOF
  1. Надішліть HTTPS-запит на адресу https://edition.cnn.com/politics. Результат має бути таким самим, як і раніше.

    $ kubectl exec "$SOURCE_POD" -c curl -- curl -sSL -o /dev/null -D - https://edition.cnn.com/politics
    ...
    HTTP/2 200
    Content-Type: text/html; charset=utf-8
    ...
  2. Перевірте журнал проксі egress gateway.

Якщо Istio розгорнуто у просторі імен istio-system, команда для друку журналу буде такою:

$ kubectl logs -l istio=egressgateway -n istio-system

Ви повинні побачити рядок, схожий на наступний:

[2019-01-02T11:46:46.981Z] "- - -" 0 - 627 1879689 44 - "-" "-" "-" "-" "151.101.129.67:443" outbound|443||edition.cnn.com 172.30.109.80:41122 172.30.109.80:443 172.30.109.112:59970 edition.cnn.com

Видалення HTTPS-шлюзу

$ kubectl delete serviceentry cnn
$ kubectl delete gateway istio-egressgateway
$ kubectl delete virtualservice direct-cnn-through-egress-gateway
$ kubectl delete destinationrule egressgateway-for-cnn

Додаткові міркування щодо безпеки

Зверніть увагу, що визначення egress Gateway в Istio не забезпечує спеціального поводження для вузлів, на яких працює сервіс egress gateway. Завдання адміністратора кластера або постачальника хмари полягає в розгортанні egress gateway на спеціалізованих вузлах та впровадженні додаткових заходів безпеки, щоб зробити ці вузли більш захищеними, ніж інші вузли мережі.

Istio не може безпечно забезпечити те, щоб весь вихідний трафік дійсно проходив через egress gateway. Istio лише дозволяє такий потік через свої sidecar проксі. Якщо зловмисники обійдуть sidecar проксі, вони можуть безпосередньо отримати доступ до зовнішніх сервісів без проходження через egress gateway. Таким чином, зловмисники уникнуть контролю та моніторингу з боку Istio. Адміністратор кластера або постачальник хмари повинні забезпечити, щоб жоден трафік не покидав мережу, оминаючи egress gateway. Механізми поза Istio повинні забезпечити цю вимогу. Наприклад, адміністратор кластера може налаштувати брандмауер для блокування всього трафіку, що не походить з egress gateway.

Політики мережі Kubernetes також можуть забороняти весь вихідний трафік, що не походить з egress gateway (див. наступний розділ для прикладу).

Крім того, адміністратор кластера або постачальник хмари можуть налаштувати мережу так, щоб вузли застосунків могли отримувати доступ до Інтернету лише через шлюз. Для цього адміністратор кластера або постачальник хмари можуть запобігти виділенню публічних IP-адрес podʼам, крім шлюзів, і налаштувати NAT-пристрої для відкидання пакетів, що не походять з egress gateways.

Застосування мережевих політик Kubernetes

У цьому розділі описується, як створити мережеву політику Kubernetes, щоб запобігти оминання шлюзу вихідного трафіку. Для тестування мережевої політики створюється простір імен test-egress, у який розгортається зразок curl, а потім виконується спроба надіслати запити до зовнішнього сервісу, захищеного шлюзом.

  1. Виконайте кроки з розділу Шлюз вихідного трафіку для HTTPS-трафіку.

  2. Створіть простір імен test-egress:

    $ kubectl create namespace test-egress
  3. Розгорніть зразок curl у просторі імен test-egress.

    Zip
    $ kubectl apply -n test-egress -f @samples/curl/curl.yaml@
  4. Перевірте, що розгорнутий pod має лише один контейнер без підключеного sidecar контейнера Istio:

    $ kubectl get pod "$(kubectl get pod -n test-egress -l app=curl -o jsonpath={.items..metadata.name})" -n test-egress
    NAME                     READY     STATUS    RESTARTS   AGE
    curl-776b7bcdcd-z7mc4    1/1       Running   0          18m
  5. Надішліть HTTPS-запит до https://edition.cnn.com/politics з podʼа curl у просторі імен test-egress. Запит буде успішним, оскільки ви ще не визначили жодних обмежувальних політик.

    $ kubectl exec "$(kubectl get pod -n test-egress -l app=curl -o jsonpath={.items..metadata.name})" -n test-egress -c curl -- curl -s -o /dev/null -w "%{http_code}\n"  https://edition.cnn.com/politics
    200
  6. Позначте простори імен, у яких запущено панель управління Istio та egress gateway. Якщо ви розгорнули Istio у просторі імен istio-system, команда буде такою:

$ kubectl label namespace istio-system istio=system
  1. Позначте міткою простір імен kube-system.

    $ kubectl label ns kube-system kube-system=true
  2. Визначте NetworkPolicy, щоб обмежити вихідний трафік із простору імен test-egress до трафіку, що спрямовується до панелі управління, шлюзу, а також до DNS-сервісу в просторі імен kube-system (порт 53).

$ cat <<EOF | kubectl apply -n test-egress -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-egress-to-istio-system-and-kube-dns
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          kube-system: "true"
    ports:
    - protocol: UDP
      port: 53
  - to:
    - namespaceSelector:
        matchLabels:
          istio: system
EOF
  1. Повторно надішліть HTTPS-запит до https://edition.cnn.com/politics. Тепер він має не виконатися, оскільки трафік заблокований мережевою політикою. Зверніть увагу, що pod curl не може оминути шлюз вихідного трафіку. Єдиний спосіб, яким він може отримати доступ до edition.cnn.com, — це використання sudecar проксі Istio та спрямування трафіку через шлюз вихідного трафіку. Це налаштування демонструє, що навіть якщо якийсь шкідливий pod зуміє обійти свій sidecar проксі, він не зможе отримати доступ до зовнішніх сайтів і буде заблокований мережевою політикою.

    $ kubectl exec "$(kubectl get pod -n test-egress -l app=curl -o jsonpath={.items..metadata.name})" -n test-egress -c curl -- curl -v -sS https://edition.cnn.com/politics
    Hostname was NOT found in DNS cache
      Trying 151.101.65.67...
      Trying 2a04:4e42:200::323...
    Immediate connect fail for 2a04:4e42:200::323: Cannot assign requested address
      Trying 2a04:4e42:400::323...
    Immediate connect fail for 2a04:4e42:400::323: Cannot assign requested address
      Trying 2a04:4e42:600::323...
    Immediate connect fail for 2a04:4e42:600::323: Cannot assign requested address
      Trying 2a04:4e42::323...
    Immediate connect fail for 2a04:4e42::323: Cannot assign requested address
    connect to 151.101.65.67 port 443 failed: Connection timed out
  2. Тепер додай проксі Istio sidecar у pod curl в просторі імен test-egress, спочатку увімкнувши автоматичне додавання sidecar проксі в просторі імен test-egress:

$ kubectl label namespace test-egress istio-injection=enabled
  1. Потім виконайте повторне розгортання curl:
Zip
$ kubectl delete deployment curl -n test-egress
$ kubectl apply -f @samples/curl/curl.yaml@ -n test-egress
  1. Перевір, що у розгорнутому pod є два контейнери, включаючи проксі Istio sidecar (istio-proxy):
$ kubectl get pod "$(kubectl get pod -n test-egress -l app=curl -o jsonpath={.items..metadata.name})" -n test-egress -o jsonpath='{.spec.containers[*].name}'
curl istio-proxy

Перш ніж продовжити, потрібно створити аналогічне правило призначення, як і для pod curl у просторі імен default, щоб спрямувати трафік простору імен test-egress через шлюз egress:

$ kubectl apply -n test-egress -f - <<EOF
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: egressgateway-for-cnn
spec:
  host: istio-egressgateway.istio-system.svc.cluster.local
  subsets:
  - name: cnn
EOF
  1. Надішліть HTTPS-запит на https://edition.cnn.com/politics. Тепер він повинен успішно пройти, оскільки трафік до шлюзу egress дозволений мережевою політикою, яку ви визначили. Шлюз потім пересилає трафік на edition.cnn.com.

    $ kubectl exec "$(kubectl get pod -n test-egress -l app=curl -o jsonpath={.items..metadata.name})" -n test-egress -c curl -- curl -sS -o /dev/null -w "%{http_code}\n" https://edition.cnn.com/politics
    200
  2. Перевірте журнали проксі шлюзу egress.

Якщо Istio розгорнуто у просторі імен istio-system, команда для друку журналу буде такою:

$ kubectl logs -l istio=egressgateway -n istio-system

Ви повинні побачити рядок, схожий на наступний:

[2020-03-06T18:12:33.101Z] "- - -" 0 - "-" "-" 906 1352475 35 - "-" "-" "-" "-" "151.101.193.67:443" outbound|443||edition.cnn.com 172.30.223.53:39460 172.30.223.53:443 172.30.223.58:38138 edition.cnn.com -

Видалення мережевих політик

  1. Видаліть ресурси, створені у цьому розділі:
Zip
$ kubectl delete -f @samples/curl/curl.yaml@ -n test-egress
$ kubectl delete destinationrule egressgateway-for-cnn -n test-egress
$ kubectl delete networkpolicy allow-egress-to-istio-system-and-kube-dns -n test-egress
$ kubectl label namespace kube-system kube-system-
$ kubectl label namespace istio-system istio-
$ kubectl delete namespace test-egress
  1. Виконайте кроки в розділі Видалення HTTPS-шлюзу.

Очищення

Вимкніть сервіс curl:

Zip
$ kubectl delete -f @samples/curl/curl.yaml@
Чи була ця інформація корисною?
Чи є у вас пропозиції щодо покращення?

Дякуємо за ваш відгук!