Prometheus on a node; panel under a URL prefix; open ports on migrate import
A panel without VPN (e.g. after the move) can use a Prometheus that lives on a node, such as the one with history left on the old server (node #0). Both directions go through the agent's WSS: the node's Prometheus scrapes 127.0.0.1:AGENT_METRICS_PORT/metrics, the agent checks its token and asks core (metrics → metrics_res, the same exposition as /metrics, at most once per 5 s per connection); the monitor tab reads it back through the agent, which forwards only GET /-/ready and /api/v1/query[_range]. The panel finds it by itself (local first, then the lowest-id node), and the install/remove buttons work on a panel without VPN. `monitor install` on a node without its own panel picks AGENT_METRICS_PORT, writes it to cli.env and restarts the agent; on the old server it keeps the collected data. The panel works behind nginx in a subfolder: relative asset and API paths, relative directory redirects, and the client address from X-Forwarded-For / X-Real-IP only from trusted proxies (loopback, TRUST_PROXY). migrate import opens the moved SUB_PORT/NODE_PORT in UFW when it is active.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// За reverse-proxy адрес клиента берётся из его заголовков, но только когда соединение пришло от
|
||||
// самого прокси (loopback или TRUST_PROXY): напрямую подставить себе чужой IP нельзя.
|
||||
func TestClientIP(t *testing.T) {
|
||||
nets := parseTrustProxy("10.0.0.0/8, 192.0.2.7, мусор")
|
||||
for _, c := range []struct {
|
||||
name, peer string
|
||||
xff []string
|
||||
real, want string
|
||||
}{
|
||||
{"напрямую, заголовки игнорируются", "203.0.113.5", []string{"1.2.3.4"}, "1.2.3.4", "203.0.113.5"},
|
||||
{"nginx на этой машине", "127.0.0.1", []string{"203.0.113.5"}, "", "203.0.113.5"},
|
||||
{"IPv6 loopback", "::1", []string{"2001:db8::5"}, "", "2001:db8::5"},
|
||||
{"клиент дописал себе адрес слева", "127.0.0.1", []string{"6.6.6.6, 203.0.113.5"}, "", "203.0.113.5"},
|
||||
{"цепочка доверенных прокси", "10.1.1.1", []string{"203.0.113.5, 10.2.2.2", "192.0.2.7"}, "", "203.0.113.5"},
|
||||
{"только X-Real-IP", "127.0.0.1", nil, "203.0.113.9", "203.0.113.9"},
|
||||
{"прокси без заголовков", "127.0.0.1", nil, "", "127.0.0.1"},
|
||||
{"мусор в X-Forwarded-For", "127.0.0.1", []string{"unknown"}, "203.0.113.9", "203.0.113.9"},
|
||||
{"чужой прокси не доверен", "198.51.100.1", []string{"203.0.113.5"}, "", "198.51.100.1"},
|
||||
} {
|
||||
h := http.Header{}
|
||||
for _, v := range c.xff {
|
||||
h.Add("X-Forwarded-For", v)
|
||||
}
|
||||
if c.real != "" {
|
||||
h.Set("X-Real-IP", c.real)
|
||||
}
|
||||
if got := clientIP(c.peer, h, nets); got != c.want {
|
||||
t.Errorf("%s: %s, ожидали %s", c.name, got, c.want)
|
||||
}
|
||||
}
|
||||
if len(nets) != 2 {
|
||||
t.Errorf("TRUST_PROXY разобран в %d сетей", len(nets))
|
||||
}
|
||||
}
|
||||
|
||||
// Каталог без слэша — относительный редирект: за прокси в подпапке абсолютный увёл бы из неё.
|
||||
func TestStaticRelativeRedirect(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
os.MkdirAll(filepath.Join(dir, "dist", "assets"), 0o755)
|
||||
os.WriteFile(filepath.Join(dir, "dist", "assets", "index.html"), []byte("x"), 0o644)
|
||||
a := &App{baseDir: dir}
|
||||
w := httptest.NewRecorder()
|
||||
a.static(&Ctx{W: w, R: httptest.NewRequest("GET", "/assets", nil)})
|
||||
if w.Code != 301 || w.Header().Get("Location") != "assets/" {
|
||||
t.Errorf("%d %q", w.Code, w.Header().Get("Location"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user