Skip to content
Snippets Groups Projects
Select Git revision
  • ded7bbd03fe25bbb0ad7e3f8f1178a55335da1ed
  • master default protected
  • simple-task/7248-eol-check-add-node-22
  • 6877_check_iml_deployment
4 results

check_gitlab_tokens

Blame
  • 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
    
    # ----------------------------------------------------------------------