Container Note - Mount Namespace

Mount Namespace 研究

本文探討 Linux 的 Mount Namespace,這是一種隔離檔案系統的技術。文章將講解其原理、應用場景和如何有效地使用它來提高系統安全性。

使用環境

Rocky Linux 8.7

os-release

環境描述

在 kubernetes 上常常需要透過一些 CLI tool 幫助維護 container,如果 image 本身不提供這些 tool,執行上就會比較麻煩。通常可以透過 kubectl cp 將 CLI tool 複製到 container 內解決問題,需要注意的是,在官方的文件上有指出,如果使用 kubectl cp 則會需要容器內已安裝 tar 指令,否則會失敗:

https://kubernetes.io/docs/reference/kubectl/cheatsheet/

top

simple-spring-boot-app 專案為例,base image 是 google 的 distroless,這個 image 內除了 java 外,並沒有其他的 command,在執行 kubectl cp 時會發生錯誤:

simple-spring-boot-app: https://github.com/tsunejui/simple-spring-boot-app

distroless: https://github.com/GoogleContainerTools/distroless

cp-error

此時需要到 node 上,透過 container 的管理工具所提供的方法 (cp 指令),將檔案複製到 container 內,一些工具如下:

  • Docker: docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
  • Podman: podman cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
  • cri-tools: crictl cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
  • nerdctl: nerdctl cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-

假設 node 上 container 的管理工具並沒有提供 cp 的功能,或者沒有安裝 container 的管理工具時,可以透過以下測試步驟,找到 process (也就是 container) 所使用的 mnt namespacemountinfo,並查看對應的 mount point,直接將 CLI tool 放置於指定的目錄即可。

測試步驟

  1. 建立 POD

https://hub.docker.com/repository/docker/tsunejui/spring-boot-actuator/general

kubectl run spring-boot-actuator --image=tsunejui/spring-boot-actuator:v0.0.1
  1. 準備執行檔

本次實驗將直接搬移檔案到與 process 相同 mnt namespace 的 mount point 內指定的目錄,並且執行。首先我們先準備一個執行檔,可以透過 C 語言撰寫一個簡單的 hello world 工具: vi hello.c

#include <stdio.h>
int main() {
   printf("Hello, World!");
   return 0;
}
  1. 編譯執行檔

測試環境如果沒有 gcc,可以透過 sudo dnf install gcc-c++ 安裝

gcc -o hello hello.c
  1. 找到 process number

在測試環境中,container 所執行的 command 是 java -jar main.jar,因此直接在 linux 上透過 ps 查看:

ps -ef | grep 'java -jar main.jar'

process

  1. 找到 merged dir

現在我們知道 container 的 pid 為 2637095,並可以到 /proc/2637095/mountinfo 上找到 / 的 mount point:

overlayfs

我們可以複製 overlayfs 的 upperdir 目錄,並且使用 findmnt 指令查看所有 mount point,搭配篩選後便能看到 merged dir,以 CRI 是 containerd 的 kubernetes 為例,通常會在 /run/containerd/io.containerd.runtime.v2.task/k8s.io/<container-id>/rootfs 內:

findmnt | grep <upperdir>

top-overlayfs

  1. 複製執行檔至 merged dir 內

hello 複製於 merged dir 內:

cp hello /run/containerd/io.containerd.runtime.v2.task/k8s.io/<containe-id>/rootfs/bin/
  1. 使用 kubectl exec 執行 hello

可以看到,執行檔成功執行:

kubectl exec po/<pod-name> -- /bin/hello

hello

參考資料