7 Commits

Author SHA1 Message Date
Chris Evich
df8f46eb2d Update docs and Containerfile to match
Fully tested README.md instructions end-to-end on F36.

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-22 14:53:41 -05:00
Chris Evich
3d6603945d Bugfix
Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-19 07:42:23 -05:00
Chris Evich
ae40196724 Pre-create podman storage root
Suspected cause of ownership problems when mounting a volume at this
location.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-19 07:33:57 -05:00
Chris Evich
9cda05620b Merge branch 'docs' into 'main'
Support externally maintained DNF cache

See merge request qontainers/pipglr!5
2022-11-19 10:52:12 +00:00
Chris Evich
cbddc54007 Resolve TODO, update volume names, update docs.
Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-18 21:56:24 -05:00
Chris Evich
0e51bfdf8e Support externally maintained DNF cache
Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-18 21:50:34 -05:00
Chris Evich
e5dfadbd4c Add wrapper error on podman start failure
Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-18 09:04:05 -05:00
3 changed files with 152 additions and 61 deletions

View File

@@ -37,16 +37,16 @@ ARG EXCLUDE_PACKAGES="\
# Base-image runs as user 'podman', temporarily switch to root # Base-image runs as user 'podman', temporarily switch to root
# for installation/setup. # for installation/setup.
USER root USER root
# Not a real build-arg. Avoiding addition of an env. layer # Helper for comparison in future RUN operations (DO NOT USE)
# only to help prevent some extra typing. ARG _DNFCMD="dnf --setopt=tsflags=nodocs -y"
ARG dnfcmd="dnf --setopt=tsflags=nodocs -y" # Set this instead, if (for example) you want to volume-mount in /var/cache/dnf
ARG DNFCMD="${_DNFCMD}"
# Avoid installing any documentation to keep image small
# During install, excluding packages is meaningless if already installed # During install, excluding packages is meaningless if already installed
RUN set -x && \ RUN set -x && \
rm -f /etc/dnf/protected.d/sudo.conf && \ rm -f /etc/dnf/protected.d/sudo.conf && \
rm -f /etc/dnf/protected.d/yum.conf && \ rm -f /etc/dnf/protected.d/yum.conf && \
$dnfcmd remove ${EXCLUDE_PACKAGES} && \ $DNFCMD remove ${EXCLUDE_PACKAGES}
dnf clean all && \
rm -rf /var/cache/dnf
# Enable callers to customize the runner version as needed, otherwise # Enable callers to customize the runner version as needed, otherwise
# assume this image will be version-tagged, so it's fine to grab the latest. # assume this image will be version-tagged, so it's fine to grab the latest.
@@ -56,11 +56,13 @@ ARG TARGETARCH="amd64"
ENV RUNNER_RPM_URL=https://gitlab-runner-downloads.s3.amazonaws.com/${RUNNER_VERSION}/rpm/gitlab-runner_${TARGETARCH}.rpm ENV RUNNER_RPM_URL=https://gitlab-runner-downloads.s3.amazonaws.com/${RUNNER_VERSION}/rpm/gitlab-runner_${TARGETARCH}.rpm
RUN for rpm in ${EXCLUDE_PACKAGES}; do x+="--exclude=$rpm "; done && \ RUN for rpm in ${EXCLUDE_PACKAGES}; do x+="--exclude=$rpm "; done && \
set -x && \ set -x && \
$dnfcmd update && \ $DNFCMD update && \
$dnfcmd install $x $RUNNER_RPM_URL && \ $DNFCMD install $x $RUNNER_RPM_URL && \
$dnfcmd upgrade && \ $DNFCMD upgrade && \
dnf clean all && \ if [[ "${DNFCMD}" == "${_DNFCMD}" ]]; then \
rm -rf /var/cache/dnf dnf clean all && \
rm -rf /var/cache/dnf; \
fi
# In case of a runner escape, prevent easy installation of packages. # In case of a runner escape, prevent easy installation of packages.
RUN rm -f /etc/dnf/protected.d/* && \ RUN rm -f /etc/dnf/protected.d/* && \
@@ -94,7 +96,8 @@ RUN sed -i -r \
/etc/containers/containers.conf && \ /etc/containers/containers.conf && \
chmod +x /usr/local/bin/gitlab-runner-wrapper && \ chmod +x /usr/local/bin/gitlab-runner-wrapper && \
chmod +x /usr/local/bin/podman-in-podman-maintenance && \ chmod +x /usr/local/bin/podman-in-podman-maintenance && \
chown -R podman.podman /home/podman && \ chown -R podman:podman /home/podman && \
chmod u+s /usr/bin/new{uid,gid}map && \
rm -f /home/podman/.bash* && \ rm -f /home/podman/.bash* && \
echo DOCKER_HOST="unix:///tmp/podman-run-1000/podman/podman.sock" > /etc/profile.d/podman.sh echo DOCKER_HOST="unix:///tmp/podman-run-1000/podman/podman.sock" > /etc/profile.d/podman.sh
@@ -105,10 +108,14 @@ VOLUME ["/home/podman/.local/share/containers/storage/",\
WORKDIR /home/podman WORKDIR /home/podman
ENTRYPOINT ["/usr/local/bin/gitlab-runner-wrapper"] ENTRYPOINT ["/usr/local/bin/gitlab-runner-wrapper"]
# Ensure root storage directory exists with correct permissions
RUN mkdir -p .local/share/containers/storage
# Gitlab-runner configuration options. Default to unprivileged (nested) # Gitlab-runner configuration options. Default to unprivileged (nested)
# runner. Privileged is required to permit nested container image building. # runner. Privileged is required to permit nested container image building.
ARG RUNNER_NAME="qontainers-pipglr" ARG RUNNER_NAME="qontainers-pipglr"
ARG PRIVILEGED_RUNNER="false" # Running inner-podman privileged is necessary at the time of this commit.
ARG PRIVILEGED_RUNNER="true"
# Tags allow pinning jobs to specific runners, comma-separated list of # Tags allow pinning jobs to specific runners, comma-separated list of
# tags to add to runner (no spaces!) # tags to add to runner (no spaces!)
ARG RUNNER_TAGS="podman-in-podman" ARG RUNNER_TAGS="podman-in-podman"
@@ -127,21 +134,16 @@ ENV REGISTER_NON_INTERACTIVE="true" \
DOCKER_HOST="unix:///tmp/podman-run-1000/podman/podman.sock" \ DOCKER_HOST="unix:///tmp/podman-run-1000/podman/podman.sock" \
DOCKER_DEVICES="/dev/fuse" \ DOCKER_DEVICES="/dev/fuse" \
DOCKER_IMAGE="registry.fedoraproject.org/fedora-minimal:latest" \ DOCKER_IMAGE="registry.fedoraproject.org/fedora-minimal:latest" \
DOCKER_CACHE_DIR="/home/podman/.cache/gitlab-runner" \ DOCKER_CACHE_DIR="/cache" \
DOCKER_VOLUMES="/cache" \
DOCKER_NETWORK_MODE="host" \ DOCKER_NETWORK_MODE="host" \
DOCKER_PRIVILEGED="$PRIVILEGED_RUNNER" DOCKER_PRIVILEGED="$PRIVILEGED_RUNNER"
# Not a real build-arg. Simply here to save lots of typing. # Not a real build-arg. Simply here to save lots of typing.
ARG _pm="--systemd=true --device=/dev/fuse --security-opt label=disable --user podman -v gitlab-runner-storage:/home/podman/.local/share/containers/storage:Z,U -v gitlab-runner-cache:/home/podman/.cache/gitlab-runner:Z,U -v gitlab-runner-config:/home/podman/.gitlab-runner:Z,U -e PODMAN_RUNNER_DEBUG" ARG _pm="--systemd=true --device=/dev/fuse --security-opt label=disable --user podman --volume pipglr-podman-root:/home/podman/.local/share/containers/storage --volume pipglr-config:/home/podman/.gitlab-runner -v pipglr-podman-cache:/cache -e PODMAN_RUNNER_DEBUG -e LOG_LEVEL"
# These labels simply make it easier to register and execute the runner. # These labels simply make it easier to register and execute the runner.
# Define them last so they are absent should a image-build failure occur. # Define them last so they are absent should a image-build failure occur.
LABEL register="podman run -it --rm $_pm --secret REGISTRATION_TOKEN,type=env \$IMAGE register" LABEL register="podman run -it --rm $_pm --secret REGISTRATION_TOKEN,type=env \$IMAGE register"
# Note: Privileged mode is required to permit building container images with inner-podman
# TODO: Figure out what's needed to run w/o --privileged. When unspecified, LABEL run="podman run -d --privileged --name pipglr $_pm \$IMAGE run"
# conmon fails with this error (from podman debug output):
#
# DEBU[0019] running conmon: /usr/bin/conmon args="[--api-version 1 -c 289...c08 -u 289...c08 -r /usr/bin/crun -b /home/podman/.local/share/containers/storage/overlay-containers/289...c08/userdata -p /tmp/podman-run-1000/containers/overlay-containers/289...c08/userdata/pidfile -n runner-8pxm3xb-project-19009784-concurrent-0-a71b53d132a29e56-predefined-0 --exit-dir /tmp/podman-run-1000/libpod/tmp/exits --full-attach -l k8s-file:/home/podman/.local/share/containers/storage/overlay-containers/289...c08/userdata/ctr.log --log-level debug --syslog --runtime-arg --cgroup-manager --runtime-arg disabled -i --conmon-pidfile /tmp/podman-run-1000/containers/overlay-containers/289...c08/userdata/conmon.pid --exit-command /usr/bin/podman --exit-command-arg --root --exit-command-arg /home/podman/.local/share/containers/storage --exit-command-arg --runroot --exit-command-arg /tmp/podman-run-1000/containers --exit-command-arg --log-level --exit-command-arg debug --exit-command-arg --cgroup-manager --exit-command-arg cgroupfs --exit-command-arg --tmpdir --exit-command-arg /tmp/podman-run-1000/libpod/tmp --exit-command-arg --network-config-dir --exit-command-arg --exit-command-arg --network-backend --exit-command-arg netavark --exit-command-arg --volumepath --exit-command-arg /home/podman/.local/share/containers/storage/volumes --exit-command-arg --runtime --exit-command-arg crun --exit-command-arg --storage-driver --exit-command-arg overlay --exit-command-arg --events-backend --exit-command-arg file --exit-command-arg --syslog --exit-command-arg container --exit-command-arg cleanup --exit-command-arg 289...c08]"
# [conmon:d]: failed to write to /proc/self/oom_score_adj: Permission denied
LABEL run="podman run -d --privileged --name gitlab-runner $_pm \$IMAGE run"

156
README.md
View File

@@ -12,70 +12,135 @@ made available as:
It's purpose is to provide an easy method to execute a GitLab runner, It's purpose is to provide an easy method to execute a GitLab runner,
to service CI/CD jobs for groups and/or repositories on to service CI/CD jobs for groups and/or repositories on
[gitlab.com](https://gitlab.com). It comes pre-configured to utilize [gitlab.com](https://gitlab.com). It comes pre-configured to utilize
the gitlab-runner app to execute with rootless podman containers, the gitlab-runner app to execute within a rootless podman container,
nested inside a rootless podman container. nested inside a rootless podman container.
This is intended to provide multiple additional layers of security This is intended to provide additional layers of security for the host,
for the host, when running potentially arbitrary CI/CD code. Though, when running potentially arbitrary CI/CD code. Though, the ultimate
the ultimate responsibility still rests with the end-user to review responsibility still rests with the end-user to review the setup and
the setup and configuration relative to their own situation/environment. configuration relative to their own security situation/environment.
### Quickstart ### Operation
Several labels are set on the built image or manifest list to support This image supports `podman container runlabel`, or if your version
easy registration and execution of a runner container. They require lacks this feature, Several labels are set on the image to support
defining several environment variables for use. easy registration and execution of a runner container using a special
bash command. See the examples below for more information.
#### [Volume Ownership Bug](https://github.com/containers/podman/issues/16576)
Some versions of podman contain a bug where named local volumes aren't owned
by the namespaced user within a rootless container (i.e. the 'podman' user).
Since the `podman` user/group inside the `pipglr` container is known, it's
possible to manually setup ownership ahead of time. This should be be done
once, prior to registering your runners:
```bash
$ for VOLUME in pipglr-podman-root pipglr-config pipglr-podman-cache; do \
PUPVM="podman unshare podman volume mount $VOLUME"
podman volume create $VOLUME && \
podman unshare chown 1000:1000 $($PUPVM) && \
podman unshare chmod 02770 $($PUPVM) && \
podman unshare ls -land $($PUPVM) ; \
done
```
If you get `podman system service` startup permission-denied errors, or
errors from gitlab-runner, unable to connect to the podman socket, this is
likely the cause. You can fix it after-the-fact using the same commands as above, just add a `-R` option to the `chown`/`chmod`, and additionally target `./*`.
#### Runner registration #### Runner registration
Each time the registration command is run, a new runner is added into Each time the registration command is run, a new runner is added into
the configuration. If your intent is to simply update or modify the the configuration. If however, you simply need to update/modify the
configuration, please edit the config.toml file within the configuration, please edit the `config.toml` file directly after mounting
`gitlab-runner-config` volume. (default) `pipglr-runner-config` (`/home/podman/.gitlab-runner/`) volume.
For modern versions of podman, registration can be performed with the
Note: These commands assume you have both `podman` and `jq` available. following commands:
Instead of `eval`, if your podman version supports `container runlabel`,
you may use that. ```bash
$ IMAGE="registry.gitlab.com/qontainers/pipglr:latest"
$ echo '<actual registration token>' | podman secret create REGISTRATION_TOKEN -
$ podman container runlabel register $IMAGE
```
Where `<actual registration token>` is the value obtained from the "runners"
settings page of a gitlab group or project. When you're finished registering
as many runners as you want, the secret is no-longer needed and may be removed:
```bash
$ podman secret rm REGISTRATION_TOKEN
```
##### Note
Some versions of podman don't support the `container runlabel` sub-command.
If this is the case, you may simulate it with the following command (in addition
to the other example commands above):
```bash ```bash
$ echo '<registration token>' | podman secret create REGISTRATION_TOKEN -
$ export IMAGE=<image FQIN:TAG>
$ eval $(podman inspect --format=json $IMAGE | jq -r .[].Labels.register) $ eval $(podman inspect --format=json $IMAGE | jq -r .[].Labels.register)
``` ```
#### Runner Startup #### Runner Startup
With one or more runners registered and configured, and `$IMAGE` set, With one or more runners successfully registered and configured, the GitLab
the GitLab runner container may be launched with the following commands. runner container may be launched with the following commands:
Note: The first time this is run, startup will take an extended amount ```bash
of time as the runner downloads and runs several (inner) support containers. $ podman container runlabel run $IMAGE
As above, instead of `eval`, if your podman version supports `container runlabel`, ```
you may use that.
Debugging: You may `export PODMAN_RUNNER_DEBUG=debug` to enable inner-podman ##### Note
debugging (or any other supported log level) to stdout.
As above, if you're missing the `container runlabel` sub-command, the following
may be used instead (assuming `$IMAGE` remains set):
```bash ```bash
$ eval $(podman inspect --format=json $IMAGE | jq -r .[].Labels.run) $ eval $(podman inspect --format=json $IMAGE | jq -r .[].Labels.run)
``` ```
#### Runner configuration
You may inspect/modify the gitlab-runner configuration as you see fit, just be
sure to use the `podman unshare` command-wrapper to enter the usernamespace.
For example, to display the config:
```bash
$ podman unshare $(podman unshare podman volume mount pipglr-config)/config.toml
```
#### Debugging
The first thing to check is the container output:
```bash
$ podman logs --since 0 pipglr
```
Before starting the runner, you may `export PODMAN_RUNNER_DEBUG=debug` to enable
debugging on the inner-podman. Whereas `export LOG_LEVEL=debug` can be used to
debug the gitlab-runner itself.
## Building ## Building
This image may be built simply with: This image may be built simply with:
`podman build -t runner .` ```bash
$ podman build -t registry.gitlab.com/qontainers/pipglr:latest .
```
This will utilize the latest stable version of podman and the latest This will utilize the latest stable version of podman and the latest
stable version of the gitlab runner. stable version of the gitlab runner.
### Multi-arch ### Notes
Assuming the host supports foreign-architecture emulation. The * If you wish to use the `testing` or `upstream` flavors of the podman base image,
`Containerfile` may be used to produce a multi-arch manifest-list. simply build with `--build-arg FLAVOR=testing` (or `upstream`).
For example:
`podman build --jobs 4 --platform linux/s390x,linux/ppc64le,linux/amd64 --manifest runner .` * Additionally or alternatively, you may specify a specific podman base image tag
with `--build-arg BASE_TAG=<value>`. Where `<value>` is either `latest`, the
podman image version (e.g. `v4`, `v4.2`, `v4.2.0`, etc.)
### Build-args ### Build-args
@@ -90,13 +155,18 @@ Several build arguments are available to control the output image:
and `vX.Y.Z` (where, `X`, `Y`, and `Z` represent the podman semantic and `vX.Y.Z` (where, `X`, `Y`, and `Z` represent the podman semantic
version numbers). It's also possible to specify an image SHA. version numbers). It's also possible to specify an image SHA.
* `EXCLUDE_PACKAGES` - A space-separated list of RPM packages to prevent * `EXCLUDE_PACKAGES` - A space-separated list of RPM packages to prevent
their existance in the final image. This is intended as a security measure their existence in the final image. This is intended as a security measure
to limit the attack-surface should a gitlab-runner process escape it's to limit the attack-surface should a gitlab-runner process escape it's
inner-container. inner-container.
* `RUNNER_VERSION` - Allows specifying an exact gitlab runner version. * `RUNNER_VERSION` - Allows specifying an exact gitlab runner version.
By default the `latest` is used, assuming the user is building a tagged By default the `latest` is used, assuming the user is building a tagged
image anyway. Valid versions may be found on the [runner image anyway. Valid versions may be found on the [runner
release page](https://gitlab.com/gitlab-org/gitlab-runner/-/releases). release page](https://gitlab.com/gitlab-org/gitlab-runner/-/releases).
* `DNFCMD` - By default this is set to `dnf --setopt=tsflags=nodocs -y`.
However, if you'd like to volume-mount in `/var/cache/dnf` then you'll
need to use
`--build-arg DNFCMD="dnf --setopt=tsflags=nodocs -y --setopt keepcache=true`"
Note: Changing `DNFCMD` will cause build-time cache cleanup to be disabled.
* `TARGETARCH` - Supports inclusion of non-x86_64 gitlab runners. This * `TARGETARCH` - Supports inclusion of non-x86_64 gitlab runners. This
value is assumed to match the image's architecture. If using the value is assumed to match the image's architecture. If using the
`--platform` build argument, it will be set automatically. `--platform` build argument, it will be set automatically.
@@ -104,12 +174,24 @@ Several build arguments are available to control the output image:
and port supports various observability and debugging features of the and port supports various observability and debugging features of the
gitlab runner. For more information see the [gitlab runner advanced gitlab runner. For more information see the [gitlab runner advanced
configuration documentation](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section). configuration documentation](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section).
* `PRIVILEGED_RUNNER` - Defaults to 'false', may be set 'true'. When * `PRIVILEGED_RUNNER` - Defaults to 'true', may be set 'true' if you're brave.
`true`, this causes inner-containers to be created with the `--privileged` However this may result in the gitlab-runner failing to launch inner-containers.
flag. This is a potential security weakness, but is necessary for Setting it false will also prevent building container images using the runner.
(among other things) allowing nested container image builds.
* `RUNNER_TAGS` - Defaults to `podman_in_podman`, may be set to any comma-separated * `RUNNER_TAGS` - Defaults to `podman_in_podman`, may be set to any comma-separated
list (with no spaces!) of tags. These show up in GitLab (not the runner list (with no spaces!) of tags. These show up in GitLab (not the runner
configuration), and determines where jobs are run. configuration), and determines where jobs are run.
* `RUNNER_UNTAGED` - Defaults to `true`, may be set to `false`. Allows * `RUNNER_UNTAGED` - Defaults to `true`, may be set to `false`. Allows
the runner to service jobs without any tags on them at all. the runner to service jobs without any tags on them at all.
### Environment variables
Nearly every option to every gitlab-runner sub-command may be specified via
environment variable. Many important/required options are set in the
`Containerfile`. However it's entirely possible to pass them in via
either of the `podman container runlabel...` container commands. To
discover them, simply append `--help` to the end of the command.
For example:
```bash
podman container runlabel $IMAGE register --help
```

View File

@@ -11,11 +11,18 @@ if [[ -n "$PODMAN_RUNNER_DEBUG" ]]; then
_debug_args="--log-level=$PODMAN_RUNNER_DEBUG" _debug_args="--log-level=$PODMAN_RUNNER_DEBUG"
fi fi
if [[ "$1" == "run" ]] && [[ ! -S "/tmp/podman-run-1000/podman/podman.sock" ]]; then SOCKET="/tmp/podman-run-1000/podman/podman.sock"
if [[ "$1" == "run" ]] && [[ ! -S "$SOCKET" ]]; then
podman $_debug_args system service -t 0 & podman $_debug_args system service -t 0 &
/usr/local/bin/podman-in-podman-maintenance & /usr/local/bin/podman-in-podman-maintenance &
# Prevent SIGHUP propagation to podman process # Prevent SIGHUP propagation to podman process
disown -ar disown -ar
sleep 1s # Give podman a chance to get going
# Verify podman is listening on it's socket
if [[ ! -S "$SOCKET" ]]; then
echo "ERROR: Inner-podman system service failed to start, expecting to find socket '$SOCKET'. Are all volume's owned & writeable by $(id -u podman):$(id -g podman)?" > /dev/stderr
exit 1
fi
fi fi
exec gitlab-runner "$@" exec gitlab-runner "$@"