Compare commits

...

2 Commits

Author SHA1 Message Date
Grendgi
ec62cc04cf Add monitoring PF CI hygiene guard
All checks were successful
CI / hygiene (push) Successful in 2s
Build and Deploy / build-and-deploy (push) Successful in 34s
CI / go (push) Successful in 42s
CI / python (push) Successful in 2s
2026-06-12 16:42:38 +03:00
Grendgi
47e259fa28 Protect monitoring PF API with internal key 2026-06-12 16:32:12 +03:00
5 changed files with 60 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
#!/usr/bin/env bash
set -euo pipefail
fail=0
while IFS= read -r -d '' path; do
base="$(basename "$path")"
case "$base" in
.DS_Store|.env)
echo "::error file=$path::tracked local-only file is forbidden"
fail=1
;;
esac
case "$path" in
*node_modules/*|node_modules/*)
echo "::error file=$path::tracked node_modules content is forbidden"
fail=1
;;
*.tmp|*.temp|*.bak|*.orig|*.rej|*.zip|*.tar|*.tar.gz|*.tgz|*.rar|*.7z)
echo "::error file=$path::tracked temporary/archive artifact is forbidden"
fail=1
;;
esac
if [ -f "$path" ]; then
size="$(wc -c < "$path" | tr -d ' ')"
if [ "${size:-0}" -gt 52428800 ]; then
echo "::error file=$path::tracked file is larger than 50 MiB"
fail=1
fi
fi
done < <(git ls-files -z)
exit "$fail"

View File

@@ -5,8 +5,15 @@ on:
pull_request: pull_request:
jobs: jobs:
hygiene:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: bash .gitea/scripts/hygiene-check.sh
go: go:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: hygiene
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
@@ -22,6 +29,7 @@ jobs:
python: python:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: hygiene
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- run: python3 -m compileall app - run: python3 -m compileall app

View File

@@ -15,6 +15,7 @@ type Config struct {
ScrapeIntervalHours int ScrapeIntervalHours int
TGBotToken string TGBotToken string
TGBotUsername string TGBotUsername string
InternalAPIKey string
WorkerPython string WorkerPython string
WorkerModule string WorkerModule string
} }
@@ -28,6 +29,7 @@ func LoadConfig() Config {
ScrapeIntervalHours: max(1, envInt("SCRAPE_INTERVAL_HOURS", 4)), ScrapeIntervalHours: max(1, envInt("SCRAPE_INTERVAL_HOURS", 4)),
TGBotToken: env("TG_BOT_TOKEN", ""), TGBotToken: env("TG_BOT_TOKEN", ""),
TGBotUsername: strings.TrimPrefix(env("TG_BOT_USERNAME", ""), "@"), TGBotUsername: strings.TrimPrefix(env("TG_BOT_USERNAME", ""), "@"),
InternalAPIKey: env("INTERNAL_API_KEY", env("PORTAL_INTERNAL_API_KEY", "")),
WorkerPython: env("WORKER_PYTHON", "python"), WorkerPython: env("WORKER_PYTHON", "python"),
WorkerModule: env("WORKER_MODULE", "app.worker"), WorkerModule: env("WORKER_MODULE", "app.worker"),
} }

View File

@@ -29,6 +29,8 @@ func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, map[string]string{"service": "monitoring-pf", "ui": "portal", "api": "go"}) writeJSON(w, http.StatusOK, map[string]string{"service": "monitoring-pf", "ui": "portal", "api": "go"})
case !strings.HasPrefix(path, "/api/v1"): case !strings.HasPrefix(path, "/api/v1"):
writeError(w, http.StatusNotFound, "not found") writeError(w, http.StatusNotFound, "not found")
case !s.checkInternalAuth(w, r):
return
case path == "/api/v1/access/me" && r.Method == http.MethodGet: case path == "/api/v1/access/me" && r.Method == http.MethodGet:
s.accessMe(w, r) s.accessMe(w, r)
case path == "/api/v1/summary" && r.Method == http.MethodGet: case path == "/api/v1/summary" && r.Method == http.MethodGet:
@@ -50,6 +52,18 @@ func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
} }
func (s Server) checkInternalAuth(w http.ResponseWriter, r *http.Request) bool {
want := strings.TrimSpace(s.App.Cfg.InternalAPIKey)
if want == "" {
return true
}
if r.Header.Get("X-Internal-Key") != want {
writeError(w, http.StatusUnauthorized, "unauthorized")
return false
}
return true
}
func (s Server) apiPath(path string) string { func (s Server) apiPath(path string) string {
base := s.App.Cfg.PublicBasePath base := s.App.Cfg.PublicBasePath
if base != "" && path == base { if base != "" && path == base {

View File

@@ -5,5 +5,6 @@ metadata:
namespace: monitoring-pf namespace: monitoring-pf
type: Opaque type: Opaque
stringData: stringData:
INTERNAL_API_KEY: "36fe89ed40c01fdc54d3cf4e3fcacc8751dc456a4a1acd394e9fed48257c5734"
TG_BOT_TOKEN: "8942895371:AAGCWTr8g0FeqdM3QWmbV_3PxoSb5c_urf0" TG_BOT_TOKEN: "8942895371:AAGCWTr8g0FeqdM3QWmbV_3PxoSb5c_urf0"
ADMIN_CHAT_ID: "" ADMIN_CHAT_ID: ""