Kubernetesのセキュリティについて整理してみた件

# はじめに
2019/1/14にCNCF(Cloud Native Computing Foundation)のブログに、以下の記事がポストされていました。

9 Kubernetes Security Best Practices Everyone Must Follow：
https://www.cncf.io/blog/2019/01/14/9-kubernetes-security-best-practices-everyone-must-follow/

このブログ記事のタイトルを直訳すると「みんなが守るべき9つのKubernetesセキュリティのベストプラクティス」なのですが、Kubernetesやその周辺技術を触れている経験からすると、ここに書かれた内容だけ対応していれば十分ではないと考えています。このため、Kubernetesのセキュリティを担保するために必要なことを、調べられる範囲で調査したものを整理したいと考えています。

# クラスタのセキュリティを強化しよう
## Kubernetes APIへのアクセスを制御しよう
KubernetesはAPIから操作されるため、APIに対してアクセスするユーザを認証し、認証したユーザに対して実行できる操作を制御(認可)することが必要となります。Kubernetes APIのセキュリティを担保するために必要なことは以下の3つです。

* すべてのAPIへのアクセスにTLS(Transport Level Security)を利用
* API認証
* API認可

API認証には既存のOIDCサーバやLDAPサーバを利用することもできますし、API認可にはRole-based Access Control(RBAC)を利用することが推奨されています。これらについての詳細は後述したいと思います。
また、2018年12月に発見されたKubernetesで初めてのメジャーな脆弱性(CVE-2018-1002105)は、Kubernetes APIを介して外部から攻撃可能な脆弱性です。Kubernetes APIを信頼できるネットワークからのみアクセス可能なよう制御することも重要です。

## kubeletへのアクセスを制御しよう
kubeletはノードとコンテナを制御するHTTPSエンドポイントを公開しています。
デフォルトでは、kubeletはAPIで認証されていないアクセスを許可してしまうため、プロダクション環境ではkubeletの認証・認可を有効する必要があります。
また、インターネットからの攻撃を防ぐため、信頼できるネットワークからのみアクセス可能なように制御しましょう。

## ワークロードの機能を制御しよう
Podなどの各オブジェクトに対して、リソース制限などを行うことによりセキュリティを高めることができます。

### クラスタのリソース使用量を制御しよう
ResourceQuotaを指定することにより、Namespaceに付与されるリソース数を制御することができます。この機能は、Namespaceに割り当てることのできるCPU・メモリ・PV(Persistent Volume)の量を制限することができますし、Pod・Service・PVC(Persistent Volume Claim)数なども制御することができます。
また、LimitRangeを指定することにより、Podなどに対してCPUやメモリのリソースの最大値・最小値を制限することができます。これにより、ユーザが不当に高い値を要求することを防ぐことができます。

### どのような権限をコンテナに持たせるか制御しよう
Podの定義にSecurityContextを設定することにより、個々のコンテナに対して、特定のユーザでの実行や特権コンテナの作成などを制御することができます。また、PodSecurityPolicyを設定することにより、クラスタに対してセキュリティポリシーによる制限を行うこともできます。
例えば、これらの機能を利用することにより、rootユーザでの実行やノードに対してのアクセスなどを制御することができるようになります。

参考までに、PodSecurityPolicyで設定可能な項目は以下の通りです。

* privileged: Running of privileged containers
* hostPID, hostIPC:   Usage of host namespaces
* hostNetwork, hostPorts: Usage of host networking and ports
* volumes:    Usage of volume types
* allowedHostPaths:   Usage of the host filesystem	
* allowedFlexVolumes: White list of Flexvolume drivers
* fsGroup:    Allocating an FSGroup that owns the pod’s volumes	
* readOnlyRootFilesystem: Requiring the use of a read only root file system
* runAsUser, runAsGroup, supplementalGroups:  The user and group IDs of the container
* allowPrivilegeEscalation, defaultAllowPrivilegeEscalation:  Restricting escalation to root privileges	
* defaultAddCapabilities, requiredDropCapabilities, allowedCapabilities:  Linux capabilities	
* seLinux:    The SELinux context of the container

参考： https://kubernetes.io/docs/concepts/policy/pod-security-policy/	

### コンテナのネットワークアクセスを制御しよう
Namespaceに対してNetworkPolicyを設定することにより、他のNamespaceのPodからのアクセスを制御することができます。NetworkPolicyを有効化するには、CalicoなどのNetworkPolicyをサポートするネットワークプラグイン(CNIプラグイン)を利用して、クラスタを事前に構築しておく必要があります。

Kubernetes Networking with Calico: 
https://www.tigera.io/blog/kubernetes-networking-with-calico/

NetworkPolicyはIngressとEgressから成り立っており、インバウンド/アウトバウンドの通信を制御することができます。

### クラウドサービスのメタデータへのAPIアクセスを制御しよう
AWS・Azure・GCEなどのクラウドサービスでは、メタデータをインスタンスとインスタンスで実行されているPodから参照することができます。メタデータにクラスタの認証情報が含まれている場合、同一クラスタ上でマルチテナントしているような環境では、この認証情報を悪用して攻撃される可能性があります。このため、クラウドサービス上でKubernetesを利用している場合、インスタンスの認証情報に付与されるアクセス権の制限や、NetworkPolicyを使用してメタデータAPIへのPodアクセスの制御などを実施する必要があります。

### どのノードにPodが配置されるか制御しよう
Podが配置されているノードを介して、機密情報を持ったPodから情報を抜かれることを防ぐため、機密情報を持ったPodは専用のノードに配置されるようにしましょう。KubernetesにはPodがどのノードに配置するか制御するため、ノードに対してTaints(汚れ)を着けておき、それを許容できるPodのみスケジューリングすることも可能です。

## 不正アクセスからクラスタのコンポーネントを守ろう
### etcdへのアクセスを制限しよう
Kubernetes APIと同様に、クラスタの構成情報を保持しているetcdへのアクセスも制御しましょう。Kubernests APIとのクライアント証明書による相互認証や、Kubernetes APIからのみアクセス可能なようにネットワーク通信を制御する必要があります。

### 監査ログを有効化しよう
異常なAPI呼び出しがないか確認するため、監査ログ機能を有効化し、取得した監査ファイルを安全なサーバへアーカイブするようにしましょう。

### アルファ版やベータ版の機能の利用を制限しよう
Kubernetesは頻繁に開発されているため、アルファ版やベータ版の機能がいくつか存在しており、これらはセキュリティ脆弱性をもたらすリスクがあります。このため、アルファ版やベータ版を利用することに懸念がある場合には、これらの機能を無効にするようにしましょう。

### 頻繁に秘密情報をローテートしよう
Kubernetesでは、証明書に短い有効期間を設定し、自動的にローテートするように設定することが可能です。これにより、攻撃者が証明書を盗んで攻撃することが難しくなります。また、Kubernetes APIのAPI認証に、外部認証プロバイダの認証トークンを利用する場合には、トークンを頻繁に更新するようにしましょう。

### 導入する前にサードパーティーのプラグインを確認しよう
Kubernetesへのサードパーティーの統合は、クラスタのセキュリティ設定を変更する可能性があります。このため、サードパーティーを統合する前に、要求されたアクセス許可の内容を確認するようにしましょう。

### etcdに格納された秘密情報を暗号化しよう
etcdデータベースには、Kubernetes APIを介してアクセス可能なあらゆる情報が含まれています。このため、暗号化ソリューションを活用してetcdのデータのバックアップを暗号化することや、EncryptionConfigurationを利用してデータを暗号化することが可能です。

## 証明書のローテート機能を活用しよう
kubeletは、Kubernetes APIへの認証に証明書を利用しています。デフォルトでは、これらの証明書の有効期限は1年に設定されているため、あまり頻繁に更新する必要はありません。
Kubernetes 1.8から、現在の証明書の有効期限が近づくと自動的に新しいキーを生成し、Kubernetes APIに新しい証明書を要求する、ベータ機能のkubelet証明書ローテーション機能がを利用することが可能です。2018年末に証明書更新漏れによる通信障害が記憶に新しいと思いますが、証明書を自動的にローテーションしてくれるとこういった障害がなくなるので便利ですね。


# 認証・認可の設定をしよう
### 認証プラグインを利用して認証しよう
Kubernetesは、クライアント証明書・bearerトークン・認証プロキシ・HTTP Basic認証などを利用して、認証プラグインを通じてAPI認証を要求することができます。利用可能な認証プラグインは以下の通りです。

* X509 Client Certs
* Static Token File
* Bootstrap Tokens
* Static Password File
* Service Account Tokens
* OpenID Connect Tokens
* Webhook Token Authentication
* Authenticating Proxy

参考： https://kubernetes.io/docs/reference/access-authn-authz/authentication/

### Role-Based Access Control(RBAC)を利用してAPIを認可しよう
Kubernetes APIに対して、Role-Based Access Control(RBAC)を使用して、アクセスを制限するようにしましょう。RABCには、Namespaceレベルのリソースとクラスタレベルのリソースの2種類が用意されていますが、Namespaceレベルのロール(Role)を利用して、クラスタレベルのロール(ClusterRole)の利用はなるべく避けるようにしましょう。（出展はCNCFのブログです）

### Admission Controlモジュールを活用しよう
Kubernetes APIがリクエストを受け取った際、Authentication / Authorization / Admission Control のフェーズを通ってリソースが登録されます。Addmission Contorlフェーズでは、そのリクエストの内容を許可するかどうかの判断や、受け取ったリソースの改変などを行って登録することが可能です。

