Skip to content

Latest commit

 

History

History
467 lines (372 loc) · 21 KB

REFERENCE.md

File metadata and controls

467 lines (372 loc) · 21 KB

Reference

Table of Contents

Classes

Public Classes

Private Classes

  • rootless_gitlab_runner::apt_repos: Adds the apt repositories the packages.install list installs from.
  • rootless_gitlab_runner::config: Renders the runner configuration, the no-detach-netns drop-in and the secret-store directory.
  • rootless_gitlab_runner::packages: Installs the packages listed under packages.install.
  • rootless_gitlab_runner::rootless_docker: Brings up the rootless docker user daemon behind a fail-loud preflight.
  • rootless_gitlab_runner::self_update: Installs the standalone apply script and liveness healthcheck, plus the optional self-update loop.
  • rootless_gitlab_runner::service: Manages the runner system service and its privilege-drop drop-in.
  • rootless_gitlab_runner::user: Owns the runner group, user and home.

Functions

Data types

Classes

rootless_gitlab_runner

Renders the runner configuration file (config.toml) from a Hiera-driven list of runners, manages the rootless-docker "no-detach-netns" systemd user drop-in and the secret-store directory, installs the apt packages listed in packages.install, and optionally: owns the runner account, brings up the rootless docker user daemon (with the subordinate ID ranges it needs) behind a fail-loud preflight, manages the runner service together with its privilege-drop drop-in, and installs the standalone apply script with the optional self-update loop. Designed to run standalone via puppet apply with an isolated confdir/vardir so it never collides with a central Puppet agent.

Runner tokens are never stored in this module or in version control. They are looked up at apply time from an off-repository secret store via the runner_tokens parameter, keyed by each runner's token_key.

Every parameter default lives in the module data layer (data/common.yaml), and each struct parameter carries a deep-merge lookup rule, so consumer data holds only deviations: a partial hash is merged over the module defaults on lookup. The documented consumption pattern is Hiera plus include rootless_gitlab_runner; a resource-style declaration bypasses Hiera merging and must pass complete struct hashes.

The manage keys are persistent ownership switches, not one-shot bootstrap flags: set once per host and left on, so every apply keeps owning and drift-correcting that concern. false means hands-off, not ensure-off. A manage key decides whether the module creates and enforces its concern's resources; most struct keys are consumed only by their own concern (so they are inert while its manage is false), while runner_account's identity keys are shared inputs every concern reads.

Examples

Greenfield standalone host (everything on), as Hiera node data
rootless_gitlab_runner::runners:
  - name: 'docker-rootless'
    url: 'https://gitlab.example.org/'
    executor: 'docker'
    image: 'ubuntu:22.04'
    token_key: 'runner_a'
rootless_gitlab_runner::runner_account:
  manage: true
  uid: 2000
rootless_gitlab_runner::packages:
  install: ['uidmap', 'dbus-user-session', 'docker-ce', 'docker-ce-cli',
            'docker-ce-rootless-extras', 'containerd.io', 'gitlab-runner']
  sources:
    manage: true
rootless_gitlab_runner::rootless_docker:
  manage: true
rootless_gitlab_runner::runner_service:
  manage: true
rootless_gitlab_runner::standalone:
  manage: true

Parameters

The following parameters are available in the rootless_gitlab_runner class:

concurrent

Data type: Integer[1]

Global concurrent value written to the runner configuration file: the name is GitLab Runner's own config.toml global key, written unchanged. Default 1.

runners

Data type: Array[Hash]

Ordered list of runner definitions, rendered as the [[runners]] sections of config.toml. Each entry is a hash; recognised keys: name, url, id (Integer), executor, token_key, host, image, privileged (Boolean), tls_verify (Boolean), socket_mount (Boolean), volumes (Array[String]), security_opt (Array[String]), environment (Array[String]), comment (String), helper_image (String), disable_entrypoint_overwrite (Boolean), oom_kill_disable (Boolean), disable_cache (Boolean), shm_size (Integer), network_mtu (Integer), cache (Hash — its presence renders the [runners.cache] tables; optional key MaxUploadedArchiveSize (Integer, default 0)), allowed_images (Array[String]), allowed_pull_policies (Array[String]). Tokens are merged in from runner_tokens[token_key] and must not appear here. host is a passthrough for GitLab Runner's own [runners.docker] host setting, pointing one runner's jobs at a specific daemon socket; the managed runner service's own DOCKER_HOST is derived from the uid and set independently of it.

