26 Commits

Author SHA1 Message Date
Chris Evich
d7ceba0847 Merge branch 'fix_permissions' into 'main'
Fix systemd config permissions

See merge request qontainers/pipglr!45
2024-05-07 19:17:01 +00:00
Chris Evich
eb37171735 Fix systemd config permissions
When the `Containerfile` `ADD` instruction runs against files from a git
repository, it's easily possible the permissions will be incorrect
inside the container.  Because systemd configuration and units are
rather important, update the setup script to ensure they're always set
correctly.

Signed-off-by: Chris Evich <cevich@redhat.com>
2024-05-07 13:38:14 -04:00
Chris Evich
da8621f46a Merge branch 'build_schedule_docs' into 'main'
Minor: Update build-schedule docs

See merge request qontainers/pipglr!46
2024-05-07 17:35:27 +00:00
Chris Evich
d8357d4688 Minor: Update build-schedule docs
In reality, the build pipeline is scheduled to run weekly, not daily.

Signed-off-by: Chris Evich <cevich@redhat.com>
2024-05-07 13:03:03 -04:00
Chris Evich
e55a0ca3a5 Merge branch 'update_cicd_runner_labels' into 'main'
Use small/medium public runners

See merge request qontainers/pipglr!47
2024-05-07 17:02:26 +00:00
Chris Evich
81d62b08c7 Use small/medium public runners
For whatever reason the `docker`/`linux` tags do not appear in the list
of public runners.  Update to values actually present, guessing at their
meaning.

Signed-off-by: Chris Evich <cevich@redhat.com>
2024-05-07 12:39:51 -04:00
Chris Evich
9cd88ce8ae Merge branch 'feature/2-refactor-setup' into 'main'
feat: Refactor setup

See merge request qontainers/pipglr!39
2024-04-19 16:36:58 +00:00
Gabriel Nützi
b78565281e feat: Refactor setup script into function calls
- The setup script has been broken up into function calls
  to improve readability and further maintenance.
  Also its beneficial for other derivations with different
  base OS.

Signed-off-by: Gabriel Nützi <gnuetzi@gmail.com>
2024-04-16 08:35:38 +02:00
Chris Evich
84b66a31fd Merge branch 'add-fips' into 'main'
feat: add FIPS download for gitlab-runner

See merge request qontainers/pipglr!43
2024-04-08 14:17:02 +00:00
Ryan Egesdahl
89457026c7 Add FIPS download for gitlab-runner
When FIPS is available on the host system, we should download the FIPS
binary for `gitlab-runner` by default. Users who do not want the FIPS
binary for some reason can use the non-FIPS binary by setting the
`ENABLE_FIPS` build-arg to `false`.
2024-04-08 00:06:30 -07:00
Chris Evich
1afb2496a0 Merge branch 'add_pre-commit' into 'main'
Add pre-commit hooks + CI

See merge request qontainers/pipglr!42
2024-04-02 18:39:52 +00:00
Chris Evich
d0687408dc Add pre-commit hooks + CI
Signed-off-by: Chris Evich <cevich@redhat.com>
2024-04-02 14:19:30 -04:00
Chris Evich
2e1e7ec90c Merge branch 'feature/2-format-readme' into 'main'
doc: Format `README.md`

See merge request qontainers/pipglr!41
2024-04-02 18:18:27 +00:00
Gabriel Nützi
2cd27dfae9 doc: Format README.md 2024-04-02 20:13:42 +02:00
Chris Evich
94e1055c5e Merge branch 'bugfix/1-typos' into 'main'
bugfix: Correct some typos

See merge request qontainers/pipglr!37
2024-04-01 20:16:39 +00:00
Gabriel Nützi
6a0ff34c9c bugfix: Correct some typos 2024-04-01 20:16:38 +00:00
Chris Evich
2161f09276 Merge branch 'del_dep_opt' into 'main'
Remove deprecated registration CLI option

Closes #12

See merge request qontainers/pipglr!32
2024-01-08 18:01:24 +00:00
Chris Evich
ded61bee62 Remove deprecated registration CLI option
Ref:
https://docs.gitlab.com/ee/update/deprecations.html#registration-tokens-and-server-side-runner-arguments-in-gitlab-runner-register-command

Thanks to @maxsatula for pointing this out.

Fixes #12

Signed-off-by: Chris Evich <cevich@redhat.com>
2024-01-08 12:57:37 -05:00
Chris Evich
1955b5d544 Merge branch 'docs_update' into 'main'
Document immediate namespace update effect

Closes #11

See merge request qontainers/pipglr!31
2024-01-08 17:51:45 +00:00
Chris Evich
a987bf649c Document immediate namespace update effect
Fixes #11

Signed-off-by: Chris Evich <cevich@redhat.com>
2024-01-08 12:47:01 -05:00
Chris Evich
f13f2a3ba2 Merge branch 'feature/gitlab-url' into 'main'
Remove extra } from GITLAB_URL

