Page MenuHomePhabricator

Automate Wikimedia IDM ("Bitu") and GrowthBook role synchronization
Closed, ResolvedPublic

Description

This task is for implementation of the Enforcement section of T419622: Verify GrowthBook access approach parts 1-3.

This should be done first for growthbook-next.wikimedia.org to confirm proper function, then for growthbook.wikimedia.org.

This depends on completion of T420690: Create Project in GrowthBook, then migrate materials and access to it and T420688: Create new Wikimedia IDM ("Bitu") LDAP groups for GrowthBook for economy, although technically it would be possible to try it out without the Project first, and then shift it to have the Project confinement notion second.

This is being dropped into Sprint 21 (it will be DP SRE work) for work tracking, but may be dragged into a subsequent sprint.

Details

Other Assignee
brouberol
Related Changes in Gerrit:
Related Changes in GitLab:
TitleReferenceAuthorSource BranchDest Branch
growthbook: bump ldap-sync image for drop loggingrepos/data-engineering/airflow-dags!2297ryankemperT420691-mr59-image-bumpmain
ldap-sync: log silently-dropped Bitu membersrepos/data-engineering/growthbook!59ryankemperT420691-silent-drop-loggingmain
growthbook: bump ldap-sync image for elevated fixrepos/data-engineering/airflow-dags!2265ryankemperT420691-bump-ldap-sync-imagemain
ldap-sync: drop secondary POSIX gate for elevatedrepos/data-engineering/growthbook!58ryankemperT420691-elevated-posix-fixmain
test_k8s/growthbook_ldap_sync: fix typo in image tagrepos/data-engineering/airflow-dags!2252brouberolT420691main
Use the global role for admins when computing a plan actionrepos/data-engineering/growthbook!55brouberolT420691main
test_k8s: bump ldap-sync imagerepos/data-engineering/airflow-dags!2247brouberolT420691main
Admin is a global role not a project rolerepos/data-engineering/growthbook!54brouberolT420691main
Add documentation to the growthbook sync dagsrepos/data-engineering/airflow-dags!2246brouberolT420691main
Admins get granted admin role globally and no project rolesrepos/data-engineering/growthbook!53brouberolT420691main
Ensure that admins don't lose their global rolerepos/data-engineering/growthbook!52brouberolT420691main
ldap-sync: make pushgateway job name configurablerepos/data-engineering/growthbook!51ryankemperT420691-pushgateway-jobmain
ldap-sync: add environments to new role entriesrepos/data-engineering/growthbook!50ryankemperT420691-projectroles-environments-fixmain
ldap-sync: fix data_yaml lint and mypy errorsrepos/data-engineering/growthbook!49ryankemperfix-data-yaml-lint-mypymain
Ensure group member lists are flattenedrepos/data-engineering/growthbook!48brouberolT420691main
Install wmf-certificates to be able to call out to internal servicesrepos/data-engineering/growthbook!47brouberolT420691main
Add missing PYTHONPATH environment variablerepos/data-engineering/growthbook!46brouberolT420691main
growthbook: instrument ldap-sync via airflowrepos/data-engineering/airflow-dags!2208ryankemperT420691-ldap-sync-dagmain
ldap-sync: enable ruff&mypy in CI + fix format/importsrepos/data-engineering/growthbook!44ryankemperT420691-lint-typecheckmain
ldap-sync: drop threshold feature & config modulerepos/data-engineering/growthbook!43ryankemperT420691-3.5-trimmain
Show related patches Customize query in GitLab

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes

Change #1270558 abandoned by Ryan Kemper:

[operations/deployment-charts@master] growthbook: Bump vendored job templ 1.0.1 → 2.0.0

Reason:

This bug is real but non-exercised, and we're not going the k8s-cronjob route that was going to necessitate it. The bumping vendored job template is worth it down the road, but there's other charts that have the same issue, so it likely warrants doing them all at once

https://gerrit.wikimedia.org/r/1270558

Haven't posted here in awhile. Current state: the actual ldap-sync script is fully done (which was the bulk of the work).

Airflow deployment ongoing. We have patch 1 ready now which sets up the initial DAG skeleton and some basic validation testing. I tested it with airflow-devenv, and verified it's working; I attached a log at the end of this comment

Steps remaining

