NodeRestriction

NodeRestrictionはKuberntesによって提供されるAdmissionPluginでkube-api-serverで--enable-admission-plugins=NodeRestionを指定することで有効化できる。

kubernetes.io

効果1

nodeが他のnodeのラベルを変更することを禁じる。

効果2

nodeにおけるnode-restriction.kubernetes.io/プレフィックスがついたラベルの変更を禁じる。(自分自身のnodeであっても禁止)

検証

効果1を検証
root@kind-worker:/# kubectl  --kubeconfig=/etc/kubernetes/kubelet.conf label nodes kind-worker  hoge=fuga
node/kind-worker labeled
root@kind-worker:/# kubectl  --kubeconfig=/etc/kubernetes/kubelet.conf label nodes kind-worker2  hoge=fuga
Error from server (Forbidden): nodes "kind-worker2" is forbidden: node "kind-worker" is not allowed to modify node "kind-worker2"

kind-workerは自身のnodeのラベルの変更はできるが、kind-worker2のラベルを変更できない。

効果2を検証
root@kind-worker:/# kubectl  --kubeconfig=/etc/kubernetes/kubelet.conf label nodes kind-worker node-restriction.kubernetes.io/hoge=fuga
Error from server (Forbidden): nodes "kind-worker" is forbidden: is not allowed to modify labels: node-restriction.kubernetes.io/hoge

kind-workerは自身のnodeのラベルの変更であっても、node-restriction.kubernetes.io/プレフィックスがついたラベルの変更はできない。

curlでKubernetesAPI

方法

  1. caとclient-certとclient-keyをkubeconfigから抽出する。
k config view --raw  | yq .clusters[0].cluster.certificate-authority-data | tr -d '\n' | base64 -d > ca                                                           
k config view --raw  | yq .users[0].user.client-certificate-data | tr -d '\n' | base64 -d > cert                                                                 
k config view --raw  | yq .users[0].user.client-key-data | tr -d '\n' | base64 -d  > key
  1. kube-api-serverのエンドポイントとcacert, cert, keyをそれぞれ指定して、curlを実行する。
curl $(k config view | yq .clusters[0].cluster.server) --cacert ca --key key --cert cert

※ 対象のclusterとuserがkubeconfigの.clusterと.userの最初のエントリになっていることを想定している。

kubectl get podscurlでやる例

curl $(k config view | yq .clusters[0].cluster.server)/api/v1/namespaces/default/pods --cacert ca --key key --cert cert

Kubernetes Authorization

authorization mode

kubernetesではauthorizationに関していくつかのモードを提供してくれている。 authorization modeの指定はkube-api-serverで--authorization-modeを使って行う。
よくある、設定は--authorization-mode=Node, RBACになっている。

Kubernetesが提供しているモードは以下の通り。

--authorization-mode=ABAC

kube-apiserverに--authorization-policy-file=SOME_FILENAMEというオプションを併せて指定して利用する。 このオプションで指定したファイルに色々とauthorizationのルールを書いていく方法

--authorization-mode=RBAC

roleとかrolebindingとか使ってAutorizationをやる有名なやつ。

kubernetes.io

--authorization-mode=Webhook

webhookを使って自前の認可サーバーに認可に関するリクエストを飛ばして、authorizationを行う方法

kubernetes.io

--authorization-mode=Node

各ノードにあるkubeletの認証

どういったリクエストが許可されるかについてはKuberntes側で予め決まっている。
バージョンごとの差分もあるので、内容については下記リンクを参照

kubernetes.io

--authorization-mode=AlwaysDeny

すべてのリクエストを拒否する

--authorization-mode=AlwaysAllow

すべてのリクエストを許可する

anonymous access

anonymous accessの有効無効はkube-api-serverで--anonymous-auth=true|falseオプションを使って行う。
AlwaysAllow以外のauthorization modeであれば、デフォルトで有効になっている。(--anonymous-auth=trueとは言ってもABACやRBACを利用している場合、そこで認可が走るため、匿名ユーザがkubernes APIで何でもできるというわけではない。)
ちなみに、匿名ユーザからのアクセスはusernameがsystem:anonymous、groupがsystem:unauthenticatedであるとkube-api-serverには解釈される。

