KubernetesでTLSを実装する
Rubaiat Hossain
2023年7月31日
0 分で読めますクラウドテクノロジーの進化に伴い、Kubernetesの需要は急速に高まっています。それに伴い、アプリケーションデータの保護を目指す開発者にとって、セキュリティが最優先事項となっています。そこで重要になるのが、トランスポート層セキュリティ(TLS)です。TLSは、アプリケーションとインターネット間の安全な接続を確保するうえで不可欠です。
TLSは、公開鍵暗号方式と共通鍵暗号方式を活用して、転送中および保存中のデータを保護します。Kubernetesで使用されるTLS証明書は通常、信頼できる認証局(CA)が発行した公開鍵証明書です。この証明書には、サーバーの識別情報、暗号化に使用する公開鍵、証明書を発行したCAのデジタル署名が含まれます。
この記事では、Kubernetesクラスター上のアプリケーションにTLS接続を実装する方法を解説し、TLSの仕組みと、アプリケーションやデータの保護における重要性を詳しくご紹介します。
KubernetesワークロードにTLSを実装する方法
Kubernetesクラスターで実行中のアプリケーションにリクエストが届くと、受信トラフィックのゲートウェイとなるネットワークコンポーネントを経由します。アプリケーションの構成によって、このコンポーネントはKubernetes Ingressコントローラー、ロードバランサーサービス、ClusterIPサービス、またはNodePortサービスになります。
Kubernetesには、クライアントとネットワークコンポーネント間の通信をTLSで暗号化するデフォルトの実装はありません。そのため、リクエストはHTTPSではなくHTTPで処理されます。ただし、TLSを使ってKubernetesアプリをHTTPSに設定すれば、簡単に安全な通信を実現できます。
KubernetesにはSecretsやConfigMapsなどの機能もあり、TLS証明書やアプリケーションに必要なその他の機密データを安全に管理できます。
かつてTLS(およびその前身であるSSL)は、安全な通信が必要なシステムとユーザーの間で実装されていました。分散システムが普及し、TLS実装のコストやパフォーマンスへの影響が大幅に低下したことに加え、プライバシーへの懸念が高まるなか、現在ではすべてのトラフィックを暗号化することが専門家の大半に推奨されています。ネットワーク暗号化の一般的な用途には、次のようなものがあります。
決済システム:銀行口座やクレジットカードなどの決済データを扱うアプリケーションでは、TLSの実装が必要です。これは規制上の要件であり、転送中のユーザーデータを安全に保つためにも欠かせません。決済データを暗号化することで、顧客の信頼を高め、安心してサービスを利用してもらえます。
サービスメッシュによるエンドツーエンドのデータ暗号化:Istioなどのサービスメッシュでエンドツーエンドのデータ暗号化を行うと、Kubernetesクラスター内のマイクロサービス間の通信をTLSで保護できます。これは、最新の分散型マイクロサービスアーキテクチャで広く採用されている手法です。
IoTとエッジコンピューティング:IoTデバイスやエッジコンピューティングシステムと連携するKubernetesアプリケーションでは、デバイスとクラウド間のデータ転送をTLSで保護し、IoTデバイスが生成したデータへの不正アクセスを防ぐ必要があります。
コンテンツデリバリーネットワーク(CDN):アプリケーションでコンテンツのキャッシュや配信にCDNを使用する場合、TLSを実装することでコンテンツの安全な転送を確保し、データの改ざんや不正アクセスを防止できます。
KubernetesでTLSを設定する
以下のセクションでは、決済処理アプリケーションを模したNode.jsプロジェクトを使い、シンプルなKubernetesアプリケーションにTLSを実装します。アプリケーションのコードと必要なKubernetes設定は、こちらのGitHubリポジトリで確認できます。
前提条件
このチュートリアルを進めるには、以下が必要です。
コンテナランタイムエンジン:この記事では、ワークステーションにDockerがインストールされ、設定済みであることを前提とします。
Kubernetesディストリビューション:Kubernetesクラスターと、デプロイメントやサービスなどの必要なリソースを作成するために、Kubernetesディストリビューションをインストールする必要があります。このチュートリアルでは、kind(v0.18.0)を使用しますが、minikubeやK3sなど、ほかのKubernetesディストリビューションも利用できます。
kubectl:このコマンドラインインターフェイスを使ってKubernetesクラスターと通信し、コマンドを実行します。このチュートリアルではバージョン1.26を使用します。
mkcert:開発マシンでカスタムドメイン名の信頼できるTLS証明書を取得するために使用します。公式の手順に従って、開発マシンに
mkcertをインストールできます。Git と Helm:デモプロジェクトをローカルで使うにはGitが、Kubernetesのパッケージマネージャーを使うにはHelmが必要です。このガイドではHelm v3.11.2を使用します。
以下のセクションでは、ローカルのKubernetesクラスターにデプロイした決済アプリのデモを使って、TLSを実装する手順を説明します。まずアプリをkindクラスターにデプロイし、HTTPSではなくHTTPを使用していることを確認します。次に、自己署名TLS証明書を生成して決済アプリに設定します。最後に、mkcertで信頼できるTLS証明書を作成し、KubernetesアプリにHTTPSでアクセスできることを確認します。
決済アプリをKubernetesにデプロイする
KubernetesベースのサンプルアプリケーションにTLSを実装するには、まず次のコマンドを実行して、アプリケーションをローカルマシンにクローンします。
次に、以下のコマンドでプロジェクトディレクトリに移動します。
プロジェクトディレクトリに移動したら、アプリケーションをKubernetesクラスターにデプロイできます。
次のコマンドでkindクラスターを作成します。
注:config.yamlファイルは、kindノードのいくつかのポートをローカルホストにバインドします。Docker Desktopではkindコンテナが仮想マシン(VM)内で実行されるため、これは必要です。DockerをネイティブにインストールしたLinuxワークステーション(Docker DesktopのVMなし)で実行する場合は、ここで--configオプションを省略できます。
次に、コンテナイメージをビルドしてkindクラスターに読み込みます。
注:必要に応じて、kind loadを使う代わりにイメージをレジストリにプッシュすることもできます。その場合は、以下で使用するdeployment.yamlファイルを編集してレジストリ情報を設定し、イメージをプルできるようにimagePullPolicyを「Always」に更新する必要があります。
クラスターが作成されたら、次のコマンドでアプリケーションをデプロイします。
KubernetesがPodやその他の必要なリソースを作成するまで、数分待ちます。次のコマンドでリソースのステータスを確認できます。
デモプロジェクトでは、nodePortサービスを使い、ポート30080で決済アプリケーションを公開します。typeフィールドを指定せずにKubernetesサービスを定義すると、デフォルトのサービス種別としてClusterIPが割り当てられます。
ただし、ClusterIPが公開するIPアドレスにアクセスできるのは、クラスター内のネットワークからのみです。そのため、ウェブブラウザーなどの外部クライアントから、クラスターで実行中のアプリケーションを表示することはできません。本番環境では、クラウドプロバイダーがパブリックにアクセス可能なIPアドレスを持つ外部ロードバランサーを提供できるよう、LoadBalancerサービスを使う方法があります。
次のコマンドで、このデモアプリケーションのサービスを確認します。
実行環境に応じて、次のいずれかの方法でデモアプリケーションにアクセスできます。
VMベース(Docker Desktopなど):
ブラウザーからデモアプリケーションにアクセスするには、config.yamlファイルで設定したkindのポートバインディングを使い、localhost:30080にアクセスします。
VMを使用しない環境(Docker Desktopを使わないLinuxなど):
ブラウザーからデモアプリケーションにアクセスするには、次のコマンドでnodePortが公開するIPアドレスを確認します。
KubernetesコントロールプレーンのINTERNAL-IP(つまり172.18.0.2)が表示されます。次の手順で決済アプリケーションにアクセスできます。
上記の手順に従い、IPアドレスをKubernetesコントロールプレーンの実際のINTERNAL-IP、またはlocalhostに置き換えてください。
上記のIPアドレスとポートをブラウザーで開くと、カード情報を入力できる決済ページが表示されます。これは模擬アプリケーションのため、実際の決済処理は行われず、入力したデータも保存されません。

