イグレスゲートウェイ
外部サービスへのアクセスタスクでは、メッシュ内のアプリケーションから外部のHTTPおよびHTTPSサービスへのアクセスを許可するようにIstioを構成する方法を示しています。ここでは、外部サービスはクライアントサイドカーから直接呼び出されます。この例では、Istioが外部サービスを呼び出すように構成する方法も示していますが、今回は専用のイグレスゲートウェイサービスを介して間接的に呼び出します。
Istio は、サービスメッシュのエッジで実行されるロードバランサーを構成するために、インゲートウェイとイーグレスゲートウェイを使用します。インゲートウェイを使用すると、メッシュへのエントリポイントを定義でき、すべての着信トラフィックがそこを通過します。イーグレスゲートウェイは対称的な概念で、メッシュからの出口ポイントを定義します。イーグレスゲートウェイを使用すると、Istio の機能(例えば、監視やルートルール)を、メッシュから出るトラフィックに適用できます。
ユースケース
サービスメッシュから出るすべてのトラフィックが、専用ノードのセットを通過する必要があるという厳格なセキュリティ要件を持つ組織を考えてみましょう。これらのノードは、クラスター内でアプリケーションを実行している残りのノードとは分離された専用マシン上で実行されます。これらの特別なノードは、イーグレス トラフィックに対するポリシーの強制に使用され、他のノードよりも徹底的に監視されます。
もう1つのユースケースは、アプリケーションノードがパブリックIPを持たないため、それらで実行されるメッシュ内のサービスがインターネットにアクセスできないクラスターです。イーグレスゲートウェイを定義し、すべてのイーグレストラフィックをそこに向けることで、イーグレスゲートウェイノードにパブリックIPを割り当てると、アプリケーションノードは制御された方法で外部サービスにアクセスできるようになります。
開始する前に
インストールガイドの指示に従って Istio をセットアップします。
リクエストを送信するためのテストソースとして使用する、curl サンプルアプリをデプロイします。
$ kubectl apply -f @samples/curl/curl.yaml@
SOURCE_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
Istioイグレスゲートウェイのデプロイ
Istio のイーグレスゲートウェイがデプロイされているかどうかを確認します。
$ kubectl get pod -l istio=egressgateway -n istio-system
ポッドが返されない場合は、次の手順を実行して Istio のイーグレスゲートウェイをデプロイします。
Istio をインストールするために
IstioOperator
CR を使用した場合は、構成に以下のフィールドを追加します。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"
HTTPトラフィック用のイグレスゲートウェイ
まず、外部サービスへの直接トラフィックを許可する ServiceEntry
を作成します。
edition.cnn.com
のServiceEntry
を定義します。$ 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
http://edition.cnn.com/politics に HTTP リクエストを送信して、
ServiceEntry
が正しく適用されたことを確認します。$ 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 オリジネーションの例と同じになるはずです。
edition.cnn.com ポート 80 へのイーグレストラフィック用の
Gateway
を作成します。
$ 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
$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cnn-egress-gateway
annotations:
networking.istio.io/service-type: ClusterIP
spec:
gatewayClassName: istio
listeners:
- name: http
hostname: edition.cnn.com
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: Same
EOF
- サイドカーからイーグレスゲートウェイへ、そしてイーグレスゲートウェイから外部サービスへトラフィックを誘導するためのルートルールを構成します。
$ 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
$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: direct-cnn-to-egress-gateway
spec:
parentRefs:
- kind: ServiceEntry
group: networking.istio.io
name: cnn
rules:
- backendRefs:
- name: cnn-egress-gateway-istio
port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: forward-cnn-from-egress-gateway
spec:
parentRefs:
- name: cnn-egress-gateway
hostnames:
- edition.cnn.com
rules:
- backendRefs:
- kind: Hostname
group: networking.istio.io
name: edition.cnn.com
port: 80
EOF
http://edition.cnn.com/politics に HTTP リクエストを再送信します。
$ 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 ...
出力は、ステップ2と同じになるはずです。
リクエストに対応する行について、イーグレスゲートウェイポッドのログを確認します。
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 -
Istio によって生成されたポッドラベルを使用して、イーグレスゲートウェイに対応するログにアクセスします。
$ kubectl logs -l gateway.networking.k8s.io/gateway-name=cnn-egress-gateway -c istio-proxy | tail
次のような行が表示されるはずです。
[2024-01-09T15:35:47.283Z] "GET /politics HTTP/1.1" 301 - via_upstream - "-" 0 0 2 2 "172.30.239.55" "curl/7.87.0-DEV" "6c01d65f-a157-97cd-8782-320a40026901" "edition.cnn.com" "151.101.195.5:80" outbound|80||edition.cnn.com 172.30.239.16:55636 172.30.239.16:80 172.30.239.55:59224 - default.forward-cnn-from-egress-gateway.0
イーグレスゲートウェイを介してリダイレクトしたのはポート 80 からの HTTP トラフィックのみであることに注意してください。ポート 443 への HTTPS トラフィックは、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
$ kubectl delete serviceentry cnn
$ kubectl delete gtw cnn-egress-gateway
$ kubectl delete httproute direct-cnn-to-egress-gateway
$ kubectl delete httproute forward-cnn-from-egress-gateway
HTTPSトラフィック用のイグレスゲートウェイ
このセクションでは、HTTPSトラフィック(アプリケーションによって生成されたTLS)をイーグレスゲートウェイ経由で誘導します。対応する ServiceEntry
とイーグレス Gateway
で、プロトコル TLS
のポート 443 を指定する必要があります。
edition.cnn.com
のServiceEntry
を定義します。$ 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
https://edition.cnn.com/politics に HTTPS リクエストを送信して、
ServiceEntry
が正しく適用されたことを確認します。$ 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 ...
edition.cnn.com 用のイーグレス
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
$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cnn-egress-gateway
annotations:
networking.istio.io/service-type: ClusterIP
spec:
gatewayClassName: istio
listeners:
- name: tls
hostname: edition.cnn.com
port: 443
protocol: TLS
tls:
mode: Passthrough
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
name: direct-cnn-to-egress-gateway
spec:
parentRefs:
- kind: ServiceEntry
group: networking.istio.io
name: cnn
rules:
- backendRefs:
- name: cnn-egress-gateway-istio
port: 443
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
name: forward-cnn-from-egress-gateway
spec:
parentRefs:
- name: cnn-egress-gateway
hostnames:
- edition.cnn.com
rules:
- backendRefs:
- kind: Hostname
group: networking.istio.io
name: edition.cnn.com
port: 443
EOF
https://edition.cnn.com/politics に HTTPS リクエストを送信します。出力は以前と同じになるはずです。
$ 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 ...
イーグレスゲートウェイのプロキシのログを確認します。
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
Istio によって生成されたポッドラベルを使用して、イーグレスゲートウェイに対応するログにアクセスします。
$ kubectl logs -l gateway.networking.k8s.io/gateway-name=cnn-egress-gateway -c istio-proxy | tail
次のような行が表示されるはずです。
[2024-01-11T21:09:42.835Z] "- - -" 0 - - - "-" 839 2504306 231 - "-" "-" "-" "-" "151.101.195.5:443" outbound|443||edition.cnn.com 172.30.239.8:34470 172.30.239.8:443 172.30.239.15:43956 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
$ kubectl delete serviceentry cnn
$ kubectl delete gtw cnn-egress-gateway
$ kubectl delete tlsroute direct-cnn-to-egress-gateway
$ kubectl delete tlsroute forward-cnn-from-egress-gateway
追加のセキュリティに関する考慮事項
Istio でイーグレス Gateway
を定義しても、イーグレスゲートウェイサービスが実行されるノードに対する特別な処理は提供されないことに注意してください。イーグレスゲートウェイを専用ノードにデプロイし、これらのノードをメッシュの残りの部分よりも安全にするための追加のセキュリティ対策を導入することは、クラスター管理者またはクラウドプロバイダーの責任です。
Istio は、すべてのイーグレストラフィックが実際にイーグレスゲートウェイを通過することを安全に強制できません。Istio は、サイドカープロキシを介してそのようなフローを可能にするだけです。攻撃者がサイドカープロキシをバイパスした場合、イーグレスゲートウェイを通過せずに外部サービスに直接アクセスする可能性があります。したがって、攻撃者は Istio の制御と監視を逃れることになります。クラスター管理者またはクラウドプロバイダーは、イーグレスゲートウェイをバイパスするトラフィックがメッシュから出ないようにする必要があります。Istio の外部メカニズムがこの要件を強制する必要があります。たとえば、クラスター管理者は、イーグレスゲートウェイから発信されていないすべてのトラフィックを拒否するようにファイアウォールを構成できます。Kubernetes ネットワークポリシーは、イーグレスゲートウェイから発信されていないすべてのイーグレストラフィックを禁止することもできます(例については、次のセクションを参照してください)。さらに、クラスター管理者またはクラウドプロバイダーは、アプリケーションノードがゲートウェイ経由でのみインターネットにアクセスできるようにネットワークを構成できます。これを行うには、クラスター管理者またはクラウドプロバイダーは、ゲートウェイ以外のポッドへのパブリックIPの割り当てを防止し、イーグレスゲートウェイで発信されていないパケットをドロップするようにNATデバイスを構成できます。
Kubernetesネットワークポリシーの適用
このセクションでは、イーグレスゲートウェイのバイパスを防ぐためのKubernetes ネットワークポリシーを作成する方法を示します。ネットワークポリシーをテストするには、名前空間 test-egress
を作成し、curl サンプルをそこにデプロイしてから、ゲートウェイで保護された外部サービスにリクエストを送信しようとします。
HTTPS トラフィック用のイーグレスゲートウェイセクションの手順に従ってください。
test-egress
名前空間を作成します。$ kubectl create namespace test-egress
curl サンプルを
test-egress
名前空間にデプロイします。$ kubectl apply -n test-egress -f @samples/curl/curl.yaml@
デプロイされたポッドに 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
test-egress
名前空間のcurl
ポッドから https://edition.cnn.com/politics に HTTPS リクエストを送信します。まだ制限ポリシーを定義していないため、リクエストは成功します。$ 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
Istio コントロールプレーンとイーグレスゲートウェイが実行されている名前空間にラベルを付けます。Istio を
istio-system
名前空間にデプロイした場合、コマンドは次のとおりです。
$ kubectl label namespace istio-system istio=system
$ kubectl label namespace istio-system istio=system
$ kubectl label namespace default gateway=true
kube-system
名前空間にラベルを付けます。$ kubectl label ns kube-system kube-system=true
test-egress
名前空間からのイーグレストラフィックを、コントロールプレーン、ゲートウェイ、およびkube-system
DNSサービス(ポート53)宛てのトラフィックに制限するNetworkPolicy
を定義します。
$ 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
$ 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
- to:
- namespaceSelector:
matchLabels:
gateway: "true"
EOF
https://edition.cnn.com/politics に以前の HTTPS リクエストを再送信します。トラフィックがネットワークポリシーによってブロックされるため、今回は失敗するはずです。
curl
ポッドはイーグレスゲートウェイをバイパスできないことに注意してください。edition.cnn.com
にアクセスする唯一の方法は、Istio サイドカープロキシを使用し、トラフィックをイーグレスゲートウェイに誘導することです。この設定は、悪意のあるポッドがサイドカープロキシをバイパスした場合でも、外部サイトにアクセスできず、ネットワークポリシーによってブロックされることを示しています。$ 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
まず、
test-egress
名前空間で自動サイドカープロキシの注入を有効にすることで、test-egress
名前空間のcurl
ポッドに Istio サイドカープロキシを注入します。$ kubectl label namespace test-egress istio-injection=enabled
次に、
curl
デプロイメントを再デプロイします。$ kubectl delete deployment curl -n test-egress $ kubectl apply -f @samples/curl/curl.yaml@ -n test-egress
デプロイされたポッドに、Istio サイドカープロキシ (
istio-proxy
) を含む2つのコンテナがあることを確認します。
$ 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
次に進む前に、default
名前空間の curl
ポッドに使用したのと同様の宛先ルールを作成し、test-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
$ 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
https://edition.cnn.com/politics に HTTPS リクエストを送信します。定義したネットワークポリシーによりエグレスゲートウェイへのトラフィックが許可されているため、今度は成功するはずです。ゲートウェイはトラフィックを
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
イーグレスゲートウェイのプロキシのログを確認します。
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 -
Istio によって生成されたポッドラベルを使用して、イーグレスゲートウェイに対応するログにアクセスします。
$ kubectl logs -l gateway.networking.k8s.io/gateway-name=cnn-egress-gateway -c istio-proxy | tail
次のような行が表示されるはずです。
[2024-01-12T19:54:01.821Z] "- - -" 0 - - - "-" 839 2504837 46 - "-" "-" "-" "-" "151.101.67.5:443" outbound|443||edition.cnn.com 172.30.239.60:49850 172.30.239.60:443 172.30.239.21:36512 edition.cnn.com -
ネットワークポリシーのクリーンアップ
- このセクションで作成したリソースを削除します。
$ 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
$ kubectl delete -f @samples/curl/curl.yaml@ -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 label namespace default gateway-
$ kubectl delete namespace test-egress
- HTTPS ゲートウェイのクリーンアップセクションの手順に従ってください。
クリーンアップ
curl サービスをシャットダウンします。
$ kubectl delete -f @samples/curl/curl.yaml@