Skip to content

Commit

Permalink
Add Docker User Mirror (#354)
Browse files Browse the repository at this point in the history
  • Loading branch information
AJGranowski authored Sep 9, 2024
1 parent 9776933 commit 99aa312
Show file tree
Hide file tree
Showing 18 changed files with 491 additions and 363 deletions.
5 changes: 0 additions & 5 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ updates:
labels: []
schedule:
interval: "weekly"
- package-ecosystem: "docker"
directory: "/images/yq/"
labels: []
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/"
labels: []
Expand Down
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ https://github.com/AJGranowski/reddit-expanded-community-filter-userscript/blob/
<!-- Justify your proposed changes. -->

## Checklist before merging
- [ ] `./toolbox -- run clean-verify` succeeds.
- [ ] `./npm -- run clean-verify` succeeds.
8 changes: 4 additions & 4 deletions .github/wiki/Developer-Documentation.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Project Philosophies
* `mainline` shall always be in a release-ready state.
* This project shall produce the primary build artifacts using only one command from a fresh clone (`./toolbox` or `npm run clean-verify`).
* This project shall produce the primary build artifacts using only one command from a fresh clone (`./npm` or `npm run clean-verify`).
* The only prerequisite dependency of this project shall be Docker.
* Continuous Integration tests shall be reproducible on a developer's machine.
* Behavior is defined by tests, not code.
Expand All @@ -17,17 +17,17 @@

# Tooling

## Toolbox
## NPM

Toolbox is a shell script designed to be the main entry point for this project. In essence, all toolbox does is run commands in a temporary Docker container defined by a Docker Compose file. This is the primary purpose of this project: a proof of concept of a completely containerized toolchain. A unified environment shared between each developer and automated runner.
`npm` is a shell script designed to be the main entry point for this project. In essence, all it does is run commands in a temporary Docker container defined by a Docker Compose file. This is the primary purpose of this project: a proof of concept of a completely containerized toolchain. A unified environment shared between each developer and automated runner.

There are some quirks though:

#### Docker volumes
Reading and writing `node_modules` with Docker is really slow on Windows. To increase performance, these intermediate directories use Docker volumes instead of a host mount at the cost of additional complexity (mainly, permissions when Docker creates these directories on the host and container).

#### Permissions
Items created by rootful Docker (like file system mounts) will be owned by root on Linux systems. To get around this, toolbox creates these items itself and sets the owner to the current user. This is accomplished by reading the `volumes:` entry of the docker compose file with `yq` to create items on the host, and forward instructions to `Dockerfile` to create and own the matching items in the image. Running Docker in a rootless context also works.
Items created by rootful Docker (like file system mounts) will be owned by root on Linux systems. To get around this, the `npm` script uses Docker User Mirror to fix the ownership of these items. Running Docker in a rootless context also works.

## ESLint
Static code analysis.
Expand Down
2 changes: 1 addition & 1 deletion .github/wiki/Developers.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ You should be able to build this project from any platform as long as Docker is
1. Clone the repo.
2. Run the build:
```sh
./toolbox
./npm
```
or
```sh
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ jobs:
id: docker-cache
uses: ScribeMD/docker-cache@fb28c93772363301b8d0a6072ce850224b73f74e # 0.5.0
with:
key: docker-${{ runner.os }}-${{ hashFiles('./Dockerfile', './images/yq/Dockerfile') }}
key: docker-${{ runner.os }}-${{ hashFiles('./images/node/Dockerfile') }}

- name: Download CI Artifacts
uses: dawidd6/action-download-artifact@bf251b5aa9c2f7eeb574a96ee720e24f801b7c11 # v6
Expand All @@ -99,14 +99,14 @@ jobs:
run: |
mkdir -p ~/.minisign
echo '${{ secrets.MINISIGN_PRIVATE_BASE64 }}' | base64 -d > ~/.minisign/minisign.key
chmod +x "./toolbox"
MINISIGN_KEY_PATH='~/.minisign/minisign.key' ./toolbox -- run sign
chmod +x "./npm" "./user-mirror"
MINISIGN_KEY_PATH='~/.minisign/minisign.key' ./npm -- run sign
rm ~/.minisign/minisign.key
- name: Verify Signatures
run: |
chmod +x "./toolbox"
./toolbox -- run sign:verify
chmod +x "./npm" "./user-mirror"
./npm -- run sign:verify
- name: Upload Signed Artifacts
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
Expand Down
7 changes: 4 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ jobs:
- name: Validate Docker Compose Files
if: ${{ steps.changed-files.outputs.paths_any_changed == 'true' }}
run: |
touch watch-build-server.env
docker compose --file docker-compose.yml config --quiet
docker compose --file docker-compose.watch-build-server.yml config --quiet
Expand All @@ -73,7 +74,7 @@ jobs:
id: docker-cache
uses: ScribeMD/docker-cache@fb28c93772363301b8d0a6072ce850224b73f74e # 0.5.0
with:
key: docker-${{ runner.os }}-${{ hashFiles('./Dockerfile', './images/yq/Dockerfile') }}
key: docker-${{ runner.os }}-${{ hashFiles('./images/node/Dockerfile') }}

- name: NPM Cache
if: ${{ steps.changed-files.outputs.paths_any_changed == 'true' }}
Expand All @@ -86,8 +87,8 @@ jobs:
- name: Build and Verify
if: ${{ steps.changed-files.outputs.paths_any_changed == 'true' }}
run: |
chmod +x "./toolbox"
./toolbox -- run clean-verify
chmod +x "./npm" "./user-mirror"
./npm -- run clean-verify
- name: Upload Code Coverage
if: ${{ matrix.docker-context == 'rootless' && steps.changed-files.outputs.paths_any_changed == 'true' }}
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Thanks for wanting to contribute! Keep in mind that this is a personal hobby pro
2. Open a [bug report][bug-issue-link]. Please include all of the requested information in the bug report template to give us the best chance of reproducing your issue.

### Do you have a change you would like merged?
1. Ensure that your changes will pass by running either `./toolbox` or `npm run clean-verify` on your machine.
1. Ensure that your changes will pass by running either `./npm` or `npm run clean-verify` on your machine.
2. Open [pull request][pull-request-link] with your change. Please include all of the requested information in the pull request template.

### Do you have a feature request?
Expand Down
22 changes: 20 additions & 2 deletions docker-compose.watch-build-server.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@ services:
build:
context: "./images/node"
network: host
cap_add:
# Minimum permissions for Docker User Mirror entrypoint:
- CAP_SETGID # Needed to switch from root to another user.
- CAP_SETUID # Needed to switch from root to another user.
- CAP_SETPCAP # Needed to change permissions when switching users.
- CAP_CHOWN # Needed to write changes to /etc/gshadow and /etc/shadow.
- CAP_DAC_OVERRIDE # Needed for Podman.
cap_drop: ["ALL"]
command: ["run", "watch-build"]
entrypoint: ["npm"]
entrypoint: [/entrypoint, --, npm]
env_file:
- watch-build-server.env
healthcheck:
test: ["CMD", "sh", "-c", "[ $(ps | grep nodemon | grep -v grep | wc -l) -ge 1 ]"]
interval: 5s
Expand Down Expand Up @@ -63,9 +72,18 @@ services:
build:
context: "./images/node"
network: host
cap_add:
# Minimum permissions for Docker User Mirror entrypoint:
- CAP_SETGID # Needed to switch from root to another user.
- CAP_SETUID # Needed to switch from root to another user.
- CAP_SETPCAP # Needed to change permissions when switching users.
- CAP_CHOWN # Needed to write changes to /etc/gshadow and /etc/shadow.
- CAP_DAC_OVERRIDE # Needed for Podman.
cap_drop: ["ALL"]
command: ["run", "serve-release"]
entrypoint: ["npm"]
entrypoint: [/entrypoint, --, npm]
env_file:
- watch-build-server.env
healthcheck:
test: ["CMD", "sh", "-c", "[ $(ps | grep http-server | grep -v npm | grep -v grep | wc -l) -eq 1 ]"]
start_period: 30s
Expand Down
10 changes: 9 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,18 @@ services:
build:
context: "./images/node"
network: host
cap_add:
# Minimum permissions for Docker User Mirror entrypoint:
- CAP_SETGID # Needed to switch from root to another user.
- CAP_SETUID # Needed to switch from root to another user.
- CAP_SETPCAP # Needed to change permissions when switching users.
- CAP_CHOWN # Needed to write changes to /etc/gshadow and /etc/shadow.
- CAP_DAC_OVERRIDE # Needed for Podman.
cap_drop: ["ALL"]
command: ["run", "clean-verify"]
entrypoint: ["npm"]
entrypoint: [/entrypoint, --, npm]
environment:
CAPABILITIES: # setpriv --bounding-set options. Must be a subset of cap_add. See https://www.man7.org/linux/man-pages/man1/setpriv.1.html#OPTIONS
MINISIGN_CONFIG_DIR: /run/secrets
network_mode: "host"
secrets:
Expand Down
61 changes: 5 additions & 56 deletions images/node/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,8 @@ FROM node:22.8.0-alpine@sha256:bec0ea49c2333c429b62e74e91f8ba1201b060110745c3a12
# Add Minisign for signing artifacts.
RUN apk add --no-cache minisign>0.11

# Fix permissions issues when using a non-root user.
ARG USER_ID
ARG USER_NAME
ARG USER_GROUP_ID
ARG USER_GROUP_NAME
RUN if [ -n "${USER_GROUP_NAME}" -a -n "${USER_NAME}" -a "${USER_NAME}" != "root" ] && if [ -n "${USER_ID}" ]; then [ ${USER_ID} -ne 0 ]; fi; then \
echo "Remove user" && \
if getent passwd "${USER_NAME}" 1>/dev/null 2>&1; then \
echo " $(getent passwd ${USER_NAME})" && \
deluser "${USER_NAME}" \
;fi && \
if [ -n "${USER_ID}" ] && getent passwd "${USER_ID}" 1>/dev/null 2>&1; then \
echo " $(getent passwd ${USER_ID})" && \
deluser "$(getent passwd "${USER_ID}" | cut -d':' -f1)" \
;fi && \
echo "Remove group" && \
if getent group "${USER_GROUP_NAME}" 1>/dev/null 2>&1; then \
echo " $(getent group ${USER_GROUP_NAME})" && \
delgroup "${USER_GROUP_NAME}" || true \
;fi && \
if [ -n "${USER_GROUP_ID}" ] && getent group "${USER_GROUP_ID}" 1>/dev/null 2>&1; then \
echo " $(getent group ${USER_GROUP_ID})" && \
delgroup "$(getent group "${USER_GROUP_ID}" | cut -d':' -f1)" || true \
;fi && \
echo "Add group" && \
if [ -n "${USER_GROUP_ID}" ]; then \
addgroup -g "${USER_GROUP_ID}" "${USER_GROUP_NAME}" || true \
;else \
addgroup "${USER_GROUP_NAME}" || true \
;fi && \
echo " $(getent group ${USER_GROUP_NAME})" && \
echo "Add user" && \
if [ -n "${USER_ID}" ]; then \
adduser -u "${USER_ID}" -s /bin/sh -G "${USER_GROUP_NAME}" -DH "${USER_NAME}" \
;else \
adduser -s /bin/sh -G "${USER_GROUP_NAME}" -DH "${USER_NAME}" \
;fi && \
echo " $(getent passwd ${USER_NAME})" \
;fi

# Create directories and files, and set the ownership of a list of items to the added user.
ARG CHOWN_LIST
ARG MKDIR_LIST
ARG TOUCH_LIST
RUN if [ -n "${MKDIR_LIST}" ]; then \
eval mkdir --parents --verbose ${MKDIR_LIST} \
;fi && \
if [ -n "${TOUCH_LIST}" ]; then \
eval touch ${TOUCH_LIST} \
;fi && \
if [ -n "${USER_NAME}" ] && [ -n "${CHOWN_LIST}" ]; then \
echo "Change ownership" && \
eval chown -c "$(getent passwd ${USER_NAME} | cut -d':' -f3):$(getent passwd ${USER_NAME} | cut -d':' -f4)" ${CHOWN_LIST} \
;fi

USER ${USER_NAME:-root}
COPY entrypoint /entrypoint
ARG GOSU_VERSION=1.17
ARG SETPRIV_VERSION=2.40.1
ARG UTIL_LINUX_VERSION=2.39.3
RUN chmod +x /entrypoint && /entrypoint --setup
Loading

0 comments on commit 99aa312

Please sign in to comment.