Non-Root Jacamar CI Downscoping (with Capabilities) via SetUID
Jacamar CI offers a robust series of configurations that seek to support advanced CI/CD workflows on HPC test resources. As part of these workflows the authorization process enables optional downscoping that limits permissions to that of the pipeline trigger user’s local account. Traditionally this would require the usage of root; however, with recent improvements we now support downscoping by leveraging Linux capabilities. This guide details how you can potentially deploy, configure, and run Jacamar CI in such a manner.
Note
If you’ve never used Jacamar CI or the GitLab Custom Executor before we highly encourage you to first explore the Administrative Tutorial.
Deployment
To begin the deployment process you will require usage of a privileged account.
Obtain the latest Jacamar CI RPMs and runner release.
With Jacamar CI release packages there is an optional deployment with configuration already enabled for use with the
gitlab-runner
account (“RPM - w/Capabilities”). This guide details configuring both.
Install both RPMs and any necessary dependencies (
sudo rpm -i ...
).
Note
Release v0.9.0 relocated all RPM installed binaries into a single
location, /opt/jacamar/bin
. This will offer a better standard moving
forward, please be aware of this when upgrading from an older version.
Switch to the non-root user account (e.g.,
gitlab-runner
) and register the runner with your target GitLab server:
gitlab-runner register
There are non-interactive options as well external tools (e.g., LLNL/gitlab-runner-auth) that can be used to manage the registration process.
Important
You will need a service account that will take responsibility for both the
gitlab-runner
and jacamar-auth
processes as well as
configurations. For the purposes of this guide we are using a
gitlab-runner
user account, if you’ve deployed the runner with the
official release this
service account likely already exists.
Configurations
Configure both the Runner and Jacamar CI in
a mostly traditional manner; however, special care must be given
to the location these files are stored. Our gitlab-runner
user
must be able to read the files and sensitive tokens/configurations
can be found in both. To accomplish this we are going to keep
both configuration in the user’s home directory
(/home/gitlab-runner/.gitlab-runner
) but you are free
to store wherever easiest.
Runner
Edit
/home/gitlab-runner/.gitlab-runner/config.toml
generated during registration to add the appropriate options to the[runners.custom]
table:concurrent = 5 check_interval = 0 [session_server] session_timeout = 1800 [[runners]] name = "Jacamar CI Cap Testing" url = "https://gitlab.example.com/" token = "<RUNNER-TOKEN>" executor = "custom" [runners.custom] config_exec = "/opt/jacamar/bin/jacamar-auth" config_args = ["config", "--configuration", "/home/gitlab-runner/.gitlab-runner/jacamar-config.toml"] prepare_exec = "/opt/jacamar/bin/jacamar-auth" prepare_args = ["prepare"] run_exec = "/opt/jacamar/bin/jacamar-auth" run_args = ["run"] cleanup_exec = "/opt/jacamar/bin/jacamar-auth" cleanup_args = ["cleanup", "--configuration", "/home/gitlab-runner/.gitlab-runner/jacamar-config.toml"]
Jacamar CI
Create the
/home/gitlab-runner/.gitlab-runner/jacamar-config.toml
we referenced in our Runner configuration:
[general] executor = "shell" data_dir = "$HOME" [auth] downscope = "setuid"
Please note we’ve demonstrated a very minimal configuration for the purposes of managing jobs under a non-root user. Please reference Jacamar CI’s configuration documentation for a complete list of potential options.
System Service
Returning to a privileged account:
Identify if the default runner process is installed,
sudo systemctl status gitlab-runner
If missing use the runner to install a system service.
Important, avoid having multiple runner services active on the same machine, this can lead to unexpected results.
Edit the
/etc/systemd/system/gitlab-runner.service
file:
[Unit] Description=GitLab Runner using Jacamar CI with Capabilities After=syslog.target network.target ConditionFileIsExecutable=/usr/bin/gitlab-runner [Service] StartLimitInterval=5 StartLimitBurst=10 ExecStart=/usr/bin/gitlab-runner "run" "--syslog" "--working-directory" "/opt/jacamar" "--config" "/home/gitlab-runner/.gitlab-runner/config.toml" "--service" "gitlab--runner" "--user" "gitlab-runner" Restart=always RestartSec=120 User=gitlab-runner Group=gitlab-runner [Install] WantedBy=multi-user.targetIt is important to target the
gitlab-runner
account (usingUser
andGroup
) as both thegitlab-runner
andjacamar-auth
applications will execute under the same user. $ getcap /opt/jacamar/bin/jacamar-auth /opt/jacamar/bin/jacamar-auth cap_setgid,cap_setuid=ep
Edit the
/etc/systemd/system/gitlab-runner.service
file:
[Unit] Description=GitLab Runner using Jacamar CI with Capabilities After=syslog.target network.target ConditionFileIsExecutable=/usr/bin/gitlab-runner [Service] StartLimitInterval=5 StartLimitBurst=10 ExecStartPre=+bash -c '/usr/bin/chown gitlab-runner:gitlab-runner /opt/jacamar/bin/jacamar-auth && /usr/bin/chmod 700 /opt/jacamar/bin/jacamar-auth && /usr/sbin/setcap cap_setuid,cap_setgid=ep /opt/jacamar/bin/jacamar-auth' ExecStart=/usr/bin/gitlab-runner "run" "--syslog" "--working-directory" "/opt/jacamar" "--config" "/home/gitlab-runner/.gitlab-runner/config.toml" "--service" "gitlab--runner" "--user" "gitlab-runner" Restart=always RestartSec=120 User=gitlab-runner Group=gitlab-runner [Install] WantedBy=multi-user.targetIt is important to target the
gitlab-runner
account (usingUser
andGroup
) as both thegitlab-runner
andjacamar-auth
applications will execute under the same user.By declaring the
=+
in ourExecStartPre
it will run the subsequent command asroot
as opposed to thegitlab-runner
user. This allows for privileged actions (e.g., setting file ownership and capabilities).
Note
If you are running a versions of systemd older than 240 you will need to use the
PermissionsStartOnly
flag as opposed to=+
[Service]
...
PermissionsStartOnly=true
ExecStartPre=bash -c '...
Restart and verify service is running:
sudo systemctl disable gitlab-runner.service sudo systemctl enable gitlab-runner.service sudo systemctl restart gitlab-runner.service sudo systemctl status gitlab-runner.service
Examine Process
Finally, with the runner polling for jobs we advise creating test cases
(.gitlab-ci.yml
)to verify desired functionality.
ci-job:
script:
- id
- /sbin/capsh --print
- sleep 120
Our example job is just a simple look that demonstrates the user executing the CI job locally, ensuring the capabilities were not improperly set (inherited), and providing sufficient time during which you can more closely examine the locally running processes:
$ ps -aef --forest
UID PID PPID C STIME TTY TIME CMD
...
gitlab-+ 1053 1 0 15:40 ? 00:00:06 /usr/bin/gitlab-runner run --syslog --working-directory /opt/jacamar --config /home/gitlab-runner/.gitlab-runner/config.toml
gitlab-+ 2517 1053 0 17:20 ? 00:00:00 \_ /opt/jacamar/bin/jacamar-auth -u run /tmp/custom-executor440226574/script347499535/script. build_script
usr 2523 2517 0 17:20 ? 00:00:00 \_ /usr/bin/jacamar --no-auth run env-script build_script
usr 2528 2523 0 17:20 ? 00:00:00 \_ /usr/bin/bash --login
usr 2553 2528 0 17:20 ? 00:00:00 \_ bash /home/usr/.jacamar-ci/scripts/short/000/group/project/981625/build_script.bash
usr 2555 2553 0 17:20 ? 00:00:00 \_ bash /home/usr/.jacamar-ci/scripts/short/000/group/project/981625/build_script.bash
usr 2559 2555 0 17:20 ? 00:00:00 \_ sleep 120
We encourage you to take whatever steps you feel sufficient to ensure your deployment meets expectations beyond even what we show here. However, at this point you are able to expand utilization or explore additional configuration options.