Docker非root使用者修改/etc/hosts檔案

牧之丨發表於2024-04-01

本文會講解在Docker容器環境下,非root使用者如何編輯修改/etc/hosts檔案。

1、背景和需求描述

環境:Docker
執行使用者:非root使用者,如普通使用者1001
需求:應用執行在容器內,需要在容器內/etc/hosts檔案中新增例如hbase主機名稱的解析。

2、解決思路

以下的篇幅會描述針對這個需求,對應嘗試的方案和思路,當然最終也解決了這個問題,文中會附上相應的實踐,有興趣的讀者可以跟著命令操作一遍。

2.1 思路1(失敗)

我們都知道,/etc/hosts檔案對於普通使用者一般只有 只讀許可權,既然非root使用者需要編輯並修改/etc/hosts檔案,那麼在映象構建過程中,將/etc/hosts檔案的許可權chmod為777或者是666,是否能實現這個需求呢?我們可以來驗證下。

2.1.1 Dockerfile準備

[root@hbs image]# cat Dockerfile 
FROM docker.io/nginx:latest
USER root
RUN useradd act -u 1001 -g root -p 1001 && \
        chmod 777 /etc/hosts
USER act

2.1.2 映象構建和執行

[root@hbs image]# docker build -t docker.io/nginx:v1 .
[root@hbs image]# docker run -it --rm docker.io/nginx:v1 /bin/bash
act@2488c623e6fe:/$ id
uid=1001(act) gid=0(root) groups=0(root)
act@2488c623e6fe:/$ cd /etc/
act@2488c623e6fe:/etc$ ls -l | grep hosts
-rw-r--r-- 1 root root     174 May  8 13:28 hosts
act@2488c623e6fe:/etc$ echo "10.10.10.10 hbs.com" >> /etc/hosts
bash: /etc/hosts: Permission denied

從上面的執行過程中可以看出,雖然在映象構建的時候中已經將/etc/hosts檔案的許可權chmod為777了,但實際以普通使用者act啟動容器時,發現/etc/hosts許可權並沒有改變,還是644,自然而然act使用者也就無法編輯修改/etc/hosts檔案了,這裡我們就要思考一下了,為啥在映象構建過程中修改/etc/hosts檔案許可權,執行時卻不生效呢?
其實這個問題,好心的網友已經有針對該問題做相應的解釋了,原因就是:

hosts檔案並不是存放在映象中的,/etc/hosts,/etc/resolv.conf和/etc/host這幾個檔案是存放在/var/lib/docker/containers/${container-id}目錄下的,容器啟動時是將這些檔案掛載到容器內的,換句話說,在映象構建過程中對/etc/hosts檔案的修改並不會同步到容器內的,這點我們可以從容器內看出來,如下。

act@2488c623e6fe:/etc$ df -h
Filesystem      Size  Used Avail Use% Mounted on
overlay          50G  5.2G   42G  11% /
tmpfs           1.9G     0  1.9G   0% /dev
tmpfs           1.9G     0  1.9G   0% /sys/fs/cgroup
/dev/vda1        50G  5.2G   42G  11% /etc/hosts
shm              64M     0   64M   0% /dev/shm
tmpfs           1.9G     0  1.9G   0% /proc/acpi
tmpfs           1.9G     0  1.9G   0% /proc/scsi
tmpfs           1.9G     0  1.9G   0% /sys/firmware

我們可以看到,/etc/hosts檔案確實是以掛載的方式mount到容器內部的。

2.2 思路2(成功,算是比較簡便的方式)

無論是原生的docker命令,或者是以YAML檔案方式(kubectl)進行部署,都支援在執行容器時,動態往/etc/hosts檔案中設定其他的域名解析。

2.2.1 docker原生命令

啟動容器時在docker run命令後加上如下命令:--add-host ${host.name}:${host.ip},我們可以來驗證下:

[root@hbs image]# docker run --add-host hbs.com:10.10.10.10 -d docker.io/nginx:v1
e8d77f865c87b133ee60eaa41666422426fb96fe4e807e2b8bec81941fb5174b
[root@hbs image]# cat /var/lib/docker/containers/e8d77f865c87b133ee60eaa41666422426fb96fe4e807e2b8bec81941fb5174b/hosts
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
10.10.10.10     hbs.com
172.17.0.3      e8d77f865c87

從上面的執行結果來看,非root使用者下,透過在執行時新增--add-host命令確實能滿足要求。

2.2.2 YAML檔案方式部署

