Skip to content

Instantly share code, notes, and snippets.

@setdjod
Last active March 5, 2026 15:08
Show Gist options
  • Select an option

  • Save setdjod/49466b59688c25e748ba1d0bbd0d0834 to your computer and use it in GitHub Desktop.

Select an option

Save setdjod/49466b59688c25e748ba1d0bbd0d0834 to your computer and use it in GitHub Desktop.
GitLab Runner Authorization Hook

GitLab Runner Authorization Hook

Simple security layer for GitLab Runner using a pre-build hook to restrict which users and projects are allowed to run CI jobs.

The runner will fetch a remote allowlist configuration and validate:

  • GITLAB_USER_ID
  • CI_PROJECT_ID

If the user or project is not listed, the job will fail immediately before the build starts.


How It Works

  1. GitLab Runner executes pre-build.sh before running the CI job.
  2. The script downloads a remote configuration (allowed-list.conf).
  3. The configuration defines:
    • allowed GitLab user IDs
    • allowed project IDs
  4. The script checks:
    • GITLAB_USER_ID
    • CI_PROJECT_ID
  5. If either is not allowed → the job exits with error.

File Structure

.
├── allowed-list.conf
└── pre-build.sh

allowed-list.conf

Contains the allowlist configuration.

Example:

ALLOWED_USER_IDS=(
  7
  8
  64
)

ALLOWED_PROJECT_IDS=(
  632
  718
  830
)

You can store this file anywhere (GitHub Gist, internal repo, etc) and load it remotely.


pre-build.sh

GitLab Runner hook that:

  • Downloads the configuration
  • Loads the allowlist
  • Validates the pipeline context

Key logic:

CONFIG_URL="https://.../allowed-list.conf"

curl ... "${CONFIG_URL}" -o "${TMP_FILE}"

source "${TMP_FILE}"

Validation examples:

if [[ ! " ${ALLOWED_USER_IDS[@]} " =~ " ${GITLAB_USER_ID} " ]]; then
  echo "ERROR: User is not authorized"
  exit 1
fi

Installation

1. Place the hook script

Example location:

/opt/gitlab-runner/hooks/pre-build.sh

Make it executable:

chmod +x pre-build.sh

2. Configure GitLab Runner

Edit config.toml:

[[runners]]
  name = "secured-runner"
  url = "https://gitlab.com"
  token = "YOUR_TOKEN"
  executor = "shell"

  pre_build_script = "/opt/gitlab-runner/hooks/pre-build.sh"

Restart runner:

gitlab-runner restart

Security Benefits

This approach allows you to:

  • Restrict who can trigger jobs
  • Restrict which projects can run on the runner
  • Manage access centrally via remote config
  • Avoid modifying runner config on every update

Example Use Cases

  • Shared internal runners
  • Protecting production deployment runners
  • Preventing unauthorized pipelines from consuming resources
  • Centralized CI access control

Environment Variables Used

Variable Description
GITLAB_USER_ID ID of the user who triggered the pipeline
GITLAB_USER_LOGIN GitLab username
CI_PROJECT_ID ID of the GitLab project

Possible Improvements

  • Cache allowlist locally
  • Add branch restrictions
  • Add group-based authorization
  • Add audit logging
  • Add rate limiting / throttling
ALLOWED_USER_IDS=(
7
8
64
94
107
191
355
442
497
584
597
)
ALLOWED_PROJECT_IDS=(
632
718
830
943
974
1017
1045
1056
1062
1119
1123
1129
1145
1171
1173
1174
)
#!/bin/bash
set -euo pipefail
CONFIG_URL="https://gist.githubusercontent.com/setdjod/49466b59688c25e748ba1d0bbd0d0834/raw/allowed-list.conf"
TMP_FILE=$(mktemp)
curl --silent --show-error --fail --location -H "Cache-Control: no-cache" -H "Pragma: no-cache" "${CONFIG_URL}" -o "${TMP_FILE}"
source "${TMP_FILE}"
rm -f "${TMP_FILE}"
if [[ -z "${GITLAB_USER_ID}" ]]; then
echo "ERROR: GITLAB_USER_ID not found"
exit 1
fi
if [[ ! " ${ALLOWED_USER_IDS[@]} " =~ " ${GITLAB_USER_ID} " ]]; then
echo "ERROR: User ${GITLAB_USER_LOGIN} (ID: ${GITLAB_USER_ID}) is not authorized"
exit 1
fi
if [[ -z "${CI_PROJECT_ID}" ]]; then
echo "ERROR: GITLAB_PROJECT_ID not found"
exit 1
fi
if [[ ! " ${ALLOWED_PROJECT_IDS[@]} " =~ " ${CI_PROJECT_ID} " ]]; then
echo "ERROR: Project ID: ${CI_PROJECT_ID} is not authorized"
exit 1
fi
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment