54 Commits

Author SHA1 Message Date
Chris Evich
ae1d652f70 Merge branch 'fix_config' into 'main'
Fix registration permission denied

Closes #1

See merge request qontainers/pipglr!20
2023-02-03 16:36:36 +00:00
Chris Evich
d66c563c31 Fix registration permission denied
For normal operations, the `runner` user's `.gitlab-runner` directory
has a symlink for (read-only) `config.toml` ->
`/var/run/secrets/config.toml`.  However, for during registration
attempts to write to this volume-mounted file result in a
`PANIC: write: permission denied` error.  Fix this by running the
registration as (namespaced) `root` user, and using the default config.
file location of `/etc/gitlab-runner`.

Signed-off-by: Chris Evich <cevich@redhat.com>
2023-02-03 11:23:51 -05:00
Chris Evich
4927c6577c Merge branch 'improve_docs' into 'main'
Emphacise need to pre-create config.toml

See merge request qontainers/pipglr!19
2023-02-03 16:12:57 +00:00
Chris Evich
9d8c463a7f Emphacise need to pre-create config.toml
The README did not make it clear enough that a blank `config.toml` file
needs to be pre-created before runner registration.  Otherwise the
`register` *runlabel* will silently fail to bind-mount the file into the
proper location inside the container.

While we're at it, also add a small example of why it may be a good idea
to edit the `config.toml` file before commiting it as a secret.

Signed-off-by: Chris Evich <cevich@redhat.com>
2023-01-31 13:43:32 -05:00
Chris Evich
c6d67f583c Merge branch 'reimplement_systemd' into 'main'
Fix podman leaking conmon processes

See merge request qontainers/pipglr!18
2023-01-30 14:36:29 +00:00
Chris Evich
6cb20272e4 Fix podman leaking conmon processes
When running in the background without a full-blown init system,
`podman system service` will leak `conmon` processes for every
gitlab-runner job that executes via the docker socket API.  These
`conmon` processes almost immediately becomes zombies, and are never
cleaned up.  Eventually the zombies will consume all available PIDs.

Many attempts to fix this in various ways have all failed.  In all cases
the GitLab Runner process will start behaving strangely (or fail
completely) after an amount of time dependent on its usage executing
jobs.

Fix this by entirely reimplementing *pipglr* to utilize systemd and a
pair of lingering user-slices.  One for podman, another for the gitlab
runner.  Include a systemd timer service to affect runner cleanup,
periodically. Also update documentation and examples accordingly.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2023-01-28 09:59:59 -05:00
Chris Evich
f44e9891d1 Merge branch 'fix_idmaps' into 'main'
Fix newuidmap...`: newuidmap: open of uid_map failed: Permission denied

See merge request qontainers/pipglr!15
2022-12-08 12:05:44 +00:00
Chris Evich
bf242e8a52 Fix newuidmap...`: newuidmap: open of uid_map failed: Permission denied
Within a container, caps need to be set on /usr/bin/new{uid,gid}map
which reflect the (inner) podman user's root namespaced ID of 10000.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-12-08 06:59:02 -05:00
Chris Evich
db6f2bceb9 Attempt fix v2.0.0 newuidmap permission denied
Recently, I believe an update or packaging problem has been causing
podman to throw errors like:

```
level=error msg="running `/usr/bin/newuidmap ...`: newuidmap: open of
uid_map failed: Permission denied\n"
```

This seems to have something to do with the shadow-utils package, which
owns this binary.  I've examined the file attribuites and permissions
along with /etc/sub{uid,gid} contents.  The only thing that seems to
resolve the issue is reinstalling shadow-utils.  Attempt that fix here
and hope it clears up the problem (present in v2.0.0)

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-12-07 06:14:45 -05:00
Chris Evich
f1423ae0b2 Merge branch 'fix_volumes' into 'main'
Fix volume mounts

See merge request qontainers/pipglr!14
2022-12-06 08:15:33 +00:00
Chris Evich
f88c1a43bd Fix volume mounts
* Add note about volume-mounts being cumulative with base-image
* Fix register & run labels to use (correct) base image's
  `/home/podman/.local/share/containers/` instead of defining
  a new (wrong/useless) `storage` volume.
* Fix register & run labels to mask over `/var/lib/containers`
  with a read-only tmpfs to block any nested rootful use of
  podman as a security precaution.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-12-06 03:07:40 -05:00
Chris Evich
5b28cca4e1 Merge branch 'docs_and_cache' into 'main'
Docs: Update volume creation section

See merge request qontainers/pipglr!13
2022-12-06 07:31:15 +00:00
Chris Evich
4fbe33bdbd Revert "Cache prior build layers for ~5 days"
This reverts commit 4c1af406f7.  For
whatever reason, trying to build this using the community runners always
results in an out-of-diskspace error.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-12-06 02:20:14 -05:00
Chris Evich
4c1af406f7 Cache prior build layers for ~5 days
This vastly improves build performance at the cost of some extra
registry server storage (which there is plenty of).   Note: The cache
TTL needs to be coordinated with the registry server's tag-cleanup
policy.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-12-06 02:18:55 -05:00
Chris Evich
51ce5e4e0a Use single uid/gid map
The podman base-image is intended to support running nested-podman both
root and rootless.  Since pipglr only ever runs rootless, eliminate the
nested usernamespace mapping needed to support nested-root usage.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-12-06 02:18:55 -05:00
Chris Evich
75bdc3196f Docs: Update volume creation section
Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-12-06 02:18:27 -05:00
Chris Evich
d36e05d452 Merge branch 'add_readme' into 'main'
Include docs inside image

