From 238d529eae5693471ae3d447ad73013e9bbe81cc Mon Sep 17 00:00:00 2001 From: Chevdor Date: Thu, 26 Aug 2021 12:20:01 +0200 Subject: [PATCH] Hardening of the Frontend docker image (#377) * move the env-config script to a sub folder * fix doc * fix ports and ref to the official image * add hardening to the docker-compose examples --- .gitignore | 1 - README.md | 13 +++++++++++++ docker-compose.yml | 5 +++++ frontend/Dockerfile | 25 +++++++++++++++++-------- frontend/nginx/nginx.conf | 5 ++--- frontend/package.json | 2 +- frontend/public/index.html | 2 +- frontend/{ => scripts}/env.sh | 8 ++++++-- frontend/scripts/start.sh | 15 +++++++++++++++ scripts/build-docker-frontend.sh | 29 +++++++++++++++++++++++------ 10 files changed, 83 insertions(+), 22 deletions(-) rename frontend/{ => scripts}/env.sh (78%) create mode 100755 frontend/scripts/start.sh diff --git a/.gitignore b/.gitignore index 43496c3..ec11c12 100644 --- a/.gitignore +++ b/.gitignore @@ -24,5 +24,4 @@ npm-debug.log* yarn-debug.log* yarn-error.log* .vscode -env-config.js .nyc diff --git a/README.md b/README.md index 8b86c1f..844b4cc 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,19 @@ If you'd like to get things runing manually using Docker, you can do the followi **NOTE:** Here we used `SUBSTRATE_TELEMETRY_URL=ws://localhost:8000/feed`. This will work if you test with everything running locally on your machine but NOT if your backend runs on a remote server. Keep in mind that the frontend docker image is serving a static site running your browser. The `SUBSTRATE_TELEMETRY_URL` is the WebSocket url that your browser will use to reach the backend. Say your backend runs on a remote server at `foo.example.com`, you will need to set the IP/url accordingly in `SUBSTRATE_TELEMETRY_URL` (in this case, to `ws://foo.example.com/feed`). + **NOTE:** Running the frontend container in *read-only* mode reduces attack surface that could be used to exploit + a container. It requires however a little more effort and mounting additionnal volumes as shown below: + + ``` + docker run --rm -it -p 80:8000 --name frontend \ + -e SUBSTRATE_TELEMETRY_URL=ws://localhost:8000/feed \ + --tmpfs /var/cache/nginx:uid=101,gid=101 \ + --tmpfs /var/run:uid=101,gid=101 \ + --tmpfs /app/tmp:uid=101,gid=101 \ + --read-only \ + parity/substrate-telemetry-frontend + ``` + With these running, you'll be able to navigate to [http://localhost:3000](http://localhost:3000) to view the UI. If you'd like to connect a node and have it send telemetry to your running shard, you can run the following: ```sh diff --git a/docker-compose.yml b/docker-compose.yml index 4b5b629..fcb46b7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,11 @@ services: build: dockerfile: Dockerfile context: ./frontend/ + read_only: true + tmpfs: + - /var/cache/nginx:uid=101,gid=101 + - /var/run:uid=101,gid=101 + - /app/tmp:uid=101,gid=101 environment: SUBSTRATE_TELEMETRY_URL: ws://localhost:8000/feed ports: diff --git a/frontend/Dockerfile b/frontend/Dockerfile index d049181..867873e 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,11 +1,12 @@ #### BUILDER IMAGE #### FROM docker.io/node:12 as builder LABEL maintainer="Chevdor " -LABEL description="Polkadot Telemetry frontend builder image" +LABEL description="Substrate Telemetry Frontend builder image" WORKDIR /opt/builder COPY . . + RUN yarn install && \ yarn build && \ yarn cache clean @@ -13,20 +14,28 @@ RUN yarn install && \ #### OUTPUT IMAGE #### FROM docker.io/nginx:stable-alpine LABEL maintainer="Chevdor " -LABEL description="Polkadot Telemetry frontend" +LABEL description="Substrate Telemetry Frontend" # Each time this container is ran, the value that's provided for this env var # determines where the frontend will try to request feed information from: ENV SUBSTRATE_TELEMETRY_URL= -WORKDIR /usr/share/nginx/html - -COPY --from=builder /opt/builder/env.sh /usr/bin/ -RUN apk add --no-cache bash; chmod +x /usr/bin/env.sh +WORKDIR /app +COPY --from=builder /opt/builder/scripts/*.sh /usr/local/bin/ +COPY --from=builder /opt/builder/build /app COPY --from=builder /opt/builder/nginx/nginx.conf /etc/nginx/nginx.conf -COPY --from=builder /opt/builder/build /usr/share/nginx/html +RUN apk add --no-cache bash && \ + chown -R nginx:nginx /app && \ + chown -R nginx:nginx /var/cache/nginx && \ + chown -R nginx:nginx /var/log/nginx && \ + chown -R nginx:nginx /etc/nginx/conf.d && \ + touch /var/run/nginx.pid && \ + chown -R nginx:nginx /var/run/nginx.pid + +# UID= 101 +USER nginx EXPOSE 8000 -CMD ["/bin/bash", "-c", "/usr/bin/env.sh && nginx -g \"daemon off;\""] +CMD ["/usr/local/bin/start.sh"] diff --git a/frontend/nginx/nginx.conf b/frontend/nginx/nginx.conf index fedd2e2..38c0731 100644 --- a/frontend/nginx/nginx.conf +++ b/frontend/nginx/nginx.conf @@ -1,4 +1,3 @@ -user nginx; worker_processes auto; worker_rlimit_nofile 30000; @@ -19,13 +18,13 @@ http { access_log /var/log/nginx/access.log main; sendfile on; - #tcp_nopush on; + keepalive_timeout 65; gzip on; include /etc/nginx/conf.d/*.conf; server { - root /usr/share/nginx/html; + root /app; index index.html; listen 8000; listen [::]:8000; diff --git a/frontend/package.json b/frontend/package.json index 71921dd..933bf61 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,7 +13,7 @@ "eject": "react-scripts-ts eject", "pretty:check": "prettier --check src/**/*.{ts,tsx}", "pretty:fix": "prettier --write src", - "clean": "rm -rf node_modules build .nyc env-config.js report*.json yarn-error.log" + "clean": "rm -rf node_modules build .nyc ./tmp/env-config.js report*.json yarn-error.log" }, "dependencies": { "@polkadot/util-crypto": "^2.8.1", diff --git a/frontend/public/index.html b/frontend/public/index.html index 4ce5295..c833056 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -6,7 +6,7 @@ Polkadot Telemetry - +