ブラウザーによって表示は異なりますが、URLバーの近くに保護されていない通信などの表示が出ます。これは、アプリケーションがHTTPSではなくHTTPで配信されていることを示しています。表示をクリックして確認できます。

アプリに自己署名TLS証明書を実装する
次に、サンプルのKubernetesアプリケーションにTLSを実装します。自己署名証明書と秘密鍵を作成します。
次に、証明書と秘密鍵を含むKubernetes Secretを作成します。
次にNginx Ingress Controllerをインストールし、決済アプリに届くリクエストをHTTPSにリダイレクトできるようにします。アプリをnodePortで公開しているため、サービス種別にNodePortを指定したカスタム値ファイルを使ってIngressをインストールする必要があります。
ingress-values.yamlというファイルを作成し、次の内容を記述します。
次に、Helmとカスタム値ファイルを使ってNginx Ingress Controllerをインストールします。
次の内容でingress.yamlファイルを作成します。
次のコマンドを使って、このマニフェストファイルを適用します。
/etc/hostsファイルに次のエントリーを追加します。
Docker Desktopを使用している場合は<control_plane_IP>を127.0.0.1に置き換えてください。それ以外の場合は、kubectl get nodes -o wideを実行して取得したIPアドレスに置き換えます。
ブラウザーでhttps://my-demo-app.local:32443にアクセスすると、HTTPSで決済アプリを利用できます。初回アクセス時には、ブラウザーに警告ページが表示され、リスクを承認して続行できます。続行すると、HTTPSでサイトにアクセスできます。

