zaki work log

作業ログやら生活ログやらなんやら

[OpenShift] sccのrunAsUserを使って特定のUIDを指定してpodを実行

root(UID:0)を含む、任意のUIDでpod(の中で動作するプロセス)を動かすには。
基本的にid:nekopさんの記事の通りです。

nekop.hatenablog.com

環境

CRCを使用。あとsccの操作はcluster-admin権限が必要(のはず)。

$ oc version
Client Version: v4.3.0
Server Version: 4.2.13
Kubernetes Version: v1.14.6+a8d983c

あと、OCP v4でoc get sccで各権限のサマリが見れなくなったのは、この辺のcustom-columnsとかで工夫するしかない?

zaki-hmkc.hatenablog.com

前置き

OpenShiftではセキュリティのため、任意のUID(非0)でpodが実行されることが求められる。 通常はネームスペース毎にランダムなUIDのレンジが割り当てられ、その設定によって実行時のUIDが決定される。

$ oc describe project scc-sample
Name:                   scc-sample
Created:                12 seconds ago
Labels:                 <none>
Annotations:            openshift.io/description=
                        openshift.io/display-name=
                        openshift.io/requester=kube:admin
                        openshift.io/sa.scc.mcs=s0:c23,c22
                        openshift.io/sa.scc.supplemental-groups=1000550000/10000
                        openshift.io/sa.scc.uid-range=1000550000/10000
:
:

この出力例でいうと、sa.scc.uid-range=1000550000/10000の部分によって、UIDは1000550000が基準になる。

といっても、何らかの理由で特定のUIDでプロセスを動かしたいという要件もあるかもしれない。

  • コンテナ化するアプリが特定のUIDで実行される前提になっている
  • ウェルノウンポートを使用しており変更できない
  • その他root権限必須

などなど。。

そこで出番なのがSecurity Context Constraints(scc)で、設定を行うことで特定のUIDで動作できるようになる。

access.redhat.com

基本のコマンド

$ oc run sample-app --image=rhel:7.7 -- tail -f /dev/null
$ oc rsh sample-app-1-txzmq bash
bash-4.2$ id
uid=1000550000(1000550000) gid=0(root) groups=0(root),1000550000
bash-4.2$ ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
1000550+       1  0.0  0.0   4412   672 ?        Ss   12:51   0:00 tail -f /dev/null
1000550+      15  0.2  0.0  11836  2920 pts/0    Ss   12:51   0:00 bash
1000550+      23  0.0  0.0  51764  3560 pts/0    R+   12:51   0:00 ps aux

nonrootを使って指定のUID(非0)で動作させる

特定のUID(非0)でpodを実行するには、デフォルトのsccのnonrootを使うことで可能。

作業用プロジェクト作成

$ oc new-project scc-sample-nonroot

デフォルトで使用されるUIDを確認する

$ oc describe projects scc-sample-nonroot
Name:                   scc-sample-nonroot
Created:                21 seconds ago
Labels:                 <none>
Annotations:            openshift.io/description=
                        openshift.io/display-name=
                        openshift.io/requester=kube:admin
                        openshift.io/sa.scc.mcs=s0:c24,c14
                        openshift.io/sa.scc.supplemental-groups=1000580000/10000
                        openshift.io/sa.scc.uid-range=1000580000/10000
Display Name:           <none>
:
:

ServiceAccount/defaultにnonroot権限を付与

$ oc adm policy add-scc-to-user nonroot -z default
securitycontextconstraints.security.openshift.io/nonroot added to: ["system:serviceaccount:scc-sample-nonroot:default"]

設定内容を確認する

$ oc describe scc nonroot
Name:                                           nonroot
Priority:                                       <none>
Access:
  Users:                                        system:serviceaccount:scc-sample-nonroot:default
  Groups:                                       <none>
:
:

デプロイ用のマニフェスト作成

手っ取り早く、前述「基本のコマンド」に対して--dry-run -o yamlを付けて実行した結果をファイルにリダイレクトする:) (status部分は要らないので削除)

apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
  creationTimestamp: null
  labels:
    run: sample-app
  name: sample-app
spec:
  replicas: 1
  selector:
    run: sample-app
  strategy:
    resources: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        run: sample-app
    spec:
      containers:
      - args:
        - tail
        - -f
        - /dev/null
        image: rhel:7.7
        name: sample-app
        resources: {}
  test: false
  triggers: null

このマニフェスト.spec.template以下に、securityContext.runAsUser記述を追記する。 89のとこは実行に使用したいUIDを指定。

      securityContext:
        runAsUser: 89

spec部分を一部抜粋するとこんな感じ

spec:
  {...}
  template:
    {...}
    spec:
      containers:
      - args:
        - tail
        - -f
        - /dev/null
        image: rhel:7.7
        name: sample-app
        resources: {}
      securityContext:
        runAsUser: 89

デプロイ

$ oc apply -f nonroot-deploy.yml 
deploymentconfig.apps.openshift.io/sample-app created
$ oc get all
NAME                      READY   STATUS              RESTARTS   AGE
pod/sample-app-1-deploy   0/1     ContainerCreating   0          2s

NAME                                 DESIRED   CURRENT   READY   AGE
replicationcontroller/sample-app-1   0         0         0       2s

NAME                                            REVISION   DESIRED   CURRENT   TRIGGERED BY
deploymentconfig.apps.openshift.io/sample-app   1          1         0         config

Runningになるまで待つ

