mirror of
https://github.com/Mo3he/Axis_Cam_Tailscale.git
synced 2026-08-19 21:56:56 +00:00
Much of this work is AXIS OS 13 preparation. Of the OS 13 breaking
changes, all are now addressed except one: recompiled against the
updated SDK for 64-bit time (Y2038), migrated to Manifest Schema v2
with declared OS compatibility, audited all binaries for executable
stack (all clean, GNU_STACK rw-), and verified the web UI end to end
over HTTPS. The only outstanding item is signing through the Axis
ACAP Portal, pending a registered vendorId.
The four ACAP 4 variants (aarch64, armv7hf, and their ROOT versions)
carried byte-identical copies of the C bridge, run script, web UI, and
Makefile per architecture, diverging only between standard and ROOT.
Merge them into a single common/app/ tree:
- param_bridge.c: proxy-port parameters gated behind -DHAS_PROXY_PORTS
(set via EXTRA_CFLAGS in the standard Dockerfiles); ROOT builds omit
them as before
- Tailscale_VPN_run: variant passed as $1 ("standard"/"root") selects
userspace vs kernel networking, port-collision checks, and IP
forwarding for advertised routes
- index.html: detects proxy support at runtime from the settings
response, hiding the proxy card and keeping the params out of save
requests on ROOT builds (fixes ROOT UI always showing proxy fields
and falsely reporting save errors)
Standard variants move to ACAP Native SDK 12.10.0 and Manifest Schema
v2 (vendorId, compatibleOsVersions); verified installable and working
on OS 10.12, 11.11, and 12.10, so OS 13 readiness costs no backward
compatibility. ROOT variants intentionally stay on SDK 1.15.1 since
OS 12+ never runs root apps.
All builds (including arm_acap3) now use the repository root as build
context with -f <variant>/Dockerfile; CI updated accordingly and a
.dockerignore added to keep the context lean. Tailscale binaries are
no longer tracked in git; *.eap outputs are now gitignored.
README: correct the standard variant's floor to OS 10.12+ and ROOT to
10.12-11.x (both live-verified), update build/update instructions for
the shared tree, and check off completed OS 13 readiness items.
178 lines
6.1 KiB
Bash
178 lines
6.1 KiB
Bash
#!/bin/sh
|
|
# Tailscale VPN run script — called by the param_bridge C binary.
|
|
# Config is sourced from $STATE_DIR/params.conf (written by param_bridge).
|
|
# $1 selects the variant: "standard" (userspace networking + local proxies)
|
|
# or "root" (kernel networking, no local proxy). Defaults to "standard".
|
|
VARIANT="${1:-standard}"
|
|
|
|
killall tailscaled 2>/dev/null || true
|
|
|
|
APP_DIR="/usr/local/packages/Tailscale_VPN"
|
|
STATE_DIR="$APP_DIR/localdata"
|
|
TAILSCALED_PATH="$APP_DIR/lib/tailscaled"
|
|
TAILSCALE_PATH="$APP_DIR/lib/tailscale"
|
|
SOCKET_PATH="$STATE_DIR/tailscaled.sock"
|
|
|
|
mkdir -p "$STATE_DIR"
|
|
chmod 755 $TAILSCALED_PATH
|
|
chmod 755 $TAILSCALE_PATH
|
|
|
|
# Defaults — overridden by sourcing params.conf written by param_bridge
|
|
CUSTOM_SERVER=""
|
|
AUTH_KEY=""
|
|
CONF_HTTP="8080"
|
|
CONF_SOCKS="1080"
|
|
ACCEPT_DNS="false"
|
|
ACCEPT_ROUTES="false"
|
|
ADVERTISE_ROUTES=""
|
|
|
|
if [ -f "$STATE_DIR/params.conf" ]; then
|
|
. "$STATE_DIR/params.conf"
|
|
fi
|
|
|
|
if [ "$VARIANT" = "root" ]; then
|
|
logger -t "Tailscale_VPN" "Starting (root mode): custom_server=${CUSTOM_SERVER:-(default)}"
|
|
else
|
|
logger -t "Tailscale_VPN" "Starting: http_port=$CONF_HTTP socks_port=$CONF_SOCKS custom_server=${CUSTOM_SERVER:-(default)}"
|
|
|
|
# Check whether a TCP port is already bound
|
|
is_port_in_use() {
|
|
local port=$1
|
|
local hex_port
|
|
hex_port=$(printf '%04X' "$port")
|
|
grep -q ":${hex_port} " /proc/net/tcp 2>/dev/null && return 0
|
|
grep -q ":${hex_port} " /proc/net/tcp6 2>/dev/null && return 0
|
|
return 1
|
|
}
|
|
|
|
if is_port_in_use "$CONF_HTTP"; then
|
|
logger -t "Tailscale_VPN" "ERROR: HTTP proxy port $CONF_HTTP is already in use. Change it in Settings."
|
|
exit 1
|
|
fi
|
|
if is_port_in_use "$CONF_SOCKS"; then
|
|
logger -t "Tailscale_VPN" "ERROR: SOCKS5 port $CONF_SOCKS is already in use. Change it in Settings."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
logger -t "Tailscale_VPN" "Starting tailscaled daemon"
|
|
if [ "$VARIANT" = "root" ]; then
|
|
$TAILSCALED_PATH \
|
|
--state="$STATE_DIR/tailscaled.state" \
|
|
--socket=$SOCKET_PATH \
|
|
>/dev/null 2>&1 &
|
|
else
|
|
$TAILSCALED_PATH \
|
|
--state="$STATE_DIR/tailscaled.state" \
|
|
--socket=$SOCKET_PATH \
|
|
--socks5-server=localhost:$CONF_SOCKS \
|
|
--outbound-http-proxy-listen=localhost:$CONF_HTTP \
|
|
--tun=userspace-networking \
|
|
>/dev/null 2>&1 &
|
|
fi
|
|
TAILSCALED_PID=$!
|
|
|
|
sleep 2
|
|
|
|
TAILSCALE_CMD="$TAILSCALE_PATH --socket=$SOCKET_PATH up --reset --hostname=$(hostname)"
|
|
|
|
if [ -n "$CUSTOM_SERVER" ]; then
|
|
TAILSCALE_CMD="$TAILSCALE_CMD --login-server $CUSTOM_SERVER"
|
|
fi
|
|
|
|
if [ -n "$AUTH_KEY" ]; then
|
|
TAILSCALE_CMD="$TAILSCALE_CMD --authkey $AUTH_KEY"
|
|
fi
|
|
|
|
if [ "$ACCEPT_DNS" = "true" ]; then
|
|
TAILSCALE_CMD="$TAILSCALE_CMD --accept-dns=true"
|
|
fi
|
|
|
|
if [ "$ACCEPT_ROUTES" = "true" ]; then
|
|
TAILSCALE_CMD="$TAILSCALE_CMD --accept-routes=true"
|
|
fi
|
|
|
|
# Advertise LAN subnets so this camera acts as a subnet router. Comma-separated
|
|
# CIDRs (e.g. 192.168.1.0/24,10.0.0.0/8). In userspace-networking mode the
|
|
# tailscaled netstack forwards tailnet traffic to these subnets, so no kernel IP
|
|
# forwarding is required. In kernel-networking (root) mode the host must
|
|
# forward packets between the tailnet and the LAN, so enable IP forwarding.
|
|
# Routes must still be approved in the Tailscale admin console either way.
|
|
if [ -n "$ADVERTISE_ROUTES" ]; then
|
|
if [ "$VARIANT" = "root" ]; then
|
|
echo 1 > /proc/sys/net/ipv4/ip_forward 2>/dev/null || true
|
|
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding 2>/dev/null || true
|
|
fi
|
|
TAILSCALE_CMD="$TAILSCALE_CMD --advertise-routes=$ADVERTISE_ROUTES"
|
|
fi
|
|
|
|
# Run `tailscale up` in the background and act on its outcome. If the node needs
|
|
# (re-)authentication, `up` blocks until the user logs in; backgrounding it
|
|
# ensures the status publisher below keeps running so the UI can surface the
|
|
# login URL (tailscaled reports BackendState=NeedsLogin + AuthURL while waiting).
|
|
# NOTE: `up` runs synchronously *inside* this backgrounded block so its real exit
|
|
# code is captured directly. We must NOT background `up` separately and `wait`
|
|
# for it from here, because in POSIX sh `wait` only works on children of the
|
|
# current shell — a subshell waiting on the parent's child returns 127.
|
|
{
|
|
eval "$TAILSCALE_CMD"
|
|
up_exit=$?
|
|
if [ "$up_exit" -eq 0 ]; then
|
|
if [ "$VARIANT" = "root" ]; then
|
|
logger -t "Tailscale_VPN" "Tailscale VPN is running (root mode)"
|
|
else
|
|
logger -t "Tailscale_VPN" "Tailscale VPN is running"
|
|
fi
|
|
# Auth succeeded with a one-time auth key — signal param_bridge to clear it
|
|
if [ -n "$AUTH_KEY" ]; then
|
|
: > "$STATE_DIR/authkey_clear"
|
|
fi
|
|
else
|
|
logger -t "Tailscale_VPN" "ERROR: tailscale up failed (exit $up_exit)"
|
|
fi
|
|
} &
|
|
TAILSCALE_UP_PID=$!
|
|
|
|
if [ "$VARIANT" != "root" ]; then
|
|
logger -t "Tailscale_VPN" "HTTP/HTTPS proxy: http://127.0.0.1:$CONF_HTTP"
|
|
logger -t "Tailscale_VPN" "SOCKS5 proxy: 127.0.0.1:$CONF_SOCKS"
|
|
fi
|
|
|
|
# Publish tailscale's real backend state as JSON for the web UI to consume.
|
|
# This is the authoritative connection signal (BackendState / Self.Online /
|
|
# TailscaleIPs / AuthURL) instead of scraping syslog. Served statically at
|
|
# /local/Tailscale_VPN/status.json.
|
|
STATUS_FILE="$APP_DIR/html/status.json"
|
|
|
|
publish_status() {
|
|
if "$TAILSCALE_PATH" --socket="$SOCKET_PATH" status --json > "$STATUS_FILE.tmp" 2>/dev/null; then
|
|
mv "$STATUS_FILE.tmp" "$STATUS_FILE" 2>/dev/null
|
|
chmod 644 "$STATUS_FILE" 2>/dev/null
|
|
else
|
|
rm -f "$STATUS_FILE.tmp" 2>/dev/null
|
|
fi
|
|
}
|
|
|
|
status_loop() {
|
|
while true; do
|
|
publish_status
|
|
sleep 5
|
|
done
|
|
}
|
|
status_loop &
|
|
STATUS_LOOP_PID=$!
|
|
|
|
# Clean up the status writer, up watcher, daemon and published status on
|
|
# stop/restart so param_bridge (which signals this script) leaves no orphans or
|
|
# stale state.
|
|
cleanup() {
|
|
[ -n "$STATUS_LOOP_PID" ] && kill "$STATUS_LOOP_PID" 2>/dev/null
|
|
[ -n "$TAILSCALE_UP_PID" ] && kill "$TAILSCALE_UP_PID" 2>/dev/null
|
|
[ -n "$TAILSCALED_PID" ] && kill "$TAILSCALED_PID" 2>/dev/null
|
|
rm -f "$STATUS_FILE" 2>/dev/null
|
|
exit 0
|
|
}
|
|
trap cleanup TERM INT
|
|
|
|
wait $TAILSCALED_PID
|