Prevent new user registration based on geo-IP data
What does this MR do and why?
Related to: https://gitlab.com/gitlab-org/modelops/anti-abuse/team-tasks/-/issues/635
This MR examines the Cloudflare geo-ip header and restricts users from registering when they are detected to be from China. The exception is if the user is invited to a paid namespace.
MR acceptance checklist
Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Database
When an invited member attempts to accept and invite and the member is restricted the member record is deleted. These are the query details for the member&.destroy
call.
Raw Query:
DELETE FROM "members"
WHERE "members"."id" = 107
Plan:
ModifyTable on public.members (cost=0.56..3.58 rows=0 width=0) (actual time=2.851..2.852 rows=0 loops=1)
Buffers: shared read=4
I/O Timings: read=2.829 write=0.000
-> Index Scan using members_pkey on public.members (cost=0.56..3.58 rows=1 width=6) (actual time=2.850..2.851 rows=0 loops=1)
Index Cond: (members.id = 107)
Buffers: shared read=4
I/O Timings: read=2.829 write=0.000
Statistics:
Time: 4.519 ms
- planning: 1.640 ms
- execution: 2.879 ms
- I/O read: 2.829 ms
- I/O write: 0.000 ms
Shared buffers:
- hits: 0 from the buffer pool
- reads: 4 (~32.00 KiB) from the OS file cache, including disk I/O
- dirtied: 0
- writes: 0
Query locks:
RELKIND | RELNAMESPACE | RELNAME | BELONGS TO RELATION | LOCKTYPE | MODE | GRANTED | FASTPATH
----------+--------------+----------------------------------------------------------------+---------------------+----------+------------------+---------+-----------
r | public | members | members | relation | RowExclusiveLock | true | true
i | public | index_members_on_expiring_at_access_level_id | members | relation | RowExclusiveLock | true | true
i | public | index_members_on_source_and_type_and_id | members | relation | RowExclusiveLock | true | true
i | public | index_members_on_user_id_created_at | members | relation | RowExclusiveLock | true | true
i | public | tmp_index_for_null_member_namespace_id | members | relation | RowExclusiveLock | true | true
i | public | idx_members_created_at_user_id_invite_token | members | relation | RowExclusiveLock | true | true
i | public | index_members_on_access_level | members | relation | RowExclusiveLock | true | true
i | public | index_members_on_expires_at | members | relation | RowExclusiveLock | true | true
i | public | members_pkey | members | relation | RowExclusiveLock | true | true
i | public | index_members_on_member_role_id | members | relation | RowExclusiveLock | true | true
i | public | index_project_members_on_id_temp | members | relation | RowExclusiveLock | true | true
i | public | index_members_on_invite_token | members | relation | RowExclusiveLock | true | true
i | public | index_members_on_user_id_and_access_level_requested_at_is_null | members | relation | RowExclusiveLock | true | true
i | public | index_members_on_invite_email | members | relation | RowExclusiveLock | true | true
i | public | index_non_requested_project_members_on_source_id_and_type | members | relation | RowExclusiveLock | true | true
i | public | index_members_on_member_namespace_id_compound | members | relation | RowExclusiveLock | true | false
i | public | index_members_on_requested_at | members | relation | RowExclusiveLock | true | false
i | public | index_members_on_source_and_type_and_access_level | members | relation | RowExclusiveLock | true | false
i | public | idx_members_on_user_and_source_and_source_type_and_member_role | members | relation | RowExclusiveLock | true | true
i | public | index_members_on_source_state_type_access_level_and_user_id | members | relation | RowExclusiveLock | true | false
How to set up and validate locally
Numbered steps to set up and validate the change are strongly suggested.
-
In rails console enable the feature flag.
Feature.enable(:prevent_registration_from_china)
-
Attempt to access the identity verification new page from a Chinese geo-ip. The response should re-direct to
/identity_verification/restricted
.▶ curl -I -H 'Cf-IPCountry: CN' http://gdk.test:3000/users/sign_up 2>&1 | \grep -E 'HTTP|Location' HTTP/1.1 302 Found Location: http://gdk.test:3000/users/identity_verification/restricted
-
Obtain a gitlab seesion cookie and CSRF token from the gitlab login page.
▶ curl -i 'http://gdk.test:3000/users/sign_in' 2>&1 | grep -E '(_gitlab_session_|csrf-token)' Set-Cookie: _gitlab_session_ebbf72ddfd0aa18cfa22445bd13f5503364fec21472ba5786d22f68a880db7e4=cbdfb460a7189cf36e2ffe963d69fa68; path=/; expires=Mon, 25 Mar 2024 22:55:47 GMT; HttpOnly <meta name="csrf-token" content="Ceep5esPocc6mqZEwyOVybV1ApiM8oNVSXp9_MGoL1Y6TttNdv4mL84zLcPqzHRPf86mI34qy7Vtkw6IxmuRzg" />
-
Use the session cookie and token to send a create request from a Chinese IP. The response should re-direct to the
/identity_verification/restricted
path.AUTHENTICITY_TOKEN='Ceep5esPocc6mqZEwyOVybV1ApiM8oNVSXp9_MGoL1Y6TttNdv4mL84zLcPqzHRPf86mI34qy7Vtkw6IxmuRzg' GL_SESSION_COOKIE='_gitlab_session_ebbf72ddfd0aa18cfa22445bd13f5503364fec21472ba5786d22f68a880db7e4=cbdfb460a7189cf36e2ffe963d69fa68' ▶ curl -i -X 'POST' -H 'Cf-IPCountry: CN' \ -b $"${GL_SESSION_COOKIE}" \ --data-binary $"authenticity_token=${AUTHENTICITY_TOKEN}&new_user%5Bfirst_name%5D=new&new_user%5Blast_name%5D=user&new_user%5Busername%5D=new7&new_user%5Bemail%5D=new7%40gmail.com&new_user%5Bpassword%5D=p@ssw0rd123" \ 'http://gdk.test:3000/users' 2>&1 | grep -E '(HTTP|Location)' HTTP/1.1 302 Found Location: http://gdk.test:3000/users/identity_verification/restricted
-
Re-attempt the get and post requests with an unrestricted IP address. The responses should not re-direct to the
/identity_verification/restricted
path.▶ curl -I -H 'Cf-IPCountry: US' http://gdk.test:3000/users/sign_up 2>&1 | \grep -E 'HTTP|Location' HTTP/1.1 200 OK
AUTHENTICITY_TOKEN='Ceep5esPocc6mqZEwyOVybV1ApiM8oNVSXp9_MGoL1Y6TttNdv4mL84zLcPqzHRPf86mI34qy7Vtkw6IxmuRzg' GL_SESSION_COOKIE='_gitlab_session_ebbf72ddfd0aa18cfa22445bd13f5503364fec21472ba5786d22f68a880db7e4=cbdfb460a7189cf36e2ffe963d69fa68' ▶ curl -i -X 'POST' -H 'Cf-IPCountry: CN' \ -b $"${GL_SESSION_COOKIE}" \ --data-binary $"authenticity_token=${AUTHENTICITY_TOKEN}&new_user%5Bfirst_name%5D=new&new_user%5Blast_name%5D=user&new_user%5Busername%5D=new7&new_user%5Bemail%5D=new7%40gmail.com&new_user%5Bpassword%5D=p@ssw0rd123" \ 'http://gdk.test:3000/users' 2>&1 | grep -E '(HTTP|Location)' HTTP/1.1 302 Found Location: http://gdk.test:3000/users/identity_verification