See merge request qontainers/pipglr!30
2024-01-08 15:28:11 +00:00
Andrey Radchishin
70b5176a33 Remove extra } 2023-12-21 09:09:45 -05:00
Chris Evich
8a21f07632 Merge branch 'feature/gitlab-url' into 'main'
Allow overriding Gitlab URL

See merge request qontainers/pipglr!29
2023-12-19 16:24:48 +00:00
Andrey Radchishin
6c752ebebf Allow overriding Gitlab URL 2023-12-18 12:28:30 -05:00
Chris Evich
2ef9e11aea Merge branch 'bump_workaround_timebomb' into 'main'
Draft: Remove workaround for #7 (closed)

Closes #9

See merge request qontainers/pipglr!28
2023-12-05 14:57:34 +00:00
Chris Evich
11e2200ff9 Remove workaround for #7
Fixes: #9

Signed-off-by: Chris Evich <cevich@redhat.com>
2023-11-21 10:22:29 -05:00
6 changed files with 382 additions and 230 deletions

1
.gitignore vendored
View File

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

View File

@@ -1,10 +1,21 @@
--- ---
default: default:
image: quay.io/buildah/stable:v1.31.0 image: quay.io/buildah/stable:v1.32
tags: tags:
- docker - saas-linux-small-amd64
- linux
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH
include:
- component: gitlab.com/blue42u/ci.pre-commit/lite@0.2.0
inputs:
job_stage: test
envars: envars:
stage: test stage: test
@@ -14,6 +25,9 @@ envars:
commit_check: commit_check:
stage: test stage: test
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- when: never
variables: variables:
BADRX: '^(squash!)|(fixup!)' BADRX: '^(squash!)|(fixup!)'
script: | script: |
@@ -26,6 +40,8 @@ commit_check:
fi fi
build: build:
tags:
- saas-linux-medium-amd64
stage: deploy stage: deploy
variables: variables:
BUILDAH_FORMAT: docker BUILDAH_FORMAT: docker

48
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,48 @@
default_language_version:
python: python3
default_install_hook_types: [pre-commit, commit-msg]
default_stages: [pre-commit]
repos:
- repo: https://github.com/executablebooks/mdformat
rev: '0.7.17'
hooks:
- id: mdformat
additional_dependencies:
- mdformat-footnote
- mdformat-tables
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: fix-byte-order-marker
- id: mixed-line-ending
- id: check-executables-have-shebangs
- id: check-symlinks
- id: destroyed-symlinks
- id: check-merge-conflict
- id: check-case-conflict
- id: no-commit-to-branch
args: [--branch, main]
- id: check-yaml
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: '0.28.0'
hooks:
# Validate the GitLab CI scripts against the schema. Doesn't catch everything but helps.
- id: check-gitlab-ci
files: '.*\.gitlab-ci\.yml$'
- repo: https://github.com/jumanjihouse/pre-commit-hooks
rev: '3.0.0'
hooks:
- id: forbid-binary
- id: require-ascii
- repo: meta
hooks:
# Un-comment (maybe temporarily) to check which hooks don't apply.
# - id: check-hooks-apply
- id: check-useless-excludes

View File

