Захист Gateways

Завдання Контроль вхідного трафіку описує, як налаштувати ingress gateway, щоб відкрити HTTP-сервіс для зовнішнього трафіку. Це завдання показує, як експонувати захищений HTTPS-сервіс за допомогою простого або взаємного TLS.

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

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

  • Запустіть httpbin:

    Zip
    $ kubectl apply -f @samples/httpbin/httpbin.yaml@
  • Для користувачів macOS переконайтеся, що ви використовуєте curl, скомпільований з бібліотекою LibreSSL:

    $ curl --version | grep LibreSSL
    curl 7.54.0 (x86_64-apple-darwin17.0) libcurl/7.54.0 LibreSSL/2.0.20 zlib/1.2.11 nghttp2/1.24.0

    Якщо попередня команда виводить версію LibreSSL, як показано, ваша команда curl має працювати коректно з інструкціями у цьому завданні. В іншому випадку, спробуйте іншу реалізацію curl, наприклад, на машині Linux.

Генерація сертифікатів та ключів для клієнта і сервера

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

  1. Створіть кореневий сертифікат і приватний ключ для підпису сертифікатів для ваших сервісів:

    $ mkdir example_certs1
    $ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example_certs1/example.com.key -out example_certs1/example.com.crt
  2. Згенеруйте сертифікат та приватний ключ для httpbin.example.com:

    $ openssl req -out example_certs1/httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs1/httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
    $ openssl x509 -req -sha256 -days 365 -CA example_certs1/example.com.crt -CAkey example_certs1/example.com.key -set_serial 0 -in example_certs1/httpbin.example.com.csr -out example_certs1/httpbin.example.com.crt
  3. Створіть другий набір таких самих сертифікатів та ключів:

    $ mkdir example_certs2
    $ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example_certs2/example.com.key -out example_certs2/example.com.crt
    $ openssl req -out example_certs2/httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs2/httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
    $ openssl x509 -req -sha256 -days 365 -CA example_certs2/example.com.crt -CAkey example_certs2/example.com.key -set_serial 0 -in example_certs2/httpbin.example.com.csr -out example_certs2/httpbin.example.com.crt
  4. Згенеруйте сертифікат та приватний ключ для helloworld.example.com:

    $ openssl req -out example_certs1/helloworld.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs1/helloworld.example.com.key -subj "/CN=helloworld.example.com/O=helloworld organization"
    $ openssl x509 -req -sha256 -days 365 -CA example_certs1/example.com.crt -CAkey example_certs1/example.com.key -set_serial 1 -in example_certs1/helloworld.example.com.csr -out example_certs1/helloworld.example.com.crt
  5. Згенеруйте клієнтський сертифікат та приватний ключ:

    $ openssl req -out example_certs1/client.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs1/client.example.com.key -subj "/CN=client.example.com/O=client organization"
    $ openssl x509 -req -sha256 -days 365 -CA example_certs1/example.com.crt -CAkey example_certs1/example.com.key -set_serial 1 -in example_certs1/client.example.com.csr -out example_certs1/client.example.com.crt

Налаштування TLS ingress gateway для одного хоста

  1. Створіть секрет для ingress gateway:

    $ kubectl create -n istio-system secret tls httpbin-credential \
      --key=example_certs1/httpbin.example.com.key \
      --cert=example_certs1/httpbin.example.com.crt
  2. Налаштуйте ingress gateway:

Спочатку визначте шлюз з розділом servers: для порту 443 і вкажіть значення для credentialName, яке має бути httpbin-credential. Значення збігається з назвою секрету. Режим TLS повинен мати значення SIMPLE.

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: mygateway
spec:
  selector:
    istio: ingressgateway # використовуйте станадртний istio ingress gateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: httpbin-credential # має збігатись з secret
    hosts:
    - httpbin.example.com
EOF

Далі налаштуйте маршрути вхідного трафіку шлюзу, визначивши відповідний віртуальний сервіс:

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
  - "httpbin.example.com"
  gateways:
  - mygateway
  http:
  - match:
    - uri:
        prefix: /status
    - uri:
        prefix: /delay
    route:
    - destination:
        port:
          number: 8000
        host: httpbin
EOF

