KubernetesのAdmission Controlについて理解のためメモをまとめてみました。Kubernetes 1.2時点での情報です。
Admission Controlとは
Admission ControlとはKubernetesのAPI Serverのリクエスト制御の機能です。APIリクエストに対して認証、認可を行ったあとのフェーズで、別途そのリクエストを受け入れるか制御を行います。また場合によって、リクエストの変更や別の操作を行います。
プラグイン形式で複数の制御の方法が用意されており、APIの起動オプション--admission-control
で有効にしたいものをカンマ区切りで指定します。
プラグイン
APIサーバーのデフォルトではAlwaysAdmit
のみが有効になっています。(参考: cmd/kube-apiserver/app/options/options.go
マニュアルでは下記の指定が推奨されています。
--admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
プラグインはチェーン式にいずれかのプラグインがdenyするまで呼ばれていくため、順序に意味があるので注意が必要です。(変更を伴うプラグインもあるため)
プラグイン一覧
deprecatedなプラグインは省略しています。推奨設定に入っているものは を付けています。
AlwaysAdmit
すべてのリクエストを許可します。APIサーバーの--admission-control
に何も指定しない場合、これが設定されています。
AlwaysPullImages
PodのimagePullPolicy
をAlways
に強制的に変更します。マルチテナントでKubernetesを利用する際これを使わないと、クレデンシャルが必要なPrivateのイメージであっても、Nodeがpull済みのものはクレデンシャル無しで別のPodが使用することができてしまいます。
AlwaysDeny
テスト用途ですべてのリクエストを禁止します。
DenyEscalatingExec
privilegeやHostIPC, HostPIDを要求するPodに対してexec
やattach
を禁止します。クラスタがprivilegeをサポートサポートする場合、この設定が強く推奨されます。
ServiceAccount
ServiceAccountというPodのためのアカウント(デフォルトではdefault
が使われる)を制御するための仕組みです。PodはAPIにアクセスするためのServiceAccountのトークンが/var/run/secrets/kubernetes.io/serviceaccount
に自動でマウントされますが、このプラグインがそのマウントをPodに追加することで実現しているようです。
SecurityContextDeny
SecurityContextの設定のうち、SELinuxOptions
、RunAsUser
などの設定をしているものを禁止します。(セキュリティ上の理由?)
ResourceQuota
リクエストがnamespace内のリソース制限(ResourceQuota
)を超えていないかチェックします。ResourceQuota
を使う場合、このプラグインを有効にしなければなりません。
このプラグインは内部的にResourceQuota
の使用量をアップデートする処理があるため、プラグインの最後尾に指定することが強く推奨されます。(前に指定すると、後のプラグインでdenyされた場合に使用量がおかしくなってしまう)
LimitRanger
namespaceのLimitRangeで指定されたPodの制限を行います。ResourceQuota
と違い、LimitRange
ではPod単体の制限を行うもので、resources
で指定するCPU, Memoryの範囲を制限することができます。
InitialResources
(experimental)
コンテナのリソース(resources
)を指定しなかった場合に、過去の実績に基づき初期のリソースを自動で設定するものです。
NamespaceLifecycle
存在しないnamespaceや削除中のnamespaceへの指定を禁止します。また、デフォルトのnamespaceを削除できないようにします。
Admissionのプラグインの仕組み
admissionの仕組みはpkg/admissionで定義されています。InterfaceにはAdmit
, Handles
の2メソッドだけが用意されており、プラグインはこれを実装します。
type Interface interface {
// リクエストの属性(Attributees)を見て、許可の場合nil, 禁止の場合errorを返す
Admit(a Attributes) (err error)
// リクエストのオペレーション(CREATE, UPDATE, DELETE, CONNECT)対象とするか
Handles(operation Operation) bool
}
プラグインの登録は各プラグインでadmission.RegisterPlugin
を呼び出すだけです。
func init() {
admission.RegisterPlugin("AlwaysAdmit", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
return NewAlwaysAdmit(), nil
})
}
最終的に各プラグインはチェーン式に呼ばれ、一つでもdenyを返すとそこで終了します。呼び出し元はpkg/apiserver/resthandler.goです。
func (admissionHandler chainAdmissionHandler) Admit(a Attributes) error {
for _, handler := range admissionHandler {
if !handler.Handles(a.GetOperation()) {
continue
}
err := handler.Admit(a)
if err != nil {
return err
}
}
return nil
}