kubernetes

Kubernetes: Admission Controlとは何か

More than 1 year has passed since last update.

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なプラグインは省略しています。推奨設定に入っているものは :thumbsup: を付けています。

AlwaysAdmit

すべてのリクエストを許可します。APIサーバーの--admission-controlに何も指定しない場合、これが設定されています。

AlwaysPullImages

PodのimagePullPolicyAlwaysに強制的に変更します。マルチテナントでKubernetesを利用する際これを使わないと、クレデンシャルが必要なPrivateのイメージであっても、Nodeがpull済みのものはクレデンシャル無しで別のPodが使用することができてしまいます。

AlwaysDeny

テスト用途ですべてのリクエストを禁止します。

DenyEscalatingExec

privilegeやHostIPC, HostPIDを要求するPodに対してexecattachを禁止します。クラスタがprivilegeをサポートサポートする場合、この設定が強く推奨されます。

ServiceAccount :thumbsup:

ServiceAccountというPodのためのアカウント(デフォルトではdefaultが使われる)を制御するための仕組みです。PodはAPIにアクセスするためのServiceAccountのトークンが/var/run/secrets/kubernetes.io/serviceaccountに自動でマウントされますが、このプラグインがそのマウントをPodに追加することで実現しているようです。

SecurityContextDeny :thumbsup:

SecurityContextの設定のうち、SELinuxOptionsRunAsUserなどの設定をしているものを禁止します。(セキュリティ上の理由?)

ResourceQuota :thumbsup:

リクエストがnamespace内のリソース制限(ResourceQuota)を超えていないかチェックします。ResourceQuotaを使う場合、このプラグインを有効にしなければなりません。

このプラグインは内部的にResourceQuotaの使用量をアップデートする処理があるため、プラグインの最後尾に指定することが強く推奨されます。(前に指定すると、後のプラグインでdenyされた場合に使用量がおかしくなってしまう)

LimitRanger :thumbsup:

namespaceのLimitRangeで指定されたPodの制限を行います。ResourceQuotaと違い、LimitRangeではPod単体の制限を行うもので、resourcesで指定するCPU, Memoryの範囲を制限することができます。

InitialResources (experimental)

コンテナのリソース(resources)を指定しなかった場合に、過去の実績に基づき初期のリソースを自動で設定するものです。

NamespaceLifecycle :thumbsup:

存在しないnamespaceや削除中のnamespaceへの指定を禁止します。また、デフォルトのnamespaceを削除できないようにします。

Admissionのプラグインの仕組み

admissionの仕組みはpkg/admissionで定義されています。InterfaceにはAdmit, Handlesの2メソッドだけが用意されており、プラグインはこれを実装します。

pkg/admission/interfaces.go
type Interface interface {
    // リクエストの属性(Attributees)を見て、許可の場合nil, 禁止の場合errorを返す
    Admit(a Attributes) (err error)

    // リクエストのオペレーション(CREATE, UPDATE, DELETE, CONNECT)対象とするか
    Handles(operation Operation) bool
}

プラグインの登録は各プラグインでadmission.RegisterPluginを呼び出すだけです。

plugin/pkg/admission/admit/admission.go
func init() {
    admission.RegisterPlugin("AlwaysAdmit", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
        return NewAlwaysAdmit(), nil
    })
}

最終的に各プラグインはチェーン式に呼ばれ、一つでもdenyを返すとそこで終了します。呼び出し元はpkg/apiserver/resthandler.goです。

pkg/admission/chain.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
}

参考