Skip to main content

KubernetesでTLSを実装する

著者
Headshot of Rubaiat Hossain

Rubaiat Hossain

feature kubernetes polp

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を実装するには、まず次のコマンドを実行して、アプリケーションをローカルマシンにクローンします。

git clone https://github.com/snyk-snippets/tls-in-k8s.git

次に、以下のコマンドでプロジェクトディレクトリに移動します。

cd k8s-tls-demo

プロジェクトディレクトリに移動したら、アプリケーションをKubernetesクラスターにデプロイできます。

次のコマンドでkindクラスターを作成します。

kind create cluster --config=config.yaml

注:config.yamlファイルは、kindノードのいくつかのポートをローカルホストにバインドします。Docker Desktopではkindコンテナが仮想マシン(VM)内で実行されるため、これは必要です。DockerをネイティブにインストールしたLinuxワークステーション(Docker DesktopのVMなし)で実行する場合は、ここで--configオプションを省略できます。

次に、コンテナイメージをビルドしてkindクラスターに読み込みます。

docker build -t demo-payment-app .
kind load docker-image demo-payment-app

注:必要に応じて、kind loadを使う代わりにイメージをレジストリにプッシュすることもできます。その場合は、以下で使用するdeployment.yamlファイルを編集してレジストリ情報を設定し、イメージをプルできるようにimagePullPolicyを「Always」に更新する必要があります。

クラスターが作成されたら、次のコマンドでアプリケーションをデプロイします。

kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

KubernetesがPodやその他の必要なリソースを作成するまで、数分待ちます。次のコマンドでリソースのステータスを確認できます。

kubectl get deployments
kubectl get pods

デモプロジェクトでは、nodePortサービスを使い、ポート30080で決済アプリケーションを公開します。typeフィールドを指定せずにKubernetesサービスを定義すると、デフォルトのサービス種別としてClusterIPが割り当てられます。

ただし、ClusterIPが公開するIPアドレスにアクセスできるのは、クラスター内のネットワークからのみです。そのため、ウェブブラウザーなどの外部クライアントから、クラスターで実行中のアプリケーションを表示することはできません。本番環境では、クラウドプロバイダーがパブリックにアクセス可能なIPアドレスを持つ外部ロードバランサーを提供できるよう、LoadBalancerサービスを使う方法があります。

次のコマンドで、このデモアプリケーションのサービスを確認します。

kubectl get services

実行環境に応じて、次のいずれかの方法でデモアプリケーションにアクセスできます。

VMベース(Docker Desktopなど):

ブラウザーからデモアプリケーションにアクセスするには、config.yamlファイルで設定したkindのポートバインディングを使い、localhost:30080にアクセスします。

VMを使用しない環境(Docker Desktopを使わないLinuxなど):

ブラウザーからデモアプリケーションにアクセスするには、次のコマンドでnodePortが公開するIPアドレスを確認します。

kubectl get nodes -o wide

KubernetesコントロールプレーンのINTERNAL-IP(つまり172.18.0.2)が表示されます。次の手順で決済アプリケーションにアクセスできます。

http://172.18.0.2:30080

上記の手順に従い、IPアドレスをKubernetesコントロールプレーンの実際のINTERNAL-IP、またはlocalhostに置き換えてください。

上記のIPアドレスとポートをブラウザーで開くと、カード情報を入力できる決済ページが表示されます。これは模擬アプリケーションのため、実際の決済処理は行われず、入力したデータも保存されません。

カード番号、有効期限、CVVの入力欄と「支払いを送信」ボタンが表示された決済ページのフォームのブラウザーウィンドウ。

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

ブラウザーに支払いフォームが表示され、サイトへの接続が安全ではないというセキュリティ警告が表示されています。

アプリに自己署名TLS証明書を実装する

次に、サンプルのKubernetesアプリケーションにTLSを実装します。自己署名証明書と秘密鍵を作成します。

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=my-demo-app.local"

次に、証明書と秘密鍵を含むKubernetes Secretを作成します。

kubectl create secret tls my-demo-app-tls --key tls.key --cert tls.crt

次にNginx Ingress Controllerをインストールし、決済アプリに届くリクエストをHTTPSにリダイレクトできるようにします。アプリをnodePortで公開しているため、サービス種別にNodePortを指定したカスタム値ファイルを使ってIngressをインストールする必要があります。

ingress-values.yamlというファイルを作成し、次の内容を記述します。

controller:
  service:
    type: NodePort
    nodePorts:
      http: 32080
      https: 32443

次に、Helmとカスタム値ファイルを使ってNginx Ingress Controllerをインストールします。

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx -f ingress-values.yaml

次の内容でingress.yamlファイルを作成します。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: demo-payment-app-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  tls:
  - hosts:
    - my-demo-app.local
    secretName: my-demo-app-tls
  rules:
  - host: my-demo-app.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: demo-payment-app
            port:
              number: 80

