PAT last used IPs are not tracked on GitLab CE
Summary
!161076 (merged) has introduced recording of the last few IP addresses from which a PAT has been used. This seems to be a free feature (see 17.9 release notes), but it actually works only on GitLab EE.
On GitLab CE, nothing is ever inserted in the personal_access_token_last_used_ips table, nothing is displayed in the Last used IPs column of the tokens UI, and the GET personal_access_tokens/:id API responses always have an empty last_used_ips array.
This issue sounds very similar to #524327 (closed), opened by @jurgenhaas, but the focus there has been mainly on the IPs not being displayed in the tokens UI - the assumption is that personal_access_token_last_used_ips is still properly populated, and that last_used_ips can indeed be retrieved via the PAT API. That's an actual bug too, in the process of being fixed by !187403 (merged), but with a completely different root cause, and I think it's best to deal with the CE-specific side of things in a new, separate, issue (this one).
Steps to reproduce
The issue can be reproduced in a GDK environment.
- setup GDK to simulate a GitLab CE instance (
echo "export FOSS_ONLY=1" > env.runitand restart) - create a PAT with
read_apiscope ($TOKENin following steps) - use it at least once, for instance to call the self-inform token API (and note the token id -
9here - for later steps):
% curl -sSf -H "PRIVATE-TOKEN: $TOKEN" \
http://localhost:3000/api/v4/personal_access_tokens/self \
| jq '{id,last_used_at}'
{
"id": 9,
"last_used_at": "2025-04-09T22:35:28.723Z"
}
- call the GET PAT by id API - last_used_ips is empty:
% curl -sSf -H "PRIVATE-TOKEN: $TOKEN" \
http://localhost:3000/api/v4/personal_access_tokens/9 \
| jq '{id,last_used_at,last_used_ips}'
{
"id": 9,
"last_used_at": "2025-04-09T22:35:28.723Z",
"last_used_ips": []
}
- check in the DB - last used IP is not recorded:
% gdk psql <<<'select * from personal_access_token_last_used_ips where personal_access_token_id = 9;'
id | personal_access_token_id | organization_id | created_at | updated_at | ip_address
----+--------------------------+-----------------+------------+------------+------------
(0 rows)
- check the PAT UI, the Preferences / Access Tokens page - the Last used IPs column is empty for this token (but that's also because #524327 (closed) anyway - so really, the API and DB checks are the one that matters)
- repeat (with same token) a few minutes later to make sure this is not a timing issue (same results)
Same steps in GDK without the env.runit file show the last used IPs being recorded in the DB, and last_used_ips not empty in API response.
What is the current bug behavior?
Last used IPs for PAT are not recorded at all on GitLab CE. As shown with GDK above, but also on an actual GitLab deployment (k8s, chart 8.9.5, with -ce images - like registry.gitlab.com/gitlab-org/build/cng/gitlab-webservice-ce:v17.9.5 for the webservices containers)
What is the expected correct behavior?
Last used IPs for PAT should be recorder in personal_access_token_last_used_ips, and last_used_ips in token API response should not be empty if the token has been used. Including on GitLab CE.
Relevant logs and/or screenshots
N/A (see Steps to reproduce)
Output of checks
Results of GitLab environment info
GitLab environment info from our production system (17.9.5 CE on k8s), where the issue has been detected first, prior to reproducing with an up-to-date GDK environment. The info comes from a registry.gitlab.com/gitlab-org/build/cng/gitlab-toolbox-ce:v17.9.5 container.
Expand for output related to GitLab environment info
System information System: Current User: git Using RVM: no Ruby Version: 3.2.5 Gem Version: 3.6.3 Bundler Version:2.6.3 Rake Version: 13.0.6 Redis Version: 7.0.15 Sidekiq Version:7.2.4 Go Version: unknown GitLab information Version: 17.9.5 Revision: dd0f8879111 Directory: /srv/gitlab DB Adapter: PostgreSQL DB Version: 14.10 URL: https://xxxxxxxxxxxxxxxxxxx HTTP Clone URL: https://xxxxxxxxxxxxxxxxxxx/some-group/some-project.git SSH Clone URL: git@xxxxxxxxxxxxxxxxxxx:some-group/some-project.git Using LDAP: no Using Omniauth: yes Omniauth Providers: saml GitLab Shell Version: 14.40.0 Repository storages: - default: tcp://gitlab-pr-gitaly-0.gitlab-pr-gitaly.gitlab-pr.svc:8075 - praefect: tcp://pr-gitlab-pr-gitaly-praefect.service.sd.diod.tech:2305 GitLab Shell path: /home/git/gitlab-shell Gitaly - default Address: tcp://gitlab-pr-gitaly-0.gitlab-pr-gitaly.gitlab-pr.svc:8075 - default Version: 17.9.5 - default Git Version: 2.47.2 - praefect Address: tcp://pr-gitlab-pr-gitaly-praefect.service.sd.diod.tech:2305 - praefect Version: 17.9.5 - praefect Git Version: 2.47.2
Results of GitLab application Check
(TODO)
Possible fixes
I suspect that the issue is that !161076 (merged) relies on Gitlab::IpAddressState.current. It seems this value is only initialised in some EE code, in ee/app/controllers/ee/application_controller.rb#L45 (likely because before !161076 (merged) it was only used for some EE-only features, like IP restrictions). In CE, Gitlab::IpAddressState.current is likely always nil.
I'm working on an MR to fix that, moving just the set_current_ip_address method to CE (as an around_action in app/controllers/application_controller.rb). I hope it would be okay to propose that, from a licence point of view (that's just 3/4 lines changing licence - not unlocking any EE feature as far as I can tell).