検証

--anonymous-auth=trueの状態でKubernetescurlでアクセスしてみる。

$ curl https://K8S_API_ENDPOINT -k
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "forbidden: User \"system:anonymous\" cannot get path \"/\"",
  "reason": "Forbidden",
  "details": {},
  "code": 403
}

RBACで弾かれているものの、普通に認可自体はできているっぽい。

今度は--anonymous-auth=falseの状態でKubernetescurlでアクセスしてみる。

$ curl https://K8S_API_ENDPOINT -k
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "Unauthorized",
  "reason": "Unauthorized",
  "code": 401
}

ちゃんとunauthorizeが返ってきている。

AppArmor

AppArmor(Application Armor)とは、Linux Security Modulesの一種であり、各プログラムにセキュリティプロファイルを結びつけ、プログラムのできることに制限をかけるプログラムである。プロファイルは、ネットワークアクセス、Raw socket アクセス、ファイルへの読み書き実行などの機能を制限することができる。
AppArmor - Wikipedia

profile

profileとはあるプログラムのできること(例えば、xxxというファイルにアクセスするなど)を記述したものになっている。

profile mode

  • enforce: 対象のプログラムがプロファイルに違反するとその処理を止める。
  • complain: 対象のプログラムがプロファイルに違反するとその処理をログに残す。
  • unconfined: 対象のプログラムがプロファイルに違反しても何もしない。

コマンド

aa-status, apparmor_status

全てのプロファイルを表示する。

aa-genprof

対話形式でプロファイルを生成してくれる。
このコマンドを実行すると、途中で対象のプログラムを実行するように求められる。
プログラムを実行すると、その実行ログに基づいて、プロファイルを生成してくれる。
作成されたプロファイルは自動でcomplainモードにセットされる。

www.server-world.info

aa-enforce

指定したプログラムのプロファイルをenforceモードに指定する。

aa-complain

指定したプログラムのプロファイルをcomplainモードに指定する。

aa-logprof

対象のプログラムのsyslogを元にプロファイルを更新してくれる。
どのプロファイルを対象とするか、どの挙動をプロファイルに含めるか等は対話形式で選択するようになっている。

aa-unconfined

TCP portやUDP portを開けるにも関わらず、AppArmorプロファイルがロードされていないプログラムの一覧を表示する。

apparmor_parse

profileの追加

apparmor_parser -a プロファイルのファイルパス

profileの削除

apparmor_parser -R プロファイルのファイルパス

profileのreplace

apparmor_parser -r プロファイルのファイルパス

DockerコンテナでAppArmorを使う場合

まず、host側でprofileを登録する。 dockerのコンテナ起動時に以下のようにする。

docker run --security-opt apparmor=プロファイル名 

matsuand.github.io

コンテナのPID namespaceの共有

docker runコマンドの--pidオプションで使用するPID namespaceを指定できる。

このオプションに与える引数は以下の2パターン

  • container:CONTINAER_ID
  • host

container:XXXを指定すれば、他のコンテナとPID namespaceを共有できる。

$ docker container run -d --rm --name app1 -it busybox sh -c 'sleep 1d'
1b0d92a2f8aa2a5b3b858aece1f1ba04b532818118b9f73e841702f26539ed5c
$ docker container run -d --rm --name app2 --pid=container:app1 -it busybox sh -c 'sleep 2d'
df31e34fa1c6a72c65985c5b05654585410230c4f5b8422a4b96a10c0acc831a
$ docker container exec -it app2 /bin/sh
/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 sh -c sleep 1d
    7 root      0:00 sh -c sleep 2d
   13 root      0:00 /bin/sh
   19 root      0:00 ps

hostを指定すればホストとPID namespaceを共有できる。

$ docker container run -d --rm --name app3 --pid=host -it busybox sh -c 'sleep 2d'
04e81362a779c10449e4967511de0115747f3f8cfb40e0a4ccb0b05983af9444
$ docker container exec -it app3 /bin/sh