Нарешті, дотримуйтесь цих інструкцій, щоб встановити змінні INGRESS_HOST та SECURE_INGRESS_PORT для доступу до шлюзу.

  1. Надішліть HTTPS-запит, щоб отримати доступ до сервісу httpbin через HTTPS:

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs1/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    ...
    HTTP/2 418
    ...
    I'm a teapot!
    ...

    Сервіс httpbin поверне код 418 I’m a Teapot.

  2. Змініть облікові дані шлюзу, видаливши секрет шлюзу, а потім створивши його заново, використовуючи інші сертифікати та ключі:

    $ kubectl -n istio-system delete secret httpbin-credential
    $ kubectl create -n istio-system secret tls httpbin-credential \
      --key=example_certs2/httpbin.example.com.key \
      --cert=example_certs2/httpbin.example.com.crt
  3. Зверніться до сервісу httpbin за допомогою curl, використовуючи новий ланцюжок сертифікатів:

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs2/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    ...
    HTTP/2 418
    ...
    I'm a teapot!
    ...
  4. Якщо ви спробуєте отримати доступ до httpbin, використовуючи попередній ланцюжок сертифікатів, спроба завершиться невдачею:

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs1/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    ...
    * TLSv1.2 (OUT), TLS handshake, Client hello (1):
    * TLSv1.2 (IN), TLS handshake, Server hello (2):
    * TLSv1.2 (IN), TLS handshake, Certificate (11):
    * TLSv1.2 (OUT), TLS alert, Server hello (2):
    * curl: (35) error:04FFF06A:rsa routines:CRYPTO_internal:block type is not 01

Налаштування TLS ingress gateway для кількох хостів

Ви можете налаштувати ingress gateway для декількох хостів, наприклад, httpbin.example.com та helloworld.example.com. Ingress gateway налаштовується за допомогою унікальних облікових даних, що відповідають кожному хосту.

  1. Відновіть облікові дані httpbin з попереднього прикладу, видаливши і створивши заново секрет з оригінальними сертифікатами і ключами:

    $ kubectl -n istio-system delete secret httpbin-credential
    $ kubectl create -n istio-system secret tls httpbin-credential \
      --key=example_certs1/httpbin.example.com.key \
      --cert=example_certs1/httpbin.example.com.crt
  2. Запустіть приклад helloworld-v1:

    ZipZip
    $ kubectl apply -f @samples/helloworld/helloworld.yaml@ -l service=helloworld
    $ kubectl apply -f @samples/helloworld/helloworld.yaml@ -l version=v1
  3. Створіть секрет helloworld-credential:

    $ kubectl create -n istio-system secret tls helloworld-credential \
      --key=example_certs1/helloworld.example.com.key \
      --cert=example_certs1/helloworld.example.com.crt
  4. Налаштуйте ingress gatewayз хостами httpbin.example.com та helloworld.example.com:

Визначте шлюз з двома секціями server для порту 443. Встановіть значення параметра credentialName на кожному порту на httpbin-credential і helloworld-credential відповідно. Встановіть режим TLS на SIMPLE.

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: mygateway
spec:
  selector:
    istio: ingressgateway # використовуйте стандартний istio ingress gateway
  servers:
  - port:
      number: 443
      name: https-httpbin
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: httpbin-credential
    hosts:
    - httpbin.example.com
  - port:
      number: 443
      name: https-helloworld
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: helloworld-credential
    hosts:
    - helloworld.example.com
EOF

Налаштуйте маршрути трафіку шлюзу, визначивши відповідну віртуальну службу.

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: helloworld
spec:
  hosts:
  - helloworld.example.com
  gateways:
  - mygateway
  http:
  - match:
    - uri:
        exact: /hello
    route:
    - destination:
        host: helloworld
        port:
          number: 5000
EOF
  1. Надішліть запит HTTPS до helloworld.example.com:

    $ curl -v -HHost:helloworld.example.com --resolve "helloworld.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs1/example.com.crt "https://helloworld.example.com:$SECURE_INGRESS_PORT/hello"
    ...
    HTTP/2 200
    ...
  2. Надішліть запит HTTPS до httpbin.example.com і також отримайте відповідь HTTP 418:

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs1/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    ...
    HTTP/2 418
    ...
    server: istio-envoy
    ...

Налаштуйте взаємний TLS ingress gateway

Ви можете розширити визначення вашого шлюзу підтримкою mutual TLS.

  1. Змініть облікові дані ingress gateway, видаливши його секрет і створивши новий. Сервер використовує сертифікат ЦС для перевірки своїх клієнтів, і ми повинні використовувати ключ ca.crt для зберігання сертифіката ЦС.

    $ kubectl -n istio-system delete secret httpbin-credential
    $ kubectl create -n istio-system secret generic httpbin-credential \
      --from-file=tls.key=example_certs1/httpbin.example.com.key \
      --from-file=tls.crt=example_certs1/httpbin.example.com.crt \
      --from-file=ca.crt=example_certs1/example.com.crt
  2. Налаштуйте ingress gateway:

Змініть визначення шлюзу, щоб встановити режим TLS на MUTUAL.

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: mygateway
spec:
  selector:
    istio: ingressgateway # використовуйте стандартний istio ingress gateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: MUTUAL
      credentialName: httpbin-credential # має збігатись з secret
    hosts:
    - httpbin.example.com