$ oc get pod
NAME                  READY   STATUS      RESTARTS   AGE
sample-app-1-deploy   0/1     Completed   0          21s
sample-app-1-xfnkt    1/1     Running     0          11s
$ oc rsh sample-app-1-xfnkt bash
bash-4.2$ 
bash-4.2$ id
uid=89(89) gid=0(root) groups=0(root)
bash-4.2$ ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
89             1  0.0  0.0   4412   696 ?        Ss   13:05   0:00 tail -f /dev/null
89            25  0.0  0.0  11836  3008 pts/0    Ss   13:05   0:00 bash
89            34  0.0  0.0  51764  3500 pts/0    R+   13:05   0:00 ps aux
bash-4.2$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
89:x:89:0:89 user:/:/sbin/nologin

runAsUserで指定したUID=89で動作しているのが確認できる。

なお、99(nobody)のように、既に存在するUIDでも動く。

$ oc edit dc sample-app
 (runAsUserの値を変更)
$ oc rsh sample-app-2-6pk9t bash
bash-4.2$ id
uid=99(nobody) gid=99(nobody) groups=99(nobody)

ただし、0(root)は指定してもデプロイに失敗する。

$ oc get event
:
:
3s          Warning   FailedCreate        replicationcontroller/sample-app-3   Error creating: pods "sample-app-3-" is forbidden: unable to validate against any security context constraint: [spec.containers[0].securityContext.securityContext.runAsUser: Invalid value: 0: must be in the ranges: [1000580000, 1000589999] spec.containers[0].securityContext.securityContext.runAsUser: Invalid value: 0: running with the root UID is forbidden]

anyuidを使ってroot(UID:0)を指定して動作

nonrootでなくanyuidを使えば、runAsUser0を指定できる。
手順はnonrootの場合と全く一緒。

準備

$ oc new-project scc-sample-anyuid

ServiceAccount/defaultにanyuidを付与

$ oc adm policy add-scc-to-user anyuid -z default

マニフェスト作成

apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
  creationTimestamp: null
  labels:
    run: sample-app
  name: sample-app
spec:
  replicas: 1
  selector:
    run: sample-app
  strategy:
    resources: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        run: sample-app
    spec:
      containers:
      - args:
        - tail
        - -f
        - /dev/null
        image: rhel:7.7
        name: sample-app
        resources: {}
      securityContext:
        runAsUser: 0
  test: false
  triggers: null

デプロイ

$ oc apply -f anyuid-deploy.yml

podが起動して確認してみると

$ oc rsh sample-app-1-jvc9p bash
[root@sample-app-1-jvc9p /]# id
uid=0(root) gid=0(root) groups=0(root)
[root@sample-app-1-jvc9p /]# ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.3  0.0   4412   684 ?        Ss   13:18   0:00 tail -f /dev/null
root           7  0.3  0.0  11840  3060 pts/0    Ss   13:19   0:00 bash
root          23  0.0  0.0  51764  3436 pts/0    R+   13:19   0:00 ps aux

rootで動いてプロンプトも#になっている。

コンテナごとにID指定

前述の2つの例は、spec.template.spec直下にsecurityContextを定義したので、全コンテナで共通設定になる。 コンテナごとにUIDを指定する場合(あるいは特定のコンテナ以外は指定が不要な場合)は、containers[*]配下に設定すればOK

    spec:
      containers:
      - args:
        - tail
        - -f
        - /dev/null
        image: rhel:7.7
        name: rhel7
        securityContext:
          runAsUser: 0
      - args:
        - tail
        - -f
        - /dev/null
        image: rhscl/perl-526-rhel7:latest
        name: perl
        resources: {}
      - args:
        - tail
        - -f
        - /dev/null
        image: rhscl/python-36-rhel7:latest
        name: python
        resources: {}
        securityContext:
          runAsUser: 5000

確認

[zaki@codeready scc]$ oc rsh -c rhel7 sample-app-2-9lssg bash
[root@sample-app-2-9lssg /]# id
uid=0(root) gid=0(root) groups=0(root)
[root@sample-app-2-9lssg /]# exit
[zaki@codeready scc]$ oc rsh -c perl sample-app-2-9lssg bash
bash-4.2$ id
uid=1001(default) gid=0(root) groups=0(root)
bash-4.2$ exit
[zaki@codeready scc]$ oc rsh -c python sample-app-2-9lssg bash
(app-root) bash-4.2$ id
uid=5000(5000) gid=0(root) groups=0(root)
(app-root) bash-4.2$ exit

perlコンテナのUIDが、プロジェクトのuid-rangeに設定されている桁数のやたら長いランダムなUIDでなく1001になっているのは、ビルドに使われたDockerfileにUSER 1001の指定があるため。たぶん。

privileged(参考)

ちなみにanyuidでもroot権限で動作させることができるが、更に強い権限にprivilegedがある。
これはコンテナ内のプロセスの権限だけでなく、ホストに対するVOLUME設定などあらゆる制限がかかってない状態でpodが動作するので、変な野良コンテナなどで試さないように。

ということで、基本的にはデフォルトのrestrictedで動作するように設計しましょう。


余談:
1年くらい前にrsyslogをpodで動かしたい要件があり、無邪気にrhelコンテナにrsyslog突っ込んだイメージを作ろうとしたら、こいつ/var/runにpidファイルを作ろうするのに、このディレクトリがrootじゃないと書き込めなかったのがそもそもの経緯。
ただしこのときは、特権動作させるのではなく、rsyslogの起動オプション-iでpidファイルの作成先を指定できることが分かったので、/var/tmp辺りに作成して回避して、ずっとpodで特権動作の調査をほったらかしてましたとさ。。。