![スクリーンショット 2019-02-01 18.12.32.png](https://qiita-image-store.s3.amazonaws.com/0/136020/f5db7f6e-79f2-dcde-f9cd-e86b8153c16d.png)

引用： https://kubernetes.io/docs/reference/access-authn-authz/controlling-access/

Admission Conrolで制御可能なものには以下の項目があります。

* AlwaysAdmit (DEPRECATED)
* AlwaysPullImages
* AlwaysDeny (DEPRECATED)
* DefaultStorageClass
* DefaultTolerationSeconds
* DenyExecOnPrivileged (deprecated)
* DenyEscalatingExec
* EventRateLimit (alpha)
* ExtendedResourceToleration
* ImagePolicyWebhook
* Initializers (alpha)
* LimitPodHardAntiAffinityTopology
* LimitRanger
* MutatingAdmissionWebhook (beta in 1.9)
* NamespaceAutoProvision
* NamespaceExists
* NamespaceLifecycle
* NodeRestriction
* OwnerReferencesPermissionEnforcement
* PersistentVolumeLabel (DEPRECATED)
* PodNodeSelector
* PersistentVolumeClaimResize
* PodPreset
* PodSecurityPolicy
* PodTolerationRestriction
* Priority
* ResourceQuota
* SecurityContextDeny
* ServiceAccount
* Storage Object in Use Protection
* ValidatingAdmissionWebhook (alpha in 1.8; beta in 1.9)

参考：　https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/

### PodにService Accountを設定しよう
Podのコンテナ内のプロセスからKubernetes APIに対してアクセスが必要な場合には、Service Accountを利用してアクセスするようにしましょう。


# コンテナイメージのセキュリティを強化しよう
## 信頼できるコンテナイメージを利用しよう
2018年11月にDocker Hubに仮想通貨を発掘する不正なコンテナが公開されていたことがニュースになりました。

Docker Hubに仮想通貨を発掘する不正なコンテナ--Kubernetes悪用の恐れも：
https://japan.zdnet.com/article/35120944/

このような脅威を防ぐため、信頼できる提供元からコンテナイメージを取得する必要性が出てきますが、Red Hatでは以下のURLの「Red Hat Container Catalog」からコンテナイメージを取得することができます。

Red Hat Container Catalog：
https://access.redhat.com/containers/

Docker Hubに公開されたコンテナを利用する場合には、そのコンテナが信頼できるものか確認するようにしましょう。

## コンテナイメージの脆弱性スキャンをしよう
様々なコンテナレジストリのプロダクトが存在していますが、コンテナレジストリには脆弱性スキャンを行う機能を持っているものがあります。脆弱性スキャンの機能を持っている主なコンテナレジストリは以下の通りです。

・Harbor: https://harbor.com/
・Red Hat Quay: https://www.openshift.com/products/quay

この他にもClairなどのコンテナの脆弱性スキャンするツールが存在していますので、これらを活用してコンテナに脆弱性が混入しないよう、コンテナの脆弱性スキャンを積極的に活用していきましょう。

# 安全にコンテナを実行しよう
## コンテナランタイムの変更を検討しよう
Kubernetes 1.12よりRuntimeClassがアルファリリースされ、Kubernetesが利用可能なコンテナランタイムを変更できるようなりました。

参考： https://kubernetes.io/blog/2018/10/10/kubernetes-v1.12-introducing-runtimeclass/

kubeletとコンテナランタイムとのインターフェースは、CRI(Container Runtime Interface)により標準化され、様々なコンテナラインタイムを選択できるようになっています。コンテナランタイムの中にはgVisorやKata Containersのように、コンテナからOSのシステムコールを直接呼び出しを防ぐコンテナランタイムも登場しています。マルチテナントの環境で、複数のテナントが一つのカーネルを共有するような環境の場合には、これらのコンテナラインタイムを活用することを検討しましょう。

コンテナランタイムについては、過去に整理した記事を書いていますのでご参考にしてください。

コンテナランタイムの動向を整理してみた件：
https://qiita.com/mamomamo/items/ed5db2ab1555078f8a24

## Secretリソースを活用しよう
おそらくこの記事を読んでいる人は既にご存知だと思いますが、データベース接続のユーザ名やパスワードなどの秘密情報を安全にコンテナに渡すため、Secretリソースを活用しましょう。ただし、Secretを定義しているマニフェストはbase64でエンコーディングされていますが暗号化されているわけではないため、kubesecなどのマニフェストを暗号化するオープンソースソフトウェアの利用も検討しましょう。

kubesec: https://github.com/shyiko/kubesec


# さいごに
本記事を書くことにしたきっかけの一つは、システム全体のセキュリティを設計をする際に、Kubernetesのセキュリティは誰が検討するのか疑問に持ったことが理由です。サーバやネットワークのセキュリティ設計はインフラエンジニアが、アプリケーションのセキュリティ設計はアプリケーションエンジニアがという従来の通りの役割分担だと、Kubernetesのセキュリティ設計が抜け落ちてしまう懸念を持ったためです。Kubernetesのセキュリティ設計の必要性に早く認識してもらい、不要なセキュリティインシデントを防ぐ一助になれば良いなと考えております。

また、調べられる範囲で調査した内容を整理してみましたが、おそらくこれだけでは十分でないとも考えています。他にもこんな機能があるよとか、ご意見がある方はコメントいただけると幸いです。


# 参考情報
* [9 Kubernetes Security Best Practices Everyone Must Follow](https://www.cncf.io/blog/2019/01/14/9-kubernetes-security-best-practices-everyone-must-follow/)
* [Kubernetes Security](https://kubernetes-security.info/)