In GitLab version 16.0.0, the default settings for “Personal Access Tokens” (PAT) were changed. Since I use PATs for my deployments, I needed an automated solution to rotate these tokens regularly.
The script requires at least write access to the GitLab API - see Fig. 1.
It makes use of two essential commands to rotate the PAT:
curl
duckdb
The curl
command is used to interact with the API of my GitLab instance. Initially, it verifies API access. If the access check is successful, the token is then rotated.
The duckdb
command is employed to parse the JSON returned by the API. For more details on using duckdb
as an alternative to jq
, see this article.
To ensure ample time for the next PAT rotation, the script uses the “expires_at” attribute. The chosen time span is similar to the duration of Let’s Encrypt Certificates.
The new PAT is written to a separate file, which is sourced at the beginning of the script.
If you choose to use the following script, make sure you change the GitLab API
url and the PAT in the pat.conf
.
vim pat.conf
PAT='CHANGEME'
vim rotate.sh
#!/bin/bash
set -e
: ${CI_API_V4_URL:=https://gitlab.example.com/api/v4/}
: ${PAT_NAME:='self'}
. ./pat.conf
IS_REVOKED=$(curl -s --header "PRIVATE-TOKEN: ${PAT}" "${CI_API_V4_URL}/personal_access_tokens/${PAT_NAME}" \
| duckdb -noheader -column -c "SELECT revoked FROM read_json_auto('/dev/stdin');" \
| tr -d '[:blank:]'
)
if [ "${IS_REVOKED}" != "false" ]; then
echo [ERROR] Token does not exist, access denied to token or the token has already been revoked. Aborting
exit 1
fi
EXPIRES_AT=$(date --date "+3 months" +"%Y-%m-%d")
NEW_PAT=$(
curl --request POST --header "PRIVATE-TOKEN: ${PAT}" "${CI_API_V4_URL}/personal_access_tokens/${PAT_NAME}/rotate?expires_at=${EXPIRES_AT}" \
| duckdb -noheader -column -c "SELECT token FROM read_json_auto('/dev/stdin');"
)
if [ -z "${NEW_PAT}" ]; then
echo '[ERROR] New personal access token is null. Not overwriting the current one in config file. Aborting'
exit 1
fi
echo "PAT='${NEW_PAT}'" > ./pat.conf