@@ -24,7 +24,7 @@ ADD /home/ /home/
# an incompatible change be introduced. # an incompatible change be introduced.
ARG RUNNER_VERSION=latest ARG RUNNER_VERSION=latest
# Permit building containers for alternate architectures. At the time # Permit building containers for alternate architectures. At the time
# of this commit, only 'arm64' is available. # of this commit, only 'arm64' is available.
ARG TARGETARCH=amd64 ARG TARGETARCH=amd64
@@ -45,10 +45,17 @@ ENTRYPOINT /lib/systemd/systemd
# Gitlab-runner configuration options, may be freely overridden at # Gitlab-runner configuration options, may be freely overridden at
# container image build time. # container image build time.
ARG DEFAULT_JOB_IMAGE=registry.fedoraproject.org/fedora-minimal:latest ARG DEFAULT_JOB_IMAGE=registry.fedoraproject.org/fedora-minimal:latest
# Allow image-builders to override the Gitlab URL
ARG GITLAB_URL=https://gitlab.com/
# Run nested containers in --privileged mode - required to allow building # Run nested containers in --privileged mode - required to allow building
# container images using podman or buildah. Otherwise may be set 'false'. # container images using podman or buildah. Otherwise may be set 'false'.
ARG NESTED_PRIVILEGED=true ARG NESTED_PRIVILEGED=true
# Download the FIPS version of gitlab-runner when enabled on the host system.
ARG ENABLE_FIPS=true
# The registration runlabel may be called multiple times to register more than # 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 # 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 # the file './config.toml' to exist (may be empty). A local-cache volume
@@ -59,11 +66,10 @@ LABEL register="podman run -it --rm \
--secret=REGISTRATION_TOKEN,type=env \ --secret=REGISTRATION_TOKEN,type=env \
-v ./config.toml:/etc/gitlab-runner/config.toml:Z \ -v ./config.toml:/etc/gitlab-runner/config.toml:Z \
-e REGISTER_NON_INTERACTIVE=true \ -e REGISTER_NON_INTERACTIVE=true \
-e CI_SERVER_URL=https://gitlab.com/ \ -e CI_SERVER_URL=${GITLAB_URL} \
-e RUNNER_NAME=pipglr \ -e RUNNER_NAME=pipglr \
-e RUNNER_EXECUTOR=docker \ -e RUNNER_EXECUTOR=docker \
-e RUNNER_SHELL=bash \ -e RUNNER_SHELL=bash \
-e REGISTER_MAINTENANCE_NOTE=Podman-In-Podman-GitLab-Runner \
-e DOCKER_HOST=unix:///home/runner/podman.sock \ -e DOCKER_HOST=unix:///home/runner/podman.sock \
-e DOCKER_IMAGE=${DEFAULT_JOB_IMAGE} \ -e DOCKER_IMAGE=${DEFAULT_JOB_IMAGE} \
-e DOCKER_CACHE_DIR=/cache \ -e DOCKER_CACHE_DIR=/cache \
@@ -71,17 +77,21 @@ LABEL register="podman run -it --rm \
-e DOCKER_NETWORK_MODE=host \ -e DOCKER_NETWORK_MODE=host \
-e DOCKER_PRIVILEGED=${NESTED_PRIVILEGED} \ -e DOCKER_PRIVILEGED=${NESTED_PRIVILEGED} \
--entrypoint=/usr/bin/gitlab-runner \$IMAGE register" --entrypoint=/usr/bin/gitlab-runner \$IMAGE register"
# Additionally, the nested-podman storage volumes must be pre-created with # Additionally, the nested-podman storage volumes must be pre-created with
# 'podman' UID/GID values to allow nested containers access. # 'podman' UID/GID values to allow nested containers access.
LABEL setupstorage="podman volume create --opt o=uid=1000,gid=1000 pipglr-storage" LABEL setupstorage="podman volume create --opt o=uid=1000,gid=1000 pipglr-storage"
# Lastly, the gitlab-runner will manage container-cache in this directory, # Lastly, the gitlab-runner will manage container-cache in this directory,
# which will also be bind-mounted into every container. So it must be # which will also be bind-mounted into every container. So it must be
# writable by both 'podman' user and 'runner' group. # writable by both 'podman' user and 'runner' group.
LABEL setupcache="podman volume create --opt o=uid=1000,gid=1001 pipglr-cache" LABEL setupcache="podman volume create --opt o=uid=1000,gid=1001 pipglr-cache"
# Helper to extract the current configuration secret to allow editing. # Helper to extract the current configuration secret to allow editing.
LABEL dumpconfig="podman run -it --rm \ LABEL dumpconfig="podman run -it --rm \
--secret config.toml --entrypoint=/bin/cat \ --secret config.toml --entrypoint=/bin/cat \
\$IMAGE /var/run/secrets/config.toml" \$IMAGE /var/run/secrets/config.toml"
# Executing the runner container depends on the config.toml secret being # Executing the runner container depends on the config.toml secret being
# set (see above) and two volumes existing with correct permissions set. # set (see above) and two volumes existing with correct permissions set.
# Note: The contents of the volumes are not critical, they may be removed # Note: The contents of the volumes are not critical, they may be removed
@@ -92,3 +102,4 @@ LABEL run="podman run -dt --name pipglr \
-v pipglr-cache:/cache \ -v pipglr-cache:/cache \
--systemd true --privileged \ --systemd true --privileged \
--device /dev/fuse \$IMAGE" --device /dev/fuse \$IMAGE"
# ==========================

261
README.md
View File

