< back to blog

GKEセキュリティ ベストプラクティスガイド

清水 孝郎
GKEセキュリティ ベストプラクティスガイド
Published by:
清水 孝郎
@
GKEセキュリティ ベストプラクティスガイド
Published:
February 20, 2022
シスディグによるファルコフィード

Falco Feedsは、オープンソースに焦点を当てた企業に、新しい脅威が発見されると継続的に更新される専門家が作成したルールにアクセスできるようにすることで、Falcoの力を拡大します。

さらに詳しく

本文の内容は、2022年2月21日現在における、Sysdig Cloud Native Learning Hub上のGKE Security: Best Practices Guide(https://sysdig.com/learn-cloud-native/kubernetes-security/gke-security-best-practices-guide) を元に日本語に翻訳・再構成した内容となっております。

サイバーセキュリティの脅威や攻撃の増加に伴い、クラウドセキュリティはクラウド・インフラストラクチャーにおいてますます重要な要素となっています。クラウド・インフラのセキュリティを確保する上で重要な役割を果たすアクターが2つあります。それは、クラウドプロバイダーカスタマーです。

クラウド・プロバイダーは、自社のインフラを保護し、ソフトウェアのセキュリティ問題に対処する責任があります。

カスタマーは、自身のアプリケーションのセキュリティに責任を負います。また、クラウド・プロバイダー・インフラストラクチャー内のデータやワークロードを保護するために提供されているコントロールを正しく使用する必要があります。

この記事では、主要なプロバイダーの1つであるGoogle Kubernetes Engine(略してGKE)を取り上げます。Kubernetesは複雑なシステムで、セキュリティのベストプラクティスを含む機能を常に改善しており、新しい機能の追加、既存の機能(PSPなど)の削除、新しい機能への置き換えを行っています。ここでは、Google Kubernetes Engine(GKE)のセキュリティとベストプラクティスについて詳しく説明します。

クラウドネイティブセキュリティ

CNCF(Cloud Native Computing Foundation)は、クラウド・ネイティブ・スタックを「基盤」「ライフサイクル」「環境」の各層で構成すると定義しています。GKEセキュリティを含むクラウドセキュリティには、これらすべての層が含まれていなければなりません。

これらの層は次の図のように描かれています。
Cloud Native Stack

図1 - クラウド・ネイティブ・スタック - Cloud Native Security Whitepaper

ライフサイクル

ライフサイクルは、「開発」「配布」「デプロイ」「ランタイム」からなります。

開発

Develop

図2 - 開発 - クラウド・ネイティブ・セキュリティ・ホワイトペーパー

"Develop "は、アーティファクトの作成に関するこのサイクルの最初の部分です。これには、Infrastructure as Code、有効なコンテナやマニフェストファイルなどが含まれます。これは、SCM(Source Control Management)に新しい変更をコミットする前の段階となります。

"Develop "の段階では、Google Cloudは以下のようなツールを提供しています。

Cloud Codeは、Kubernetesアプリケーションを開発する際に、設定ファイルの作業に時間を費やすことなく、コードやアプリケーションの作成に集中することができます。

Center of Internet Security (CIS) Benchmarkは、開発チームや組織に、デフォルトで安全なワークロードを作成するためのガイドを提供します。Google Cloudでは、これらのベストプラクティスのセキュリティ推奨事項を提供しています。CIS ベンチマーク

その他の重要な検討事項:

Google Cloud の Container-Optimized OS(COS)を使用する。

イメージを常に最新の状態に保つ。

コンテナが必要とするアプリケーションのみをインストールする。

K8sのマニフェストファイルを検証するために、リンティングツールを使用する。

ディストリビューション

Distribute
図3 - Distribute - Cloud Native Security Whitepaper

次のステップでは、前のステップで準備されたそれらのマニフェストまたはアーティファクトを使用します。そのマニフェストやアーティファクトをビルドして、レジストリに送信する必要があります。そして、さまざまな種類のテストを実行し、イメージをスキャンし、署名して、これらの新しく作成されたリソースにポリシーを適用します。

"Distribute"の段階では、Google Cloudは以下のようなツールを提供しています。

Cloud Buildは、Kubernetesアプリケーションの開発中に、ファイルの設定ではなくコードを書くことに集中することができます。

Google Artifact RegistryGoogle Container Registryは、ビルドの成果物や依存関係を安全に保管するために使用できます。これらは、ディープスキャン機能を使って自動的にスキャンされ、新しい脆弱性があれば特定されます。トリガーを作成して、パッケージに対するアップストリームの変更を検出し、それらを強制的に再ビルドしてスキャンし、さらなるポリシーの適用に使用できる新しい証明を作成することができます。

デプロイ

Deploy
図4 - Deploy - Cloud Native Security Whitepaper

ここでは、対象となる実行環境にアプリケーションをデプロイする前にチェックが行われます。このチェックでは、実施されているすべてのポリシーとコンプライアンスを満たす必要があります。

Google CloudはBinary Authorizationツールを提供しています。これは、レジストリとポリシーを結びつけるものです。コンテナイメージのメタデータに基づいて、デプロイメントポリシーを定義することができます。このポリシーは、前のステップで行ったスキャンの結果や、既存のQAプロセスに基づいて設定することができます。

ランタイム

Runtime
図5 - ランタイム - Cloud Native Security Whitepaper

このフェーズは、図1に描かれているように、コンピュート、アクセス、ストレージで構成されています。

コンピュートは、Google Cloudのグローバルなインフラ上でアプリケーションを作成し、実行するためのGKEインフラにおける重要な基盤です。しかし、その前に安全性を確保する必要があります。

ネームスペースは、リソースを互いに論理的に分離する機能を提供します。これにより、組織やセキュリティ、さらにはパフォーマンスの向上に役立ちます。1つのGKEクラスター内に複数のネームスペースを持つことができます。

ネームスペースを作成する簡単な方法を以下に示します:

$> kubectl create namespace online-web-development

$> cat >> online-web-staging.yaml <<EOF

apiVersion: v1

kind: Namespace

metadata:

name: online-web-staging

EOF

$> kubectl apply -f online-web-staging.yaml

RBACでは、より細かく特定の権限を設定することができます。これにより、Google Cloudのユーザーやユーザーグループを設定して、クラスター内のKubernetesオブジェクトやクラスター内の特定のネームスペースを操作することができます。また、既存のロールを定期的に監査し、使用されていないロールや非アクティブなロールを削除することもお勧めします。RoleとRoleBindingはネームスペースレベルで適用されるKubernetesオブジェクトであり、ClusterRoleとClusterRoleBindingはクラスターレベルで適用されます。

# Role

apiVersion: rbac.authorization.k8s.io/v1

kind: Role

metadata:

Name: cluster-metadata-viewer

Namespace: default

rules:

apiGroups:

“”

resources:

configmaps

resourceNames:

cluster-metadata

verbs:

get

# RoleBinding

apiVersion: rbac.authorization.k8s.io/v1

kind: RoleBinding

metadata:

Name: cluster-metadata-viewer

Namespace: default

roleRef:

apiGroups: rbac.authorization.k8s.io

Kind: Role

Name: cluster-metadata-viewer

subject:

apiGroups: rbac.authorization.k8s.io

Kind: Group

Name: system: authenticated

Pod Security Policy (PSP)は、クラスターレベルでセキュリティを制御し、ポッドの作成や更新の認証を設定するためのポリシーです。これは、特権コンテナの実行(フィールド名:privileged)、ホストネームスペースの使用(フィールド名:hostPID、hostIPC)、ポッドのボリュームを所有するFSGroupの割り当て(フィールド名:fsGroup)などに適用できます。完全な情報はこちらでご覧いただけます。なお、PSPはKubernetes v1.21で非推奨となっており、v1.25で削除される予定です。

Pod Security Policyは、Admission Controllerを有効にすることで、オプションで実装・施行することができます。ただし、注意が必要です。ポリシーを認証せずに施行すると、クラスター内にポッドを作成することができなくなります。GKEでは、Open Policy Agent(OPA)を採用しているGatekeeperを使用してPSPを適用することを推奨しています。

Workload Identityは、GKEで動作するポッドからGoogleの一連のサービス(Storage Buckets、App Engineなど)への認証を行うためにGoogleが推奨する方法です。これは、Kubernetesサービスアカウント(KSA)をGoogle Cloud IAMサービスアカウント(GSA)にリンクさせることで実現されます。これにより、KubernetesのシークレットやGSAのキーを管理することなく、Kubernetesのネイティブリソースを使用してワークロードのアイデンティティを定義し、GCP内のサービスへのアクセスを制御することが可能になります。これは、IAMとKSAの認証情報の間に信頼できる関係を作るワークロードアイデンティティプールによって処理されます。

リソースリクエストとリミットは、ノードやクラスターレベルのリソースが意図的(ハッキング)または非意図的(設定ミス)を防ぐために設定すべき非常に重要なプロパティです。

# Pod definition

apiVersion: v1

kind: Pod

metadata:

name: frontend

spec:

containers:

name: app

image: busybox

resources:

requests:

memory: “32Mi”

cpu: “200m”

limits:

memory: “64Mi”

cpu: “250m”

  • クラスターのアップグレードは、GKEセキュリティのベストプラクティスの一環としても実施する必要があります。定期的なアップグレードでは、重大なセキュリティ上の欠陥やその他のバグを修正したり、新しい機能を導入したりします。GKEには、クラスターのバージョンとアップグレードのタイミングを管理するリリースチャネルがあり、ノードプールのアップグレードも可能です。下位レベル(サンドボックスやプリプロダクションなど)では自動アップグレードを行い、アップグレード後にレビューを行うことを常に推奨します。そのレベルですべてが期待通りに動作したら、次の自動アップグレードを次のレベルの環境、つまり本番環境に適用することができます。


アクセスは、図1に示すように、ランタイムの一部であるセキュリティのもう一つの重要な側面です。

Google Cloud Projectを作成すると、デフォルトでは自分だけがプロジェクトやそのリソースにアクセスできます。誰がそのプロジェクトにアクセスできるのか、何ができるのかを管理するために、Identity Access Management(IAM)は、Google Cloudのリソースとそのアクセスを管理する機能を提供します。ロールベース・アクセス・コントロール(RBAC)がクラスターやネームスペース・レベルでアクセスを制御するのに対し、IAMはプロジェクト・レベルで機能します。

もう1つのGKEセキュリティのベストプラクティスは、ネットワークをセグメント化または分離することです。これは、仮想プライベートクラウド(VPC)ネットワークで実現できます。GKEクラスターは、同じネットワーク上にあれば、異なる地域(例えば、us-east1とeurope-west1)にあっても、内部IPで相互に通信することができます。

GKEでは、プライベートクラスターとは、コントロールプレーンのノードをパブリックインターネットからアクセスできないようにするクラスターのことです。プライベートクラスターでは、ノードはパブリックIPアドレスを持たず、プライベートアドレスのみを持ちます。これにより、ワークロードを孤立した環境で実行することができます。コントロールプレーンノードとワーカーノードは、VPCピアリングを使用して相互に通信します。

デフォルトでは、Kubernetesクラスターのネットワークトラフィックはオープンであり、ポッドやサービスが相互に通信できることを意味します。ポッド間の通信に制限を設けたり、必要なサービス間のみを許可するように制限したりすることは、Network Policyによって実施することができます。Network Policyでは、ラベルを使ってポッドを選択し、そのポッドに向けたネットワークトラフィックをルールで定義します。

# Deny all traffic in the default namespace

apiVersion: networking.k8s.io/v1

kind: NetworkPolicy

metadata:

name: deny-all

namespace: default

spec:

podSelector: {}

policyTypes:

Ingress

Egress

# NetworkPolicy that selects pods with label app=hello,

# and specifies Ingress policy to allow traffic only from Pods

# with the label app=foo: (GitHub repo)

apiVersion: networking.k8s.io/v1

kind: NetworkPolicy

metadata:

name: hello-allow-from-foo

spec:

policyTypes:

- Ingress

podSelector:

matchLabels:

app: hello

ingress:

- from:

- podSelector:

matchLabels:

app: foo

ネットワークポリシーは、kubeletが使用する10250番ポートや10255番ポートなど、機密性の高いポートへのネットワークアクセスを制御するためにも使用できます。

Authorized ネットワークでは、CIDR範囲を指定して、その範囲内のIPアドレスがHTTPSを使用してクラスターコントロールプレーンのエンドポイントにアクセスすることを許可できます。マスターAuthorized ネットワークを有効にした後、Authorized ネットワークを追加すると、指定したIPアドレスのセットへのアクセスをさらに制限することができます。実際に、クラスターの認証や認可に脆弱性がある場合に、クラスターへのアクセスを保護するのに役立ちます。

LoadBalancerのサービスタイプを作成すると、Google Cloudはプロジェクト内に、プロジェクト外からアクセス可能な安定したIPアドレスを持つネットワークロードバランサーを構成します。このサービスをGKEクラスター内でアクセスできるようにしたい場合は、Kubernetesのサービスマニフェストにアノテーションを追加します。しかし、インターネットに面したロードバランサーが必要で、すべてのIPアドレスに対してオープンにしたくない場合は、サービス仕様にloadBalancerSourceRangesを追加して、接続を許可するIPアドレスブロックを制限することができます。

# Internal LB and Limit IP address on loadBalancerSourceRanges

# Resource internal load balancing.

apiVersion: v1

kind: Service

metadata:

name: internal-load-balancer

annotations:

networking.gke.io/load-balancer-type: “Internal”

labels:

app: hello

spec:

type: LoadBalancer

selector:

app: hello

ports:

port: 80

targetPort: 8080

protocol: TCP

loadBalancerSourceRanges:

10.88.0.0/16

35.35.35.10/32

GKEは、コンテナネイティブ/Layer 7ロードバランサーも導入しています。このingressオプションにより、いくつかの種類のロードバランサーがポッドを直接ターゲットにして、トラフィックをポッドに均等に分配できるようになります。これにより、ポッドをロードバランシングの第一級市民とみなすことができる。これは、ネットワークエンドポイントグループ(NEG)と呼ばれるGoogle Cloudのデータモデルで実現できます。NEGは、IP-ポートのペアで表されるネットワークエンドポイントの集まりです。GKE IngressでNEGを使用する場合、Googleが管理するKubernetesのingressコントローラが採用され、L7ロードバランサーのあらゆる側面の作成が容易になります。 これには、仮想IPアドレスの作成、フォワーディングルール、ヘルスチェック、ファイアウォールルールなどが含まれます。

# Ingress Resource

apiVersion: networking.k8s.io/v1

kind: Ingress

metadata:

name: my-ingress

labels:

app.kubernetes.io/name: sre-devops

annotations:

# To force https which requires the secretName

kubernetes.io/ingress.allow-http: “false”

# Annotation for an internal L7 load balancer

kubernetes.io/ingress.class: gce-internal

# Annotation for an external L7 load balancer

kubernetes.io/ingress.class: “gce”

spec:

tls:

hosts:

“my-example.foo.com”

secretName: foo.com

rules:

host: “my-example.foo.com”

http:

paths:

path: /*

pathType: ImplementationSpecific

backend:

service:

name: my-service

port:

number: 443

# Service Resource

# To bind the service to ingress, Type: ClusterIP is needed and

# annotation: cloud.google.com/neg: ‘{“ingress”: true}’

apiVersion: v1

kind: Service

metadata:

name: my-service

annotations:

# Required annotation

cloud.google.com/neg: ‘{“ingress”: true}’

# To force https from the LB to the backend

cloud.google.com/app-protocols: ‘{“my-https-port”: “HTTPS”}’

# Applies backend-config named allowed-ingress-acls

cloud.google.com/backend-config: ‘{“default”: allowed-ingress-acls}’

spec:

# Make sure this is set and not LoadBalancer

type: clusterIP

ports:

Name: my-https-port

Port: 443

Protocol: TCP

targetPort: 8080

selector:

ストレージは、GKE上で実行するアプリケーションのデータを保存する場所です。いくつかのオプションから選ぶことができます。

データベースの場合は、Cloud SQLDatastore、またはCloud Spanner

オブジェクト・ストレージには、Cloud Storage

プライベートコンテナイメージ、helmチャート、ビルドアーティファクトには、Artifact Registryを使用することができます。

NAS(Network Attached Storage)を必要とするアプリケーションにはFilestoreを使用することができます。

ブロック・ストレージを必要とするアプリケーションには、パーシステントディスクを使用します。このディスクは、手動でプロビジョニングすることも、GKEによって動的にプロビジョニングすることもできます。

PersistentVolume(PV)は、ポッドがストレージとして使用できるクラスターリソースです。一方、PersistentVolumeClaim(PVC)は、Compute EngineのパーシステントディスクでバックアップされたPersistentVolumeを、クラスターで使用するために動的にプロビジョニングするために使用できます。

アプリケーションが使用する暗号化されたStorageClassは、Google Key Management Store (KMS)を使用して作成することができます。

Google Cloud Audit Logs

GKEセキュリティのもう1つの重要な側面は、監査ログです。これは以下の点で重要です。

クラスターのセキュリティを評価するための監査証跡

既存または将来の脅威の分析

以前のインシデントや将来の脅威に基づいて、既存のネットワークポリシーやセキュリティポリシーを更新する

GKEは、クラウド監視クラウドロギングで構成される、優れたGoogle Cloudのオペレーションスイートを提供します。

クラウドロギングでは、ダッシュボードやアラートを設定することができ、お客様やチームが脅威を特定するのに役立ちます。これにより、継続的に監視することができ、クラウドロギングによって報告された内容に基づいて、既存のセキュリティポリシーを更新することができます。

まとめ

Kubernetesは、成長と拡大を続ける技術です。そのため、GKEのセキュリティもそれに合わせたものである必要があります。システムを保護し、セキュリティを確保することは、時に圧倒的な負担になります。この記事で紹介したGKEセキュリティの助けを借りれば、これを達成することができます。

たとえば、以下のような方法があります。

シフトレフト - セキュリティは、ソフトウェア開発ライフサイクルの左側、つまり開発から始めるべきです。Cloud Codeを使い、イメージをスキャンし、Artifact Registryにアーティファクトを保存することが良いスタートとなります。

システムのアップグレード - コンテナアプリケーション、Kubernetesなどが該当します。GKEは、この分野を支援するためのリリースチャネルを提供しています。

アクセス制限 - コンピュート、Kubernetesリソース、ストレージ、ワークロード/サービスなどへのアクセスを制限する必要があります。そのために、GKEはRBAC、IAM、ロードバランシング、ネットワークポリシー、プライベートクラスター、サービスメッシュなどを提供しています。RBAやIAMなど、理解しやすく、実装しやすいものから始めるようにすればよいのです。

最小限の特権、リソースとリクエストの制限 - これは、ルートユーザーがポッド内の任意のプロセスを実行できないようにするために重要です(良いスタートです)。また、ノードやクラスターが過剰に使用されるのを防ぐために、リソースとリクエストの制限を適宜設定する必要があります。

クラウドロギングを利用した継続的な監査 - 脅威を特定するのに役立ち、既存のネットワーク・ポリシーやセキュリティ・ポリシーを更新することができます。

リソース

コントロールプレーンアクセスのための認証ネットワークの追加 - https://cloud.google.com/kubernetes-engine/docs/how-to/authorized-networks

HTTP(S)ロードバランシングのためのGKE Ingress - https://cloud.google.com/kubernetes-engine/docs/concepts/ingress

ストレージの概要 - https://cloud.google.com/kubernetes-engine/docs/concepts/storage-overview

About the author

コンテナ、Kubernetes、ホストのセキュリティ

セキュリティの専門家と一緒に、クラウド防御の最適な方法を探索しよう