次のコマンドを使って、このマニフェストファイルを適用します。

kubectl apply -f ingress.yaml

/etc/hostsファイルに次のエントリーを追加します。

<control_plane_IP> my-demo-app.local

Docker Desktopを使用している場合は<control_plane_IP>を127.0.0.1に置き換えてください。それ以外の場合は、kubectl get nodes -o wideを実行して取得したIPアドレスに置き換えます。

ブラウザーでhttps://my-demo-app.local:32443にアクセスすると、HTTPSで決済アプリを利用できます。初回アクセス時には、ブラウザーに警告ページが表示され、リスクを承認して続行できます。続行すると、HTTPSでサイトにアクセスできます。

カード番号、有効期限、CVVの入力欄がある決済フォームを表示したブラウザー。アドレスバーにはHTTPS接続が「保護されていない」と表示されています。

TLS証明書が自己署名証明書であるため、ブラウザーにこのエラーが表示されます。URLのHTTPS部分に取り消し線が引かれていることからも確認できます。

アプリに信頼できるTLS証明書を実装する

この問題を解決するには、mkcertを使って開発環境で信頼される証明書を作成できます。そのために、ローカルCAを作成し、ローカルドメイン用の信頼できる証明書を生成します。

mkcert -install
mkcert my-demo-app.local

前のSecretを削除し、mkcert用の新しいSecretを作成します。

kubectl delete secret my-demo-app-tls
kubectl create secret tls my-demo-app-tls --key 
my-demo-app.local-key.pem --cert my-demo-app.local.pem

最後に、次のコマンドを実行してIngressリソースを再デプロイします。

kubectl delete ingress demo-payment-app-ingress
kubectl apply -f ingress.yaml

KubernetesアプリケーションのTLSを検証する

これで、信頼できるTLS証明書を使って決済アプリにアクセスできるはずです。ブラウザーを再起動してからhttps://my-demo-app.local:32443にアクセスし、新しい証明書を有効にします。今度は警告が表示されず、通常のTLSで保護されたウェブサイトと同様に、URLのHTTPS部分に取り消し線が引かれていないことを確認できます。

カード番号、有効期限、CVVの入力欄と「支払いを送信」ボタンが表示された決済ページのブラウザーウィンドウ。

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

カード番号、有効期限、CVVの入力欄がある決済フォームと、接続が安全であることを確認するセキュリティパネルを表示したブラウザー。

更新後のコード例は、GitHubプロジェクトリポジトリのsolutionブランチで確認できます。

まとめ

機密データを扱う際、TLSは必要なセキュリティを提供します。しかし、手順がわからなければTLSの実装は難しい場合があります。

このチュートリアルでは、ローカルのKubernetesクラスターにデプロイしたアプリケーションにTLSを実装する方法を学びました。これらの原則は、オンプレミス環境やクラウドプロバイダー上でホストされているアプリケーションにも応用できます。

本番環境のシステムにTLSを導入する際は、Let's Encryptなどの信頼できる認証局(CA)が発行するパブリック証明書の利用を検討しましょう。そのためにはサイトのドメインにアクセスできる必要がありますが、ロードバランサーサービスを使ってアプリケーションを公開したり、cert-managerのような証明書マネージャーで複数の証明書を管理したりするなど、追加の設定を行うこともできます。ロードバランサー経由でアプリケーションを公開すると、可用性、スケーラビリティ、耐障害性が向上します。また、証明書マネージャーを使えば、今後のクラスター証明書の発行と管理を簡単に行えます。

ソースコードの段階からインフラを保護

Snykはワークフロー内のIaCセキュリティとコンプライアンスを自動化し、構成ドリフトや不足しているリソースを検出します。

続きを読む

feature customer snowflake
Article

開発初期からセキュリティを確保:Snowflake Cortex Code向けSnyk Studioインテグレーションを発表

Snyk StudioがSnowflake Cortex Codeと連携し、開発中にAI生成コード、依存関係、コンテナの脆弱性をスキャンします。

feature insights announcement
Blog

Node-gypサプライチェーン侵害:binding.gypに潜む自己増殖型npmワーム

新たなnpmワームがbinding.gypを悪用し、インストール時にnode-gypを起動。ライフサイクルスクリプトを使わずに悪意あるパッケージからコードを実行します。認証情報を窃取し、GitHub上に潜伏し、メンテナー間で自己増殖します。

Article

Miasmaサプライチェーン攻撃:@redhat-cloud-servicesのnpmパッケージで悪意あるコードを確認

Miasmaと呼ばれるサプライチェーンワームが、@redhat-cloud-servicesの数十件のnpmリリースで見つかりました。悪意あるpreinstallフックは認証情報を窃取し、クラウドIDを調査して、他のパッケージを再公開する可能性があります。