EOF
  1. Спробуйте надіслати HTTPS-запит, використовуючи попередній підхід, і подивіться, що це не вдасться:

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
    --cacert example_certs1/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    * TLSv1.3 (OUT), TLS handshake, Client hello (1):
    * TLSv1.3 (IN), TLS handshake, Server hello (2):
    * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
    * TLSv1.3 (IN), TLS handshake, Request CERT (13):
    * TLSv1.3 (IN), TLS handshake, Certificate (11):
    * TLSv1.3 (IN), TLS handshake, CERT verify (15):
    * TLSv1.3 (IN), TLS handshake, Finished (20):
    * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
    * TLSv1.3 (OUT), TLS handshake, Certificate (11):
    * TLSv1.3 (OUT), TLS handshake, Finished (20):
    * TLSv1.3 (IN), TLS alert, unknown (628):
    * OpenSSL SSL_read: error:1409445C:SSL routines:ssl3_read_bytes:tlsv13 alert certificate required, errno 0
  2. Передайте сертифікат клієнта і приватний ключ curl і повторно надішліть запит. Передайте сертифікат клієнта з прапорцем --cert і ваш приватний ключ з прапорцем --key в curl:

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs1/example.com.crt --cert example_certs1/client.example.com.crt --key example_certs1/client.example.com.key \
      "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    ...
    HTTP/2 418
    ...
    server: istio-envoy
    ...
    I'm a teapot!
    ...

Додаткова інформація

Формати ключів

Istio підтримує кілька різних форматів секретів для інтеграції з різними інструментами, такими як cert-manager:

  • TLS секрет з ключами tls.key та tls.crt, як описано вище. Для взаємного TLS можна використовувати ключ ca.crt.
  • Загальний секрет з ключами key та cert. Для взаємного TLS можна використовувати ключ cacert.
  • Загальний секрет з ключами key та cert. Для взаємного TLS можна використовувати окремий загальний секрет з назвою <secret>-cacert, який містить ключ cacert. Наприклад, httpbin-credential має key та cert, а httpbin-credential-cacert має cacert.
  • Значення ключа cacert може бути зв’язкою сертифікатів CA, яка складається з окремих об’єднаних сертифікатів CA.

SNI маршрутизація

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

Усунення несправностей

  • Перевірте значення змінних середовища INGRESS_HOST та SECURE_INGRESS_PORT. Переконайтеся, що вони мають дійсні значення відповідно до результатів наступних команд:

    $ kubectl get svc -n istio-system
    $ echo "INGRESS_HOST=$INGRESS_HOST, SECURE_INGRESS_PORT=$SECURE_INGRESS_PORT"
  • Переконайтеся, що значення INGRESS_HOST є IP-адресою. У деяких хмарних платформах, наприклад, AWS, ви можете отримати доменне імʼя замість IP-адреси. Це завдання очікує IP-адресу, тому вам потрібно буде перетворити її за допомогою команд, схожих на такі:

    $ nslookup ab52747ba608744d8afd530ffd975cbf-330887905.us-east-1.elb.amazonaws.com
    $ export INGRESS_HOST=3.225.207.109
  • Перевірте журнал контролера шлюзу на наявність повідомлень про помилки:

    $ kubectl logs -n istio-system <gateway-service-pod>
  • Якщо ви використовуєте macOS, перевірте, чи використовуєте ви curl, скомпільований з бібліотекою LibreSSL, як описано в розділі Перш ніж розпочати.

  • Перевірте, чи секрети успішно створені в просторі імен istio-system:

    $ kubectl -n istio-system get secrets

    Секрети httpbin-credential та helloworld-credential повинні бути показані у переліку секретів.

  • Перевірте журнали, щоб підтвердити, що агент ingress gateway надіслав пару ключ/сертифікат до шлюзу входу:

    $ kubectl logs -n istio-system <gateway-service-pod>

    Журнал має показувати, що секрет httpbin-credential був доданий. Якщо використовується взаємний TLS, то також має зʼявитися секрет httpbin-credential-cacert. Перевірте, що в журналі відображається, що агент шлюзу отримав запити SDS від шлюзу входу, що імʼя ресурсу є httpbin-credential, і що шлюз входу отримав пару ключ/сертифікат. Якщо використовується взаємний TLS, журнал має показувати, що ключ/сертифікат був надісланий до шлюзу входу, що агент шлюзу отримав запит SDS з імʼям ресурсу httpbin-credential-cacert, і що шлюз входу отримав кореневий сертифікат.

Очищення

  1. Видаліть конфігурацію шлюзу та маршрути:
$ kubectl delete gateway mygateway
$ kubectl delete virtualservice httpbin helloworld
  1. Видаліть секрети, сертифікати та ключі:

    $ kubectl delete -n istio-system secret httpbin-credential helloworld-credential
    $ rm -rf ./example_certs1 ./example_certs2
  2. Вимкніть служби httpbin і helloworld:

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

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