See merge request qontainers/pipglr!12
2022-12-01 19:23:04 +00:00
Chris Evich
29a806305e Include docs inside image
Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-30 09:47:18 -05:00
Chris Evich
19a7e501be Docs update
Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-30 09:44:34 -05:00
Chris Evich
b591a3d7c5 Remove broken workflow rules
They were preventing builds on main and on tags.  Rather than try and
debug them, just kill them as they're mostly unnecessary for this repo.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-25 23:28:47 -05:00
Chris Evich
b21181a446 Fix build job not running on main or tag push
Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-25 23:26:35 -05:00
Chris Evich
c80dfffa14 Merge branch 'switch_to_buildah' into 'main'
Switch to using buildah

See merge request qontainers/pipglr!11
2022-11-26 04:20:51 +00:00
Chris Evich
cbb3eb0e22 Switch to using buildah
Observations show the runtime is about the same (since performance is mostly
storage-io bound.  However, buildah images are more compact and there
are simpler possibilities for build-caching available for future use.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-25 23:15:26 -05:00
Chris Evich
98e2bee2a5 Merge branch 'kill_vars' into 'main'
Remove unnecessary variables

See merge request qontainers/pipglr!10
2022-11-24 16:51:33 +00:00
Chris Evich
4b25e62b52 Remove unnecessary variables
Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-24 11:48:23 -05:00
Chris Evich
1c08727c5d Merge branch 'simplify_metadata' into 'main'
Simplify image metadata

See merge request qontainers/pipglr!9
2022-11-24 02:41:37 +00:00
Chris Evich
af84720d71 Simplify image metadata
Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-23 21:38:06 -05:00
Chris Evich
c748c4f928 Merge branch 'image_metadata' into 'main'
Add metadata to image

See merge request qontainers/pipglr!8
2022-11-24 01:47:48 +00:00
Chris Evich
5164551f98 Add metadata to image
Also, pin base image to a specific version instead of `latest`.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-23 18:54:24 -05:00
Chris Evich
63b00ad2e7 Merge branch 'fix_maintenance' into 'main'
Fix maintenance task

See merge request qontainers/pipglr!7
2022-11-23 17:12:59 +00:00
Chris Evich
2bda4c3be6 Fix maintenance task
The function was defined but never called, resulting in immediate exit
of the maintenance script.  Fix this, also add a configuration build-arg and
ENV to control the cleaning interval.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-23 12:10:07 -05:00
Chris Evich
3b72178739 Merge branch 'cachevol' into 'main'
Add missing cache volume

See merge request qontainers/pipglr!6
2022-11-22 22:31:28 +00:00
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
a1931efcc1 Add missing cache volume
Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-22 11:35:36 -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
Chris Evich
b4dd3c667c Fix image tagging (again)
This CI environment is using a busybox `sh` so doesn't support all the
advanced features of bash.  Reimplement IMAGE_TAG processing so it
functions as intended.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-16 10:53:52 -05:00
Chris Evich
e214a0549c Merge branch 'bugfix' into 'main'
Fix CI/CD typo and other misc items

See merge request qontainers/pipglr!4
2022-11-16 15:07:01 +00:00
Chris Evich
04b61422a9 Be helpful to CI/CD job maintainers
It's often hard to debug/maintain/improve a job when you can't observe
any of the (many!) auto-generated CI env. vars.  Print them all out on
every job.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-16 10:01:51 -05:00
Chris Evich
341fbb8030 Fix kaniko command-line
For whatever reason, line-breaks must be used regardless of
string-block marker `|` or `>-`.  Fix this.

Also, support a fork/pull/MR model allowing contributors to run
pipelines on their fork w/ push to their registry.  In this case, images
should be tagged by MR number to be helpful.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-16 09:46:19 -05:00
Chris Evich
64ce093a87 Ignore local pre-commit configuration files
Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-16 09:23:17 -05:00
Chris Evich
5daaa407c8 Tweak some tag defaults
Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-16 09:23:17 -05:00
Chris Evich
af48308a15 Merge branch 'set_name' into 'main'
Add runner-name build-arg

See merge request qontainers/pipglr!3
2022-11-16 14:05:06 +00:00
Chris Evich
98a0c4c9ab Merge branch 'upd_img_tag' into 'main'
Update image tagging w/ `mr` prefix for MRs

See merge request qontainers/pipglr!2
2022-11-16 14:04:45 +00:00
Chris Evich
9c95cf5074 Add runner-name build-arg
Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-16 08:53:48 -05:00
Chris Evich
952ff81c4f Update image tagging w/ mr prefix for MRs
Prevent any possiblilty of confusion for tags (prefixed with a `v`) vs MR tags
(previously no prefix).

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-13 18:45:10 -05:00
Chris Evich
8066716b67 Add background cleanup process
When given the "run" argument, in addition to launching `podman system
service` in the background, also start a small periodic maintenance
script.  It's only job is to clean up stale images, containers, and
volumes from old jobs.  Currently hard-coded to trigger every 2 days,
this could be tweaked via build-args or env. var.

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-10 14:19:09 -05:00
Chris Evich
ba2dc82ac8 Fix disused session-port var in run label
Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-10 12:07:06 -05:00
Chris Evich
b9e5e066c8 Update documentation
Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-10 11:53:56 -05:00
Chris Evich
125d7cb9ac Tag images based on build context
For tag-pipelines, tag image with the repo. tag

For MR's, tag the image with the MR number

For Branches, use the branch name except for `main` use `latest`.

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-10 11:47:20 -05:00
15 changed files with 471 additions and 243 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/.pre-commit-config.yaml

View File

@@ -1,26 +1,56 @@
---
stages:
- build
build:
stage: build
default:
image: quay.io/buildah/stable:v1.28.0
tags:
- docker
- linux
image:
name: gcr.io/kaniko-project/executor:v1.6.0-debug
entrypoint: ["/busybox/sh", "-c"]
envars:
stage: test
script: |
echo "Select CI env. vars.:";
printenv | egrep '^CI_' | sort
commit_check:
stage: test
variables:
BASE_TAG: latest
FLAVOR: stable
BADRX: '^(squash!)|(fixup!)'
script: |
dnf install -y git
shortlogtmp=$(mktemp -p '' commit_check_tmp_XXXX)
git log --oneline --no-show-signature "${CI_MERGE_REQUEST_DIFF_BASE_SHA}..HEAD" > "$shortlogtmp"
if egrep -q "$BADRX" "$shortlogtmp"; then
egrep "$BADRX" "$shortlogtmp"
die "Found the above commits matching '$BADRX'"
fi
build:
stage: deploy
variables:
BUILDAH_FORMAT: docker
BUILDAH_ISOLATION: chroot
STORAGE_DRIVER: vfs
before_script:
- echo "$CI_REGISTRY_PASSWORD" | buildah login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
script:
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
# N/B: There could be more than one merge-request open with this branch's HEAD
- |
IMAGE_TAG="${CI_COMMIT_BRANCH}";
if [[ -n "$CI_COMMIT_TAG" ]]; then
IMAGE_TAG="${CI_COMMIT_TAG}";
elif [[ -n "$CI_OPEN_MERGE_REQUESTS" ]]; then
IMAGE_TAG=mr$(echo "${CI_OPEN_MERGE_REQUESTS}" | cut -d, -f -1 | cut -d\! -f 2);
elif [[ "$CI_COMMIT_BRANCH" == "main" ]]; then
IMAGE_TAG="latest";
fi
echo "Building/Pushing to: ${CI_REGISTRY_IMAGE}:${IMAGE_TAG}";
- >-
/kaniko/executor \
--context $CI_PROJECT_DIR \
--dockerfile $CI_PROJECT_DIR/Containerfile \
--destination "$CI_REGISTRY_IMAGE:${CI_COMMIT_TAG:-latest}" \
--build-arg "BASE_TAG=$BASE_TAG" \
--build-arg "FLAVOR=$FLAVOR"
buildah build \
--label "org.opencontainers.image.source=${CI_PROJECT_URL}" \
--label "org.opencontainers.image.revision=$CI_COMMIT_SHA" \
--label "org.opencontainers.image.created=$CI_JOB_STARTED_AT" \
--label "org.opencontainers.image.version=${IMAGE_TAG}" \
-t "${CI_REGISTRY_IMAGE}:${IMAGE_TAG}" "$CI_PROJECT_DIR"
- buildah images
- buildah push "${CI_REGISTRY_IMAGE}:${IMAGE_TAG}"

View File

@@ -1,144 +1,80 @@
# pipglr/Containerfile
#
# Builds a Podman-in-Podman Gitlab-Runner image for
# executing Gitlab CI/CD jobs. Requires configuration
# steps specific to Gitlab projects. For more info. see
# https://docs.gitlab.com/runner/executors/docker.html#use-podman-to-run-docker-commands
#
FROM quay.io/centos/centos:stream9
ARG FLAVOR="stable"
ARG BASE_TAG="latest"
FROM quay.io/podman/$FLAVOR:$BASE_TAG
ADD /setup.sh /xpackages.txt /root/
ADD /containers.conf /home/podman/.config/containers/containers.conf
ADD /podman.service /podman.socket /prune.service /prune.timer /home/podman/.config/systemd/user/
ADD /runner.service /home/runner/.config/systemd/user/
ADD kmsglog.conf /etc/systemd/system.conf.d/
# This is a list of packages to remove and/or exclude from the image.
# Primarily this is done for security reasons, should a runner process
# escape confinement. Having fewer things to poke, lowers the attack
# surface-area.
#
# This list was formed manually by running these commands in the base image:
# for package in $(rpm -qa); do \
# if dnf erase $package; then echo "$package" >> exclude; fi; \
# done; \
# cat exclude
#
# After adding those packages to this file, the container build was run
# and package list adjusted, untill no dependency errors were raised.
ARG EXCLUDE_PACKAGES="\
fedora-repos-modular \
findutils \
libxcrypt-compat \
openldap-compat \
podman-gvproxy \
rootfiles \
sudo \
vim-minimal \
yum"
# Allow image-builders to choose another version becides "latest" should
# an incompatible change be introduced.
ARG RUNNER_VERSION=latest
# Base-image runs as user 'podman', temporarily switch to root
# for installation/setup.
USER root
# Not a real build-arg. Avoiding addition of an env. layer
# only to help prevent some extra typing.
ARG dnfcmd="dnf --setopt=tsflags=nodocs -y"
# During install, excluding packages is meaningless if already installed
RUN set -x && \
rm -f /etc/dnf/protected.d/sudo.conf && \
rm -f /etc/dnf/protected.d/yum.conf && \
$dnfcmd remove ${EXCLUDE_PACKAGES} && \
dnf clean all && \
rm -rf /var/cache/dnf
# Permit building containers for alternate architectures. At the time
# of this commit, only 'arm64' is available.
ARG TARGETARCH=amd64
# 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.
ARG RUNNER_VERSION="latest"
# When building a multi-arch manifest-list, this buid-arg is set automatically.
ARG TARGETARCH="amd64"
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 && \
set -x && \
$dnfcmd update && \
$dnfcmd install $x $RUNNER_RPM_URL && \
$dnfcmd upgrade && \
dnf clean all && \
rm -rf /var/cache/dnf
# Allow image-builders to choose an alternate nested-container pruning cycle.
# For most people the default is probably fine. This setting is dependent
# on the number and frequency of jobs run, along with the amount of disk-space
# available for both /cache and /home/podman/.local/share/containers volumes.
ARG PRUNE_INTERVAL=daily # see systemd.timer for allowable values
# In case of a runner escape, prevent easy installation of packages.
RUN rm -f /etc/dnf/protected.d/* && \
rpm -e dnf && \
rm -f $(type -P rpm)
# All-in-one packaging/image-setup script to keep things simple.
RUN PRUNE_INTERVAL=${PRUNE_INTERVAL} \
RUNNER_VERSION=${RUNNER_VERSION} \
bash /root/setup.sh
ADD /config.toml /home/podman/.gitlab-runner/config.toml
# The global "listen_address" option is used for metrics and
# debugging. Disable it by default since use requires special/
# additional host configuration.
# Ref: https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section
ARG RUNNER_LISTEN_ADDRESS="disabled"
ENV RUNNER_LISTEN_ADDRESS=$RUNNER_LISTEN_ADDRESS
RUN if [[ "$RUNNER_LISTEN_ADDRESS" == "disabled" ]]; then \
sed -i -r \
-e "s/.*@@RUNNER_LISTEN_ADDRESS@@.*//g" \
/home/podman/.gitlab-runner/config.toml; \
else \
sed -i -r \
-e "s/@@RUNNER_LISTEN_ADDRESS@@/$RUNNER_LISTEN_ADDRESS/g" \
/home/podman/.gitlab-runner/config.toml; \
fi
VOLUME /cache /home/podman/.local/share/containers
ENTRYPOINT /lib/systemd/systemd
# A small wrapper is needed to launch a background podman system service
# process for the gitlab-runner to connect to.
ADD /gitlab-runner-wrapper /usr/local/bin/
# Base image UTS NS configuration causes runner to break when launching
# nested rootless containers.
RUN sed -i -r \
-e 's/^utsns.+host.*/utsns="private"/' \
/etc/containers/containers.conf && \
chmod +x /usr/local/bin/gitlab-runner-wrapper && \
chown -R podman.podman /home/podman && \
rm -f /home/podman/.bash* && \
echo DOCKER_HOST="unix:///tmp/podman-run-1000/podman/podman.sock" > /etc/profile.d/podman.sh
# Gitlab-runner configuration options, may be freely overridden at
# container image build time.
ARG DEFAULT_JOB_IMAGE=registry.fedoraproject.org/fedora-minimal:latest
# Run nested containers in --privileged mode - required to allow building
# container images using podman or buildah. Otherwise may be set 'false'.
ARG NESTED_PRIVILEGED=true
# Runtime rootless-mode configuration
USER podman
VOLUME ["/home/podman/.local/share/containers/storage/",\
"/home/podman/.gitlab-runner/"]
WORKDIR /home/podman
ENTRYPOINT ["/usr/local/bin/gitlab-runner-wrapper"]
# Gitlab-runner configuration options. Default to unprivileged (nested)
# runner. Privileged is required to permit nested container image building.
ARG PRIVILEGED_RUNNER="false"
# Tags allow pinning jobs to specific runners, comma-separated list of
# tags to add to runner (no spaces!)
ARG RUNNER_TAGS="podman_in_podman"
# Permit running jobs without any tag at all
ARG RUNNER_UNTAGGED="true"
ENV REGISTER_NON_INTERACTIVE="true" \
RUNNER_TAG_LIST="$RUNNER_TAGS" \
REGISTER_RUN_UNTAGGED="$RUNNER_UNTAGGED" \
REGISTER_ACCESS_LEVEL="ref_protected" \
REGISTER_MAXIMUM_TIMEOUT="3600" \
CI_SERVER_URL="https://gitlab.com/" \
RUNNER_EXECUTOR="docker" \
RUNNER_SHELL="bash" \
REGISTER_MAINTENANCE_NOTE="Podman-in-Podman containerized runner" \
DOCKER_HOST="unix:///tmp/podman-run-1000/podman/podman.sock" \
DOCKER_DEVICES="/dev/fuse" \
DOCKER_IMAGE="registry.fedoraproject.org/fedora-minimal:latest" \
DOCKER_CACHE_DIR="/home/podman/.cache/gitlab-runner" \
DOCKER_NETWORK_MODE="host" \
DOCKER_PRIVILEGED="$PRIVILEGED_RUNNER"
# 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"
# 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.
LABEL register="podman run -it --rm $_pm --secret REGISTRATION_TOKEN,type=env \$IMAGE register"
# TODO: Figure out what's needed to run w/o --privileged. When unspecified,
# 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 -p $RUNNER_SESSION_PORT:$RUNNER_SESSION_PORT \$IMAGE run"
# The registration runlabel may be called multiple times to register more than
# one runner. Each expects a REGISTRATION_TOKEN secret to be pre-defined and
# the file './config.toml' to exist (may be empty). A local-cache volume
# '/cache' is configured for bind-mounting into all interrior-containers
# for container-runtime use, as recommended by the docs. Other settings
# may be changed if you know what you're doing.
LABEL register="podman run -it --rm \
--secret=REGISTRATION_TOKEN,type=env \
-v ./config.toml:/etc/gitlab-runner/config.toml:Z \
-e REGISTER_NON_INTERACTIVE=true \
-e CI_SERVER_URL=https://gitlab.com/ \
-e RUNNER_NAME=pipglr \
-e RUNNER_EXECUTOR=docker \
-e RUNNER_SHELL=bash \
-e REGISTER_MAINTENANCE_NOTE=Podman-In-Podman-GitLab-Runner \
-e DOCKER_HOST=unix:///home/runner/podman.sock \
-e DOCKER_IMAGE=${DEFAULT_JOB_IMAGE} \
-e DOCKER_CACHE_DIR=/cache \
-e DOCKER_VOLUMES=/cache \
-e DOCKER_NETWORK_MODE=host \
-e DOCKER_PRIVILEGED=${NESTED_PRIVILEGED} \
--entrypoint=/usr/bin/gitlab-runner \$IMAGE register"
# Additionally, the nested-podman storage volumes must be pre-created with
# 'podman' UID/GID values to allow nested containers access.
LABEL setupstorage="podman volume create --opt o=uid=1000,gid=1000 pipglr-storage"
# Lastly, the gitlab-runner will manage container-cache in this directory,
# which will also be bind-mounted into every container. So it must be
# writable by both 'podman' user and 'runner' group.
LABEL setupcache="podman volume create --opt o=uid=1000,gid=1001 pipglr-cache"
# Helper to extract the current configuration secret to allow editing.
LABEL dumpconfig="podman run -it --rm \
--secret config.toml --entrypoint=/bin/cat \
\$IMAGE /var/run/secrets/config.toml"
# Executing the runner container depends on the config.toml secret being
# set (see above) and two volumes existing with correct permissions set.
# Note: The contents of the volumes are not critical, they may be removed
# and re-created (see above) to quickly free-up disk space.
LABEL run="podman run -dt --name pipglr \
--secret config.toml,uid=1001,gid=1001 \
-v pipglr-storage:/home/podman/.local/share/containers \
-v pipglr-cache:/cache \
--systemd true --privileged \
--device /dev/fuse \$IMAGE"

222
README.md
View File

@@ -3,105 +3,195 @@
This container image is built daily from this `Containerfile`, and
made available as:
* FIXME
* `registry.gitlab.com/qontainers/pipglr:latest`
-or-
* `registry.gitlab.com/qontainers/pipglr:<version>`
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
[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.
This is intended to provide multiple additional layers of security
for the host, when running potentially arbitrary CI/CD code. Though,
the ultimate responsibility still rests with the end-user to review
the setup and configuration relative to their own situation/environment.
This is intended to provide additional layers of security for the host,
when running potentially arbitrary CI/CD code. Though, the ultimate
responsibility still rests with the end-user to review the setup and
configuration relative to their own security situation/environment.
### Quickstart
### Operation
Several labels are set on the built image or manifest list to support
easy registration and execution of a runner container. They require
defining several environment variables for use.
This image leverages the podman `runlabel` feature heavily. Several
labels are set on the image to support easy registration and execution
of the runner container. While it's possible to use the container
with your own command-line, it's highly recommended to base them
off of one of the labels. See the examples below for more information.
#### Runner registration
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
configuration, please edit the config.toml file within the
`gitlab-runner-config` volume.
Note: These commands assume you have both `podman` and `jq` available.
***Note:*** Some older versions of podman don't support the
`container runlabel` sub-command. If this is the case, you may simulate
it with the following, substituting `<label>` with one of the predefined
values (i.e. `register`, `setupconfig`, etc.):
```bash
$ echo '<registration token>' | podman secret create REGISTRATION_TOKEN -
$ export IMAGE=<image FQIN:TAG>
$ eval $(podman inspect --format=json $IMAGE | jq -r .[].Labels.register)
$ IMAGE="registry.gitlab.com/qontainers/pipglr:latest"
$ eval $(podman inspect --format=json $IMAGE | jq -r .[].Labels.<label>)
```
#### Runner Startup
#### Runner registration (step 1)
With one or more runners registered and configured, and `$IMAGE` set,
the GitLab runner container may be launched with the following commands.
All runners must be connected to a project or group runner configuration
on your gitlab instance (or `gitlab.com`). This is done using a special
registration *runlabel*. The command can (and probably should) be run
more than once (using the same `config.toml`) to configure and register
multiple runners. This is necessary for the *pipglr* container to execute
multiple jobs in parallel. For example, if you want to support running
four jobs at the same time, you would use the `register` *runlabel*
four times.
Note: The first time this is run, startup will take an extended amount
of time as the runner downloads and runs several (inner) support containers.
Debugging: You may `export PODMAN_RUNNER_DEBUG=debug` to enable inner-podman
debugging (or any other supported log level) to stdout.
Before using the `register` *runlabel*, you must set your unique
*registration* (a.k.a. *activation*) token as a podman *secret*. This
secret may be removed once the registration step is complete. The
**<actual registration token>** value (below) should be replaced with
the value obtained from the "runners" settings page of a gitlab
group or project's *CI/CD Settings*. Gitlab version 16 and later
refers to this value as an *activation* token, but the usage is the same.
```bash
$ eval $(podman inspect --format=json $IMAGE | jq -r .[].Labels.run)
$ IMAGE="registry.gitlab.com/qontainers/pipglr:latest"
$ echo '<actual registration token>' | podman secret create REGISTRATION_TOKEN -
```
Next, ***a blank `config.toml` file*** needs to be created. Without this, the
`reigster` *runlabel* will return a permission-denied error. Once the empty
`config.toml` file is created, you may register one or more runners by repeating
the registration *runlabel* as follows:
```bash
$ IMAGE="registry.gitlab.com/qontainers/pipglr:latest"
$ touch ./config.toml # important: file must exist, even if empty.
$ podman container runlabel register $IMAGE
...repeat as desired...
$ podman secret rm REGISTRATION_TOKEN # if desired
```
#### Runner Configuration (step 2)
During the registration process (above), a boiler-plate (default) `config.toml` file
will be created/updated for you. At this point you may edit the configuration
if desired before committing it as a *podman secret*. Please refer to the
[gitlab runner documentation](https://docs.gitlab.com/runner/configuration/)
for details.
```bash
$ $EDITOR ./config.toml # if desired
$ podman secret create config.toml ./config.toml
$ rm ./config.toml # if desired
```
This may be necessary, for example, to increase the default `concurrency` value
to reflect the number of registered runners. If you need to edit this file
after commiting it as a secret, there's
[ a `dumpconfig` *runlabel* for that](README.md#configuration-editing).
#### Volume setup (step 3)
Since several users are utilized inside the container volumes must be
specifically configured to permit access. This is done using several
*runlabels* as follows:
```bash
$ IMAGE="registry.gitlab.com/qontainers/pipglr:latest"
$ podman container runlabel setupstorage $IMAGE
$ podman container runlabel setupcache $IMAGE
```
Note: These volumes generally do not contain any critical operational data,
they may be re-created anytime to quickly free up host disk-space if
it's running low. Simply remove them with the command
`podman volume rm pipglr-storage pipglr-cache`. The reuse the `setupstorage`
and `setupcache` *runlabels* as in the above example.
#### Runner Startup (step 4)
With the runner configuration saved as a Podman secret, and the runner volumes
created, the GitLab runner container may be launched with the following commands:
```bash
$ IMAGE="registry.gitlab.com/qontainers/pipglr:latest"
$ podman container runlabel run $IMAGE
```
### Configuration Editing
The gitlab-runner configuration contains some sensitive values which
should be protected. The pipglr container assumes the entire configuration
will be passed in as a Podman secret. This makes editing it slightly
convoluted, so a handy *runlabel* `dumpconfig` is available.
It's intended use is as follows:
```bash
$ IMAGE="registry.gitlab.com/qontainers/pipglr:latest"
$ podman container runlabel dumpconfig $IMAGE > ./config.toml
$ $EDITOR ./config.toml
$ podman secret rm config.toml
$ podman secret create config.toml ./config.toml
$ rm ./config.toml # if desired
```
### Debugging
The first thing to check is the container output. This shows three things:
Systemd, Podman, and GitLab-Runner output. For example:
```bash
$ podman logs --since 0 pipglr
```
Next, try running a pipglr image built with more verbose logging. Both
the `runner.service` and `podman.service` files have a `log-level` option.
Simply increase one or both to the "info", or "debug" level. Start the
debug container, and reproduce the problem.
## Building
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
stable version of the gitlab runner.
### Multi-arch
Assuming the host supports foreign-architecture emulation. The
`Containerfile` may be used to produce a multi-arch manifest-list.
For example:
`podman build --jobs 4 --platform linux/s390x,linux/ppc64le,linux/amd64 --manifest runner .`
### Build-args
Several build arguments are available to control the output image:
* `FLAVOR` - Choose from 'stable', 'testing', or 'upstream'. These
select the podman base-image to utilize - which may affect the
podman version, features, and stability. For more information
see [the podmanimage README](https://github.com/containers/podman/blob/main/contrib/podmanimage/README.md).
* `BASE_TAG` - When `FLAVOR="stable"`, allows granular choice over the
exact podman version. Possible values include, `latest`, `vX`, `vX.Y`,
and `vX.Y.Z` (where, `X`, `Y`, and `Z` represent the podman semantic
version numbers). It's also possible to specify an image SHA.
* `EXCLUDE_PACKAGES` - A space-separated list of RPM packages to prevent
their existance in the final image. This is intended as a security measure
to limit the attack-surface should a gitlab-runner process escape it's
inner-container.
* `PRUNE_INTERVAL` - A systemd.timer compatible `OnCalendar` value that
determines how often to prune Podman's storage of disused containers and
images. Defaults to "daily", but should be adjusted based on desired
caching-effect balanced against available storage space and job
execution rate.
* `RUNNER_VERSION` - Allows specifying an exact gitlab runner version.
By default the `latest` is used, assuming the user is building a tagged
image anyway. Valid versions may be found on the [runner
release page](https://gitlab.com/gitlab-org/gitlab-runner/-/releases).
* `TARGETARCH` - Supports inclusion of non-x86_64 gitlab runners. This
value is assumed to match the image's architecture. If using the
`--platform` build argument, it will be set automatically.
* `RUNNER_LISTEN_ADDRESS` - Disabled by default, setting this to the FQDN
and port supports various observability and debugging features of the
gitlab runner. For more information see the [gitlab runner advanced
configuration documentation](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section).
* `PRIVILEGED_RUNNER` - Defaults to 'false', may be set 'true'. When
`true`, this causes inner-containers to be created with the `--privileged`
flag. This is a potential security weakness, but is necessary for
(among other things) allowing nested container image builds.
* `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
configuration), and determines where jobs are run.
* `RUNNER_UNTAGED` - Defaults to `true`, may be set to `false`. Allows
the runner to service jobs without any tags on them at all.
value is assumed to match the image's architecture. If using the
`--platform` build argument, it will be set automatically. Note:
as of this writing, only `amd64` and `arm64` builds of the gitlab-runner
are available.
* `NESTED_PRIVILEGED` - Defaults to 'true', may be set 'false' to prevent
nested containers running in `--privileged` mode. This will affect
the ability to build container images in CI jobs using tools like
podman or buildah.
### Environment variables
Nearly every option to every gitlab-runner sub-command may be specified via
environment variable. Some of these are set in the `Containerfile` for
the `register` *runlabel*. If you need to set additional runtime
env. vars., please do so via additional `Environment` optionns in the
`runner.service` file. See the *systemd.nspawn* man page for important
value-format details.

View File

@@ -1,7 +0,0 @@
# Ref: https://docs.gitlab.com/runner/configuration/advanced-configuration.html
concurrent = 8
# N/B: This is DIVIDED among the number of registered runners
check_interval = 10
listen_address = "@@RUNNER_LISTEN_ADDRESS@@" # Will be removed if undefined

13
containers.conf Normal file
View File

@@ -0,0 +1,13 @@
[containers]
netns="host"
userns="host"
ipcns="host"
utsns="private"
cgroupns="host"
cgroups="disabled"
log_driver = "k8s-file"
[engine]
cgroup_manager = "cgroupfs"
events_logger="file"
runtime="crun"

View File

@@ -1,16 +0,0 @@
#!/bin/bash
set -e
unset _debug_args
if [[ -n "$PODMAN_RUNNER_DEBUG" ]]; then
_debug_args="--log-level=$PODMAN_RUNNER_DEBUG"
fi
if [[ "$1" == "run" ]] && [[ ! -S "/tmp/podman-run-1000/podman/podman.sock" ]]; then
podman $_debug_args system service -t 0 &
# Prevent SIGHUP propigation to podman process
disown -ar
fi
exec gitlab-runner "$@"

3
kmsglog.conf Normal file
View File

@@ -0,0 +1,3 @@
[Manager]
LogTarget=kmsg
LogColor=yes

18
podman.service Normal file
View File

@@ -0,0 +1,18 @@
[Unit]
Description=Podman API Service
Requires=podman.socket
After=podman.socket
Documentation=man:podman-system-service(1)
StartLimitIntervalSec=0
[Service]
Delegate=true
Type=exec
KillMode=process
Environment=LOGGING="--log-level=warn"
ExecStart=/usr/bin/podman $LOGGING system service
StandardOutput=journal+console
StandardError=inherit
[Install]
WantedBy=default.target

11
podman.socket Normal file
View File

@@ -0,0 +1,11 @@
[Unit]
Description=Podman API Socket
Documentation=man:podman-system-service(1)
[Socket]
ListenStream=/home/runner/podman.sock
SocketGroup=runner
SocketMode=0660
[Install]
WantedBy=sockets.target

6
prune.service Normal file
View File

@@ -0,0 +1,6 @@
[Unit]
Description=Prune all disused podman volumes, images, and containers
[Service]
Type=oneshot
ExecStart=/usr/bin/podman system prune --all --force

6
prune.timer Normal file
View File

@@ -0,0 +1,6 @@
[Unit]
Description=Execute the prune service periodically
[Timer]
OnCalendar=@@@PRUNE_INTERVAL@@@
RemainAfterElapse=no

7
runner.service Normal file
View File

@@ -0,0 +1,7 @@
[Unit]
Description=Gitlab-runner service
[Service]
ExecStart=/usr/bin/gitlab-runner --log-level=warn run --user runner --working-directory=/home/runner
StandardOutput=journal+console
StandardError=inherit

109
setup.sh Normal file
View File

@@ -0,0 +1,109 @@
# This script is intended to be run during container-image build. Any
# other usage outside this context is likely to cause harm.
set -eo pipefail
for varname in PRUNE_INTERVAL RUNNER_VERSION TARGETARCH; do
if [[ -z "${!varname}" ]]; then
echo "Error: \$$varname must be non-empty."
fi
done
# Make image smaller by not installing docs.
DNF="dnf --setopt=tsflags=nodocs -y"
for rpm in $(egrep -v '^(# )+' < /root/xpackages.txt); do
x+="--exclude=$rpm ";
done
set -x # show what's happening to make debugging easier
# DNF itself or a dependence may need upgrading, take care of it first.
$DNF upgrade
$DNF $x install \
podman \
systemd
# Gitlab-runner package contains scriptlets which do not function properly inside a
# container-build environment where systemd is not active/running.
$DNF $x --setopt=tsflags=noscripts install \
https://gitlab-runner-downloads.s3.amazonaws.com/$RUNNER_VERSION/rpm/gitlab-runner_${TARGETARCH}.rpm
# Allow removing dnf, sudo, etc. packages. Also don't start unnecessary or broken
# systemd services, like anything kernel related or login gettys.
rm -rf \
/etc/dnf/protected.d/* \
/etc/sytemd/system/getty.target.wants/* \
/etc/sytemd/system/multi-user.target.wants/* \
/etc/sytemd/system/sysinit.target.wants/* \
/etc/sytemd/system/timers.target.wants/* \
/lib/systemd/system/graphical.target.wants/* \
/lib/systemd/system/multi-user.target.wants/{getty.target,systemd-ask-password-wall.path} \
/lib/systemd/system/sys-kernel*.mount
# Remove unnecessary packages, see xpackages.txt to learn how this list was generated.
# This makes the image smaller and reduces the attack-surface.
dnf remove -y $(egrep -v '^(# )+' /root/xpackages.txt)
# Wipe out the DNF cache, then remove it entirely, again to make the image smaller.
$DNF clean all
rm -rf /var/cache/dnf /var/log/dnf* /var/log/yum.*
rpm -e dnf
# Workaround https://bugzilla.redhat.com/show_bug.cgi?id=1995337
rpm --setcaps shadow-utils
# Prevent copying of skel since it can interfere with the gitlab-runner
mkdir -p /home/podman /home/runner
# Guarantee uid/gid 1000 for user 'podman' / 1001 for user 'runner'.
groupadd -g 1000 podman
groupadd -g 1001 runner
# Separate users for services to increase process isolation.
# The 'podman' user's socket service writes /home/runner/podman.socket
useradd -M -u 1000 -g podman -G runner podman
useradd -M -u 1001 -g runner runner
# Allow 'podman' user to create socket file under /home/runner.
chmod 770 /home/runner
# Overwrite defaults, only user 'podman' permited to have a user-namespace
# Split the namespaced ID's around the containers root (ID 0) and the user
# IDs 1000 and 1001 (defined above) to prevent hijacking from a nested container.
echo -e "podman:1:999\npodman:1002:64533" | tee /etc/subuid > /etc/subgid
# Host volume mount necessary for nested-podman to use overlayfs2 for container & volume storage.
mkdir -p /home/podman/.local/share/containers
# Nested-container's local container-cache volume mount, recommended by gitlab-runner docs.
mkdir -p /cache
# Both the gitlab-runner and podman need access to the cache directory / volume mount.
chown podman:runner /cache
# Setup persistent 'podman' user services to start & run without a login.
mkdir -p /var/lib/systemd/linger
touch /var/lib/systemd/linger/podman
# Setup 'podman' socket and a container-storage pruning service for 'podman' user.
mkdir -p /home/podman/.config/systemd/user/{sockets.target.wants,default.target.wants}
cd /home/podman/.config/systemd/user/
ln -s $PWD/podman.socket ./sockets.target.wants/ # Added from Containerfile
ln -s $PWD/prune.timer ./default.target.wants/ # also from Containerfile
# Substitute value from --build-arg if specified, otherwise use default from Containerfile.
sed -i -e "s/@@@PRUNE_INTERVAL@@@/$PRUNE_INTERVAL/" ./prune.timer
# Containerfile ADD instruction does not properly set ownership/permissions.
chown -R 1000:1000 /home/podman
chmod 700 /home/podman
# Setup persistent 'runner' user services to start & run without a login.
touch /var/lib/systemd/linger/runner
mkdir -p /home/runner/.config/systemd/user/default.target.wants
cd /home/runner/.config/systemd/user/
# Does not depend on podman.socket file availablility, will retry if not present.
ln -s $PWD/runner.service ./default.target.wants/
# gitlab-runner will create side-car '.runner_system_id' file next to 'config.toml'
# on first startup. Ensure access is allowed. Also link to future config file
# presented as a container-secret.
mkdir -p /home/runner/.gitlab-runner
ln -s /var/run/secrets/config.toml /home/runner/.gitlab-runner/config.toml
# Containerfile ADD instruction does not properly set ownership/permissions.
chown -R runner:runner /home/runner
chmod -R 700 /home/runner/.gitlab-runner

21
xpackages.txt Normal file
View File

@@ -0,0 +1,21 @@
# This list was formed by running the following commands in the base image:
# for package in $(rpm -qa); do if dnf erase $package; then echo "$package" >> remove; fi; done
# cat remove
# Including those packages in this file. Finally, repeatedly running the container build
# untill no dependency errors were raised.
criu
criu-libs
crypto-policies-scripts
dejavu-sans-fonts
findutils
fonts-filesystem
gdb-gdbserver
langpacks-core-en
langpacks-core-font-en
langpacks-en
libnet
protobuf-c
rootfiles
vim-minimal
yum