Skip to content

Prevent new user registration based on geo-IP data

Ian Anderson requested to merge jihu_redirection into master

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.

  1. In rails console enable the feature flag.

    Feature.enable(:prevent_registration_from_china)
  2. 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
  3. 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" />
  4. 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
  5. 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
Edited by Ian Anderson

Merge request reports