Skip to content

feat(grafana): auth proxy + per-user PostgreSQL datasources#41

Merged
cchwala merged 6 commits intomainfrom
feat/grafana-auth-proxy
Apr 30, 2026
Merged

feat(grafana): auth proxy + per-user PostgreSQL datasources#41
cchwala merged 6 commits intomainfrom
feat/grafana-auth-proxy

Conversation

@cchwala
Copy link
Copy Markdown
Member

@cchwala cchwala commented Apr 30, 2026

PR Summary

This PR implements secure, multi-tenant support for Grafana and SFTP onboarding, with the following major changes:

  • Grafana Authentication Proxy & Per-User Datasources

    • Added an authentication proxy for Grafana, enabling per-user PostgreSQL datasources.
    • Each user (e.g., demo_openmrg, demo_orange_cameroun) is mapped to a dedicated PostgreSQL login role and Grafana datasource.
    • The webserver injects the correct X-WEBAUTH-USER header, ensuring requests are mapped to the correct Grafana user and data source.
    • Disabled anonymous access and the Grafana login form; only trusted requests from the webserver are allowed.
  • Grafana Proxy Security & Testing

    • Strips any X-WEBAUTH-USER header supplied by the browser to prevent identity forgery.
    • Added tests for the Grafana proxy, focusing on header injection and authentication logic.
  • Security Fixes

    • Fixed GF_AUTH_PROXY_WHITELIST to avoid using an invalid hostname (webserver), which caused Grafana to skip proxy initialization and broke authentication.
    • Removed host port 3000 mapping; Grafana is now only accessible within the Docker Compose network.
  • Multi-Tenant Grafana Setup

    • Added init_grafana service and bootstrap script to automate onboarding of additional organizations (org 2) via the Grafana API.
    • Dashboards and datasources for org 2 are created at runtime, not via provisioning files (prevents startup crashes).
    • Dashboard definitions now use security-barrier views for tenant DB roles.
    • User config extended with grafana_org_id for correct org mapping.
  • Documentation

    • Added a multi-tenancy section to the README, including a deployment and onboarding guide for new tenants.
  • Automated SSH Key Management

    • Refactored the SSH key generation script to automate multi-tenant SSH key and authorized_keys setup (commit not shown in full log above but present in branch).

- grafana/provisioning/datasources/postgres.yml: replace single myuser
  datasource with per-user datasources (demo_openmrg, demo_orange_cameroun)
  each connecting as the matching PG login role, plus an admin datasource
  connecting as webserver_role for cross-tenant operator dashboards.

- docker-compose.yml: enable Grafana auth proxy mode (GF_AUTH_PROXY_ENABLED),
  trust X-WEBAUTH-USER header from the webserver container only
  (GF_AUTH_PROXY_WHITELIST=webserver), disable anonymous access and the
  Grafana login form.

- webserver/main.py (grafana_proxy): strip any X-WEBAUTH-USER header supplied
  by the browser (prevents identity forgery), then inject current_user.id so
  Grafana maps the request to the correct Grafana user.

Data isolation chain:
  Flask session → X-WEBAUTH-USER → Grafana user → per-user datasource
  → PG role → RLS / security-barrier views
@cchwala cchwala changed the title feat(grafana): auth proxy + per-user PostgreSQL datasources (PR8) feat(grafana): auth proxy + per-user PostgreSQL datasources Apr 30, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 30, 2026

Codecov Report

❌ Patch coverage is 75.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 81.58%. Comparing base (b60f4a4) to head (82c1f4f).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
webserver/main.py 75.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #41      +/-   ##
==========================================
+ Coverage   81.18%   81.58%   +0.39%     
==========================================
  Files          24       24              
  Lines        2371     2373       +2     
==========================================
+ Hits         1925     1936      +11     
+ Misses        446      437       -9     
Flag Coverage Δ
mno_simulator 86.72% <ø> (ø)
parser 85.48% <ø> (ø)
webserver 65.18% <75.00%> (+1.97%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

cchwala added 5 commits April 30, 2026 08:45
GF_AUTH_PROXY_WHITELIST requires IP/CIDR notation; passing the
service hostname 'webserver' caused Grafana to log:

  'could not parse the network: invalid CIDR address: webserver/32'

and silently skip auth proxy initialization entirely.  All iframe
requests therefore arrived as anonymous (userId=0) and were
redirected to the Grafana welcome page instead of the dashboard.

Fix: remove the whitelist entry and the host port-3000 mapping.
Port 3000 is now only reachable from within the Docker Compose
network, providing equivalent isolation without the broken setting.
- docker-compose.yml: fix Grafana healthcheck regex; add init_grafana service
  that bootstraps org 2 via API on startup
- grafana/init_grafana.py: new bootstrap script - creates org 2, datasource,
  copies dashboards from org 1, creates tenant users
- grafana/Dockerfile: image for init_grafana service
- grafana/provisioning/dashboards/dashboards.yml: remove org 2 entry (was
  crashing Grafana at startup with org.notFound); org 2 handled by init_grafana
- grafana/provisioning/datasources/postgres.yml: remove org 2 entry (same
  reason); simplify org 1 config
- grafana/provisioning/dashboards/definitions/*.json: replace cml_data_1h
  aggregate with cml_data_1h_secure security-barrier view so tenant DB roles
  can query without permission errors
- webserver/configs/users.json: add grafana_org_id field per user
- webserver/main.py: read grafana_org_id from user config; pass to tem- webserver/main.py: read grafana_org_id from user config; pass to tem- webserver/main.py: read grafana_org_id from user config; patml- webserver/main.py: read grafana_org_id from user config; pass to tem- websing migration
…orized_keys setup

- Refactored generate_ssh_keys.sh to loop over tenants, generate keys, and set up per-tenant authorized_keys
- Ensured idempotency and clear output for onboarding new tenants
- Updated authorized_keys for demo_openmrg and demo_orange_cameroun
- Validated SFTP upload and stack health after changes

This commit ensures robust, automated SSH key management for all tenants.
@cchwala cchwala merged commit 6d5fb01 into main Apr 30, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant