Skip to content

Commit

Permalink
Fix subnet protection for non-full network states (eg. limited, etc.) (
Browse files Browse the repository at this point in the history
  • Loading branch information
lmagyar authored Nov 15, 2024
1 parent de0aae9 commit 6810dd3
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 38 deletions.
14 changes: 14 additions & 0 deletions tailscale/rootfs/command/with-contenv-merge
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/command/execlineb -S0

ifelse
{
importas -D 0 S6_KEEP_ENV S6_KEEP_ENV
eltest 0${S6_KEEP_ENV} -eq 0
}
{
s6-envdir -Lfn -- /run/s6/container_environment
exec
$@
}

$@

This file was deleted.

22 changes: 22 additions & 0 deletions tailscale/rootfs/etc/NetworkManager/dispatcher.d/protect-subnets
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/command/with-contenv-merge bashio
# shellcheck shell=bash
# The shebang 'with-contenv-merge' above is identical with 'with-contenv', but doesn't clear the current environment containing the dispatcher variables

case "${NM_DISPATCHER_ACTION}" in
up|down|dhcp4-change|dhcp6-change)
bashio::log.info "Handling Network Manager action ${DEVICE_IP_IFACE-} ${NM_DISPATCHER_ACTION}"
unprotect-subnet-routes
if ! protect-subnet-routes; then
# Better stop add-on than risking losing all network connections
bashio::log.error "Failed to protect subnet routes. Halting add-on to prevent network loss."
echo -n 1 > /run/s6-linux-init-container-results/exitcode
exec /run/s6/basedir/bin/halt
fi
;;
connectivity-change)
bashio::log.debug "Unhandled Network Manager action ${NM_DISPATCHER_ACTION} ${CONNECTIVITY_STATE-}"
;;
*)
bashio::log.debug "Unhandled Network Manager action ${DEVICE_IP_IFACE-} ${NM_DISPATCHER_ACTION}"
;;
esac
52 changes: 31 additions & 21 deletions tailscale/rootfs/usr/bin/protect-subnet-routes
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@
# In case of non userspace networking,
# add local subnets to ip rules with higher priority than Tailscale's routing
# ==============================================================================
readonly PROTECTION_RULE_PRIORITY=5000

declare -a routes=()
declare route family
declare response
declare wait_counter=0

if bashio::config.false "userspace_networking"; then
if bashio::config.false "userspace_networking" && \
(! bashio::config.has_value "accept_routes" || bashio::config.true "accept_routes")
then
# If it is called after network configuration is changed, we need to drop cached network info
bashio::cache.flush_all
# It is possible to get "ERROR: Got unexpected response from the API: System is not ready with state: setup"
# So we wait a little
# So we wait a little, 60*5s = 300s = 5m
while ! bashio::api.supervisor GET "/addons/self/options/config" false &> /dev/null; do
if (( wait_counter++ == 18 )); then
if (( wait_counter++ == 60 )); then
bashio::log.error "Supervisor is unreachable"
bashio::exit.nok
fi
Expand All @@ -28,24 +31,31 @@ if bashio::config.false "userspace_networking"; then
fi

readarray -t routes < <(subnet-routes local)
if (( 0 < ${#routes[@]} )); then
bashio::log.info "Adding local subnets to ip rules with higher priority than Tailscale's routing,"
bashio::log.info "to prevent routing local subnets if the same subnet is routed within your tailnet."
fi
for route in "${routes[@]}"; do
if [[ "${route}" =~ .*:.* ]]; then
family="-6"
else
family="-4"
fi
bashio::log.info " Adding route ${route} to ip rules"
if ! response=$(ip "${family}" rule add to "${route}" priority 5000 table main 2>&1); then
if [[ "${response}" != "RTNETLINK answers: File exists" ]]; then
echo "${response}"
bashio::exit.nok
bashio::log.info "Adding local subnets to ip rules with higher priority than Tailscale's routing,"
bashio::log.info "to prevent routing local subnets if the same subnet is routed within your tailnet."
if (( 0 == ${#routes[@]} )); then
# Do not remote this warning, usually this is superfluous,
# but I've run into situation where Supervisor needed a restart to return valid interface address data
# (that seems to be a hard to reproduce bug, better have some log in the future than not.)
# See: https://github.com/home-assistant/supervisor/issues/5361
bashio::log.warning " There are no local subnets to protect!"
bashio::log.warning " Maybe this is a temporary situation due to configuration change underway."
else
for route in "${routes[@]}"; do
if [[ "${route}" =~ .*:.* ]]; then
family="-6"
else
bashio::log.notice " Route ${route} is already added to ip rules"
family="-4"
fi
fi
done
bashio::log.info " Adding route ${route} to ip rules"
if ! response=$(ip "${family}" rule add to "${route}" priority ${PROTECTION_RULE_PRIORITY} table main 2>&1); then
if [[ "${response}" != "RTNETLINK answers: File exists" ]]; then
echo "${response}"
bashio::exit.nok
else
bashio::log.notice " Route ${route} is already added to ip rules"
fi
fi
done
fi
fi
2 changes: 1 addition & 1 deletion tailscale/rootfs/usr/bin/subnet-routes
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function appendarray() {
readarray -t -O "${#array[@]}" array
}

if ! [[ $1 =~ ^(local|advertised)$ ]]; then
if ! [[ "${1-}" =~ ^(local|advertised)$ ]]; then
echo "Usage: subnet-routes local|advertised" 1>&2
exit 1
fi
Expand Down
7 changes: 5 additions & 2 deletions tailscale/rootfs/usr/bin/unprotect-subnet-routes
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
# In case of non userspace networking,
# remove local subnets from ip rules
# ==============================================================================
readonly PROTECTION_RULE_PRIORITY=5000

declare -a routes=()
declare route family

if bashio::config.false "userspace_networking"; then
if bashio::config.false "userspace_networking" && \
(! bashio::config.has_value "accept_routes" || bashio::config.true "accept_routes")
then
readarray -t routes < <( \
{ ip -4 rule list; ip -6 rule list; } \
| { grep -E '^5000:' || true ;} \
| { grep -E "^${PROTECTION_RULE_PRIORITY}:" || true ;} \
| sed -nr 's/^\d+:\s+from all to ([^\s]+) lookup main$/\1/p')
for route in "${routes[@]}"; do
bashio::log.info "Removing route ${route} from ip rules"
Expand Down

0 comments on commit 6810dd3

Please sign in to comment.