Захист Gateways
Завдання Контроль вхідного трафіку описує, як налаштувати ingress gateway, щоб відкрити HTTP-сервіс для зовнішнього трафіку. Це завдання показує, як експонувати захищений HTTPS-сервіс за допомогою простого або взаємного TLS.
Перш ніж почати
Налаштуйте Istio, дотримуючись інструкцій з Посібника з встановлення.
Запустіть httpbin:
$ 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.
Створіть кореневий сертифікат і приватний ключ для підпису сертифікатів для ваших сервісів:
$ 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Згенеруйте сертифікат та приватний ключ для
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Створіть другий набір таких самих сертифікатів та ключів:
$ 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Згенеруйте сертифікат та приватний ключ для
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Згенеруйте клієнтський сертифікат та приватний ключ:
$ 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 для одного хоста
Створіть секрет для 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Налаштуйте 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 для доступу до шлюзу.
Спочатку створіть Kubernetes Gateway:
$ cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: mygateway
namespace: istio-system
spec:
gatewayClassName: istio
listeners:
- name: https
hostname: "httpbin.example.com"
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: httpbin-credential
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: default
EOFПотім, налаштуйте маршрути вхідного трафіку шлюзу, визначивши відповідний HTTPRoute:
$ cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httpbin
spec:
parentRefs:
- name: mygateway
namespace: istio-system
hostnames: ["httpbin.example.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /status
- path:
type: PathPrefix
value: /delay
backendRefs:
- name: httpbin
port: 8000
EOFНарешті, отримайте адресу і порт шлюзу з ресурсу Gateway:
$ kubectl wait --for=condition=programmed gtw mygateway -n istio-system
$ export INGRESS_HOST=$(kubectl get gtw mygateway -n istio-system -o jsonpath='{.status.addresses[0].value}')
$ export SECURE_INGRESS_PORT=$(kubectl get gtw mygateway -n istio-system -o jsonpath='{.spec.listeners[?(@.name=="https")].port}')Надішліть 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.Змініть облікові дані шлюзу, видаливши секрет шлюзу, а потім створивши його заново, використовуючи інші сертифікати та ключі:
$ 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Зверніться до сервісу
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! ...Якщо ви спробуєте отримати доступ до
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 налаштовується за допомогою унікальних облікових даних, що відповідають кожному хосту.
Відновіть облікові дані
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Запустіть приклад
helloworld-v1:$ kubectl apply -f @samples/helloworld/helloworld.yaml@ -l service=helloworld $ kubectl apply -f @samples/helloworld/helloworld.yaml@ -l version=v1Створіть секрет
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Налаштуйте 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Налаштуйте Gateway з двома слухачами для порту 443. Встановіть значення
certificateRefs на кожному слухачі на httpbin-credential та helloworld-credential
відповідно.
$ cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: mygateway
namespace: istio-system
spec:
gatewayClassName: istio
listeners:
- name: https-httpbin
hostname: "httpbin.example.com"
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: httpbin-credential
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: default
- name: https-helloworld
hostname: "helloworld.example.com"
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: helloworld-credential
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: default
EOFНалаштуйте маршрути трафіку шлюзу для сервісу helloworld:
$ cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: helloworld
spec:
parentRefs:
- name: mygateway
namespace: istio-system
hostnames: ["helloworld.example.com"]
rules:
- matches:
- path:
type: Exact
value: /hello
backendRefs:
- name: helloworld
port: 5000
EOFНадішліть запит 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 ...Надішліть запит 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.
Змініть облікові дані 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Налаштуйте 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Оскільки Kubernetes Gateway API наразі не підтримує термінацію mutual TLS в Gateway, ми використовуємо Istio-специфічну опцію, gateway.istio.io/tls-terminate-mode: MUTUAL, щоб зробити це:
$ cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: mygateway
namespace: istio-system
spec:
gatewayClassName: istio
listeners:
- name: https
hostname: "httpbin.example.com"
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: httpbin-credential
options:
gateway.istio.io/tls-terminate-mode: MUTUAL
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: default
EOFСпробуйте надіслати 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Передайте сертифікат клієнта і приватний ключ
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 Secret з ключами
tls.keyтаtls.crt, як описано вище. Для взаємного TLS можна використовувати ключca.crt. - TLS Secret з ключами
tls.keyіtls.crt, як описано вище. Для взаємного TLS, окремий загальний Secret з назвою<secret>-cacert, з ключемcacert. Наприклад,httpbin-credentialмаєtls.keyіtls.crt, аhttpbin-credential-cacertмаєcacert. - Загальний Secret з ключами
keyтаcert. Для взаємного TLS можна використовувати ключcacert. - Загальний Secret з ключами
keyтаcert. Для взаємного TLS можна використовувати окремий загальний секрет з назвою<secret>-cacert, який містить ключcacert. Наприклад,httpbin-credentialмаєkeyтаcert, аhttpbin-credential-cacertмаєcacert. - Для взаємного TLS можна посилатися на окремий загальний Secret з ключем
cacertабоca.crtза допомогоюcaCertCredentialName. Він має перевагу над сертифікатами CA в Secret, на який посилаються зcredentialName(s). - Значення ключа
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, і що шлюз входу отримав кореневий сертифікат.
Очищення
- Видаліть конфігурацію шлюзу та маршрути:
$ kubectl delete gateway mygateway
$ kubectl delete virtualservice httpbin helloworld$ kubectl delete -n istio-system gtw mygateway
$ kubectl delete httproute httpbin helloworldВидаліть секрети, сертифікати та ключі:
$ kubectl delete -n istio-system secret httpbin-credential helloworld-credential $ rm -rf ./example_certs1 ./example_certs2Вимкніть служби
httpbinіhelloworld:$ kubectl delete -f samples/httpbin/httpbin.yaml $ kubectl delete deployment helloworld-v1 $ kubectl delete service helloworld