|
1
|
#!/bin/sh
|
|
2
|
# -----------------------------------------------------------------------------
|
|
3
|
# FishMon365 Tailscale Fix – Production Installer
|
|
4
|
# -----------------------------------------------------------------------------
|
|
5
|
#
|
|
6
|
# Author: Per Otto Opstad
|
|
7
|
# Company: ITVakta AS / FishMon365
|
|
8
|
# Website: https://www.fishmon365.com
|
|
9
|
# Contact: per@itvakta.no
|
|
10
|
#
|
|
11
|
# Created: 2026-04
|
|
12
|
# Version: 1.0
|
|
13
|
#
|
|
14
|
# Purpose:
|
|
15
|
# Workaround for Tailscale instability on pfSense where auth-key is reused
|
|
16
|
# on every service start, causing nodes to enter a broken state
|
|
17
|
# (e.g. "invalid key", "logged out", "NoState").
|
|
18
|
#
|
|
19
|
# This installer:
|
|
20
|
# - Deploys a watchdog script for automatic recovery
|
|
21
|
# - Disables repeated use of auth-key in pfSense rc script
|
|
22
|
# - Ensures persistent and self-healing Tailscale operation
|
|
23
|
#
|
|
24
|
# Usage:
|
|
25
|
# cd /tmp && rm -f install_tailscale_fix.sh && \
|
|
26
|
# fetch -qo install_tailscale_fix.sh \
|
|
27
|
# https://your.domain.xxx/tailscale/install_tailscale_fix.sh && \
|
|
28
|
# chmod 700 install_tailscale_fix.sh && \
|
|
29
|
# sh ./install_tailscale_fix.sh
|
|
30
|
#
|
|
31
|
# Notes:
|
|
32
|
# - Safe to run multiple times (idempotent)
|
|
33
|
# - Designed specifically for pfSense environments
|
|
34
|
# - Requires Tailscale package to be installed
|
|
35
|
#
|
|
36
|
# Changelog:
|
|
37
|
# 1.0 (2026-04)
|
|
38
|
# - Initial production version
|
|
39
|
# - Added watchdog-based recovery
|
|
40
|
# - Added automatic rc-script patching
|
|
41
|
#
|
|
42
|
# -----------------------------------------------------------------------------
|
|
43
|
#
|
|
44
|
set -eu
|
|
45
|
|
|
46
|
# === Variabler ===
|
|
47
|
WATCHDOG_SCRIPT="/root/tailscale_watchdog.sh"
|
|
48
|
RCFILE="/usr/local/etc/rc.d/pfsense_tailscaled"
|
|
49
|
CRON_SCHEDULE="*/5 * * * *"
|
|
50
|
CRON_LINE="${CRON_SCHEDULE} ${WATCHDOG_SCRIPT}"
|
|
51
|
TMP_CRON="/tmp/tailscale_watchdog.cron.$$"
|
|
52
|
LOCKFILE="/tmp/tailscale_watchdog.lock"
|
|
53
|
|
|
54
|
cleanup() {
|
|
55
|
rm -f "$TMP_CRON" 2>/dev/null || true
|
|
56
|
}
|
|
57
|
trap cleanup EXIT INT TERM
|
|
58
|
|
|
59
|
require_root() {
|
|
60
|
if [ "$(id -u)" -ne 0 ]; then
|
|
61
|
echo "ERROR: Dette skriptet må kjøres som root." >&2
|
|
62
|
exit 1
|
|
63
|
fi
|
|
64
|
}
|
|
65
|
|
|
66
|
check_environment() {
|
|
67
|
echo "[1/6] Sjekker miljø..."
|
|
68
|
|
|
69
|
if [ ! -f "$RCFILE" ]; then
|
|
70
|
echo "ERROR: Fant ikke rc-script: $RCFILE" >&2
|
|
71
|
echo "Er dette riktig pfSense-boks med Tailscale installert?" >&2
|
|
72
|
exit 1
|
|
73
|
fi
|
|
74
|
|
|
75
|
if [ ! -x /usr/local/bin/tailscale ]; then
|
|
76
|
echo "ERROR: Fant ikke /usr/local/bin/tailscale" >&2
|
|
77
|
echo "Sørg for at Tailscale-pakken er installert først." >&2
|
|
78
|
exit 1
|
|
79
|
fi
|
|
80
|
|
|
81
|
if [ ! -x /usr/sbin/service ]; then
|
|
82
|
echo "ERROR: Fant ikke /usr/sbin/service" >&2
|
|
83
|
exit 1
|
|
84
|
fi
|
|
85
|
|
|
86
|
if ! command -v crontab >/dev/null 2>&1; then
|
|
87
|
echo "ERROR: Fant ikke crontab-kommandoen." >&2
|
|
88
|
exit 1
|
|
89
|
fi
|
|
90
|
}
|
|
91
|
|
|
92
|
write_watchdog_script() {
|
|
93
|
echo "[2/6] Oppretter/oppdaterer watchdog-script..."
|
|
94
|
|
|
95
|
cat > "$WATCHDOG_SCRIPT" <<'EOF'
|
|
96
|
#!/bin/sh
|
|
97
|
|
|
98
|
LOCKFILE="/tmp/tailscale_watchdog.lock"
|
|
99
|
RCFILE="/usr/local/etc/rc.d/pfsense_tailscaled"
|
|
100
|
TS="/usr/local/bin/tailscale"
|
|
101
|
SERVICE="/usr/sbin/service"
|
|
102
|
RC_SERVICE="pfsense_tailscaled"
|
|
103
|
LOGTAG="tailscale-watchdog"
|
|
104
|
|
|
105
|
log() {
|
|
106
|
logger -t "$LOGTAG" "$1"
|
|
107
|
}
|
|
108
|
|
|
109
|
cleanup() {
|
|
110
|
rm -f "$LOCKFILE"
|
|
111
|
}
|
|
112
|
trap cleanup EXIT INT TERM
|
|
113
|
|
|
114
|
[ -e "$LOCKFILE" ] && exit 0
|
|
115
|
touch "$LOCKFILE"
|
|
116
|
|
|
117
|
get_status() {
|
|
118
|
"$TS" status 2>&1 || true
|
|
119
|
}
|
|
120
|
|
|
121
|
is_known_error() {
|
|
122
|
echo "$1" | grep -Eiq 'logged out|invalid key|API key does not exist|NoState'
|
|
123
|
}
|
|
124
|
|
|
125
|
is_healthy() {
|
|
126
|
"$TS" ip -4 >/dev/null 2>&1
|
|
127
|
}
|
|
128
|
|
|
129
|
patch_rcfile_if_needed() {
|
|
130
|
if grep -q '^pfsense_tailscaled_up_flags="--auth-key=' "$RCFILE"; then
|
|
131
|
cp "$RCFILE" "${RCFILE}.bak_watchdog" 2>/dev/null || true
|
|
132
|
sed -i '' 's/^pfsense_tailscaled_up_flags="--auth-key=/#pfsense_tailscaled_up_flags="--auth-key=/' "$RCFILE"
|
|
133
|
log "Patched rc file (disabled auth-key)"
|
|
134
|
fi
|
|
135
|
}
|
|
136
|
|
|
137
|
# Enforce patch every run
|
|
138
|
patch_rcfile_if_needed
|
|
139
|
|
|
140
|
STATUS="$(get_status)"
|
|
141
|
|
|
142
|
if is_healthy && ! is_known_error "$STATUS"; then
|
|
143
|
exit 0
|
|
144
|
fi
|
|
145
|
|
|
146
|
if ! is_known_error "$STATUS"; then
|
|
147
|
exit 0
|
|
148
|
fi
|
|
149
|
|
|
150
|
log "Detected known Tailscale error"
|
|
151
|
log "Restarting $RC_SERVICE"
|
|
152
|
$SERVICE "$RC_SERVICE" restart >/dev/null 2>&1
|
|
153
|
sleep 15
|
|
154
|
|
|
155
|
STATUS="$(get_status)"
|
|
156
|
|
|
157
|
if is_healthy && ! is_known_error "$STATUS"; then
|
|
158
|
log "Recovered after restart"
|
|
159
|
exit 0
|
|
160
|
fi
|
|
161
|
|
|
162
|
log "Still failing after restart"
|
|
163
|
exit 1
|
|
164
|
EOF
|
|
165
|
|
|
166
|
chmod 700 "$WATCHDOG_SCRIPT"
|
|
167
|
chown root:wheel "$WATCHDOG_SCRIPT" 2>/dev/null || true
|
|
168
|
}
|
|
169
|
|
|
170
|
install_or_update_cron() {
|
|
171
|
echo "[3/6] Oppretter/oppdaterer cron..."
|
|
172
|
|
|
173
|
{
|
|
174
|
crontab -l 2>/dev/null | grep -v "$WATCHDOG_SCRIPT" || true
|
|
175
|
echo "$CRON_LINE"
|
|
176
|
} > "$TMP_CRON"
|
|
177
|
|
|
178
|
crontab "$TMP_CRON"
|
|
179
|
}
|
|
180
|
|
|
181
|
run_fix_now() {
|
|
182
|
echo "[4/6] Kjører fix nå..."
|
|
183
|
rm -f "$LOCKFILE" 2>/dev/null || true
|
|
184
|
"$WATCHDOG_SCRIPT" || true
|
|
185
|
}
|
|
186
|
|
|
187
|
verify_installation() {
|
|
188
|
echo "[5/6] Verifiserer..."
|
|
189
|
|
|
190
|
CRON_MATCH="$(crontab -l 2>/dev/null | grep -F "$WATCHDOG_SCRIPT" || true)"
|
|
191
|
|
|
192
|
if [ -n "$CRON_MATCH" ]; then
|
|
193
|
echo "OK: Cron-linje funnet:"
|
|
194
|
echo " $CRON_MATCH"
|
|
195
|
else
|
|
196
|
echo "WARNING: Fant ikke cron-linje for watchdog."
|
|
197
|
fi
|
|
198
|
|
|
199
|
echo
|
|
200
|
echo "Auth-key linjer i rc-script:"
|
|
201
|
grep -n 'auth-key' "$RCFILE" || true
|
|
202
|
|
|
203
|
echo
|
|
204
|
echo "Tailscale status (kort):"
|
|
205
|
if /usr/local/bin/tailscale ip -4 >/dev/null 2>&1; then
|
|
206
|
TSIP="$(/usr/local/bin/tailscale ip -4 | head -n 1)"
|
|
207
|
echo " IPv4: $TSIP"
|
|
208
|
else
|
|
209
|
echo " IPv4: utilgjengelig"
|
|
210
|
fi
|
|
211
|
/usr/local/bin/tailscale status 2>/dev/null | head -n 5 || true
|
|
212
|
|
|
213
|
echo
|
|
214
|
echo "Siste watchdog-linjer i system.log:"
|
|
215
|
grep 'tailscale-watchdog' /var/log/system.log 2>/dev/null | tail -n 10 || true
|
|
216
|
}
|
|
217
|
|
|
218
|
print_summary() {
|
|
219
|
echo "--------------------------------------"
|
|
220
|
echo "Installasjon fullført"
|
|
221
|
echo "Watchdog: $WATCHDOG_SCRIPT"
|
|
222
|
echo "RC-script: $RCFILE"
|
|
223
|
echo "Cron: $CRON_SCHEDULE"
|
|
224
|
echo
|
|
225
|
echo "Nyttige kommandoer:"
|
|
226
|
echo " Sjekk cron: crontab -l"
|
|
227
|
echo " Kjør manuelt: $WATCHDOG_SCRIPT"
|
|
228
|
echo " Sjekk status: tailscale status"
|
|
229
|
echo " Sjekk IP: tailscale ip -4"
|
|
230
|
echo " Sjekk logg: grep tailscale-watchdog /var/log/system.log | tail -n 50"
|
|
231
|
echo " Restart TS: service pfsense_tailscaled restart"
|
|
232
|
echo " Sjekk auth-key: grep -n auth-key $RCFILE"
|
|
233
|
}
|
|
234
|
|
|
235
|
echo "=== FishMon365 Tailscale Fix Installer ==="
|
|
236
|
|
|
237
|
require_root
|
|
238
|
check_environment
|
|
239
|
write_watchdog_script
|
|
240
|
install_or_update_cron
|
|
241
|
run_fix_now
|
|
242
|
verify_installation
|
|
243
|
print_summary
|