Run Grafana as its unprivileged user
5f7ef9d parent: 9a2f9a8 modified
justfile +1 -1 | @@ -17,7 +17,7 @@ fire: | ||
| 17 | 17 | |
| 18 | 18 | # Export today's currently available atop history without waiting for midnight. |
| 19 | 19 | 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 | |
| 21 | 21 | |
| 22 | 22 | # Inspect the most recent scheduled-export output. |
| 23 | 23 | 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)" --initialize | 20 | + 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 | ||
| 6 | 6 | with ``modal deploy modal_grafana.py``. |
| 7 | 7 | """ |
| 8 | 8 | |
| 9 | +import os | |
| 9 | 10 | import subprocess |
| 10 | 11 | import time |
| 11 | 12 | |
| @@ -13,6 +14,8 @@ import modal | ||
| 13 | 14 | |
| 14 | 15 | APP_NAME = "atop-grafana" |
| 15 | 16 | IDLE_SECONDS = 300 |
| 17 | +GRAFANA_UID = 472 | |
| 18 | +GRAFANA_GID = 0 | |
| 16 | 19 | |
| 17 | 20 | app = modal.App(APP_NAME) |
| 18 | 21 | grafana_secret = modal.Secret.from_name( |
| @@ -52,8 +55,15 @@ image = ( | ||
| 52 | 55 | @modal.web_server(3000, startup_timeout=120) |
| 53 | 56 | def grafana() -> None: |
| 54 | 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 | 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 | 68 | # modal.web_server waits for port 3000. Catch an immediate startup failure |
| 59 | 69 | # 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 subprocess | 10 | import subprocess |
| 10 | import time | 11 | 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 = 300 | 16 | 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 failure | 68 | # 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. |