skip to Main Content

Hello I am trying to build an image which can compile and run a c++ program securely.

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>rnrnusing namespace std;rnrnint main() {rn   int a = 10;rn   int b = 20;rn   cout << a << " " << b << endl;rn   int temp = a;rn   a = b;rn   b = temp;rn   cout << a << " " << b << endl;rn   return 0;rn}
    network_mode: bridge
    privileged: false
    read_only: true
    tmpfs: /tmp
    security_opt:
      - "no-new-privileges"
    cap_drop:
      - "all"

Here worker is a golang binary which reads code from environment variable and stores it in /tmp folder as main.cpp, and then tries to compile and run it using g++ /tmp/main.cpp && ./tmp/a.out (using golang exec)

I am getting this error scratch_4-gpp-1 | Error : fork/exec /tmp/a.out: permission denied, from which what I can understand / know that executing anything from tmp directory is restricted.

Since, I am using read_only root file system, I can only work on tmp directory, Please guide me how I can achieve above task keeping my container secured.

2

Answers


  1. Chosen as BEST ANSWER

    Complete summary of solution -

    @davidmaze mentioned to add an anonymous volume using

    version: '3.8'
    services:
      ...
        read_only: true
        volumes:
          - /build # which will be read-write
    
    

    as I replied I am still getting an error Cannot create temporary file in ./: Read-only file system when I tried to compile my program. When I debugged my container to see file system changes in read_only:false mode, I found that compiler is trying to save the a.out file in /bin folder, which is suppose to be read only.

    So I added this additional line before the entry point and my issue was solved.

    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" ]
    
    

  2. Docker’s default options for a tmpfs include noexec. docker run --tmpfs allows an extended set of mount options, but neither Compose tmpfs: nor the extended syntax of volumes: allows changing anything other than the size option.

    One straightforward option here is to use an anonymous volume. Syntactically this looks like a normal volumes: line, except it only has a container path. The read_only: option will make the container’s root filesystem be read-only, but volumes are exempted from this.

    version: '3.8'
    services:
      ...
        read_only: true
        volumes:
          - /build # which will be read-write
    

    This will be a "normal" Docker volume, so it will be disk-backed and you’ll be able to see it in docker volume ls.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search