在k8s容器雲環境下,以YAML檔案的方式部署應用,也能滿足這種需求,但對k8s有版本要求,從1.7版本之後,k8s支援了HostAliases特性實現向Pod的/etc/hosts檔案中新增條目,我們可以來驗證下:

[root@hbs ~]# kubectl version
Client Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.1", GitCommit:"b7394102d6ef778017f2ca4046abbaa23b88c290", GitTreeState:"clean", BuildDate:"2019-04-08T17:11:31Z", GoVersion:"go1.12.1", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.1", GitCommit:"b7394102d6ef778017f2ca4046abbaa23b88c290", GitTreeState:"clean", BuildDate:"2019-04-08T17:02:58Z", GoVersion:"go1.12.1", Compiler:"gc", Platform:"linux/amd64"}

[root@hbs appdata]# kubectl get deployment nonexistent-goose-hbs-helm -o yaml -n helm-test > helm-test.yaml
[root@hbs appdata]# vim helm-test.yaml

    spec:
      hostAliases:
      - ip: "1.2.3.4"
        hostnames:
        - "hbs.com"
      containers:
      - image: docker.io/nginx:latest

[root@hbs appdata]# kubectl apply -f helm-test.yaml -n helm-test
[root@hbs appdata]# kubectl exec nonexistent-goose-hbs-helm-7d846fffb5-zdsln cat /etc/hosts -n helm-test
# Kubernetes-managed hosts file.
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
10.244.0.12     nonexistent-goose-hbs-helm-7d846fffb5-zdsln

# Entries added by HostAliases.
1.2.3.4 hbs.com

從上面 的執行結果來看,使用hostAliases屬性也同樣能滿足我們的要求,但還有一個特例,就是如果Pod啟用hostNetwork,將不能使用這個特性,因為kubelet只管理非hostNetwork型別的Pod的hosts檔案。

2.3 思路3(成功,sudo修改映象)

這種是本文所採用的方式,因為本文的具體場景需求是:透過一個批次排程平臺來啟動一個Pod,Pod的YAML檔案生成是由批次排程平臺來決定的,經調研批次排程平臺無法支援透過hostAliases屬性來動態生成/etc/hosts,所以出發點還是在映象這一塊。
既然在映象構建過程中chmod /etc/hosts檔案不生效,詳情可看2.1 思路1,那麼換另外一種思路,就是在容器啟動後,動態chmod /etc/hosts許可權,然後進行編輯,這種思路就轉換為另外一個問題,就是普通使用者是無法執行chmod命令,經調研,sudo能讓普通使用者切換為root使用者然後有許可權執行指定的命令,具體實踐可看以下:

[root@hbs image]# cat Dockerfile
FROM docker.io/dustise/yum.centos7:latest
USER root
RUN useradd act -u 1001 -g root && \
        yum install -y sudo && \
        echo "act ALL=(root) NOPASSWD: /usr/bin/chmod" >>  /etc/supoers
USER act

其中主要關注**echo "act ALL=(root) NOPASSWD: /usr/bin/chmod" >>  /etc/supoers**這一句命令,意思是普通使用者act在執行sudo chmod這句命令時能短暫切換為root使用者並且執行chmod操作,NOPASSWD命令是切換使用者無需輸入密碼。

[root@hbs image]# docker build -t docker.io/yum.centos7:v3 . --no-cache
[root@hbs image]# docker run -it --rm docker.io/yum.centos7:v3 /bin/bash
[act@9c9f5c15abc8 /]$ id
uid=1001(act) gid=0(root) groups=0(root)
[act@9c9f5c15abc8 /]$ sudo chmod 777 /etc/hosts
[act@9c9f5c15abc8 /]$ echo "11.22.33.44 hbs.com" >> /etc/hosts
[act@9c9f5c15abc8 /]$ cat /etc/hosts
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3      9c9f5c15abc8
11.22.33.44 hbs.com

從上面的執行結果來看,執行sudo命令時,系統會去/etc/supoers檔案中查詢是否支援當前使用者進行相應的操作,由於在映象構建過程已經將act ALL=(root) NOPASSWD: /usr/bin/chmod寫入到/etc/supoers檔案中了,所以普通使用者執行sudo chmod 777 /etc/hosts這句命令時能切換到root使用者,然後執行chmod操作,/etc/hosts檔案的許可權一修改,自然而然就可以進行編輯追加了。

3、參考資料

Docker修改hosts
linux /etc/supoers檔案詳解
Adding entries to Pod /etc/hosts with HostAliases



相關文章