TLS証明書が自己署名証明書であるため、ブラウザーにこのエラーが表示されます。URLのHTTPS部分に取り消し線が引かれていることからも確認できます。
アプリに信頼できるTLS証明書を実装する
この問題を解決するには、mkcertを使って開発環境で信頼される証明書を作成できます。そのために、ローカルCAを作成し、ローカルドメイン用の信頼できる証明書を生成します。
前のSecretを削除し、mkcert用の新しいSecretを作成します。
最後に、次のコマンドを実行してIngressリソースを再デプロイします。
KubernetesアプリケーションのTLSを検証する
これで、信頼できるTLS証明書を使って決済アプリにアクセスできるはずです。ブラウザーを再起動してからhttps://my-demo-app.local:32443にアクセスし、新しい証明書を有効にします。今度は警告が表示されず、通常のTLSで保護されたウェブサイトと同様に、URLのHTTPS部分に取り消し線が引かれていないことを確認できます。

URLバーの鍵アイコンをクリックして、TLS証明書がブラウザーで信頼されていることと、接続が安全であることを確認します。

更新後のコード例は、GitHubプロジェクトリポジトリのsolutionブランチで確認できます。
まとめ
機密データを扱う際、TLSは必要なセキュリティを提供します。しかし、手順がわからなければTLSの実装は難しい場合があります。
このチュートリアルでは、ローカルのKubernetesクラスターにデプロイしたアプリケーションにTLSを実装する方法を学びました。これらの原則は、オンプレミス環境やクラウドプロバイダー上でホストされているアプリケーションにも応用できます。
本番環境のシステムにTLSを導入する際は、Let's Encryptなどの信頼できる認証局(CA)が発行するパブリック証明書の利用を検討しましょう。そのためにはサイトのドメインにアクセスできる必要がありますが、ロードバランサーサービスを使ってアプリケーションを公開したり、cert-managerのような証明書マネージャーで複数の証明書を管理したりするなど、追加の設定を行うこともできます。ロードバランサー経由でアプリケーションを公開すると、可用性、スケーラビリティ、耐障害性が向上します。また、証明書マネージャーを使えば、今後のクラスター証明書の発行と管理を簡単に行えます。
ソースコードの段階からインフラを保護
Snykはワークフロー内のIaCセキュリティとコンプライアンスを自動化し、構成ドリフトや不足しているリソースを検出します。



