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:
@@ -184,6 +184,8 @@ func New() *CLI {
|
||||
{"NODE_PORT", env("NODE_PORT", "")},
|
||||
// IP ретрансляторов (старый сервер после переноса): им — доверие к заголовку PROXY.
|
||||
{"RELAY_FROM", env("RELAY_FROM", "")},
|
||||
// Reverse-proxy перед панелью (кроме loopback): от них — X-Forwarded-For / X-Real-IP.
|
||||
{"TRUST_PROXY", env("TRUST_PROXY", "")},
|
||||
// Когда панель приняла перенос: с этого момента считаются клиенты, узнавшие новый адрес.
|
||||
{"MOVED_IN_AT", env("MOVED_IN_AT", "")},
|
||||
// Вкладка «Перенос» пишет сюда порты, PANEL_MOVED и RELAY_FROM.
|
||||
@@ -211,6 +213,8 @@ func New() *CLI {
|
||||
{"CLI_ENV_FILE", cliEnv},
|
||||
{"AWGCTRL_PORT", env("AWGCTRL_PORT", "3005")},
|
||||
{"INTERNAL_AUTH_KEY_FILE", env("INTERNAL_AUTH_KEY_FILE", "/etc/amnezia/amneziawg/internal_auth_private.key")},
|
||||
// Prometheus на ноде (`monitor install`): здесь он скрейпит метрики панели.
|
||||
{"AGENT_METRICS_PORT", env("AGENT_METRICS_PORT", "")},
|
||||
},
|
||||
pidFile: "/tmp/awg-agent.pid", logFile: filepath.Join(c.logs, "awg-agent.log"),
|
||||
},
|
||||
@@ -249,6 +253,18 @@ func New() *CLI {
|
||||
return c
|
||||
}
|
||||
|
||||
// setSvcEnv — значение, записанное в cli.env уже после запуска CLI, для следующего start.
|
||||
func (c *CLI) setSvcEnv(name, key, value string) {
|
||||
s := c.svc[name]
|
||||
for i := range s.env {
|
||||
if s.env[i][0] == key {
|
||||
s.env[i][1] = value
|
||||
return
|
||||
}
|
||||
}
|
||||
s.env = append(s.env, [2]string{key, value})
|
||||
}
|
||||
|
||||
func (c *CLI) panelMoved() bool { return strings.ToLower(os.Getenv("PANEL_MOVED")) == "on" }
|
||||
|
||||
func without(s []string, v string) []string {
|
||||
|
||||
@@ -219,6 +219,14 @@ func (c *CLI) migrateImport(file string) {
|
||||
errorf(" Не удалось записать %s: %v", c.envFile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
// Подписка и хаб переезжают на порты старого сервера — их надо открыть, как install.sh.
|
||||
opened, failed := migrate.OpenFirewall(plan.SubPort, plan.NodePort)
|
||||
if len(opened) > 0 {
|
||||
fmt.Printf(" %s UFW: открыты %s\n", green("✓"), strings.Join(opened, " "))
|
||||
}
|
||||
if len(failed) > 0 {
|
||||
errorf(" UFW: не удалось открыть %s — откройте вручную: ufw allow <порт>/tcp", strings.Join(failed, " "))
|
||||
}
|
||||
c.restartFresh()
|
||||
|
||||
// Сертификат хаба приехал в архиве, а нет — awg-ui создаёт его при старте.
|
||||
|
||||
+48
-5
@@ -4,6 +4,8 @@ package cli
|
||||
// cli/src/monitor.ts). Работает от root (ставит пакет, пишет в /etc): панель сама пакетов не
|
||||
// ставит, она только просит (через awg-ctrl) или показывает эту команду. Всё слушает
|
||||
// 127.0.0.1: наружу Prometheus не торчит, а панель читает его по HTTP на той же машине.
|
||||
// На ноде без своей панели и скрейп, и чтение идут через агента: метрики панели он берёт у
|
||||
// core по WSS (AGENT_METRICS_PORT), а запросы вкладки «Монитор» пропускает к Prometheus.
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
@@ -11,6 +13,8 @@ import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -121,7 +125,8 @@ func copyFile(src, dst string) {
|
||||
}
|
||||
}
|
||||
|
||||
func writePromConfig(uiPort string) {
|
||||
// writePromConfig — target: порт панели на этой машине или агента ноды.
|
||||
func writePromConfig(target string) {
|
||||
if exists(promConf) {
|
||||
copyFile(promConf, promConf+".bak-"+backupStamp())
|
||||
}
|
||||
@@ -135,7 +140,7 @@ scrape_configs:
|
||||
scheme: http
|
||||
metrics_path: /metrics
|
||||
`+authBlock()+` static_configs:
|
||||
- targets: ['127.0.0.1:`+uiPort+`']
|
||||
- targets: ['127.0.0.1:`+target+`']
|
||||
`), 0o644)
|
||||
}
|
||||
|
||||
@@ -172,8 +177,22 @@ func (c *CLI) monitorInstall() {
|
||||
if !reDebian.MatchString(osFamily()) {
|
||||
mfail("Автоустановка поддерживает только Debian и Ubuntu. На другом дистрибутиве поставьте Prometheus сами и подключите его кнопкой в панели (токен метрик).")
|
||||
}
|
||||
uiPort := os.Getenv("UI_PORT")
|
||||
if uiPort == "" {
|
||||
// Нода без своей панели (в том числе сервер, с которого панель переехала): метрики панели
|
||||
// Prometheus получает от агента, а тот — от core.
|
||||
viaAgent := c.isNode && !contains(c.all, "ui")
|
||||
target, newPort := os.Getenv("UI_PORT"), false
|
||||
if viaAgent {
|
||||
if target = os.Getenv("AGENT_METRICS_PORT"); target == "" {
|
||||
if target = freeLocalPort(); target == "" {
|
||||
mfail("Не нашлось свободного порта для агента")
|
||||
}
|
||||
if err := c.writeEnvFile(updateEnvVar(c.readEnvFile(), "AGENT_METRICS_PORT", target)); err != nil {
|
||||
mfail("Не удалось записать " + c.envFile + ": " + err.Error())
|
||||
}
|
||||
c.setSvcEnv("agent", "AGENT_METRICS_PORT", target)
|
||||
newPort = true
|
||||
}
|
||||
} else if target == "" {
|
||||
mfail("На этой машине нет панели (UI_PORT не задан): Prometheus скрейпит метрики панели.")
|
||||
}
|
||||
// Чужой Prometheus не трогаем: перезаписать его prometheus.yml значит сломать чужой мониторинг.
|
||||
@@ -195,7 +214,12 @@ func (c *CLI) monitorInstall() {
|
||||
|
||||
mlog("2/4 токен и конфиг")
|
||||
writePromToken()
|
||||
writePromConfig(uiPort)
|
||||
writePromConfig(target)
|
||||
if newPort {
|
||||
mlog("агент отдаёт метрики панели на 127.0.0.1:" + target + " — перезапуск агента")
|
||||
c.stop("agent")
|
||||
c.start("agent")
|
||||
}
|
||||
|
||||
mlog("3/4 слушаем только " + promListen + ", хранение " + promRetention)
|
||||
setPromArgs()
|
||||
@@ -208,6 +232,9 @@ func (c *CLI) monitorInstall() {
|
||||
for range 30 {
|
||||
if promReady() {
|
||||
fmt.Printf("\n ✓ Prometheus работает на %s, метрики панели скрейпятся каждые 15 с.\n Во вкладке «Монитор» графики появятся через минуту-две.\n\n", promListen)
|
||||
if viaAgent {
|
||||
fmt.Print(" Панель читает его через агента этой ноды.\n\n")
|
||||
}
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
@@ -215,6 +242,22 @@ func (c *CLI) monitorInstall() {
|
||||
mfail("Prometheus не ответил за 30 с: journalctl -u prometheus")
|
||||
}
|
||||
|
||||
// freeLocalPort — свободный порт из того же диапазона, что у install.sh (32768–55535).
|
||||
func freeLocalPort() string {
|
||||
for range 50 {
|
||||
n, err := rand.Int(rand.Reader, big.NewInt(55535-32768+1))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
p := strconv.Itoa(32768 + int(n.Int64()))
|
||||
if l, err := net.Listen("tcp", "127.0.0.1:"+p); err == nil {
|
||||
l.Close()
|
||||
return p
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func yesNo(b bool, yes, no string) string {
|
||||
if b {
|
||||
return yes
|
||||
|
||||
Reference in New Issue
Block a user