1つのEKSクラスタを複数のIAMユーザで使いまわしている時に、Admin的なユーザ以外にはkube-systemでリソースを作成して欲しくないケースがある。(レアケースだとは思うが。。。)
RBACで出来なくもなさそうだが、特定のNamespaceだけ除外する、というのがあまりキレイに出来ないので、OPA Gatekeeperを使って実装した時のメモ。
今回は以下の条件で作成した。
- kube-systemでのPodの作成を抑止する
- IAMユーザのARNをConstraint側で渡せるようにする
- NamespaceもConstraint側で記載するようにし、Template側で縛らないようにする
OPA Gatekeeperのインストール
こちらより最新版のOPA Gatekeeperのバージョンを確認し、インストールする。
export OPA_GK_VERSION=3.9
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-${OPA_GK_VERSION}/deploy/gatekeeper.yaml
Podがあがっていることを確認する。
$ kubectl get pod -n gatekeeper-system
NAME READY STATUS RESTARTS AGE
gatekeeper-audit-779ffb6c54-hshsd 1/1 Running 0 21s
gatekeeper-controller-manager-7b4c8d7b97-57d77 1/1 Running 0 21s
gatekeeper-controller-manager-7b4c8d7b97-pmqmv 1/1 Running 0 21s
gatekeeper-controller-manager-7b4c8d7b97-zkqn9 1/1 Running 0 20s
ConstraintTemplateの作成
OPA Gatekeeperで与える制約の元となるテンプレートを作成する。
作成したテンプレートは以下となる。
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sdenyarn
spec:
crd:
spec:
names:
kind: K8sDenyArn
validation:
openAPIV3Schema:
type: object
properties:
denyArns:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sdenyarn
violation[{"msg": msg}] {
denyArns := input.parameters.denyArns[_]
namespace := input.review.namespace
operation := input.review.operation
userInfo := input.review.userInfo
userInfo.extra.arn[_] == denyArns
operation == "CREATE"
msg := sprintf("denied namespace: %v operation: %v arn: %v", [namespace, operation, userInfo.extra.arn])
}
以下解説する。
names:
kind: K8sDenyArn
validation:
openAPIV3Schema:
type: object
properties:
denyArns:
type: array
items:
type: string
上記の箇所ではカスタムリソースを生成するとともに、パラメータとしてdenyArns
というものを用意した。これにより、ARNを可変値として渡せるようにし、K8sDenyArn
リソース作成時に決まるものとした。
denyArns := input.parameters.denyArns[_]
上記箇所でK8sDenyArn
リソース作成時に設定したARNを取得している。
operation := input.review.operation
上記箇所でPodに対するオペレーション(CREATE, UPDATE等)を取得している。なお、input.review
がどのような値を持っているかはこちらの公式の説明を確認するとよい。
userInfo := input.review.userInfo
上記箇所でユーザ情報を取得している。
この中身はEKSで実行した場合は以下となっていた。
{
"extra": {
"accessKeyId": [
"xxxxxxx"
],
"arn": [
"arn:aws:iam::xxxx:user/username"
],
"canonicalArn": [
"arn:aws:iam::xxxx:user/username"
],
"sessionName": [
""
]
},
"groups": [
"system:masters",
"system:authenticated"
],
"uid": "aws-iam-authenticator:xxxx:xxxx",
"username": "kubernetes-admin"
}
見て分かるようにextraのところにAWSに関連する情報が含まれているため、これを利用してリソース作成を抑制する。
userInfo.extra.arn[_] == denyArns
operation == "CREATE"
上記箇所はエラーとする条件を記載している。violation内の評価式は全てがTrueだとリソース操作を抑止するため、ここでは
- 指定したARNがリソース作成時のパラメータに含まれているか
- リソース操作がCREATEになっているか
をチェックして、条件が一致する場合は抑止するようにしている。
Constraintの作成
次にContraintとなる、先程定義したkind: K8sDenyArn
を作成する。
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDenyArn
metadata:
name: deny-arn
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces:
- "kube-system"
parameters:
denyArns:
- "arn:aws:iam::xxxxx:user/username"
namespaceはparametersで渡すことも出来るのだが、matchで絞った方が早いのでmatch部分に書くようにした。
動作確認
それぞれのリソースを適用した状態で、実際にPodを作成してみる。
最初に、kube-systemとdefaultにそれぞれ作成してみる。
$ kubectl run nginx --image nginx -n kube-system
Error from server (Forbidden): admission webhook "validation.gatekeeper.sh" denied the request: [deny-arn] denied namespace: kube-system operation: CREATE arn: ["arn:aws:iam::xxxx:user/username"]
$ kubectl run nginx --image nginx -n default
pod/nginx created
kube-systemのみ失敗した。
次に、K8sDenyArnのARNを変更して実行する。
$ kubectl run nginx --image nginx -n kube-system
pod/nginx created
$ kubectl run nginx --image nginx -n default
pod/nginx created
どちらも成功した。ARNによる制御が上手く行っていることが確認できた。