それなりにセキュアなGKEクラスタを構築する

## はじめに

GKEはGCPのサービスのひとつで、kubernetesのマネージドサービスになります。
GCPコンソールもしくはコマンドラインから、簡単にkubernetesクラスタを構築することができます。
リージョンの設定など初期設定を済ます必要はありますが、以下のコマンドを実行するだけでクラスタ構築が完了します。

```
gcloud container clusters create sample-cluster
```

趣味で触る場合はさておき、実際に業務でGKEを利用する場合はしっかりとセキュリティ面を考慮する必要があり、そのあたりを意識せずにGKEを利用するのは危険です。

本記事では、GKEのセキュリティに関するドキュメントを参考にし、それなりにセキュアなGKEクラスタの構築方法を紹介したいと思います。本来は最新情報などのキャッチアップも含め本家のサイトを参考にしていただくのが一番です。

このような機能があるんだなと、なんとなくの全体感を掴んでいただければ幸いです。

## 基本

### Kubernetesのダッシュボードを無効にする
Kuberntesダッシュボードとは、クラスタをWebUIで管理できるようなツールになります。

https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/

この管理ツールを起動するにはサービスアカウントに強い権限を付与する必要がある為、代わりにGKEのコンソールを利用することが望ましいです。
GKEの新しめのバージョンであれば、デフォルトで無効になっており、有効にするには別途インストールが必要になるのでそれほど意識する必要はないのではと思います。

参考リンク
https://cloud.google.com/kubernetes-engine/docs/how-to/hardening-your-cluster?hl=ja#disable_kubernetes_dashboard

### アクセス制御にはRBACを利用する

古いバージョンでのアクセス制御はABACと呼ばれる属性ベースのアクセスコントロール機能を利用していましたが、現在はRBACという役割ベースのアクセス制御機能を利用するのが一般的になっています。

GKE 1.8以降はデフォルトでABACは無効になっていますのであえて利用することはないでしょう。

Cloud IAMでも、ある程度リソースに対するアクセス制御は可能ですが、クラスターやnamespace内のリソースに対して細かいアクセス制御を行う場合はRBACを利用することになります。

まだベータ版ではありますが、GKEではロールをGoogle Groupに割りあてることができるようになっており、ユーザー管理が捗りそうです。

参考リンク
https://cloud.google.com/kubernetes-engine/docs/how-to/hardening-your-cluster?hl=ja#leave_abac_disabled_default_for_110
https://cloud.google.com/kubernetes-engine/docs/how-to/role-based-access-control?hl=ja
https://cloud.google.com/kubernetes-engine/docs/concepts/access-control?hl=ja
https://kubernetes.io/docs/reference/access-authn-authz/rbac/

### 古いクライアント認証方式を有効にしない

GKEでは現在、OAuthによるクライアント認証を推奨しています。以前に使われていたパスワード認証や証明書による認証は最新のクラスタではデフォルト無効になっています。不必要に有効にしないようにしましょう。

Googleは併せて認証情報のローテーションを定期的に行うことを推奨しています。

参考リンク
https://cloud.google.com/kubernetes-engine/docs/how-to/hardening-your-cluster?hl=ja#restrict_authn_methods
https://cloud.google.com/kubernetes-engine/docs/how-to/credential-rotation?hl=ja
https://kubernetes.io/docs/reference/access-authn-authz/authentication/

### Pod間通信を制限する

ネットワークポリシーを利用することで、Pod間の通信を制御することができます。
例えば、データストアが稼働しているPodに対してどこからもアクセスを許可するわけではなく、限られたPodからのみアクセスを許可するといったことが可能になります。
何も設定しない場合は、Pod間での通信に何も制御はかからないので、アプリケーションの特性に応じて適切な通信制御を行いましょう。

ネットワークポリシーを有効にするには`--enable-network-policy`を使用します。

```Bash
gcloud container clusters create sample-cluster --enable-network-policy
```

