fix(tailscale): готовность демона, флаги tailscaled и нейтрализация standalone-сервиса

- удалён неподдерживаемый флаг --netfilter-mode=on, вызывавший ошибку старта на OpenWrt
- проба готовности парсит status --json (BackendState), а не exit-code: незалогиненный узел отвечает NeedsLogin с non-zero и раньше считался неготовым — корень «did not become ready» при первом входе
- перед стартом своего tailscaled останавливается и отключается init-скрипт официального пакета: его демон держал UDP 41641, из-за чего наш умирал мгновенно
- при сбое старта в лог пишется хвост tailscaled.log для диагностики
This commit is contained in:
Dushnilin
2026-08-26 19:03:16 +03:00
parent 9bc6fe28fd
commit 6a85384111
2 changed files with 60 additions and 5 deletions
@@ -310,12 +310,46 @@ function install_kernel_routing(section) {
return true;
}
// The official package ships its own init script; if it autostarted at boot,
// its daemon holds UDP 41641 and ours dies instantly. Neutralize it the same
// way component install does - stop + disable, idempotent.
function file_executable(path) {
let st = fs.stat(path);
return st != null && st.mode != null && (int(st.mode) & 73) != 0;
}
function neutralize_standalone_service() {
let init = "/etc/init.d/tailscale";
if (!file_executable(init))
return;
command_status(command_from_args([ init, "stop" ]) + " >/dev/null 2>&1");
command_status(command_from_args([ init, "disable" ]) + " >/dev/null 2>&1");
log_message("standalone tailscale service stopped and disabled", "info");
}
function log_file_tail(path) {
let data = "";
try {
data = as_string(fs.readfile(path)) || "";
} catch (e) {
return "";
}
let lines = split(trim(data), "\n");
let start = length(lines) > 5 ? length(lines) - 5 : 0;
let tail = [];
for (let i = start; i < length(lines); i++)
push(tail, lines[i]);
return join(" | ", tail);
}
function start_daemon(section) {
if (running_pid(section)) {
log_message("tailscaled for " + section_name(section) + " already running", "debug");
return true;
}
neutralize_standalone_service();
let runtime_dir = section_runtime_dir(section);
let socket_path = runtime_dir + "/tailscaled.sock";
let log_file = runtime_dir + "/tailscaled.log";
@@ -326,10 +360,10 @@ function start_daemon(section) {
"--tun=tailscale0",
"--statedir=" + state_dir,
"--socket=" + socket_path,
"--port=" + TAILSCALED_PORT,
"--netfilter-mode=on"
"--port=" + TAILSCALED_PORT
];
fs.unlink(log_file);
let cmdline = command_from_args(args) +
" >" + shell_quote(log_file) + " 2>&1 </dev/null & echo $!";
let output = trim(command_output(cmdline));
@@ -340,15 +374,24 @@ function start_daemon(section) {
}
write_file(section_pid_file(section), as_string(pid));
// Wait until the client socket answers before running `up`.
// Wait until the client socket answers before running `up`. Probe parses
// JSON instead of trusting the exit code: an unlogged-in node answers
// with BackendState=NeedsLogin and a non-zero exit code, which must still
// count as "daemon ready".
for (let attempt = 0; attempt < 30; attempt++) {
if (command_success_from_args([ TAILSCALE_BIN, "--socket", socket_path, "status", "--peers=false" ]))
let probe = command_output_from_args([ TAILSCALE_BIN, "--socket", socket_path, "status", "--json", "--peers=false" ]);
if (index(as_string(probe), "\"BackendState\"") >= 0)
return true;
if (!pid_alive(pid))
break;
command_success_from_args([ "sleep", "1" ]);
}
log_message("tailscaled did not become ready for " + section_name(section), "warn");
let tail = log_file_tail(log_file);
if (!pid_alive(pid))
log_message("tailscaled for " + section_name(section) + " exited during startup; log tail: " + (tail == "" ? "<empty>" : tail), "warn");
else
log_message("tailscaled did not become ready for " + section_name(section) + "; log tail: " + (tail == "" ? "<empty>" : tail), "warn");
return false;
}
+12
View File
@@ -89,7 +89,19 @@ run() {
run installed || fail "installed must succeed with stubs present"
# --- start ------------------------------------------------------------------
# Fake official init script: must be stopped+disabled before our daemon starts.
INIT_LOG="$WORK_DIR/init.log"
mkdir -p /etc/init.d
cat >"/etc/init.d/tailscale" <<EOF
#!/bin/sh
printf '%s\n' "\$*" >> '$INIT_LOG'
exit 0
EOF
chmod +x "/etc/init.d/tailscale"
run start-runtime || fail "start-runtime exited non-zero"
grep -Fq "stop" "$INIT_LOG" || fail "standalone tailscale service was not stopped"
grep -Fq "disable" "$INIT_LOG" || fail "standalone tailscale service was not disabled"
PID_FILE="$WORK_DIR/run/ts_native/tailscaled.pid"
[ -f "$PID_FILE" ] || fail "pid file was not written"