/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 /init
(omitted...)
23907 root      0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 1b0d92a2f8aa2a5b3b858aece1f1ba04b532818118b9f73e841702f26539ed5c -address /run/containerd/containerd.sock
23927 root      0:00 sh -c sleep 1d
24003 root      0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id df31e34fa1c6a72c65985c5b05654585410230c4f5b8422a4b96a10c0acc831a -address /run/containerd/containerd.sock
24020 root      0:00 sh -c sleep 2d
24370 root      0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 04e81362a779c10449e4967511de0115747f3f8cfb40e0a4ccb0b05983af9444 -address /run/containerd/containerd.sock
24389 root      0:00 sh -c sleep 2d
24519 root      0:00 /bin/sh
24576 root      0:00 ps

ClusterRoleとRoleBindingの組み合わせ

Namespaced Resource

  • Role
  • Rolebinding

Cluster Resource

  • ClusterRole
  • ClusterRolebinding

Role系のリソースとBinding系のリソースの組み合わせ

以下は直感的に理解できる。

  • RoleとRoleBindingの組み合わせ(青線)
  • ClusterRoleとClusterRoleBindingの組み合わせ (緑線)
  • RoleとClusterRoleBindingの組み合わせは存在しない。

ClusterRoleとRoleBindingの組み合わせ(赤線)はどうなるのだろうか?

ClusterRoleとRoleBindingの組み合わせ

結論

RoleBindingが所属しないnamespaceのリソースに対してはホワイトリストが適用されない。(getしたりcreateしたりとかできない。)

検証
$ kubectl describe clusterrole hoge
Name:         hoge
Labels:       <none>
Annotations:  <none>
PolicyRule:
  Resources    Non-Resource URLs  Resource Names  Verbs
  ---------    -----------------  --------------  -----
  pods/status  []                 []              [get]
  pods         []                 []              [get]
$ kubectl -n default describe rolebinding hoge
Name:         hoge
Labels:       <none>
Annotations:  <none>
Role:
  Kind:  ClusterRole
  Name:  hoge
Subjects:
  Kind  Name   Namespace
  ----  ----   ---------
  User  user1
$ kubectl auth can-i get pods --namespace=kube-system --as=user1
no
$ kubectl auth can-i get pods --namespace=default --as=user1
yes

しゃくとり法(two pointer approach)

どういう場合で使えるか

こちらの記事を引用させていただくと、以下のような問題を解くのに使える。

長さのn数列 a1, a2, a3, ... , anにおいて
- 「条件」を満たす区間 (連続する部分列) のうち、最小の長さを求めよ
- 「条件」を満たす区間 (連続する部分列) のうち、最大の長さを求めよ
- 「条件」を満たす区間 (連続する部分列) を数え上げよ

尺取り法

範囲を表現するleftとrightを用意する。(0<= left <= right <= n)
初期状態ではleftもrightも0(つまり、leftもrightも数列の先頭にいる)。

以下を繰り返す

  • [left, right)の範囲で条件を満たす限り、rightを進める。→rightが進められなくなる(rightが数列の末尾に達する OR rightを進めると条件を満たさなくなってしまう)。
    • left<rightなら、部分解を求めてleftを進める。
    • left==rightなら、leftもrightも両方とも進める。

example

問題

要素の和が20以下になる連続する部分列を数え上げよ。

プログラム

package main

import "fmt"

func shakutori(data []int, left, right int, cond func(d []int, l, r int) bool) int {
    if right+1 <= len(data) && cond(data, left, right+1) {
        return shakutori(data, left, right+1, cond)
    }
    if right+1 <= len(data) && left == right {
        return shakutori(data, left+1, right+1, cond)
    }
    if left+1 <= len(data) {
        return right - left + shakutori(data, left+1, right, cond)
    }
    return 0
}


func main() {
    data := []int{1, 3, 9, 1, 20, 2, 9, 33, 11, 6}
    left, right := 0, 0
    cond := func(d []int, l, r int) bool {
        sum := 0
        for _, v := range d[l:r] {
            sum += v
        }
        return sum <= 20
    }
    ans := shakutori(
        data,
        left,
        right,
        cond,
    )
    fmt.Println(ans)
}

right - leftの所が部分解になっている。

参考

www.kumilog.net

bmf-tech.com

qiita.com