参考リンク
https://cloud.google.com/kubernetes-engine/docs/how-to/network-policy?hl=ja
https://kubernetes.io/docs/concepts/services-networking/network-policies/

### 必要最低限のサービスアカウントを利用する

GKEのノードに付与されるデフォルトのサービスアカウントは必要のない権限も含まれている為、そのままでの利用は非推奨とされています。専用のサービスアカウントを作成しそちらを利用するようにしましょう。
クラスタ構築には最低限 monitoring.viewer、monitoring.metricWriter、logging.logWriterの3つのみあればOKです。Google Cloud APIへのアクセスが必要になった場合は、こちらに権限を追加するのも一つの手ですが、より安全な手段として後述のWorkload Identityの利用も視野に入れてください。

サービスアカウントを指定する場合は、クラスタ構築時`--service-account`で専用のサービスアカウントを指定します。

```Bash
gcloud container clusters create sample-cluster --service-account=[SA_NAME]@[PROJECT_ID].iam.gserviceaccount.com
```

参考リンク
https://cloud.google.com/kubernetes-engine/docs/how-to/hardening-your-cluster?hl=ja#use_least_privilege_sa


### GKEノードのバージョンを最新に保ち続ける

GKEのマスタはGoogleが自動でアップグレードを行ってくれますが、ノードのアップグレードはこちらが面倒を見ることになります。とはいえ、GKEノードの自動アップグレードを有効にすることで、セキュリティパッチが自動的にあたるようになりセキュリティが向上します。手動でアップグレードを行う場合は定期的に行うようにしましょう。

自動アップグレードを有効にするには`--enable-autoupgrade`フラグを付与してクラスタを構築します。

```Bash
$ gcloud beta container clusters create sample-cluster --enable-autoupgrade
```

なお、GUIでクラスタを作成する場合、ノードの自動アップグレードはデフォルトで有効になっています。

参考リンク
https://cloud.google.com/kubernetes-engine/docs/how-to/node-auto-upgrades?hl=ja



### クラスタマスターにアクセスできるネットワークを限定する

マスター承認済みネットワークを指定することで、マスターにアクセスできるアクセスを特定のIPに制限することができます。どこからでもマスターにアクセスできてしまうのはセキュリティ的に非常によろしくないので、必ずこの機能を有効にして限られた経路からしかクラスタにアクセスできないようにするべきです。

限定公開クラスタと組み合わせることで、よりセキュアなクラスタを構築することができます。
マスター承認済みネットワークを指定するには`--enable-master-authorized-networks`と`--master-authorized-networks`をクラスタ構築時に指定します。

```Bash
gcloud container clusters create sample-cluster --enable-master-authorized-networks --master-authorized-networks 111.111.111.111/32
```

ネットワークを複数指定する場合は、カンマで区切ります。

```Bash
gcloud container clusters create sample-cluster --enable-master-authorized-networks --master-authorized-networks 111.111.111.111/32,111.111.111.0/24
```

参考リンク
https://cloud.google.com/kubernetes-engine/docs/how-to/authorized-networks?hl=ja

### 限定公開クラスタを作成する

デフォルトではクラスタのノードはpublicなネットワークに晒されることになりますが、ネイティブVPCを有効にすることで内部IPのみを持つノードでクラスタを構築することができます。インターネットから隔離されたノードは、インターネットに対するインバウンド・アウトバウンド通信に制限がかかります。

限定公開クラスタのマスターにアクセスするには、パブリックなエンドポイントとプライベートなエンドポイントのどちらかに対してリクエストを送ることになります。前者を無効にすることで、よりセキュアな構成にできますがアクセスできる経路がより限定的になるので、そこは運用面とのトレードオフになります。
仮にパブリックなエンドポイントを有効にしたとしても、マスタ承認済みネットワークを指定することで安全にマスタにアクセスすることは可能です。

次の例は、パブリックエンドポイントが有効な限定公開クラスタを構築するコマンドです。

