Libertyコンテナを非rootユーザーで実行する

traditional WASでは`wasadmin`とかいうユーザーで実行している場合が多かったが、Libertyのコンテナを非rootユーザーで実行したい場合にどうすればよいのかを確認。

## デフォルト設定

イメージをpullする。

```
docker pull websphere-liberty:kernel
```

コンテナを起動して中の様子を見てみる。

```
$ docker run --rm --name liberty -d websphere-liberty:kernel
885d6abee39cf9eba8b610bf3b8e0462bfcd404345c82e152c4cf8b8a4c326aa
$ docker exec -it liberty bash
default@885d6abee39c:/$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
default      1     0 24 01:38 ?        00:00:09 /opt/ibm/java/jre/bin/java -javaagent:/opt/ibm/wlp/bin/tools/ws-javaagen
default     86     0  0 01:38 pts/0    00:00:00 bash
default     99    86  0 01:38 pts/0    00:00:00 ps -ef
default@885d6abee39c:/$ id default
uid=1001(default) gid=0(root) groups=0(root)
default@885d6abee39c:/$
```

以前はrootユーザーで起動していたが、いつの間にか非rootユーザーで起動するようになっていた。

2018年10月9日に[この変更がマージされた](https://github.com/WASdev/ci.docker/commits/b8d21d7bcc0401fc7f5d014a3a7ff41e3e6a49b2/ga/developer/kernel/Dockerfile)模様。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/86449/dc9c9a41-b74d-ce31-ad38-80ab833808be.png)

Dockerfileは以下のようになっている。

```dockerfile:Dockerfile
FROM ibmjava:8-jre

LABEL maintainer="Arthur De Magalhaes <arthurdm@ca.ibm.com> (@arthurdm)"

RUN apt-get update \
    && apt-get install -y --no-install-recommends unzip \
    && rm -rf /var/lib/apt/lists/* \
    && mkdir /licenses/ \
    && useradd -u 1001 -r -g 0 -s /sbin/nologin default

COPY *.html /licenses/

# Install WebSphere Liberty
ENV LIBERTY_VERSION 18.0.0_03
ARG LIBERTY_URL
ARG DOWNLOAD_OPTIONS=""
RUN LIBERTY_URL=${LIBERTY_URL:-$(wget -q -O - https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/wasdev/downloads/wlp/index.yml  | grep $LIBERTY_VERSION -A 6 | sed -n 's/\s*kernel:\s//p' | tr -d '\r' )}  \
    && wget $DOWNLOAD_OPTIONS $LIBERTY_URL -U UA-IBM-WebSphere-Liberty-Docker -O /tmp/wlp.zip \
    && unzip -q /tmp/wlp.zip -d /opt/ibm \
    && rm /tmp/wlp.zip \
    && chown -R 1001:0 /opt/ibm/wlp \
    && chmod -R g+rw /opt/ibm/wlp
ENV PATH=/opt/ibm/wlp/bin:$PATH

# Add labels for consumption by IBM Product Insights
LABEL "ProductID"="fbf6a96d49214c0abc6a3bc5da6e48cd" \
      "ProductName"="WebSphere Application Server Liberty" \
      "ProductVersion"="18.0.0.3"

# Set Path Shortcuts
ENV LOG_DIR=/logs \
    WLP_OUTPUT_DIR=/opt/ibm/wlp/output

# Configure WebSphere Liberty
RUN /opt/ibm/wlp/bin/server create \
    && rm -rf $WLP_OUTPUT_DIR/.classCache /output/workarea

COPY docker-server /opt/ibm/docker/

# Create symlinks && set permissions for non-root user
RUN mkdir /logs \
    && mkdir /etc/wlp \
    && mkdir /lib.index.cache \
    && mkdir -p /home/default \
    && mkdir /output \
    && chmod -t /output \
    && rm -rf /output \
    && ln -s $WLP_OUTPUT_DIR/defaultServer /output \
    && ln -s /opt/ibm/wlp/usr/servers/defaultServer /config \
    && ln -s /opt/ibm /liberty \
    && mkdir -p /config/configDropins/defaults \
    && chown -R 1001:0 /config \
    && chmod -R g+rw /config \
    && chown -R 1001:0 /opt/ibm/docker/docker-server \
    && chmod -R g+rwx /opt/ibm/docker/docker-server \
    && chown -R 1001:0 /opt/ibm/wlp \
    && chmod -R g+rw /opt/ibm/wlp \
    && chown -R 1001:0 /logs \
    && chmod -R g+rw /logs \
    && chown -R 1001:0 /etc/wlp \
    && chmod -R g+rw /etc/wlp \
    && chown -R 1001:0 /lib.index.cache \
    && chmod -R g+rw /lib.index.cache \
    && chown -R 1001:0 /home/default \
    && chmod -R g+rw /home/default

USER 1001

EXPOSE 9080 9443

ENV KEYSTORE_REQUIRED true

ENTRYPOINT ["/opt/ibm/docker/docker-server"]
CMD ["/opt/ibm/wlp/bin/server", "run", "defaultServer"]
```

`useradd`したり`mkdir`したり`chown`したり`chmod`していて、`uid=1001(default) gid=0(root)`でプロセスを実行している。

## Dockerfileで実行ユーザーを変更する

`uid=1001(default) gid=0(root)`が要件に合わない場合は、もとの`Dockerfile`でやっているのと同じことを改めてやる必要がある。`USER 0`としてrootユーザーを変えてから`RUN`する。例えば、以下のようにする。

```dockerfile:Dockerfile
FROM websphere-liberty:kernel
USER 0
RUN groupadd -r -g 2001 wasadmin \
  && useradd -r -u 2001 -g wasadmin -s /usr/sbin/nologin wasadmin \
  && mkdir -p /home/wasadmin \
  && chown -R wasadmin:wasadmin /config \
  && chown -R wasadmin:wasadmin /opt/ibm/docker/docker-server \
  && chown -R wasadmin:wasadmin /opt/ibm/wlp \
  && chown -R wasadmin:wasadmin /logs \
  && chown -R wasadmin:wasadmin /etc/wlp \
  && chown -R wasadmin:wasadmin /lib.index.cache \
  && chown -R wasadmin:wasadmin /home/wasadmin
USER wasadmin
```

ビルドする。

```shell-session
$ docker build -t liberty-wasadmin:kerenel .
Sending build context to Docker daemon   2.56kB
Step 1/4 : FROM websphere-liberty:kernel
 ---> 35024447bc12
Step 2/4 : USER 0
 ---> Using cache
 ---> ee4d50e3b63d
Step 3/4 : RUN groupadd -r -g 2001 wasadmin   && useradd -r -u 2001 -g wasadmin -s /usr/sbin/nologin wasadmin   && mkdir -p /home/wasadmin   && chown -R wasadmin:wasadmin /config   && chown -R wasadmin:wasadmin /opt/ibm/docker/docker-server   && chown -R wasadmin:wasadmin /opt/ibm/wlp   && chown -R wasadmin:wasadmin /logs   && chown -R wasadmin:wasadmin /etc/wlp   && chown -R wasadmin:wasadmin /lib.index.cache   && chown -R wasadmin:wasadmin /home/wasadmin
 ---> Running in e70f65971556
Removing intermediate container e70f65971556
 ---> c3e51722bb45
Step 4/4 : USER wasadmin
 ---> Running in dab25bd33eca
Removing intermediate container dab25bd33eca
 ---> 9204ed106eea
Successfully built 9204ed106eea
Successfully tagged liberty-wasadmin:kerenel
$
```

コンテナを起動。

```shell-session
$ docker run --rm -it --name liberty liberty-wasadmin:kerenel
Launching defaultServer (WebSphere Application Server 18.0.0.3/wlp-1.0.22.cl180320180905-2337) on IBM J9 VM, version 8.0.5.25 - pxa6480sr5fp25-20181030_01(SR5 FP25) (en_US)
[AUDIT   ] CWWKE0001I: The server defaultServer has been launched.
[AUDIT   ] CWWKE0100I: This product is licensed for development, and limited production use. The full license terms can be viewed here: https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/wasdev/license/base_ilan/ilan/18.0.0.3/lafiles/en.html
[AUDIT   ] CWWKG0093A: Processing configuration drop-ins resource: /opt/ibm/wlp/usr/servers/defaultServer/configDropins/defaults/keystore.xml
[WARNING ] CWWKF0009W: The server has not been configured to install any features.
[AUDIT   ] CWWKF0011I: The server defaultServer is ready to run a smarter planet.
```

別のターミナルからコンテナに入って確認する。

```shell-session
sotoiwa@soto-no-air:~/workspace/liberty-user
$ docker exec -it -u 0 liberty bash
root@0e92f807eb4d:/# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
wasadmin     1     0 35 12:51 pts/0    00:00:17 /opt/ibm/java/jre/bin/java -javaagent:/opt/ibm/wlp/bin/tools/ws-javaagent.jar -Djava.awt.headless=true
root        91     0  1 12:51 pts/1    00:00:00 bash
root       101    91  0 12:51 pts/1    00:00:00 ps -ef
root@0e92f807eb4d:/#
```

ただし、このようにDockerfileでchwonをするとレイヤーは重複することになり、イメージサイズは増えてしまうため、ユーザーを変更しないで済むのならそのほうがよい。

## Kubernetesで実行ユーザーを変更する

Kubernetes上で稼働させる場合、Podまたはコンテナの`securityContext`を使って実行ユーザーを指定できる。

（参考）
[Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)

```yaml
spec:
  securityContext:
    runAsUser: 2001
    fsGroup: 3001
```

`runAsUser`はプロセスの実行ユーザーを指定する。`fsGroup`はプロセスの実行ユーザーが追加で所属するグループと、Volumeをマウントした場合のオーナーグループを指定する。例えば上記の設定だとプロセスは以下のような権限で稼働する。

```
/ $ id
uid=2001 gid=0(root) groups=3001
/ $
```

この例では`uid=2001`のユーザーがコンテナ内にいないので`gid=0`となっているが、ユーザーがいればそのユーザーのプライマリグループがgidとなる。

securityContextで実行ユーザーを設定できるが、コンテナ内のファイルシステムの権限を変更するわけではないので、Dockerイメージのビルド時にファイルシステムの権限を適切に設定することは結局必要と思われる。