@@ -1,79 +1,84 @@
# Podmand-In-Podman Gitlab Runner
This project provides a Gitlab Runner which runs inside a container launched
with `podman`. The Gitlab Runner itself uses an independent `podman` instance
inside to launch jobs.
## Overview ## Overview
This container image is built daily from this `Containerfile`, and This container image is built weekly from this `Containerfile`, and made
made available as: available as:
* `registry.gitlab.com/qontainers/pipglr:latest` - `registry.gitlab.com/qontainers/pipglr:latest`
-or- -or-
* `registry.gitlab.com/qontainers/pipglr:<version>` - `registry.gitlab.com/qontainers/pipglr:<version>`
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
to service CI/CD jobs for groups and/or repositories on CI/CD jobs for groups and/or repositories on [gitlab.com](https://gitlab.com).
[gitlab.com](https://gitlab.com). It comes pre-configured to utilize It comes pre-configured to utilize the gitlab-runner app to execute within a
the gitlab-runner app to execute within a rootless podman container, rootless podman container, nested inside a rootless podman container.
nested inside a rootless podman container.
This is intended to provide additional layers of security for the host, This is intended to provide additional layers of security for the host, when
when running potentially arbitrary CI/CD code. Though, the ultimate running potentially arbitrary CI/CD code. Though, the ultimate responsibility
responsibility still rests with the end-user to review the setup and still rests with the end-user to review the setup and configuration relative to
configuration relative to their own security situation/environment. their own security situation/environment.
**Note**: While this can run entirely under a regular user, it will require **Note**: While this can run entirely under a regular user, it will require root
root access for the first two setup steps (below). access for the first two setup steps (below).
### Operation ### Operation
This image leverages the podman `runlabel` feature heavily. Several This image leverages the podman `runlabel` feature heavily. Several labels are
labels are set on the image to support easy registration and execution set on the image to support easy registration and execution of the runner
of the runner container. While it's possible to use the container container. While it's possible to use the container with your own command-line,
with your own command-line, it's highly recommended to base them it's highly recommended to base them off of one of the labels. See the examples
off of one of the labels. See the examples below for more information. below for more information.
***Note:*** Some older versions of podman don't support the **_Note:_** Some older versions of podman don't support the `container runlabel`
`container runlabel` sub-command. If this is the case, you may simulate sub-command. If this is the case, you may simulate it with the following,
it with the following, substituting `<label>` with one of the predefined substituting `<label>` with one of the predefined values (i.e. `register`,
values (i.e. `register`, `setupconfig`, etc.): `setupconfig`, etc.):
```bash ```bash
$ IMAGE="registry.gitlab.com/qontainers/pipglr:latest" $ IMAGE="registry.gitlab.com/qontainers/pipglr:latest"
$ eval $(podman inspect --format=json $IMAGE | jq -r .[].Labels.<label>) $ eval $(podman inspect --format=json $IMAGE | jq -r .[].Labels.<label>)
``` ```
#### Persistent containers (step 1) #### Persistent Containers (step 1)
By default on many distributions, regular users aren't permitted to leave By default on many distributions, regular users aren't permitted to leave
background processes running after they log out. Since this is likely background processes running after they log out. Since this is likely desired
desired for running the pipglr container long-term, `systemd` needs to be for running the pipglr container long-term, `systemd` needs to be configured to
configured to override this policy. For this, you (`$USER`) will need override this policy. For this, you (`$USER`) will need root access on the
root access on the system. system.
```bash ```bash
$ sudo loginctl enable-linger $USER $ sudo loginctl enable-linger $USER
``` ```
Side-effect: This will allow your user to persist other user-level systemd Side-effect: This will allow your user to persist other user-level systemd
services as well. For example `podman.socket` is handy to enable for services as well. For example `podman.socket` is handy to enable for
`podman remote` access. You could also [setup `podman remote` access. You could also
quadlet](https://www.redhat.com/sysadmin/quadlet-podman) or a systemd unit [setup quadlet](https://www.redhat.com/sysadmin/quadlet-podman) or a systemd
so pipglr starts up on system boot. unit so pipglr starts up on system boot.
#### Expanded user-namespace (step 2) ***This is probably important*** #### Expanded User-Namespace (step 2) **_This is probably important_**
As an added protection/safety measure, pipglr excludes three UID/GIDs As an added protection/safety measure, pipglr excludes three UID/GIDs from being
from being used by job-level containers. One for `root`, another for used by job-level containers. One for `root`, another for `runner` and a third
`runner` and a third for `podman`. However, some container images for `podman`. However, some container images you may want to use for jobs
you may want to use for jobs (mainly Debian/Ubuntu), assign one/more (mainly Debian/Ubuntu), assign one/more essential users a high UID/GID value
essential users a high UID/GID value (like `65535`). (like `65535`).
At the same time, most distributions also set `65536` as the default maximum At the same time, most distributions also set `65536` as the default maximum
number (including ID `0`) of IDs to allocate for user-namespaces (via `/etc/login.defs`). This number (including ID `0`) of IDs to allocate for user-namespaces (via
creates a problem you won't realize until the runner actually picks up a job `/etc/login.defs`). This creates a problem you won't realize until the runner
😞 The main symptom of this issue will be messages in the pipglr containers log, actually picks up a job. The main symptom of this issue will be messages in
similar to (abbreviated): the pipglr containers log, similar to (abbreviated):
``` ```text
...cut... ...cut...
running `/usr/bin/newuidmap ...cut...`: newuidmap: write to uid_map failed: Operation not permitted running `/usr/bin/newuidmap ...cut...`: newuidmap: write to uid_map failed: Operation not permitted
Error: cannot set up namespace using "/usr/bin/newuidmap": exit status 1 Error: cannot set up namespace using "/usr/bin/newuidmap": exit status 1
@@ -82,66 +87,71 @@ Error: cannot set up namespace using "/usr/bin/newuidmap": exit status 1
or or
``` ```text
E: setgroups 65534 failed - setgroups (22: Invalid argument) E: setgroups 65534 failed - setgroups (22: Invalid argument)
``` ```
***The good news is, working around this is relatively simple:*** **_The good news is, working around this is relatively simple:_**
As root, edit the two files `/etc/subuid` and `/etc/subgid` to expand the As root, edit the two files `/etc/subuid` and `/etc/subgid` to expand the by 3
by 3 IDs. For example assuming a user running the pipglr container is IDs. For example assuming a user running the pipglr container is called
called `johndoe`, the contents of these files should be edited to allocate `johndoe`, the contents of these files should be edited to allocate `65539` IDs
`65539` IDs like: like:
`jogndoe:<some number>:65539` `johndoe:<some number>:65539`
Where `<some number>` was set by your OS when the `johndoe` user was created Where `<some number>` was set by your OS when the `johndoe` user was created
(you can ignore this). Only the last number needs to be increased. (you can ignore this). Only the last number needs to be increased. This change
will be effective on next login, or immediately by running:
#### Runner registration (step 3) `podman system migrate`
All runners must be connected to a project or group runner configuration _Note:_ This will stop any currently running containers.
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.
Before using the `register` *runlabel*, you must set your unique #### Runner Registration (step 3)
*registration* (a.k.a. *activation*) token as a podman *secret*. This
secret may be removed once the registration step is complete. The All runners must be connected to a project or group runner configuration on your
**<actual registration token>** value (below) should be replaced with gitlab instance (or `gitlab.com`). This is done using a special registration
the value obtained from the "runners" settings page of a gitlab _runlabel_. The command can (and probably should) be run more than once (using
group or project's *CI/CD Settings*. Gitlab version 16 and later the same `config.toml`) to configure and register multiple runners. This is
refers to this value as an *activation* token, but the usage is the same. 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.
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 ```bash
$ IMAGE="registry.gitlab.com/qontainers/pipglr:latest" $ IMAGE="registry.gitlab.com/qontainers/pipglr:latest"
$ echo '<actual registration token>' | podman secret create REGISTRATION_TOKEN - $ echo '<actual registration token>' | podman secret create REGISTRATION_TOKEN -
``` ```
Next, ***a blank `config.toml` file*** needs to be created. Without this, the Next, **_a blank `config.toml` file_** needs to be created. Without this, the
`reigster` *runlabel* will return a permission-denied error. Once the empty `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 `config.toml` file is created, you may register one or more runners by repeating
the registration *runlabel* as follows: the registration _runlabel_ as follows:
```bash ```bash
$ IMAGE="registry.gitlab.com/qontainers/pipglr:latest" $ IMAGE="registry.gitlab.com/qontainers/pipglr:latest"
$ touch ./config.toml # important: file must exist, even if empty. $ touch ./config.toml # important: file must exist, even if empty.
$ podman container runlabel register $IMAGE $ podman container runlabel register $IMAGE
...repeat as desired... # ...repeat as desired...
$ podman secret rm REGISTRATION_TOKEN # if desired $ podman secret rm REGISTRATION_TOKEN # if desired
``` ```
#### Runner Configuration (step 4) #### Runner Configuration (step 4)
During the registration process (above), a boiler-plate (default) `config.toml` file During the registration process (above), a boiler-plate (default) `config.toml`
will be created/updated for you. At this point you may edit the configuration file will be created/updated for you. At this point you may edit the
if desired before committing it as a *podman secret*. Please refer to the configuration if desired before committing it as a _podman secret_. Please refer
[gitlab runner documentation](https://docs.gitlab.com/runner/configuration/) to the
for details. [gitlab runner documentation](https://docs.gitlab.com/runner/configuration/) for
details.
```bash ```bash
$ $EDITOR ./config.toml # if desired $ $EDITOR ./config.toml # if desired
@@ -150,15 +160,15 @@ $ rm ./config.toml # if desired
``` ```
This may be necessary, for example, to increase the default `concurrency` value 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 to reflect the number of registered runners. If you need to edit this file after
after committing it as a secret, there's committing it as a secret, there's
[ a `dumpconfig` *runlabel* for that](README.md#configuration-editing). [a `dumpconfig` _runlabel_ for that](README.md#configuration-editing).
#### Volume setup (step 5) #### Volume Setup (step 5)
Since several users are utilized inside the container volumes must be Since several users are utilized inside the container volumes must be
specifically configured to permit access. This is done using several specifically configured to permit access. This is done using several _runlabels_
*runlabels* as follows: as follows:
```bash ```bash
$ IMAGE="registry.gitlab.com/qontainers/pipglr:latest" $ IMAGE="registry.gitlab.com/qontainers/pipglr:latest"
@@ -166,16 +176,17 @@ $ podman container runlabel setupstorage $IMAGE
$ podman container runlabel setupcache $IMAGE $ podman container runlabel setupcache $IMAGE
``` ```
Note: These volumes generally do not contain any critical operational data, Note: These volumes generally do not contain any critical operational data, they
they may be re-created anytime to quickly free up host disk-space if may be re-created anytime to quickly free up host disk-space if it's running
it's running low. Simply remove them with the command low. Simply remove them with the command
`podman volume rm pipglr-storage pipglr-cache`. Then reuse the `setupstorage` `podman volume rm pipglr-storage pipglr-cache`. Then reuse the `setupstorage`
and `setupcache` *runlabels* as in the above example. and `setupcache` _runlabels_ as in the above example.
#### Runner Startup (step 6) #### Runner Startup (step 6)
With the runner configuration saved as a Podman secret, and the runner volumes 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: created, the GitLab runner container may be launched with the following
commands:
```bash ```bash
$ IMAGE="registry.gitlab.com/qontainers/pipglr:latest" $ IMAGE="registry.gitlab.com/qontainers/pipglr:latest"
@@ -184,11 +195,10 @@ $ podman container runlabel run $IMAGE
### Configuration Editing ### Configuration Editing
The gitlab-runner configuration contains some sensitive values which The gitlab-runner configuration contains some sensitive values which should be
should be protected. The pipglr container assumes the entire configuration protected. The pipglr container assumes the entire configuration will be passed
will be passed in as a Podman secret. This makes editing it slightly in as a Podman secret. This makes editing it slightly convoluted, so a handy
convoluted, so a handy *runlabel* `dumpconfig` is available. _runlabel_ `dumpconfig` is available. It's intended use is as follows:
It's intended use is as follows:
```bash ```bash
$ IMAGE="registry.gitlab.com/qontainers/pipglr:latest" $ IMAGE="registry.gitlab.com/qontainers/pipglr:latest"
@@ -201,17 +211,17 @@ $ rm ./config.toml # if desired
### Debugging ### Debugging
The first thing to check is the container output. This shows three things: The first thing to check is the container output. This shows three things:
Systemd, Podman, and GitLab-Runner output. For example: Systemd, Podman, and GitLab-Runner output. For example:
```bash ```bash
$ podman logs --since 0 pipglr $ podman logs --since 0 pipglr
``` ```
Next, try running a pipglr image built with more verbose logging. Both Next, try running a pipglr image built with more verbose logging. Both the
the `runner.service` and `podman.service` files have a `log-level` option. `runner.service` and `podman.service` files have a `log-level` option. Simply
Simply increase one or both to the "info", or "debug" level. Start the increase one or both to the `info`, or `debug` level. Start the debug container,
debug container, and reproduce the problem. and reproduce the problem.
## Building ## Building
@@ -221,37 +231,36 @@ This image may be built simply with:
$ podman build -t registry.gitlab.com/qontainers/pipglr:latest . $ 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
stable version of the gitlab runner. version of the gitlab runner.
### Build-args ### Build-Arguments
Several build arguments are available to control the output image: Several build arguments are available to control the output image:
* `PRUNE_INTERVAL` - A systemd.timer compatible `OnCalendar` value that - `PRUNE_INTERVAL`: A systemd.timer compatible `OnCalendar` value that
determines how often to prune Podman's storage of disused containers and determines how often to prune Podman's storage of disused containers and
images. Defaults to "daily", but should be adjusted based on desired images. Defaults to `daily`, but should be adjusted based on desired
caching-effect balanced against available storage space and job caching-effect balanced against available storage space and job execution
execution rate. rate.
* `RUNNER_VERSION` - Allows specifying an exact gitlab runner version. - `RUNNER_VERSION`: Allows specifying an exact gitlab runner version. By default
By default the `latest` is used, assuming the user is building a tagged the `latest` is used, assuming the user is building a tagged image anyway.
image anyway. Valid versions may be found on the [runner Valid versions may be found on the
release page](https://gitlab.com/gitlab-org/gitlab-runner/-/releases). [runner release page](https://gitlab.com/gitlab-org/gitlab-runner/-/releases).
* `TARGETARCH` - Supports inclusion of non-x86_64 gitlab runners. This - `TARGETARCH`: Supports inclusion of non-x86_64 gitlab runners. This value is
value is assumed to match the image's architecture. If using the assumed to match the image's architecture. If using the `--platform` build
`--platform` build argument, it will be set automatically. Note: argument, it will be set automatically. Note: as of this writing, only `amd64`
as of this writing, only `amd64` and `arm64` builds of the gitlab-runner and `arm64` builds of the gitlab-runner are available.
are available. - `GITLAB_URL`: Defaults to `https://gitlab.com/` but can be set to point to a
* `NESTED_PRIVILEGED` - Defaults to 'true', may be set 'false' to prevent self hosted instance of Gitlab.
nested containers running in `--privileged` mode. This will affect - `NESTED_PRIVILEGED`: Defaults to `true`, may be set `false` to prevent nested
the ability to build container images in CI jobs using tools like containers running in `--privileged` mode. This will affect the ability to
podman or buildah. build container images in CI jobs using tools like podman or buildah.
### Environment variables ### Environment Variables
Nearly every option to every gitlab-runner sub-command may be specified via Nearly every option to every gitlab-runner sub-command may be specified via
environment variable. Some of these are set in the `Containerfile` for environment variable. Some of these are set in the `Containerfile` for the
the `register` *runlabel*. If you need to set additional runtime `register` _runlabel_. If you need to set additional runtime env. vars., please
env. vars., please do so via additional `Environment` optionns in the do so via additional `Environment` optionns in the `runner.service` file. See
`runner.service` file. See the *systemd.nspawn* man page for important the _systemd.nspawn_ man page for important value-format details.
value-format details.

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash
#
# This script is intended to be run during container-image build. Any # This script is intended to be run during container-image build. Any
# other usage outside this context is likely to cause harm. # other usage outside this context is likely to cause harm.
# #
@@ -20,117 +21,185 @@
set -eo pipefail set -eo pipefail
for varname in PRUNE_INTERVAL RUNNER_VERSION TARGETARCH; do function die() {
if [[ -z "${!varname}" ]]; then echo -n '!! ERROR:' >&2
echo "Error: \$$varname must be non-empty." printf " %s\n" "$@" >&2
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
# WORKAROUND: crun: write to `/proc/self/oom_score_adj`: Permission denied
# Ref: https://github.com/containers/podman/pull/19843
if [[ $(date +%Y%m%d) -gt 20231115 ]]; then
echo "FIXME: Please check if crun 1.9.3+ is available in CentOS Stream-9."
echo "If so, this crun-1.8.7 workaround may be removed."
exit 1 exit 1
fi }
dnf $x install -y crun-1.8.7
$DNF $x install \ function check_vars() {
podman \ for varname in PRUNE_INTERVAL RUNNER_VERSION TARGETARCH; do
systemd if [[ -z "${!varname}" ]]; then
die "Env. variable '$varname' must be non-empty."
fi
done
}
function main() {
# Show what's happening to make debugging easier
set -x
# Make image smaller by not installing docs.
dnf=(dnf --setopt=tsflags=nodocs -y)
install_packages
setup_user
setup_service_podman
setup_service_runner
setup_gitlab_config
finalize_ownership
}
function install_packages() {
readarray xpackages < <(grep -vE '^(# )+' </root/xpackages.txt)
local exclude_args=()
for rpm in "${xpackages[@]}"; do
exclude_args+=("--exclude=$rpm")
done
# DNF itself or a dependence may need upgrading, take care of it first.
"${dnf[@]}" upgrade &&
"${dnf[@]}" "${exclude_args[@]}" install \
podman \
systemd
# Gitlab-runner package contains scriptlets which do not function properly inside a
# container-build environment where systemd is not active/running.
if [[ ${ENABLE_FIPS} == true && $(cat /proc/sys/crypto/fips_enabled) == 1 ]]; then
PACKAGE_FILE="gitlab-runner_${TARGETARCH}-fips.rpm"
else
PACKAGE_FILE="gitlab-runner_${TARGETARCH}.rpm"
fi
"${dnf[@]}" "${exclude_args[@]}" \
--setopt=tsflags=noscripts install \
"https://gitlab-runner-downloads.s3.amazonaws.com/$RUNNER_VERSION/rpm/${PACKAGE_FILE}"
# 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 # Allow removing dnf, sudo, etc. packages. Also don't start unnecessary or broken
# systemd services, like anything kernel related or login gettys. # systemd services, like anything kernel related or login gettys.
rm -rf \ rm -rf \
/etc/dnf/protected.d/* \ /etc/dnf/protected.d/* \
/etc/sytemd/system/getty.target.wants/* \ /etc/systemd/system/getty.target.wants/* \
/etc/sytemd/system/multi-user.target.wants/* \ /etc/systemd/system/multi-user.target.wants/* \
/etc/sytemd/system/sysinit.target.wants/* \ /etc/systemd/system/sysinit.target.wants/* \
/etc/sytemd/system/timers.target.wants/* \ /etc/systemd/system/timers.target.wants/* \
/lib/systemd/system/graphical.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/multi-user.target.wants/{getty.target,systemd-ask-password-wall.path} \
/lib/systemd/system/sys-kernel*.mount /lib/systemd/system/sys-kernel*.mount
# Remove unnecessary packages, see xpackages.txt to learn how this list was generated. # Allow removing dnf, sudo, etc. packages.
# This makes the image smaller and reduces the attack-surface. rm -rf \
dnf remove -y $(egrep -v '^(# )+' /root/xpackages.txt) /etc/dnf/protected.d/*
# 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 "${xpackages[@]}"
# Wipe out the DNF cache, then remove it entirely, again to make the image smaller. # Wipe out the DNF cache, then remove it entirely, again to make the image smaller.
$DNF clean all "${dnf[@]}" clean all
rm -rf /var/cache/dnf /var/log/dnf* /var/log/yum.* rm -rf /var/cache/dnf /var/log/dnf* /var/log/yum.*
rpm -e dnf rpm -e dnf
# Workaround https://bugzilla.redhat.com/show_bug.cgi?id=1995337 # Workaround base-image failing to confer capabilties properly on
rpm --setcaps shadow-utils # /usr/bin/new{u,g}idmap to `cap_set{u,g}id=ep` in new image layers.
rpm --setcaps shadow-utils
}
# Prevent copying of skel since it can interfere with the gitlab-runner function setup_user() {
mkdir -p /home/podman /home/runner # Guarantee uid/gid 1000 for user 'podman' / 1001 for user 'runner'.
# Guarantee uid/gid 1000 for user 'podman' / 1001 for user 'runner'. groupadd -g 1000 podman
groupadd -g 1000 podman groupadd -g 1001 runner
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 # Separate users for services to increase process isolation.
# Split the namespaced ID's around the containers root (ID 0), podman (ID 1000), and # The 'podman' user's socket service writes /home/runner/podman.socket
# runner (ID 1001) such that the user-namespace of any nested containers cannot # Prevent copying of skel since it can interfere with the gitlab-runner
# read or write any files owned by these users (and/or hijack nested container processes). mkdir -p /home/podman /home/runner
# N/B: The range-end (999+64536) ensures a total of 65535 IDs are available for nested-containers. useradd -M -u 1000 -g podman -G runner podman
# This requires the host provide a sufficiently large range, i.e. `pipglr:<start>:65539` useradd -M -u 1001 -g runner runner
echo -e "podman:1:999\npodman:1002:64536" | 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. # Allow only podman in `/home/podman`.
mkdir -p /var/lib/systemd/linger chmod 700 /home/podman
touch /var/lib/systemd/linger/podman # Allow 'podman' user to create socket file under `/home/runner`.
# Setup 'podman' socket and a container-storage pruning service for 'podman' user. chmod 770 /home/runner
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. # Set permissions.
touch /var/lib/systemd/linger/runner chown -R runner:runner /home/runner
mkdir -p /home/runner/.config/systemd/user/default.target.wants chown -R podman:podman /home/podman
cd /home/runner/.config/systemd/user/
# Does not depend on podman.socket file availablility, will retry if not present. # Overwrite defaults, only user 'podman' permitted to have a user-namespace
ln -s $PWD/runner.service ./default.target.wants/ # Split the namespaced ID's around the containers root (ID 0), podman (ID 1000), and
# gitlab-runner will create side-car '.runner_system_id' file next to 'config.toml' # runner (ID 1001) such that the user-namespace of any nested containers cannot
# on first startup. Ensure access is allowed. Also link to future config file # read or write any files owned by these users (and/or hijack nested container processes).
# presented as a container-secret. # N/B: The range-end (999+64536) ensures a total of 65535 IDs are available for nested-containers.
mkdir -p /home/runner/.gitlab-runner # This requires the host provide a sufficiently large range, i.e. `pipglr:<start>:65539`
ln -s /var/run/secrets/config.toml /home/runner/.gitlab-runner/config.toml echo -e "podman:1:999\npodman:1002:64536" | tee /etc/subuid >/etc/subgid
# Containerfile ADD instruction does not properly set ownership/permissions. }
chown -R runner:runner /home/runner
chmod -R 700 /home/runner/.gitlab-runner function setup_volumes() {
# 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
}
function setup_service_podman() {
# 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
}
function setup_service_runner() {
# Setup persistent 'runner' user services to start & run without a login.
touch /var/lib/systemd/linger/runner
# Setup persistent 'runner' user services to start & run without a login.
mkdir -p /home/runner/.config/systemd/user/default.target.wants
cd /home/runner/.config/systemd/user/
# Does not depend on podman.socket file availability, will retry if not present.
ln -s "$(pwd)/runner.service" ./default.target.wants/
}
function setup_gitlab_config() {
# 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
chmod -R 700 /home/runner/.gitlab-runner
}
function finalize_ownership() {
# Adjust ownership to all files created after `setup_user`.
# and also to the `ADD` instruction in the `Containerfile`.
chown -R runner:runner /home/runner
chown -R podman:podman /home/podman
# Ensure correct permissions of system configuration files.
# Somehow these can be set incorrectly during Containerfile
# ADD instruction.
local path
for path in "/etc/systemd/system.conf.d" "/etc/systemd/system/user-.slice.d"; do
chown root:root ${path}/*
chmod 0644 ${path}/*
done
}
check_vars
main