```Bash
gcloud container clusters create sample --enable-ip-alias --enable-private-nodes --master-ipv4-cidr 172.16.0.0/28 --enable-master-authorized-networks --master-authorized-networks 111.111.111.111/32
```

`--enable-ip-alias`はVPCネイティブに切り替えます。
`--enable-private-nodes`を指定すると、クラスタのノードに外部IPが付与されません。
`--master-ipv4-cidr`はマスタのCIDRを指定します。`--enable-private-nodes`を指定する場合は必須です。

パブリックエンドポイントを無効にしたさらにセキュアな限定公開クラスタ

```Bash
gcloud container clusters create sample-cluster --enable-ip-alias --enable-private-nodes --master-ipv4-cidr 172.16.0.0/28 --enable-private-endpoint --enable-master-authorized-networks --master-authorized-networks 10.146.0.0/20
```

`--enable-private-endpoint`により、マスタはプライベートエンドポイントのみ持つようになります。
外部からのアクセスは無効となり、クラスタのノードやPodからのみアクセスを受け付けます。それ以外の内部IPアドレスからアクセスさせたい場合は`--master-authorized-networks`を指定します。

参考リンク
https://cloud.google.com/kubernetes-engine/docs/how-to/private-clusters?hl=ja

### アプリケーションレイヤーでの秘匿情報の暗号化を行う

パスワードやトークンなどの秘匿情報は、kubenetesではSecretリソースとして取り扱います。この秘匿情報はetcdに平文で保存されます。より安全にSecretを取り扱いたい場合は、Google KMSを利用したアプリケーションレイヤでの暗号化を行いましょう。こうすることで例え悪意のあるマルウェアがetcdにアクセスできたとしても情報を守ることができます。

この機能を有効にするにはCloud KMSにて鍵を作成し、クラスタの作成時に`database-encryption-key`で指定するだけです。

```Bash
gcloud container clusters create sample-cluster --database-encryption-key projects/[KEY_PROJECT_ID]/locations/[LOCATION]/keyRings/[RING_NAME]/cryptoKeys/[KEY_NAME] 
```

参考リンク
https://cloud.google.com/kubernetes-engine/docs/how-to/encrypting-secrets?hl=ja


## ベータ版の機能

以下の機能は2019年12月23日現在ベータ版の機能です。
ベータ版の機能はSLAやテクニカルサポートの義務がなく、変更が発生する場合があります。
これらの機能をコマンドから利用するには`gcloud beta`コマンドを使用しなければなりません。

### ポッドセキュリティポリシーを利用する

ポッドセキュリティポリシーを利用することで、各Podで利用される機能を制限することができます。例えば、特権コンテナの利用を制限したり、安全でないsysctlを制限するなど、クラスタ全体に特定のポリシーを適用します。

この機能を利用するにはアドミッションコントローラーの該当プラグインを有効にする必要があり、以下のように`--enable-pod-security-policy`を指定してクラスタを構築します。

```Bash
gcloud beta container clusters create sample-cluster --enable-pod-security-policy
```

ポッドセキュリティポリシーを有効にする前に、ポリシーの定義ファイルを作成し承認しておく必要があります。これらの作業を行わず、上記のコマンドを実行した場合はポッドの作成が行えなくなるのでご注意ください。

参考リンク
https://cloud.google.com/kubernetes-engine/docs/how-to/pod-security-policies?hl=ja
https://kubernetes.io/docs/concepts/policy/pod-security-policy/



### Shielded VMを利用する

Shielded VMとは、セキュリティ機能がより一層強化されたCompute Engine VMで、インスタンスが改ざんされていないかを確認できる機能を持っています。GKEノードをこのShielded VM上に構築することで、GKEノードがShileded VMの機能の恩恵を受けることができます。

`--enable-shielded-nodes`を指定し、Shielded GKE Nodeを有効にします。

```Bash
gcloud beta container clusters create sample-cluster --enable-shielded-nodes
```

