このチュートリアルでは、Anthos Service Mesh の下り(外向き)ゲートウェイと上り(内向き)ゲートウェイを使用して、mTLS(mutual Transport Layer Security)でクラスタ間のトラフィックを保護する方法について説明します。このチュートリアルは、ネットワーク、セキュリティ、プラットフォームを担当する Kubernetes クラスタ管理者を対象としています。ここで説明する制御は、セキュリティ要件が厳しい組織や規制の前提条件を満たす場合に特に役立ちます。このチュートリアルには、関連するコンセプト ガイドが付属しています。
このチュートリアルは、Kubernetes と Anthos Service Mesh に精通していることを前提としています。
目標
- Terraform を使用してインフラストラクチャを設定します。
- 2 つのプライベート サブネットを持つカスタム VPC ネットワークを作成します。
- Anthos Service Mesh を有効にして 2 つの Kubernetes クラスタを作成します。
- 1 つの GKE クラスタ
- カスタム VPC ネットワークで実行されている 1 つの Kubernetes Operations(kOps)クラスタ
- クラスタを GKE Hub に登録します。
- GKE クラスタに MySQL クライアントをデプロイします。
- kOps クラスタに MySQL サーバーをデプロイします。
- mTLS を使用してサーバーを公開するように下り(外向き)ゲートウェイと上り(内向き)ゲートウェイを構成します。
- さまざまなクラスタまたは VPC で実行されている MySQL クライアントを使用して、MySQL サーバーへのアクセスをテストします。
費用
このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。
- Google Kubernetes Engine(GKE)
- Compute Engine
- Container Registry
- Anthos Service Mesh
- クラウド ネットワーキングとロード バランシング
料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。
このドキュメントに記載されているタスクの完了後、作成したリソースを削除すると、それ以上の請求は発生しません。詳細については、クリーンアップをご覧ください。
始める前に
このチュートリアルでは、Google Cloud プロジェクトが必要です。新しいプロジェクトを作成することも、すでに作成済みのプロジェクトを選択することもできます。
-
Google Cloud コンソールでプロジェクトの選択ページに移動します。
-
Google Cloud プロジェクトを選択または作成します。
Google Cloud コンソールで、Cloud Shell に移動します。
Google Cloud コンソールの下部で Cloud Shell セッションが開き、コマンドライン プロンプトが表示されます。Cloud Shell は、Google Cloud CLI など、Google Cloud CLI がすでにインストールされているシェル環境です。セッションが初期化されるまで数秒かかることがあります。
- Cloud Shell では、作成または選択したプロジェクトで作業していることを確認します。
export PROJECT_ID=PROJECT_ID gcloud config set project ${PROJECT_ID}
PROJECT_ID
は、実際のプロジェクト ID に置き換えます。 - Google Cloud に使用するメールアドレスの環境変数を作成します。
export GOOGLE_CLOUD_EMAIL_ADDRESS=GOOGLE_CLOUD_EMAIL_ADDRESS
GOOGLE_CLOUD_EMAIL_ADDRESS
は、Google Cloud で使用するメールアドレスに置き換えてください。 - コンピューティング リソースのリージョンとゾーンを設定します。
export REGION=us-central1 export ZONE=us-central1-b gcloud config set compute/region ${REGION} gcloud config set compute/zone ${ZONE}
このチュートリアルでは、リージョンに
us-central1
を使用し、ゾーンにus-central1-b
を使用します。選択したリージョンにデプロイできます。 - 必要な Identity and Access Management(IAM)のロールを設定します。プロジェクト オーナーの場合は、インストールを完了するために必要なすべての権限が付与されます。それ以外の場合は、Cloud Shell で次のコマンドを実行して、管理者に Identity and Access Management(IAM)のロールを付与するよう依頼してください。
ROLES=( 'roles/container.admin' \ 'roles/gkehub.admin' \ 'roles/iam.serviceAccountAdmin' \ 'roles/iam.serviceAccountKeyAdmin' \ 'roles/resourcemanager.projectIamAdmin' \ 'roles/compute.securityAdmin' \ 'roles/compute.instanceAdmin' \ 'roles/storage.admin' \ 'roles/serviceusage.serviceUsageAdmin' ) for role in "${ROLES[@]}" do gcloud projects add-iam-policy-binding ${PROJECT_ID} \ --member "user:${GOOGLE_CLOUD_EMAIL_ADDRESS}" \ --role="$role" done
- チュートリアルに必要な API を有効にします。
gcloud services enable \ anthos.googleapis.com \ anthosgke.googleapis.com \ anthosaudit.googleapis.com \ compute.googleapis.com \ container.googleapis.com \ cloudresourcemanager.googleapis.com \ serviceusage.googleapis.com \ stackdriver.googleapis.com \ monitoring.googleapis.com \ logging.googleapis.com \ cloudtrace.googleapis.com \ meshca.googleapis.com \ meshconfig.googleapis.com \ iamcredentials.googleapis.com \ gkeconnect.googleapis.com \ gkehub.googleapis.com
環境の準備
Cloud Shell で、次のリポジトリのクローンを作成します。
git clone https://github.com/GoogleCloudPlatform/anthos-service-mesh-samples cd anthos-service-mesh-samples/docs/mtls-egress-ingress
お使いの環境の Terraform を更新します。デフォルトでは、Google Cloud コンソールには Terraform 0.12 が付属しています。このチュートリアルでは、Terraform 0.13.5 以降がインストールされていることを前提としています。次のコマンドを実行して、一時的に別のバージョンの Terraform を使用できます。
mkdir ~/bin curl https://releases.hashicorp.com/terraform/0.13.5/terraform_0.13.5_linux_amd64.zip -o ~/bin/terraform.zip unzip ~/bin/terraform.zip -d ~/bin/
terraform
サブフォルダに移動して、Terraform を初期化します。cd terraform/ ~/bin/terraform init
前に作成した環境変数に基づいて
terraform.tfvars
ファイルを作成します。cat << EOF > terraform.tfvars project_id = "${PROJECT_ID}" region = "${REGION}" zones = ["${ZONE}"] EOF
次のステップでは、初期インフラストラクチャを作成します。これを行うには、この構成の Terraform 実行プランを作成して適用します。このプランのスクリプトとモジュールにより、次のものが作成されます。
- 2 つのプライベート サブネットを持つカスタム VPC ネットワーク
- Anthos Service Mesh が有効になっている 2 つの Kubernetes クラスタ
- 1 つの GKE クラスタ
- カスタム VPC ネットワークで実行される 1 つの kOps クラスタ
この実行プランでは GKE Hub にもクラスタを登録します。
実行プランを実行します。
~/bin/terraform plan -out mtls-terraform-plan ~/bin/terraform apply "mtls-terraform-plan"
出力は次のようになります。
Apply complete! Resources: 27 added, 0 changed, 0 destroyed. Outputs: server_token = <sensitive>
<sensitive>
部分は、コンソールには表示されませんが、たとえば、~/bin/terraform output server_token
のようにクエリできます。terraform
ディレクトリからサーバー クラスタのkubeconfig
ファイルを取得します。次に、取得したファイルをclient-cluster
構成ファイルとマージします。cd .. export KUBECONFIG=client-server-kubeconfig cp ./terraform/server-kubeconfig $KUBECONFIG gcloud container clusters get-credentials client-cluster --zone ${ZONE} --project ${PROJECT_ID}
これで、
client-server-kubeconfig
ファイルに両方のクラスタの構成が保持されるようになりました。この構成を確認するには、次のコマンドを実行します。kubectl config view -ojson | jq -r '.clusters[].name'
次のような出力が表示されます。
gke_PROJECT_ID_us-central1-c_client-cluster server-cluster.k8s.local
後で使用できるように、2 つのクラスタのコンテキストを取得します。
export CLIENT_CLUSTER=$(kubectl config view -ojson | jq -r '.clusters[].name' | grep client) export SERVER_CLUSTER=$(kubectl config view -ojson | jq -r '.clusters[].name' | grep server) echo -e "${CLIENT_CLUSTER}\n${SERVER_CLUSTER}"
この場合も、出力は次のようになります。
gke_PROJECT_ID_us-central1-c_client-cluster server-cluster.k8s.local
これで、これらのクラスタ名を今後の
kubectl
コマンドのコンテキストとして使用できるようになりました。クライアント クラスタを参照します。
kubectl --context ${CLIENT_CLUSTER} get pods -n istio-system
サーバー クラスタを参照します。
kubectl --context ${SERVER_CLUSTER} get pods -n istio-system
クライアント側を構成する
コンセプト ガイドで説明されているように、クライアント側では、Anthos Service Mesh で下り(外向き)ゲートウェイを構成する必要があります。
このセクションでは、送信元に基づいて外部トラフィックを識別し、カスタム証明書を使用して通信を暗号化するために、Anthos Service Mesh 要素を構成します。さらに、そのトラフィックのみを宛先(コンテナ内の MySQL DB)にルーティングする必要があります。通常、これを行うには Kubernetes の Service を使用します。この場合、そのトラフィックをメッシュ通信内でキャッチする必要があります。トラフィックをキャッチするには、Istio 要素を使用して Service の特別な定義を作成します。次の要素を定義します。
- 下り(外向き)ゲートウェイ
- サービス エントリ
- 仮想サービス
- TLS 証明書(Secret として)
- 宛先ルール
Cloud Shell で、サーバー側のコンテキスト(先ほど作成した
$SERVER_CLUSTER
)を使用してistio-ingressgateway
サービスのロードバランサの IP アドレスに対するクエリを実行し、サーバー側の上り(内向き)ゲートウェイの IP アドレスを取得します。INGRESS_HOST=$(kubectl -n istio-system --context ${SERVER_CLUSTER} get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
INGRESS_HOST
はホストの IP アドレス部分でしかないため、完全修飾ドメイン名(FQDN)を作成する必要があります。この手順が必要なのは、証明書が適切に機能するためにはドメイン名が必要であるためです。このチュートリアルでは、ワイルドカード DNS サービス nip.io を使用して、上り(内向き)IP アドレスの FQDN を作成します。このサービスによって、ドメインを所有せずに FQDN を作成できます。
環境変数に FQDN サービス URL を格納します。
export SERVICE_URL="${INGRESS_HOST}.nip.io"
これで、
SERVICE_URL
を FQDN として定義したため、クライアント クラスタの Istio の部分の定義を開始できます。
下り(外向き)ゲートウェイを作成する
最初に、外部サービスに対して送信されたトラフィックをリッスンする下り(外向き)ゲートウェイを作成します。
Cloud Shell で、次の YAML ファイルを作成し、
client-egress-gateway.yaml
という名前を付けます。cat <<EOF > client-egress-gateway.yaml apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: istio-egressgateway-mysql spec: selector: istio: egressgateway servers: - port: number: 15443 name: tls protocol: TLS hosts: - $SERVICE_URL tls: mode: ISTIO_MUTUAL EOF
前述の YAML ファイルをクライアント クラスタに適用します。
kubectl --context ${CLIENT_CLUSTER} apply -f client-egress-gateway.yaml
ポートに注意してください。ここでは、下り(外向き)サーバー スイッチに
default
ポート(15443
)を使用しました。別のポートを使用する場合は、下り(外向き)ゲートウェイのservice
オブジェクトを編集してカスタムポートを追加する必要があります。hosts
スイッチは、トラフィックの行き先を表示するエンドポイントを定義します。
サービス エントリを定義する
次のステップでは、外部サービスの情報をサービス メッシュに提供します。Istio には、メッシュのサービス エンドポイントが保存される独自のレジストリがあります。Istio が Kubernetes 上にインストールされている場合、クラスタで定義されたサービスは Istio レジストリに自動的に追加されます。次の図に示すように、サービス エントリの定義を使用して、Istio レジストリに新しいエンドポイントを追加します。
Cloud Shell で、次の YAML ファイルを作成し、
client-service-entry.yaml
という名前を付けます。cat <<EOF > client-service-entry.yaml apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: mysql-external spec: hosts: - $SERVICE_URL location: MESH_EXTERNAL ports: - number: 3306 name: tcp protocol: TCP - number: 13306 name: tls protocol: TLS resolution: DNS endpoints: - address: $SERVICE_URL ports: tls: 13306 EOF
前述の YAML ファイルをクライアント クラスタに適用します。
kubectl --context ${CLIENT_CLUSTER} apply -f client-service-entry.yaml
この YAML ファイルのクライアント サービス定義は、予想されるトラフィックの種類(MySQL L4 - Network Layer 4、ポート 3306 を使用)をサービスに指示します。また、通信が「メッシュ外部」に対して行われるように定義します。エンドポイント セクションで、先ほど設定した FQDN アドレス
$SERVICE_URL
(サーバー クラスタ(kOps)の上り(内向き)ゲートウェイにマッピングされます)に、フローが配置されることを定義します。
仮想サービスを定義する
仮想サービスは、ホストがアドレス指定されたときに適用される一連のトラフィック ルーティング ルールです。各ルーティング ルールは、特定のプロトコルのトラフィックの一致条件を定義します。トラフィックが一致した場合は、レジストリで定義された名前付き宛先サービス(またはそのサブセットまたはバージョン)に送信されます。詳細については、Istio のドキュメントをご覧ください。
仮想サービスの定義は、外部サービスに到達するトラフィックのルーティングを適用する方法を Istio に指示します。次の定義を使用して、クライアントから下り(外向き)ゲートウェイのポート 15443
にトラフィックをルーティングするようメッシュに指示します。下り(外向き)ゲートウェイから、ポート 13306
のホスト $SERVICE_URL
にトラフィックをルーティングします。このポートでは、サーバー側の上り(内向き)ゲートウェイがリッスンしています。
次の YAML ファイルを作成し、
client-virtual-service.yaml
という名前を付けます。cat <<EOF > client-virtual-service.yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: direct-mysql-through-egress-gateway spec: hosts: - $SERVICE_URL gateways: - istio-egressgateway-mysql - mesh tcp: - match: - gateways: - mesh port: 3306 route: - destination: host: istio-egressgateway.istio-system.svc.cluster.local subset: mysql port: number: 15443 weight: 100 - match: - gateways: - istio-egressgateway-mysql port: 15443 route: - destination: host: $SERVICE_URL port: number: 13306 weight: 100 EOF
YAML 定義をクライアント クラスタに適用します。
kubectl --context ${CLIENT_CLUSTER} apply -f client-virtual-service.yaml
YAML ファイルで
gateways
スイッチを編集することで、構成を適用するゲートウェイを指定できます。この定義で重要なのは、メッシュ内のすべてのサイドカーを意味する予約語
mesh
を使用することです。Istio のドキュメントによると、このフィールドを省略すると、デフォルト ゲートウェイ(メッシュ)が使用され、メッシュ内のすべてのサイドカーにルールが適用されます。ゲートウェイ名のリストを指定すると、ルールは指定したゲートウェイにのみ適用されます。ルールをゲートウェイとサイドカーに適用するには、ゲートウェイ名の 1 つとしてmesh
を指定します。
次のセクションでは、クライアント prod のプロキシ match.gateways.mesh
から発信されたトラフィックの処理方法を定義します。また、match.gateways.istio-egressgateway-mysql
スイッチを使用して、下り(外向き)から外部サービスへトラフィックをルーティングする方法も定義します。
クライアントから下り(外向き)ゲートウェイへの宛先ルールを定義する
これで、トラフィックを外部サービスにルーティングする方法を定義できました。次に、適用するトラフィック ポリシーを定義する必要があります。先ほど定義した仮想サービスは、2 つのルーティング ケースを同時に処理します。1 つはサイドカー プロキシから下り(外向き)ゲートウェイへのトラフィックを処理し、もう 1 つは下り(外向き)ゲートウェイから外部サービスへのトラフィックを処理します。
このようなケースと宛先ルールを照合するには、2 つの個別のルールが必要です。次の図は、プロキシから下り(外向き)ゲートウェイへのトラフィックを処理する最初のルールを示しています。この定義では、Anthos Service Mesh に mTLS 通信用のデフォルトの証明書を使用するように指示します。
Cloud Shell で、次の YAML ファイルを作成し、
client-destination-rule-to-egress-gateway.yaml
という名前を付けます。cat <<EOF > client-destination-rule-to-egress-gateway.yaml apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: egressgateway-for-mysql spec: host: istio-egressgateway.istio-system.svc.cluster.local subsets: - name: mysql trafficPolicy: loadBalancer: simple: ROUND_ROBIN portLevelSettings: - port: number: 15443 tls: mode: ISTIO_MUTUAL sni: $SERVICE_URL EOF
前述の YAML 定義をクライアント クラスタに適用します。
kubectl --context ${CLIENT_CLUSTER} apply -f client-destination-rule-to-egress-gateway.yaml
前述の YAML ファイルでは、
hosts
スイッチを使用して、クライアント プロキシから下り(外向き)ゲートウェイにトラフィックをルーティングする方法を定義しました。また、下り(外向き)ゲートウェイで自動的に開いているポートの 1 つであるポート15443
でISTIO_MUTUAL
を使用するようにメッシュの構成も行いました。
下り(外向き)ゲートウェイから外部サービスへの宛先ルールを作成する
次の図は、2 番目の宛先ルールを示しています。これは、下り(外向き)ゲートウェイから外部サービスへのトラフィックの処理方法をメッシュに指示します。
追加された証明書を外部サービスとの相互 TLS 通信に使用するように、メッシュに指示する必要があります。
Cloud Shell で、
anthos-service-mesh-samples/docs/mtls-egress-ingress
ディレクトリから証明書を作成します。./create-keys.sh
スクリプトでパスワードを求められたときには、必ずパスワードを入力してください。
生成されたファイルを現在のディレクトリにコピーします。
cp ./certs/2_intermediate/certs/ca-chain.cert.pem ca-chain.cert.pem cp ./certs/4_client/private/$SERVICE_URL.key.pem client-$SERVICE_URL.key.pem cp ./certs/4_client/certs/$SERVICE_URL.cert.pem client-$SERVICE_URL.cert.pem
証明書を格納する Kubernetes Secret を作成し、後でこの証明書をゲートウェイで参照できるようにします。
kubectl --context ${CLIENT_CLUSTER} create secret -n istio-system \ generic client-credential \ --from-file=tls.key=client-$SERVICE_URL.key.pem \ --from-file=tls.crt=client-$SERVICE_URL.cert.pem \ --from-file=ca.crt=ca-chain.cert.pem
前述のコマンドにより、次の証明書ファイルが Secret に追加されます。
client-$SERVICE_URL.key.pem client-$SERVICE_URL.cert.pem ca-chain.cert.pem
ここでは、現在のベスト プラクティスに従って、ファイル マウントではなく Secret Discovery Service(SDS)を使用して、証明書が配布されます。これにより、新しい証明書を追加するときに Pod が再起動されることを回避します。Istio 1.8/1.9 以降では、この方法により、ゲートウェイの Secret の読み取りアクセス権(RBAC)が不要になりました。
証明書を
DestinationRule
に追加し、client-destination-rule-to-external-service.yaml
と名付けます。cat <<EOF > client-destination-rule-to-external-service.yaml apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: originate-mtls-for-mysql spec: host: $SERVICE_URL trafficPolicy: loadBalancer: simple: ROUND_ROBIN portLevelSettings: - port: number: 13306 tls: mode: MUTUAL credentialName: client-credential sni: $SERVICE_URL EOF
前述の YAML 定義をクライアント クラスタに適用します。
kubectl --context ${CLIENT_CLUSTER} apply -f client-destination-rule-to-external-service.yaml
このルールは、事前に Secret を作成した場合にのみ機能します。Secret により、下り(外向き)ゲートウェイから外部エンドポイントへの mTLS 暗号化に証明書が使用されます。
これらの手順を完了すると、クライアント側の設定が終了し次の図のようになります。
サーバー側を構成する
コンセプト ガイドで説明したように、サーバー側の Anthos Service Mesh で上り(内向き)ゲートウェイを構成する必要があります。必要なファイルを作成する前に、ソリューションのサーバー部分を実現するために必要なコンポーネントを確認することをおすすめします。
基本的には、送信元と証明書に基づいて受信トラフィックを識別します。また、そのトラフィックのみを宛先であるコンテナの MySQL DB にルーティングすることも可能です。これを行うには、通常、Kubernetes の Service を使用しますが、この例ではメッシュ コミュニケーションの受信トラフィックを識別するため、以下を含む Service の特別な定義が必要です。
- TLS 証明書(Secret として)
- 上り(内向き)ゲートウェイ
- 仮想サービス
上り(内向き)ゲートウェイの Secret を作成する
下り(外向き)ゲートウェイの場合と同様に、上り(内向き)ゲートウェイの通信を保護するために同じ証明書が必要です。これを行うには、証明書を Secret に格納し、この Secret を上り(内向き)ゲートウェイ オブジェクトで定義します。
Cloud Shell で、次のコマンドを実行する場所に、生成されたファイルをコピーします。
cp ./certs/3_application/private/$SERVICE_URL.key.pem server-$SERVICE_URL.key.pem cp ./certs/3_application/certs/$SERVICE_URL.cert.pem server-$SERVICE_URL.cert.pem
サーバー Secret を作成します。
kubectl --context ${SERVER_CLUSTER} create secret -n istio-system \ generic mysql-credential \ --from-file=tls.key=server-$SERVICE_URL.key.pem \ --from-file=tls.crt=server-$SERVICE_URL.cert.pem \ --from-file=ca.crt=ca-chain.cert.pem
次の証明書ファイルを Secret に追加しました。
server-$SERVICE_URL.key.pem server-$SERVICE_URL.cert.pem ca-chain.cert.pem
上り(内向き)ゲートウェイを定義する
クライアント側クラスタからトラフィックを受信するには、証明書を使用して TLS 通信を復号して検証できる上り(内向き)ゲートウェイを指定する必要があります。
この図は、上り(内向き)ゲートウェイがクラスタ内に存在する場所を示しています。トラフィックが通過すると、セキュリティ基準と転送基準を満たしているかどうか検査されます。
Cloud Shell で、次の YAML ファイルを使用し、
server-ingress-gatway.yaml
という名前を付けます。cat <<EOF > server-ingress-gatway.yaml apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: gateway-mysql spec: selector: istio: ingressgateway # Istio default gateway implementation servers: - port: number: 13306 name: tls-mysql protocol: TLS tls: mode: MUTUAL credentialName: mysql-credential hosts: - "$SERVICE_URL" EOF
前述の YAML 定義をクライアント クラスタに適用します。
kubectl --context ${SERVER_CLUSTER} apply -f server-ingress-gatway.yaml
tls:
は特に重要なセクションですので、注意してください。このセクションでは、mTLS が必要であることを定義します。これが意図したとおりに機能するように、証明書を含む作成した Secret を提供する必要があります。上り(内向き)サービスをパッチ適用してポート
13306
を有効にします。このポートを有効にするには、次の JSON ファイルを作成し、gateway-patch.json
という名前を付けます。cat <<EOF > gateway-patch.json [{ "op": "add", "path": "/spec/ports/0", "value": { "name": "tls-mysql", "protocol": "TCP", "targetPort": 13306, "port": 13306 } }] EOF
ゲートウェイ サービスにパッチを適用します。
kubectl --context ${SERVER_CLUSTER} -n istio-system patch --type=json svc istio-ingressgateway -p "$(cat gateway-patch.json)"
上り(内向き)ゲートウェイでポートを開いたら、新しい上り(内向き)ゲートウェイからトラフィックを受け取り、データベース Pod に転送する必要があります。これを行うには、メッシュの内部オブジェクト(仮想サービス)を使用します。
仮想サービスを定義する
前述のように、仮想サービスはメッシュ内のトラフィックを形成するトラフィック一致パターンの定義です。次の図に示すように、上り(内向き)ゲートウェイから MySQL DB サービスへのトラフィックを正しく識別して転送する必要があります。
Cloud Shell で、次の YAML ファイルを作成し、
server-virtual-service.yaml
という名前を付けます。cat <<EOF > server-virtual-service.yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: mysql-virtual-service spec: hosts: - "$SERVICE_URL" gateways: - gateway-mysql tcp: - route: - destination: port: number: 3306 host: mysql.default.svc.cluster.local EOF
この仮想サービスがトラフィックが来る上り(内向き)ゲートウェイを参照することが重要です。ゲートウェイは
gateway-mysql
という名前です。この仮想サービスは L4 で適用されるため、MySQL トラフィックを説明する
tcp
フラグを指定する必要があります。このフラグは、基本的に、このトラフィックに L4 が使用されていることをメッシュに通知します。上り(内向き)サービスではトラフィックを転送するためにポート
13306
が使用されていることにお気づきかと思います。仮想サービスは、その出力を選択して変換し3306
に戻します。最後に、サーバーの Kubernetes クラスタ内の MySQL サーバーにトラフィックを転送します。この例では、サーバーは標準の MySQL ポート
3306
でリッスンしています。YAML 定義をサーバー クラスタに適用します。
kubectl --context ${SERVER_CLUSTER} apply -f server-virtual-service.yaml
この 2 つの定義は、mTLS でカプセル化された MySQL クライアント リクエストを復号し、メッシュ内の MySQL データベースに転送します。
メッシュの内部転送も暗号化を使用して行われますが、この場合の暗号化はメッシュの内部証明書に基づいて行われることを理解することが重要です。mTLS 終端はゲートウェイで行われます。
これで、MySQL サーバーとの通信の完全に暗号化された相互通信方法ができました。この暗号化形式は MySQL クライアントとサーバーに対して透過的であるため、アプリケーションの変更は不要です。これにより、証明書の変更やローテーションなどのタスクが簡単になります。また、この通信方法は、多くのさまざまなシナリオで使用できます。
設定をテストする
クライアント側とサーバー側の配置が完了したため、設定をテストできます。
ここで、クライアント側からサーバー側へのトラフィックの一部を生成します。トラフィックのフローを追って、トラフィックのルーティングと暗号化、復号が想定どおりに行われることを確認します。
Cloud Shell で、MySQL サーバーをサーバー クラスタにデプロイします。
kubectl --context ${SERVER_CLUSTER} apply -f server/mysql-server/mysql.yaml
クライアント クラスタで MySQL クライアントを開始します。
kubectl run --context ${CLIENT_CLUSTER} --env=SERVICE_URL=$SERVICE_URL -it --image=mysql:5.6 mysql-client-1 --restart=Never -- /bin/bash
コンテナが開始されると、次のようなシェルが表示されます。
root@mysql-client-1:/#
MySQL サーバーに接続します。
mysql -pyougottoknowme -h $SERVICE_URL
次のコマンドを使用して、DB とテーブルを追加します。
CREATE DATABASE test_encrypted_connection; USE test_encrypted_connection; CREATE TABLE message (id INT, content VARCHAR(20)); INSERT INTO message (id,content) VALUES(1,"Crypto Hi");
接続してテーブルを追加したら、MySQL 接続と Pod を終了します。
exit exit
exit を 2 回入力する必要があります。1 回目は DB 接続を終了し、2 回目は Pod を終了するためです。終了時に Pod が応答しなくなった場合は、Ctrl+C キーを押して bash シェルをキャンセルして終了します。
この手順を行うことで、有意義なロギング出力を生成し、詳細に分析できます。
次のセクションでは、クライアント側トラフィックがプロキシと下り(外向き)ゲートウェイを通過していることを確認します。また、トラフィックが上り(内向き)ゲートウェイを通ってサーバー側に着信することを確認できるかどうかもテストします。
クライアント側プロキシと下り(外向き)ゲートウェイをテストする
Cloud Shell のクライアント側プロキシで、Istio プロキシのログを表示できることを確認します。
kubectl --context ${CLIENT_CLUSTER} logs -l run=mysql-client-1 -c istio-proxy -f
デバッグ出力は次のようになります。
[2021-02-10T21:19:08.292Z] "- - -" 0 - "-" "-" 176 115 10 - "-" "-" "-" "-" "192.168.1.4:15443" outbound|15443|mysql|istio-egressgateway.istio-system.svc.cluster.local 192.168.1.12:58614 34.66.165.46:3306 192.168.1.12:39642 - -
ログ出力を終了するには、Ctrl+C キーを押します。
このログエントリでは、クライアントが IP アドレス
34.66.165.46
のポート3306
で実行されているサーバーをリクエストしていることが確認できます。リクエストは、IP アドレス192.168.1.4
のポート15443
をリッスンするistio-egressgateway
に転送(outbound
)されます。この転送は仮想サービス(client-virtual-service.yaml
)で定義したものです。下り(外向き)ゲートウェイ プロキシのログを読み取ります。
kubectl --context ${CLIENT_CLUSTER} logs -n istio-system -l app=istio-egressgateway -f
デバッグ出力は次のようになります。
[2021-02-10T21:19:08.292Z] "- - -" 0 - "-" "-" 176 115 19 - "-" "-" "-" "-" "34.66.165.46:13306" outbound|13306||34.66.165.46.nip.io 192.168.1.4:53542 192.168.1.4:15443 192.168.1.12:58614 34.66.165.46.nip.io -
ログ出力を終了するには、Ctrl+C キーを押します。
このログエントリでは、IP アドレス
192.168.1.4
のポート15443
をリッスンするistio-egressgateway
にルーティングされたクライアント リクエストが、さらに IP アドレス34.66.165.46
のポート13306.
をリッスンするサービス メッシュ外部に存在する外部サービスにルーティングされることを確認できます。この転送は、仮想サービス(client-virtual-service.yaml
)の 2 つ目のパートで定義したものです。
サーバー側の上り(内向き)ゲートウェイをテストする
Cloud Shell のサーバー側で、上り(内向き)ゲートウェイ プロキシのログを表示します。
kubectl --context ${SERVER_CLUSTER} logs -n istio-system -l app=istio-ingressgateway -f
ログの出力は次のようになります。
[2021-02-10T21:22:27.381Z] "- - -" 0 - "-" "-" 0 78 5 - "-" "-" "-" "-" "100.96.4.8:3306" outbound|3306||mysql.default.svc.cluster.local 100.96.1.3:55730 100.96.1.3:13306 100.96.1.1:42244 34.66.165.46.nip.io -
ログ出力を終了するには、Ctrl+C キーを押します。
このログエントリでは、IP アドレス
34.66.165.46
のポート13306
をリッスンしているistio-ingressgateway
にルーティングされた外部クライアント リクエストが、ポート3306.
のサービス名mysql.default.svc.cluster.local
によって識別されたメッシュ内の MySQL サービスにさらにルーティングされていることを確認できます。この転送は、上り(内向き)ゲートウェイ(server-ingress-gateway.yaml
)で定義したものです。MySQL サーバーの場合、Istio プロキシログを表示します。
kubectl --context ${SERVER_CLUSTER} logs -l app=mysql -c istio-proxy -f
出力は次のようになります。
[2021-02-10T21:22:27.382Z] "- - -" 0 - "-" "-" 1555 1471 4 - "-" "-" "-" "-" "127.0.0.1:3306" inbound|3306|mysql|mysql.default.svc.cluster.local 127.0.0.1:45894 100.96.4.8:3306 100.96.1.3:55730 outbound_.3306_._.mysql.default.svc.cluster.local -
ログ出力を終了するには、Ctrl+C キーを押します。
このログエントリで、IP アドレス
100.96.4.8
のポート3306
をリッスンしている MySQL データベース サーバーへの受信呼び出しを確認できます。呼び出しは、IP アドレス100.96.1.3
の上り(内向き)Pod から行われます。詳細については、Envoy のアクセスログを取得するをご覧ください。
データベースをテストして、生成された入力を確認します。
MYSQL=$(kubectl --context ${SERVER_CLUSTER} get pods -n default | tail -n 1 | awk '{print $1}') kubectl --context ${SERVER_CLUSTER} exec $MYSQL -ti -- /bin/bash
作成されたデータベースを確認します。
mysql -pyougottoknowme USE test_encrypted_connection; SELECT * from message;
出力は次のようになります。
+------+-----------+ | id | content | +------+-----------+ | 1 | Crypto Hi | +------+-----------+ 1 row in set (0.00 sec)
MySQL データベースを終了します。
exit exit
exit
を 2 回入力する必要があります。1 回目は DB 接続を終了し、2 回目は Pod を終了するためです。
証明書を省略してアクセスをテストする
挿入された証明書を使用してアクセスが機能することをテストおよび確認したら、反対の場合についてもテストします。すなわち、下り(外向き)ゲートウェイと証明書の挿入を省略した場合の動作をテストします。このテストはネガティブ テストとも呼ばれます。
このテストは、サイドプロキシ インジェクションを有効にせずに名前空間で別の Pod を起動することによって実行できます。
Cloud Shell で、新しい名前空間を作成します。
kubectl --context ${CLIENT_CLUSTER} create ns unencrypted
Pod を作成し、コンテナ内で対話型シェルを起動します。
kubectl --context ${CLIENT_CLUSTER} run -it --image=mysql:5.6 \ mysql-client-2 --env=SERVICE_URL=$SERVICE_URL \ -n unencrypted --restart=Never -- /bin/bash
対話型シェルが起動したら、データベースへの接続を試行します。
mysql -pyougottoknowme -h $SERVICE_URL
30 秒後、次のような出力が表示されます。
Warning: Using a password on the command line interface can be insecure. ERROR 2003 (HY000): Can't connect to MySQL server on '104.154.164.12.nip.io' (110)
この Pod は下り(外向き)ゲートウェイを省略し、インターネット経由で上り(内向き)ゲートウェイ(
$SERVICE_URL
)に直接アクセスしようとするので、この警告は想定内です。サービスの IP アドレスの解決を試行します。
resolveip $SERVICE_URL
出力は次のようになります。実際にご利用の IP アドレスは異なります。
IP address of 104.154.164.12.nip.io is 104.154.164.12
これで、FQDN が解決可能であり、証明書の挿入を省略したことが原因で実際に接続の失敗が発生することが証明されました。
MySQL の接続と MySQL サーバー Pod を終了します。
exit exit
詳細な調査
このチュートリアルの対象外の題目の 1 つは、通常、下り(外向き)構成は、istio-system
名前空間でホストされるため、社内の別のロールまたは組織が所有するということです。ネットワーク管理者だけが、このチュートリアルで説明するリソースを直接作成、変更できるように、Kubernetes RBAC 権限を構成します。
サービス メッシュを使用して安全な通信を確保する方法を学んだので、次に、データを安全に交換する必要があるアプリケーションと、証明書レイヤまでの暗号化を制御するアプリケーションを使用してサービス メッシュを試してみます。開始するには、Anthos Service Mesh をインストールします。
2 つの GKE クラスタを使用して、このチュートリアルの手法で統合することを試してみます。この手法は、2 つの外部 Kubernetes クラスタ間にある Anthos プラットフォームでも機能します。
サービス メッシュは、クラスタ内部と外部サービスのセキュリティを改善するための優れた方法です。試行対象の最後の 1 つのユースケースは、2 つ目の Kubernetes クラスタではなく、サードパーティ プロバイダ(決済機関など)である mTLS エンドポイントを設定することです。
クリーンアップ
このチュートリアルで使用したリソースに対する Google Cloud アカウントへの課金を回避するには、プロジェクトを削除します。
プロジェクトを削除する
- Google Cloud コンソールで、[リソースの管理] ページに移動します。
- プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
- ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。
次のステップ
- コンパニオンのコンセプト ガイドを読む。
- engress ゲートウェイの構成に関するその他のベスト プラクティスについて、GKE クラスタでの Anthos Service Mesh engress ゲートウェイの使用: チュートリアルで確認する。
- GKE 強化ガイドと付属の Terraform モジュールを確認する。
- Google Cloud に関するリファレンス アーキテクチャ、図、ベスト プラクティスを確認する。Cloud Architecture Center をご覧ください。