nandi/atop-analyzepublic Fork 0
5f7ef9d
Commits
Clone
git clone https://git.rickub.com/nandi/atop-analyze.git
git clone ssh://git@rickub.com/nandi/atop-analyze.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Run Grafana as its unprivileged user

nandithebull committed 2026-09-21T20:03:11-07:00 Browse files
5f7ef9d parent: 9a2f9a8
modified justfile +1 -1
@@ -17,7 +17,7 @@ fire:
1717
1818 # Export today's currently available atop history without waiting for midnight.
1919 test-today:
20- modal run modal_atop_analyze.py --date "$$(date +%Y%m%d)" --initialize
20+ modal run modal_atop_analyze.py --date "$(date +%Y%m%d)" --initialize
2121
2222 # Inspect the most recent scheduled-export output.
2323 logs:
@@ -17,7 +17,7 @@ fire:
17 17
18 # Export today's currently available atop history without waiting for midnight.18 # Export today's currently available atop history without waiting for midnight.
19 test-today:19 test-today:
20- modal run modal_atop_analyze.py --date "$$(date +%Y%m%d)" --initialize20+ modal run modal_atop_analyze.py --date "$(date +%Y%m%d)" --initialize
21 21
22 # Inspect the most recent scheduled-export output.22 # Inspect the most recent scheduled-export output.
23 logs:23 logs:
modified modal_grafana.py +11 -1
@@ -6,6 +6,7 @@ the Pocket ID Generic OAuth settings, including ``GF_SERVER_ROOT_URL``. Deploy
66 with ``modal deploy modal_grafana.py``.
77 """
88
9+import os
910 import subprocess
1011 import time
1112
@@ -13,6 +14,8 @@ import modal
1314
1415 APP_NAME = "atop-grafana"
1516 IDLE_SECONDS = 300
17+GRAFANA_UID = 472
18+GRAFANA_GID = 0
1619
1720 app = modal.App(APP_NAME)
1821 grafana_secret = modal.Secret.from_name(
@@ -52,8 +55,15 @@ image = (
5255 @modal.web_server(3000, startup_timeout=120)
5356 def grafana() -> None:
5457 """Run a public Grafana login page; its data-source credentials stay server-side."""
58+ def drop_privileges() -> None:
59+ # Modal ignores the Docker image's USER instruction for Functions.
60+ # The upstream Grafana image uses unprivileged UID 472 and group 0.
61+ os.setgid(GRAFANA_GID)
62+ os.setuid(GRAFANA_UID)
63+
5564 process = subprocess.Popen(
56- ["grafana", "server", "--homepath=/usr/share/grafana", "--config=/etc/grafana/grafana.ini"]
65+ ["grafana", "server", "--homepath=/usr/share/grafana", "--config=/etc/grafana/grafana.ini"],
66+ preexec_fn=drop_privileges,
5767 )
5868 # modal.web_server waits for port 3000. Catch an immediate startup failure
5969 # early so its cause appears in Modal logs rather than as a proxy timeout.
@@ -6,6 +6,7 @@ the Pocket ID Generic OAuth settings, including ``GF_SERVER_ROOT_URL``. Deploy
6 with ``modal deploy modal_grafana.py``.6 with ``modal deploy modal_grafana.py``.
7 """7 """
8 8
9+import os
9 import subprocess10 import subprocess
10 import time11 import time
11 12
@@ -13,6 +14,8 @@ import modal
13 14
14 APP_NAME = "atop-grafana"15 APP_NAME = "atop-grafana"
15 IDLE_SECONDS = 30016 IDLE_SECONDS = 300
17+GRAFANA_UID = 472
18+GRAFANA_GID = 0
16 19
17 app = modal.App(APP_NAME)20 app = modal.App(APP_NAME)
18 grafana_secret = modal.Secret.from_name(21 grafana_secret = modal.Secret.from_name(
@@ -52,8 +55,15 @@ image = (
52 @modal.web_server(3000, startup_timeout=120)55 @modal.web_server(3000, startup_timeout=120)
53 def grafana() -> None:56 def grafana() -> None:
54 """Run a public Grafana login page; its data-source credentials stay server-side."""57 """Run a public Grafana login page; its data-source credentials stay server-side."""
58+ def drop_privileges() -> None:
59+ # Modal ignores the Docker image's USER instruction for Functions.
60+ # The upstream Grafana image uses unprivileged UID 472 and group 0.
61+ os.setgid(GRAFANA_GID)
62+ os.setuid(GRAFANA_UID)
63+
55 process = subprocess.Popen(64 process = subprocess.Popen(
56- ["grafana", "server", "--homepath=/usr/share/grafana", "--config=/etc/grafana/grafana.ini"]65+ ["grafana", "server", "--homepath=/usr/share/grafana", "--config=/etc/grafana/grafana.ini"],
66+ preexec_fn=drop_privileges,
57 )67 )
58 # modal.web_server waits for port 3000. Catch an immediate startup failure68 # modal.web_server waits for port 3000. Catch an immediate startup failure
59 # early so its cause appears in Modal logs rather than as a proxy timeout.69 # early so its cause appears in Modal logs rather than as a proxy timeout.