Remaining steps AFAIK until this project is more-or-less wrapped:

  1. Patch 2a (deployment-charts): chart changes
  2. Patch 2b (private puppet): add the API key value (already exists in private puppet under the growthbook chart's namespace, where the GB backend reads it; need to duplicate under the airflow-test-k8s namespace so the DAG's task pod can read it too)
  3. helmfile apply on airflow-test-k8s -> Secret gets the key
  4. Patch 2c (airflow-dags, one-line): pin IMAGE tag
  5. Trigger DAG manually via Airflow UI to test
  6. Patch 3 (airflow-dags, one-line): flip schedule=None -> "*/30 * * * *"
airflow-devenv log

Validated the secret-name resolution end-to-end via airflow-devenv

Spun up a dev instance; the DAG-serialization step succeeded, confirming the scheduler imports the DAG cleanly (no KeyError on os.environ["SERVICE_IDENTIFIER"]):

$ airflow-devenv create --branch T420691-ldap-sync-dag --dags-folder test_k8s
- Creating PG database dev_ryankemper ✅
- Installing airflow dev environment dev-ryankemper ✅
- Waiting for the kerberos pod to start ✅
- Kerberos credentials successfully setup ✅
- Waiting for the scheduler pod to be ready ✅
- Forcing DAG serialization ✅
- Waiting for the webserver pod to be ready ✅

Confirmed SERVICE_IDENTIFIER resolves to the per-instance value via the chart's app.airflow.env helper, and the matching <SERVICE_IDENTIFIER>-secret-config Secret exists in the namespace:

$ kubectl -n airflow-dev exec airflow-dev-ryankemper-scheduler-6b76b64bb8-p7s6t -- printenv SERVICE_IDENTIFIER
airflow-dev-ryankemper

$ kubectl -n airflow-dev get secrets | grep ryankemper
airflow-dev-ryankemper-secret-config           Opaque    2    11m

Inspected the DAG's serialized operator: the secretKeyRef.name resolves to <SERVICE_IDENTIFIER>-secret-config, matching the pattern the chart already uses for its own internal keys:

$ kubectl -n airflow-dev exec airflow-dev-ryankemper-scheduler-6b76b64bb8-p7s6t -- \
    python3 -c 'import json; from kubernetes.client import ApiClient; from airflow.models import DagBag; dag = DagBag().get_dag("growthbook_ldap_sync"); print(json.dumps(ApiClient().sanitize_for_serialization(dag.get_task("sync").env_vars), indent=2))'
[
  {
    "name": "GROWTHBOOK_LDAP_SYNC_API_KEY",
    "valueFrom": {
      "secretKeyRef": {
        "key": "GROWTHBOOK_LDAP_SYNC_API_KEY",
        "name": "airflow-dev-ryankemper-secret-config"
      }
    }
  },
  ...
]

Note the actual GROWTHBOOK_LDAP_SYNC_API_KEY key isn't in the Secret yet, that's ongoing work

Change #1286748 had a related patch set uploaded (by Ryan Kemper; author: Ryan Kemper):

[operations/puppet@production] global_config: add ldap-sync external services

https://gerrit.wikimedia.org/r/1286748

Change #1286750 had a related patch set uploaded (by Ryan Kemper; author: Ryan Kemper):

[operations/deployment-charts@master] airflow-test-k8s: add ldap-sync task-pod egress

https://gerrit.wikimedia.org/r/1286750

brouberol opened https://gitlab.wikimedia.org/repos/data-engineering/growthbook/-/merge_requests/47

Install wmf-ca-certificates to be able to call out to internal services

Change #1288855 had a related patch set uploaded (by Brouberol; author: Brouberol):

[operations/puppet@production] global_config: add ldap-ro-{eqiad,codfw} to external-services

https://gerrit.wikimedia.org/r/1288855

Change #1288856 had a related patch set uploaded (by Brouberol; author: Brouberol):

[operations/deployment-charts@master] airflow-test-k8s: add the ldap-ro task pod external services policy

https://gerrit.wikimedia.org/r/1288856

Change #1288855 merged by Brouberol:

[operations/puppet@production] global_config: add ldap-ro-{eqiad,codfw} to external-services

https://gerrit.wikimedia.org/r/1288855

Change #1288856 merged by Brouberol:

[operations/deployment-charts@master] airflow-test-k8s: add the ldap-ro task pod external services policy

https://gerrit.wikimedia.org/r/1288856

Change #1288864 had a related patch set uploaded (by Brouberol; author: Brouberol):

[operations/puppet@production] global_config: add gerrit.w.o to external-services

https://gerrit.wikimedia.org/r/1288864

Change #1288865 had a related patch set uploaded (by Brouberol; author: Brouberol):

[operations/deployment-charts@master] airflow-test-k8s: add the gerrit task pod external services policy

https://gerrit.wikimedia.org/r/1288865

Change #1288864 merged by Brouberol:

[operations/puppet@production] global_config: add gerrit.w.o to external-services

https://gerrit.wikimedia.org/r/1288864

Change #1288865 merged by Brouberol:

[operations/deployment-charts@master] airflow-test-k8s: add the gerrit task pod external services policy

https://gerrit.wikimedia.org/r/1288865

Change #1288894 had a related patch set uploaded (by Brouberol; author: Brouberol):

[operations/deployment-charts@master] airflow-test-k8s: add the prometheus-pushgateway task pod external services policy

https://gerrit.wikimedia.org/r/1288894

Change #1288894 merged by Brouberol:

[operations/deployment-charts@master] airflow-test-k8s: add the prometheus-pushgateway task pod external services policy

https://gerrit.wikimedia.org/r/1288894

After talking with @dr0ptp4kt and @mpopov, we decided to

  • only allow members of growthbook-admin, growthbook-customelevatedaccess or growthbook-readonly to be able to SSO-login to growthbook
  • delete all growthbook users (which will move them to orphaned users)
  • re-run the script to only restore the users that require access in the first place

This will ensure that users that were auto-created before we automated user management get orphaned and don't take a seat while being un-used.,

Change #1289384 had a related patch set uploaded (by Brouberol; author: Brouberol):

[operations/puppet@production] idp: restrict growthbook UI login to users belonging to the growthbook LDAP groups

https://gerrit.wikimedia.org/r/1289384

Change #1289384 merged by Brouberol:

[operations/puppet@production] idp: restrict growthbook UI login to the growthbook LDAP groups

https://gerrit.wikimedia.org/r/1289384

Change #1289752 had a related patch set uploaded (by Brouberol; author: Brouberol):

[operations/puppet@production] idp: restrict growthbook-next UI login to users in the growthbook LDAP groups

https://gerrit.wikimedia.org/r/1289752

Change #1289752 merged by Brouberol:

[operations/puppet@production] idp: restrict growthbook-next UI login to users in the growthbook LDAP groups

https://gerrit.wikimedia.org/r/1289752

Change #1291975 had a related patch set uploaded (by Brouberol; author: Brouberol):

[operations/puppet@production] idp/idp_test: temporarily rollback growthbook(-next) access to nda/wmf

https://gerrit.wikimedia.org/r/1291975

Change #1291975 merged by Brouberol:

[operations/puppet@production] idp/idp_test: temporarily rollback growthbook(-next) access to nda/wmf

https://gerrit.wikimedia.org/r/1291975

As of now, we have enabled the synchronization of LDAP groups and Growthbook roles for both growthbook-next.w.o and growthbook.w.o. The synchronization is managed by these DAGs.

We have tested the case of orphaning a user, them logging back into growthbok (their user gets assigned a No Access global role as well as no project role). They will then get assigned their "proper" role the next time the DAG runs.

For example, @Gehel had its user orphaned. He logged back into growthbook, and the DAG issued the following log afterwards

{
  "ts": "2026-05-27T14:00:13+0000",
  "level": "INFO",
  "logger": "ldap_sync.applier",
  "actor": "ldap-sync",
  "sync_run_id": "48267a07-55db-4e58-831a-c1af553fc3a7",
  "dry_run": false,
  "message": "action",
  "user_email": "glederrey@wikimedia.org",
  "user_uid": null,
  "growthbook_member_id": "u_6v4betpmlup0ss2",
  "current_role": "NO_ACCESS",
  "target_role": "READ_ONLY",
  "action": "add",
  "action_result": "success",
  "rule_number": 4,
  "source_bitu_groups": [
    "GrowthBook-ReadOnly"
  ],
  "source_posix_groups": [
    "analytics-privatedata-users",
    "deployment"
  ],
  "drift_detected": true
}

I paired with @mpopov to deploy the feature, and we're happy to call this done.

This is live in both production and staging right now. There's one small outstanding patch to improve log clarity.

Change #1286750 abandoned by Ryan Kemper:

[operations/deployment-charts@master] airflow-test-k8s: add ldap-sync task-pod egress

Reason:

superceded by subsequent patches

https://gerrit.wikimedia.org/r/1286750

Change #1286748 abandoned by Ryan Kemper:

[operations/puppet@production] global_config: add ldap-sync external services

Reason:

superseded by 903438182a + be7f33bdc7 (ldap-ro + gerrit egress merged separately); growthbook-api egress turned out unnecessary (internal .discovery.wmnet service)

https://gerrit.wikimedia.org/r/1286748