-
Hahn Axel (hahn) authoredHahn Axel (hahn) authored
check_gitlab_tokens 8.18 KiB
#!/bin/bash
# ======================================================================
#
# Check Gitlab tokens of groups and projects.
# It warns if tokens expire soon.
#
# requirements:
# - rest-api-client - https://git-repo.iml.unibe.ch/iml-open-source/bash-rest-api-client
# - curl
#
# ----------------------------------------------------------------------
# 2024-10-29 v1.0 <axel.hahn@unibe.ch>
# 2024-10-30 v1.1 <axel.hahn@unibe.ch> GITLAB_TOKEN=SKIP responds OK without tests
# 2024-11-20 v1.2 <axel.hahn@unibe.ch> Update rest api client
# ======================================================================
cd "$( dirname "$0" )" || exit
. "$( dirname $0 )/inc_pluginfunctions" || exit 1
export self_APPVERSION=1.2
sSkipvalue="SKIP"
GITLAB_API='https://gitlab.example.com/api/v4'
GITLAB_TOKEN="$sSkipvalue"
GITLAB_CONFIG=/etc/icinga2/gitlab.cfg
REST_CLIENT="/opt/rest-api-client/http.class.sh"
projectUrls=
OUT_TOKENS=/tmp/gitlab-tokens__$USER.json
OUT_USERS=/tmp/gitlab-users__$USER.json
typeset -i iSince=395
typeset -i iTokenCount
typeset -i iTokensFound
NL="
"
typeset -i iWarnLimit=30
typeset -i iCriticalLimit=10
typeset -i iCountWarn=0
typeset -i iCountCritical=0
output=""
# ----------------------------------------------------------------------
# functions
# ----------------------------------------------------------------------
# Show help
function showHelp(){
local _self; _self=$(basename $0)
cat <<EOF
$( ph.showImlHelpHeader )
Check gitlab tokens and warn if tokens expire soon.
This check fetches the gitlbab tokens created in the last $iSince days
from the Gitlab API. It skips
- personal access tokens of users
- revoked tokens
The script can run several seconds depending on count of tokens, projects
and users. Maybe you want to call it with a longer interval.
SYNTAX:
$_self [OPTIONS]
OPTIONS:
-h or --help show this help.
-w VALUE warning level (default: $iWarnLimit)
-c VALUE critical level (default: $iCriticalLimit)
-g FILE path to GITLAB_CONFIG; default: $GITLAB_CONFIG
There you can set/ override:
GITLAB_API='${GITLAB_API}'
GITLAB_CONFIG=<TOKEN>
REST_CLIENT="${REST_CLIENT}"
-r FILE path to REST api client
default: $REST_CLIENT
The parameter overrides the variable REST_CLIENT.
-s DAYS Number of days for max age of token; default: $iSince
PARAMETERS:
None.
EXAMPLES:
$_self -w 28 -c 7
Set other warning and critical level
$_self -g ./gitlab.cfg
Set a custom gitlab config file
$_self -r /opt/bash-api-client/bash-api-client.sh
Set a custom gitlab config file
EOF
}
# Fetch data from gitlab api with multiple page requests
#
# param string url
# param string output file
# param int optional: number of items per page; default: 100
function _getPagesToFile(){
local url="$1"
local outfile="$2"
local iPerPage=${3:-100}
local page=0
test -f "${outfile}" && rm "${outfile}"
grep -q "?" <<< "$url" || url="${url}?"
while true; do
(( page++ ))
pageUrl="$url&per_page=${iPerPage}&page=${page}"
# echo "Request: $pageUrl"
http.makeRequest "$pageUrl"
if ! http.isOk > /dev/null; then
echo "ERROR: Request failed: $pageUrl"
http.getResponseHeader
http.getResponse
ph.abort
fi
# if response is "[]" then we are done
if ! http.getResponse | grep -q "^\[\]$"; then
http.getResponse >> "${outfile}"
else
break
fi
done
}
# Get web link of a project to see its tokens
#
# global string projectUrls string to cache project name + link
#
# param string username eg. projects_14_bot_nnnnnn
# return string
function getWeblink(){
local myusername="$1"
sType=$( cut -f 1 -d "_" <<< "$myusername" )s
# sType is one of users|projects
sId2=$( cut -f 2 -d "_" <<< "$myusername" )
local data
local myurl
if ! grep -q "^/$sType/$sId2:" <<< "$projectUrls" ; then
http.makeRequest "/$sType/$sId2"
if ! http.isOk > /dev/null; then
echo "ERROR: Request /$sType/$sId2 failed: $pageUrl"
http.getResponseHeader
http.getResponse
ph.abort
fi
data="$( http.getResponse )"
myname=$( getKey "$data" "name" )
myurl=$( getKey "$data" "web_url" )
if [ -n "$myurl" ]; then
projectUrls+="/$sType/$sId2:$myname <$myurl/-/settings/access_tokens>$NL"
fi
fi
grep "^/$sType/$sId2:" <<< "$projectUrls" | cut -f 2- -d ":"
}
# Get a single value from json
# param string json data
# param string key
# return string
function getKey(){
jq -r ".$2" <<< "$1" | grep -v "null"
}
# ----------------------------------------------------------------------
# MAIN
# ----------------------------------------------------------------------
# --- check param -h
case "$1" in
"--help"|"-h")
showHelp
exit 0
;;
*)
esac
REST_CLIENT=$( ph.getValueWithParam $REST_CLIENT r "$@")
GITLAB_CONFIG=$( ph.getValueWithParam $GITLAB_CONFIG g "$@")
# --- check requirements
ph.require curl
. "${GITLAB_CONFIG}" || ph.abort "UNKNOWN: Could not source gitlab config $GITLAB_CONFIG"
if [ "$GITLAB_TOKEN" = "$sSkipvalue" ]; then
ph.status "The check was configured to skip: GITLAB_TOKEN=$sSkipvalue"
ph.exit
fi
. "${REST_CLIENT}" || ph.abort "UNKNOWN: Could not source $REST_CLIENT"
http.help >/dev/null || ph.abort "UNKNOWN: http functions not available. Check -r $REST_CLIENT."
iWarnLimit=$( ph.getValueWithParam $iWarnLimit w "$@")
iCriticalLimit=$( ph.getValueWithParam $iCriticalLimit c "$@")
iSince=$( ph.getValueWithParam $iSince s "$@")
http.init
http.addHeader "PRIVATE-TOKEN: $GITLAB_TOKEN"
http.setBaseUrl "$GITLAB_API"
startdate="$( date +%Y-%m-%dT00:00:00Z --date "$iSince days ago")"
sDateWarn="$( date +%Y%m%d --date "${iWarnLimit} days" )"
sDateCritical="$( date +%Y%m%d --date "${iCriticalLimit} days" )"
# get all tokens
# see https://docs.gitlab.com/ee/api/personal_access_tokens.html
_getPagesToFile "/personal_access_tokens/?revoked=false&created_after=${startdate}" "$OUT_TOKENS"
# get all users
# see https://docs.gitlab.com/ee/api/users.html
_getPagesToFile "/users?exclude_humans=true&exclude_external=true&exclude_internal=true&created_after=${startdate}&search=_bot_" "$OUT_USERS"
# IDs / Einträge zählen:
iTokenCount=$( jq ".[].id " < "$OUT_TOKENS" | wc -l )
iTokensFound=0
for i in $( seq 1 $iTokenCount )
do
# get nth token
entry="$( jq ".[$i]" < "$OUT_TOKENS")"
# hide non active tokens
if [ "$( getKey "$entry" "active" )" = "false" ]; then
continue
fi
# hide tokens without name
sName=$( getKey "$entry" "name" )
if [ -z "$sName" ]; then
continue
fi
# hide tokens referencing a username that doesn't contain "_[number]_bot_"
sUserid=$( getKey "$entry" "user_id" )
myusername="$( jq ".[] | select(.id == $sUserid)" < "$OUT_USERS" | jq ".username" | cut -f 1-3 -d "_" | tr -d '"') "
if ! grep -q "_[0-9]*_bot" <<< "$myusername" ; then
continue
fi
iTokensFound+=1
# check expiration
sExpire=$( getKey "$entry" "expires_at" )
# remove "-" from date to get an integer
sExpire2=${sExpire//\-}
sStatus="OK "
if [ "$sExpire2" -le "$sDateWarn" ]; then
if [ "$sExpire2" -le "$sDateCritical" ]; then
iCountCritical+=1
sStatus="Critical"
else
iCountWarn+=1
sStatus="Warning "
fi
fi
myproject="$( getWeblink "$myusername" )"
output+="$sExpire $sStatus $sName - $myproject${NL}"
done
if [ $iCountCritical -gt 0 ]; then
ph.setStatus "critical"
elif [ $iCountWarn -gt 0 ]; then
ph.setStatus "warning"
else
ph.setStatus "ok"
fi
ph.status "$iTokensFound Gitlab Tokens (max $iSince days old) .. critical: $iCountCritical ($iCriticalLimit days) .. warnings: $iCountWarn ($iWarnLimit days)"
echo
echo "$output"
# cleanup
rm -f "$OUT_TOKENS" "$OUT_USERS"
ph.exit
# ----------------------------------------------------------------------