デフォルトでセキュアブートは無効になっています。
セキュアブートとは、ブートコンポーネントの署名を検証し、異常があった場合はブートをキャンセルする機能です。`--shielded-secure-boot`でセキュアブートを有効にできますが、ブートコンポーネントの中に未署名のモジュールが存在していた場合ブートが失敗します。

```Bash
gcloud beta container clusters create sample-cluster --shielded-secure-boot
```

参考リンク
https://cloud.google.com/kubernetes-engine/docs/how-to/shielded-gke-nodes?hl=ja
https://cloud.google.com/security/shielded-cloud/shielded-vm?hl=ja


### Workload Identityを利用する

Workload Identityは、Googleがもっとも推奨するGKEからGoogleのCloud APIにアクセスするための手段です。従来、サービスアカウントに対してAPIへのアクセス権を付与する形では、そのノードで稼働するコンテナ全てがその権限を持つことになります。
クレデンシャルをアプリケーションに埋め込む方法もありますが、Workload Identityを利用するとより安全にPodに対してAPIへのアクセス権を付与することができるようになります。

Workload Identityを利用するには`--identity-namespace`を指定します。

```Bash
gcloud beta container clusters create sample-cluster --identity-namespace=[PROJECT_ID].svc.id.goog
```

参考リンク
https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity?hl=ja

### Binary Authorizationを利用する

Binary Authorizationは、署名を検証し不正なコンテナがGKE上にデプロイされることを防ぎます。CI/CDパイプラインにおいて、指定されたテストをパスしているか？正しい手順でビルドされたか？などを検証し、信頼できるイメージのみデプロイできるようなフローを実現することが可能になります。

参考リンク
https://cloud.google.com/binary-authorization/?hl=ja
https://cloud.google.com/solutions/binary-auth-with-cloud-build-and-gke?hl=ja
https://cloud.google.com/solutions/secure-software-supply-chains-on-google-kubernetes-engine?hl=ja

### よりセキュアなノードイメージを利用する

コンテナ用に最適化されたイメージであるcos_containerdをノードイメージに指定できます。
以下のように`--image-type=cos_containerd`をクラスタ構築時に付与します。

```
gcloud container clusters create sample-cluster --image-type=cos_containerd
```

参考リンク
https://cloud.google.com/kubernetes-engine/docs/concepts/using-containerd?hl=ja
https://cloud.google.com/container-optimized-os/docs/concepts/security?hl=ja

### GKE Sandboxを利用する

ウェブホスティングサービスなど、ソースコードのアップロードを許可しているような信頼できないワークロードを運用する場合、GKE Sandboxの利用がおすすめです。GKE Sandboxは、アプリケーションとホストカーネルを分離し、信頼できないコードからノードを守ってくれます。この仕組みはgVisorを利用して実現されています。

GKE Sandbox環境下ではIstioを利用できないといった制限があるので複数ありますので制限事項を理解した上で用途に合うアプリケーションに適用するのが良いかと思います。

GKE Sandboxを利用するには、`--sandbox type=gvisor`を指定し専用のノードプールを作成します。`--image-type=cos_containerd`も併せて指定してください。

```Bash
gcloud beta container node-pools create sample-sandbox-pool --cluster=sample-cluster --image-type=cos_containerd --sandbox type=gvisor
```

なお、デフォルトのノードプールではGKE Sandboxを利用できません。


参考リンク
https://cloud.google.com/kubernetes-engine/sandbox/?hl=ja
https://cloud.google.com/kubernetes-engine/docs/concepts/sandbox-pods?hl=ja

## まとめ

セキュアなGKEを構築する上で用意されている機能概要を簡潔にまとめてみました。各項目に参考サイトのリンクが貼ってありますので、詳細な情報や具体的な設定方法はそちらを参考にしてください。

本記事では紹介しきれなかった機能もありますので、そちらも合わせて本家のセキュリティガイドに目を通してみることをオススメします。

参考リンク
https://cloud.google.com/kubernetes-engine/docs/concepts/security-overview
https://cloud.google.com/kubernetes-engine/docs/how-to/hardening-your-cluster