你好,我正在尝试构建一个可以安全编译和运行 C++ 程序的映像。
FROM golang:latest as builder
WORKDIR /app
COPY . .
RUN go mod download
RUN env CGO_ENABLED=0 go build -o /worker
FROM alpine:latest
RUN apk update && apk add --no-cache g++ && apk add --no-cache tzdata
ENV TZ=Asia/Kolkata
WORKDIR /
COPY --from=builder worker /bin
ARG USER=default
RUN addgroup -S $USER && adduser -S $USER -G $USER
USER $USER
ENTRYPOINT [ "worker" ]
version: "3.9"
services:
gpp:
build: .
environment:
- token=test_token
- code=#include <iostream>\r\n\r\nusing namespace std;\r\n\r\nint main() {\r\n int a = 10;\r\n int b = 20;\r\n cout << a << \" \" << b << endl;\r\n int temp = a;\r\n a = b;\r\n b = temp;\r\n cout << a << \" \" << b << endl;\r\n return 0;\r\n}
network_mode: bridge
privileged: false
read_only: true
tmpfs: /tmp
security_opt:
- "no-new-privileges"
cap_drop:
- "all"
这里 worker 是一个 golang 二进制文件,它从环境变量中读取 code 并将其存储在 /tmp 文件夹中作为 main.cpp,然后尝试使用
g++ /tmp/main.cpp
&& ./tmp/a.out
编译并运行它(使用 golang exec)
我收到此错误
scratch_4-gpp-1 | Error : fork/exec /tmp/a.out: permission denied
,从中我可以理解/知道从 tmp 目录执行任何内容都受到限制。
由于我使用的是只读根文件系统,所以我只能在 tmp 目录上工作,请指导我如何实现上述任务以保证容器的安全。
Docker 的 tmpfs 的默认选项包括 noexec
。
docker run --tmpfs
允许扩展安装选项集,但 Compose
tmpfs:
和 volumes:
的扩展语法都不允许更改大小选项以外的任何内容。
这里一个简单的选择是使用匿名卷。从语法上讲,这看起来像普通的 volumes:
行,只是它只有一个容器路径。
read_only:
选项将使容器的根文件系统变为只读,但卷不受此限制。
version: '3.8'
services:
...
read_only: true
volumes:
- /build # which will be read-write
这将是一个“普通”Docker 卷,因此它将由磁盘支持,您将能够在 docker volume ls
中看到它。
@davidmaze 提到使用
添加匿名卷
version: '3.8'
services:
...
read_only: true
volumes:
- /build # which will be read-write
正如我回答的那样,当我尝试编译程序时,我仍然收到错误Cannot create temporary file in ./: Read-only file system
。当我调试容器以查看
read_only:false
模式下的文件系统更改时,我发现编译器正在尝试将
a.out
文件保存在
/bin
文件夹中,这是假设的 只读。所以我在入口点之前添加了这一行,我的问题就解决了。
FROM golang:latest as builder
WORKDIR /app
COPY . .
RUN go mod download
RUN env CGO_ENABLED=0 go build -o /worker
FROM alpine:latest
RUN apk update && apk add --no-cache g++ && apk add --no-cache tzdata
ENV TZ=Asia/Kolkata
WORKDIR /
COPY --from=builder worker /bin
ARG USER=default
RUN addgroup -S $USER && adduser -S $USER -G $USER
USER $USER
WORKDIR /build <---- this line
ENTRYPOINT [ "worker" ]
services:
my-service:
...
tmpfs:
- /tmp:exec,mode=777