runner_defaults

Data type: Hash

Hash merged under every runners entry ($runner_defaults + $runner, keys set on the entry win), so multi-runner data does not repeat url, image, executor and friends. Recognised keys as in runners.

runner_tokens

Data type: Sensitive[Hash[String, String]]

Sensitive map of token_key => runner token, supplied by the off-repository secret store. Typed Sensitive so the value is redacted in the catalog and reports; the module ships lookup_options (convert_to: Sensitive) so a plain-YAML or hiera-eyaml secret store is wrapped automatically on lookup — consumers write ordinary Hiera data. Empty by default so a checkout without secrets renders blank tokens. When non-empty, every runner must carry a token_key that resolves here or the apply fails (typo/missed-provisioning guard). The rendered configuration content is Sensitive too, so tokens never reach the compiled catalog, reports, or --show_diff output.

secret_store_path

Data type: Stdlib::Absolutepath

Path of the off-repository secret store directory. The directory itself is managed (root-owned, 0700); the secret file inside it is host-provisioned and never managed. Default /etc/gitlab-runner-infra.

configuration_file

Data type:

Struct[{
    path => Stdlib::Absolutepath,
  }]

The rendered runner configuration file (GitLab Runner's config.toml). Owner derives from runner_account.name and group from runner_account.group (which defaults to the name); the mode is fixed 0600 (the file carries the runner tokens).

Options:

  • :path Stdlib::Absolutepath: Where the file is written. Default /etc/gitlab-runner/config.toml.
runner_account

Data type:

Struct[{
    manage => Boolean,
    name   => Rootless_gitlab_runner::Username,
    group  => Optional[Rootless_gitlab_runner::Username],
    uid    => Optional[Integer[1]],
    home   => Stdlib::Absolutepath,
  }]

The OS account the runner manager and the rootless docker daemon run as. The identity keys are read by every concern even when manage: false (socket derivation, file ownership, service ExecStart); the toggle only decides whether the module creates and enforces the group, user, and home.

Options:

  • :manage Boolean: Whether to manage the runner group, user, and home. Keep off where another configuration-management system owns the account. The subordinate UID/GID ranges rootless docker needs are owned by rootless_docker.manage. Default false.
  • :name Rootless_gitlab_runner::Username: Username of the runner account. Default gitlab-runner.
  • :group Optional[Rootless_gitlab_runner::Username]: Name of the account's primary group. Defaults to the account name (the data layer cannot express that default, so an unset key falls back to name in code). Set it for an externally provisioned account whose primary group is named differently (e.g. account ci-worker, group ci): it feeds every group ownership the module manages — the group resource and the user's primary group where manage is on, and the group of the runner configuration file, its directory, and the account's systemd user tree.
  • :uid Optional[Integer[1]]: Numeric uid of the runner account. No default: the uid is host data, not something a module can sensibly invent. It derives the rootless runtime paths (/run/user/<uid>, the docker socket) and is enforced on the user when runner_account.manage is on; the apply fails loud when it is unset but needed — with runner_account.manage, rootless_docker.manage, runner_service.manage or standalone.self_update.manage on, or to derive the docker socket path for a socket_mount runner.
  • :home Stdlib::Absolutepath: Home directory of the runner account. Default /home/gitlab-runner.
packages

Data type:

Struct[{
    install => Array[String[1]],
    sources => Struct[{
      manage        => Boolean,
      docker        => Rootless_gitlab_runner::Apt_source,
      gitlab_runner => Rootless_gitlab_runner::Apt_source,
    }],
  }]

The apt-packages concern: what to install, and the apt sources serving it.

Options:

  • :install Array[String[1]]: Packages to ensure installed. The empty default installs nothing. Install only: the module never removes packages absent from the list and never pins or upgrades.
  • :sources Struct[{ manage => Boolean, docker => Apt_source, gitlab_runner => Apt_source }]: The apt sources the install list installs from (Docker's and GitLab Runner's, via puppetlabs/apt). sources.manage (default false) decides ownership: keep it off where apt sources are owned elsewhere (central configuration management, e.g. Foreman/Katello, or a mirror). The docker and gitlab_runner sub-hashes each carry location and key_source — verbatim apt::source parameter names — overridden only for a mirror; the defaults point at the vendors' repositories and their rolling signing-key endpoints.
rootless_docker

Data type:

Struct[{
    manage      => Boolean,
    subid_start => Integer[1],
    subid_count => Integer[65536],
  }]

The rootless docker runtime: "rootless mode" is Docker's own name for running the daemon unprivileged.

Options:

  • :manage Boolean: Whether to bring up the rootless docker user daemon: provision the subordinate UID/GID ranges and enforce subid_count as a grow-only minimum width (a module-owned range narrower than declared is widened in place and the rootless daemon restarted; a foreign or already-wider range is never rewritten, and the runner account may be owned elsewhere), enable lingering and run dockerd-rootless-setuptool.sh install (guarded, as the runner user), behind a fail-loud preflight that asserts the prerequisites instead of half-installing. Also stops and masks the rootful system docker.service/docker.socket and containerd.service, which a fresh docker-ce install starts as root, so the only Docker daemon on the host is the unprivileged one. cgroup-v2 controller delegation is deliberately not managed (it would be the module's only system-wide write); without it CPU/IO job limits are silently unenforced — see the README Limitations. Default false.
  • :subid_start Integer[1]: First subordinate UID/GID allocated to the runner account (field two of the /etc/subuid entry format, per subid(5)). Default 231072. shadow's own auto-allocation hands out 65536-wide ranges from 100000, so the third auto-allocated user on a host starts at 231072; a range overlapping another user's is warned about at compile time.
  • :subid_count Integer[65536]: Number of subordinate UIDs/GIDs allocated (field three of the entry; rootless docker needs at least 65536), enforced as a grow-only minimum width. Default 165536, which also covers BuildKit's rootless image layout.
runner_service

Data type:

Struct[{
    manage           => Boolean,
    environment      => Optional[Array[Pattern[/\A[^\r\n]+\z/]]],
    timeout_stop_sec => Optional[Variant[Integer[0], String[1]]],
  }]

The systemd system service running the runner manager, privilege-dropped to the runner account.

Options:

  • :manage Boolean: Whether to manage the runner system service, its privilege-drop systemd drop-in, and the configuration directory's mode so a privilege-dropped manager can traverse to its configuration file. Default false.
  • :environment Optional[Array[Pattern[/\A[^\r\n]+\z/]]]: Additional Environment= lines (KEY=value) rendered into the service drop-in, alongside the module-owned DOCKER_HOST line. DOCKER_HOST itself is derived from runner_account.uid and cannot be set here: a DOCKER_HOST line fails the compile, and a runner whose jobs need a different daemon uses the per-runner host key. The Pattern enforces the type: each line must be non-empty and single-line, so a value containing a newline is rejected and cannot inject an extra systemd directive into the drop-in.
  • :timeout_stop_sec Optional[Variant[Integer[0], String[1]]]: TimeoutStopSec= written into the manager service drop-in — how long systemd waits for a graceful drain before escalating to SIGKILL. Unset by default, so systemd's DefaultTimeoutStopSec (typically 90s) applies; set it to the longest job a drain should wait for (GitLab's documented example is 7200). Accepts a seconds integer or a systemd time span (e.g. 2h).
standalone

Data type:

Struct[{
    manage                    => Boolean,
    control_repository_path   => Stdlib::Absolutepath,
    control_repository_branch => String[1],
    puppet_confdir            => Stdlib::Absolutepath,
    puppet_vardir             => Stdlib::Absolutepath,
    puppet_bindir             => Stdlib::Absolutepath,
    healthcheck_interval      => String[1],
    self_update               => Struct[{
      manage         => Boolean,
      apply_interval => String[1],
      apply_timeout  => String[1],
    }],
  }]

The standalone topology: a host that applies itself via puppet apply, as opposed to a fleet host deployed by a Puppet server — with the optional self-update loop nested inside.

Options:

  • :manage Boolean: Declares the host standalone: installs the apply script (/usr/local/sbin/rootless-gitlab-runner-apply, the single definition of the apply command for timer-driven and manual runs) and the liveness healthcheck (manager service, rootless docker daemon), whether or not the self-update loop is enabled. Default false.
  • :control_repository_path Stdlib::Absolutepath: Root-owned checkout of the control repository on the host (the apply and self-update target). The apply's manifest, module directory, Hiera configuration and optional Puppetfile derive strictly from the documented layout beneath it (puppet/manifests/site.pp, puppet/modules, puppet/hiera.yaml, and Puppetfile at the root — r10k installs it each tick when present). Default /opt/gitlab-runner-infra.
  • :control_repository_branch String[1]: Branch the self-update loop follows (protected, signed). Default main.
  • :puppet_confdir Stdlib::Absolutepath: Isolated Puppet confdir for the apply — never the central agent's. Default /etc/gitlab-runner-infra/puppet.
  • :puppet_vardir Stdlib::Absolutepath: Isolated Puppet vardir for the apply — never the central agent's. Default /var/lib/grunner-puppet.
  • :puppet_bindir Stdlib::Absolutepath: Directory holding the puppet (and typically r10k) executables, prepended to the self-update service's PATH so the timer-driven, non-login apply can find them. Default /opt/puppetlabs/bin; the AIO-reuse topology sets /opt/puppetlabs/puppet/bin (gem executables live there, not in the symlink farm).
  • :healthcheck_interval String[1]: systemd time span between liveness healthcheck runs. Default 15min.
  • :self_update Struct[{ manage => Boolean, apply_interval => String[1], apply_timeout => String[1] }]: The self-update loop: manage (default false) installs a oneshot service + timer that fetch the control repository, verify the commit signature, reset to the remote branch, run r10k puppetfile install and re-apply. It also layers the loop-supervision checks (apply-timer state, checkout staleness, bootstrap-gem presence) into the healthcheck script, which itself installs with standalone.manage. Only valid on a standalone host: enabling it with standalone.manage off fails at compile time. Never enable it where a Puppet server or r10k already deploys the host. apply_interval (default 5min) is the timer's cadence; apply_timeout (default 15min) the apply service's TimeoutStartSec= (a oneshot unit has no default start timeout).

Functions

rootless_gitlab_runner::toml_escape

Type: Puppet Language

Every field the runner config interpolates comes from review-gated Hiera data, but a stray double-quote, backslash or newline would otherwise produce invalid TOML — or, with a crafted value, inject an unintended key (a newline in a comment could open privileged = true). Escaping every interpolated field closes that gap regardless of the data's provenance, per the TOML basic-string rules (https://toml.io/en/v1.0.0#string): backslash and double-quote are backslash-escaped, and the control characters that must not appear literally (newline, carriage return, tab) become their \n / \r / \t escapes.

Backslash is escaped first so the backslashes introduced by the later substitutions are not doubled again.

rootless_gitlab_runner::toml_escape(String $value)

Every field the runner config interpolates comes from review-gated Hiera data, but a stray double-quote, backslash or newline would otherwise produce invalid TOML — or, with a crafted value, inject an unintended key (a newline in a comment could open privileged = true). Escaping every interpolated field closes that gap regardless of the data's provenance, per the TOML basic-string rules (https://toml.io/en/v1.0.0#string): backslash and double-quote are backslash-escaped, and the control characters that must not appear literally (newline, carriage return, tab) become their \n / \r / \t escapes.

Backslash is escaped first so the backslashes introduced by the later substitutions are not doubled again.

Returns: String the escaped string, safe to place between double quotes in TOML

value

Data type: String

the raw string to escape

Data types

Rootless_gitlab_runner::Apt_source

location and key_source are verbatim apt::source parameter names: the repository URL (suite = OS codename) and the URL of its armored signing key, stored as an apt keyring. Both the Docker and GitLab Runner sources under packages.sources share this shape, pointing at a vendor repository or a mirror.

Alias of

Struct[{
  location   => Stdlib::HTTPUrl,
  key_source => Stdlib::HTTPUrl,
}]

Rootless_gitlab_runner::Username

The runner user is interpolated raw into shell execs (the subuid/subgid usermod execs and the preflight awk in rootless_docker.pp) and into systemd directives. Constraining it to the shadow-utils NAME_REGEX shape — a lowercase or underscore start, then lowercase letters, digits, underscore or hyphen, up to 32 characters — excludes shell-hostile characters (quotes, spaces, $, ;, newlines) by construction, so those interpolations cannot be broken out of. $ (the Samba machine-account suffix) is deliberately excluded as shell-hostile.

Alias of Pattern[/\A[a-z_][a-z0-9_-]{0,31}\z/]