Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 31 additions & 22 deletions tailscale/DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,28 +40,36 @@ however, it is nice to know where you need to go later on.

## Configuration

This add-on has almost no additional configuration options for the
add-on itself.
The add-on by default exposes "Exit Node" capabilities that you can enable from
your Tailscale account. Additionally, if the Supervisor managed your network
(which is the default), the add-on will also advertise routes to your subnets on
all supported interfaces to Tailscale.

However, when logging in to Tailscale, you can configure your Tailscale
network right from their interface.
Consider disabling key expiry to avoid losing connection to your Home Assistant
device. See [Key expiry][tailscale_info_key_expiry] for more information.

Logging in to Tailscale, you can configure your Tailscale network right from
their interface.

<https://login.tailscale.com/>

The add-on exposes "Exit Node" capabilities that you can enable from your
Tailscale account. Additionally, if the Supervisor managed your network (which
is the default), the add-on will also advertise routes to your subnets on all
supported interfaces to Tailscale.
1. Navigate to the [Machines page][tailscale_machines] of the admin console, and
find your Home Assistant instance.

Consider disabling key expiry to avoid losing connection to your Home Assistant
device. See [Key expiry][tailscale_info_key_expiry] for more information.
1. Click on the **&hellip;** icon at the right side and select the "Edit route
settings..." option. The "Exit node" and "Subnet routes" functions can be
enabled here.

1. Click on the **&hellip;** icon at the right side and select the "Disable key
expiry" option.

```yaml
accept_dns: true
accept_routes: true
advertise_exit_node: true
advertise_connector: true
advertise_routes:
- local_subnets
- 192.168.1.0/24
- fd12:3456:abcd::/64
exit_node: 100.101.102.103
Expand Down Expand Up @@ -89,7 +97,7 @@ userspace_networking: true
If you are experiencing trouble with MagicDNS on this device and wish to
disable, you can do so using this option.

When not set, this option is enabled by default.
This option is enabled by default.

MagicDNS may cause issues if you run things like Pi-hole or AdGuard Home
on the same machine as this add-on. In such cases disabling `accept_dns`
Expand All @@ -103,7 +111,7 @@ your tailnet.

More information: [Subnet routers][tailscale_info_subnets]

When not set, this option is enabled by default.
This option is enabled by default.

### Option: `advertise_exit_node`

Expand All @@ -114,7 +122,7 @@ route all your public internet traffic as needed, like a consumer VPN.

More information: [Exit nodes][tailscale_info_exit_nodes]

When not set, this option is enabled by default.
This option is enabled by default.

**Note:** You can't advertise this device as an exit node and at the same time
specify an exit node to use. See also the "Option: `exit_node`" section of this
Expand All @@ -134,7 +142,7 @@ all nodes on the tailnet will use that IP address for their traffic egress.

More information: [App connectors][tailscale_info_app_connectors]

When not set, this option is enabled by default.
This option is enabled by default.

### Option: `advertise_routes`

Expand All @@ -149,8 +157,8 @@ If you want to disable this option, specify an empty list in the configuration

More information: [Subnet routers][tailscale_info_subnets]

When not set, the add-on by default will advertise routes to your subnets on all
supported interfaces.
The add-on by default will advertise routes to your subnets on all supported
interfaces by adding `local_subnets` to the list.

### Option: `exit_node`

Expand Down Expand Up @@ -209,7 +217,7 @@ This option allows you to enable Tailscale Serve or Funnel features to present
your Home Assistant instance with a valid certificate on your tailnet or on the
internet.

When not set, this option is disabled by default.
This option is disabled by default.

Tailscale can provide a TLS certificate for your Home Assistant instance within
your tailnet domain.
Expand Down Expand Up @@ -284,14 +292,14 @@ internet.

Only ports 443, 8443, and 10000 are allowed by Tailscale.

When not set, port 443 is used by default.
Port 443 is used by default.

### Option: `snat_subnet_routes`

This option allows subnet devices to see the traffic originating from the subnet
router, and this simplifies routing configuration.

When not set, this option is enabled by default.
This option is enabled by default.

To support advanced [Site-to-site networking][tailscale_info_site_to_site] (e.g.
to traverse multiple networks), you can disable this functionality, and follow
Expand All @@ -310,7 +318,7 @@ nodes, subnet routers, and app connectors), to only allow return packets for
existing outbound connections. Inbound packets that don't belong to an existing
connection are dropped.

When not set, this option is disabled by default.
This option is disabled by default.

### Option: `tags`

Expand All @@ -325,7 +333,7 @@ This add-on supports [Tailscale's Taildrop][tailscale_info_taildrop] feature,
which allows you to send files to your Home Assistant instance from other
Tailscale devices.

When not set, this option is enabled by default.
This option is enabled by default.

Received files are stored in the `/share/taildrop` directory.

Expand All @@ -335,7 +343,7 @@ The add-on uses [userspace networking mode][tailscale_info_userspace_networking]
to make your Home Assistant instance (and optionally the local subnets)
accessible within your tailnet.

When not set, this option is enabled by default.
This option is enabled by default.

If you need to access other clients on your tailnet from your Home Assistant
instance, disable userspace networking mode, which will create a `tailscale0`
Expand Down Expand Up @@ -463,3 +471,4 @@ SOFTWARE.
[tailscale_info_tags]: https://tailscale.com/kb/1068/tags
[tailscale_info_taildrop]: https://tailscale.com/kb/1106/taildrop
[tailscale_info_userspace_networking]: https://tailscale.com/kb/1112/userspace-networking
[tailscale_machines]: https://login.tailscale.com/admin/machines
44 changes: 30 additions & 14 deletions tailscale/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,37 @@ map:
- share:rw
ports:
41641/udp: null
options:
accept_dns: true
accept_routes: true
advertise_exit_node: true
advertise_connector: true
advertise_routes:
- local_subnets
log_level: info
login_server: "https://controlplane.tailscale.com"
share_homeassistant: disabled
share_on_port: 443
snat_subnet_routes: true
stateful_filtering: false
tags: []
taildrop: true
userspace_networking: true
schema:
accept_dns: bool?
accept_routes: bool?
advertise_exit_node: bool?
advertise_connector: bool?
accept_dns: bool
accept_routes: bool
advertise_exit_node: bool
advertise_connector: bool
advertise_routes:
- "match(^(((25[0-5]|(2[0-4]|1\\d|[1-9]?)\\d)\\.){3}(25[0-5]|(2[0-4]|1\\d|[1-9]?)\\d)\\/(3[0-2]|[12]?\\d)|[a-fA-F\\d.:]+:[a-fA-F\\d.:]+\\/(12[0-8]|(1[01]|[1-9]?)\\d))$)?"
- "match(^(?:local.subnets|(?:(?:25[0-5]|(?:2[0-4]|1\\d|[1-9]?)\\d)\\.){3}(?:25[0-5]|(?:2[0-4]|1\\d|[1-9]?)\\d)\\/(?:3[0-2]|[12]?\\d)|[a-fA-F\\d.:]+:[a-fA-F\\d.:]+\\/(?:12[0-8]|(?:1[01]|[1-9]?)\\d))$)"
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex pattern local.subnets uses a literal dot which matches any character. It should be local_subnets to match the exact string as used in the default configuration and documentation.

Suggested change
- "match(^(?:local.subnets|(?:(?:25[0-5]|(?:2[0-4]|1\\d|[1-9]?)\\d)\\.){3}(?:25[0-5]|(?:2[0-4]|1\\d|[1-9]?)\\d)\\/(?:3[0-2]|[12]?\\d)|[a-fA-F\\d.:]+:[a-fA-F\\d.:]+\\/(?:12[0-8]|(?:1[01]|[1-9]?)\\d))$)"
- "match(^(?:local_subnets|(?:(?:25[0-5]|(?:2[0-4]|1\\d|[1-9]?)\\d)\\.){3}(?:25[0-5]|(?:2[0-4]|1\\d|[1-9]?)\\d)\\/(?:3[0-2]|[12]?\\d)|[a-fA-F\\d.:]+:[a-fA-F\\d.:]+\\/(?:12[0-8]|(?:1[01]|[1-9]?)\\d))$)"

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See stage2_hook.sh above.

exit_node: "match(^(?:(?:(?:25[0-5]|(?:2[0-4]|1\\d|[1-9]?)\\d)\\.){3}(?:25[0-5]|(?:2[0-4]|1\\d|[1-9]?)\\d)|[a-fA-F\\d.:]+:[a-fA-F\\d.:]+|[a-fA-F\\d]{8}(?:-[a-fA-F\\d]{4}){3}-[a-fA-F\\d]{12}|auto:any)$)?"
log_level: list(trace|debug|info|notice|warning|error|fatal)?
login_server: url?
share_homeassistant: list(disabled|serve|funnel)?
share_on_port: match(^(443|8443|10000)$)?
snat_subnet_routes: bool?
stateful_filtering: bool?
log_level: list(trace|debug|info|notice|warning|error|fatal)
login_server: url
share_homeassistant: list(disabled|serve|funnel)
share_on_port: match(^(?:443|8443|10000)$)
snat_subnet_routes: bool
stateful_filtering: bool
tags:
- "match(^tag:[a-zA-Z][a-zA-Z0-9-]*$)?"
taildrop: bool?
userspace_networking: bool?
- "match(^tag:[a-zA-Z][a-zA-Z0-9-]*$)"
taildrop: bool
userspace_networking: bool
59 changes: 20 additions & 39 deletions tailscale/rootfs/etc/s6-overlay/s6-rc.d/post-tailscaled/run
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,29 @@ declare keyexpiry
# Default options
options+=(--hostname "$(bashio::info.hostname)")

# Accept magicDNS by default when not set, or when explicitly enabled
if ! bashio::config.has_value "accept_dns" || \
bashio::config.true "accept_dns";
then
# Accept magicDNS when explicitly enabled
if bashio::config.true "accept_dns"; then
options+=(--accept-dns)
else
options+=(--accept-dns=false)
fi

# Accept routes by default when not set, or when explicitly enabled
if ! bashio::config.has_value "accept_routes" || \
bashio::config.true "accept_routes";
then
# Accept routes when explicitly enabled
if bashio::config.true "accept_routes"; then
options+=(--accept-routes)
else
options+=(--accept-routes=false)
fi

# Check exit node configuration
if (! bashio::config.has_value "advertise_exit_node" || \
bashio::config.true "advertise_exit_node") && \
if bashio::config.true "advertise_exit_node" && \
bashio::config.has_value "exit_node"
then
bashio::exit.nok "You can't advertise this device as an exit node and at the same time specify an exit node to use"
fi

# Advertise as exit node by default when not set, or when explicitly enabled
if ! bashio::config.has_value "advertise_exit_node" || \
bashio::config.true "advertise_exit_node";
then
# Advertise as exit node when explicitly enabled
if bashio::config.true "advertise_exit_node"; then
options+=(--advertise-exit-node)
else
options+=(--advertise-exit-node=false)
Expand All @@ -61,42 +54,33 @@ else
options+=(--exit-node-allow-lan-access=false)
fi

# Advertise app connector by default when not set, or when explicitly enabled
if ! bashio::config.has_value "advertise_connector" || \
bashio::config.true "advertise_connector";
then
# Advertise app connector when explicitly enabled
if bashio::config.true "advertise_connector"; then
options+=(--advertise-connector)
else
options+=(--advertise-connector=false)
fi

# Get configured control server
if bashio::config.has_value "login_server";
then
login_server=$(bashio::config "login_server")
options+=(--login-server="${login_server}")
fi
login_server=$(bashio::config "login_server")
options+=(--login-server="${login_server}")

# Enable stateful filtering (it's disabled by default from v1.66.4)
if ! bashio::config.has_value "stateful_filtering" || \
bashio::config.false "stateful_filtering";
then
options+=(--stateful-filtering=false)
else
# Enable stateful filtering when explicitly enabled
if bashio::config.true "stateful_filtering"; then
options+=(--stateful-filtering)
else
options+=(--stateful-filtering=false)
fi

# Support advanced site-to-site networking, disable source addresses NAT
if ! bashio::config.has_value "snat_subnet_routes" || \
bashio::config.true "snat_subnet_routes";
then
# Enable source NAT when explicitly enabled
if bashio::config.true "snat_subnet_routes"; then
options+=(--snat-subnet-routes)
else
options+=(--snat-subnet-routes=false)
fi

# Get configured tags
tags=$(bashio::config "tags//[] | join(\",\")" "")
tags=$(bashio::config "tags | join(\",\")" "")
options+=(--advertise-tags="${tags}")

# Advertise subnet routes
Expand Down Expand Up @@ -161,8 +145,7 @@ fi

# Warn about colliding subnet routes if non-userspace networking and accepting routes are enabled
if bashio::config.false "userspace_networking" && \
(! bashio::config.has_value "accept_routes" || \
bashio::config.true "accept_routes");
bashio::config.true "accept_routes";
then
readarray -t colliding_routes < <( \
comm -1 -2 \
Expand All @@ -184,9 +167,7 @@ then
fi

# Notify about userspace networking
if ! bashio::config.has_value "userspace_networking" || \
bashio::config.true "userspace_networking";
then
if bashio::config.true "userspace_networking"; then
bashio::log.notice \
"The add-on uses userspace networking mode."
bashio::log.notice \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,4 @@ fi
echo "" >&3

# Set up serve or funnel
exec /opt/tailscale $(bashio::config 'share_homeassistant') --https=$(bashio::config 'share_on_port' '443') --set-path=/ "http://127.0.0.1:$(bashio::core.port)"
exec /opt/tailscale $(bashio::config 'share_homeassistant') --https=$(bashio::config 'share_on_port') --set-path=/ "http://127.0.0.1:$(bashio::core.port)"
6 changes: 2 additions & 4 deletions tailscale/rootfs/etc/s6-overlay/s6-rc.d/tailscaled/run
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@ if bashio::var.has_value "${udp_port}"; then
options+=(--port=${udp_port})
fi

# Use userspace networking by default when not set, or when explicitly enabled
if ! bashio::config.has_value "userspace_networking" || \
bashio::config.true "userspace_networking";
then
# Use userspace networking when explicitly enabled
if bashio::config.true "userspace_networking"; then
options+=(--tun=userspace-networking)
fi

Expand Down
21 changes: 7 additions & 14 deletions tailscale/rootfs/etc/s6-overlay/scripts/stage2_hook.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,30 +59,25 @@ if bashio::var.has_value "${proxy_and_funnel_port}"; then
fi

# Disable protect-subnets service when userspace-networking is enabled or accepting routes is disabled
if ! bashio::config.has_value "userspace_networking" || \
bashio::config.true "userspace_networking" || \
if bashio::config.true "userspace_networking" || \
bashio::config.false "accept_routes";
then
rm /etc/s6-overlay/s6-rc.d/post-tailscaled/dependencies.d/protect-subnets
fi

# If advertise_routes is configured, do not wait for the local network to be ready to collect subnet information
if bashio::config.exists "advertise_routes";
# If local subnets are not configured in advertise_routes, do not wait for the local network to be ready to collect subnet information
if ! bashio::config "advertise_routes" | grep -Eq "^local.subnets$";
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex pattern ^local.subnets$ uses a literal dot which matches any character. It should be ^local_subnets$ to match the exact string 'local_subnets' as defined in the configuration.

Suggested change
if ! bashio::config "advertise_routes" | grep -Eq "^local.subnets$";
if ! bashio::config "advertise_routes" | grep -Eq "^local_subnets$";

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was intentional, if users use "local-subnets", with a "-", it just works.
Change it to a strict match for "_"? (note to myself: also change the config.yaml)

then
rm /etc/s6-overlay/s6-rc.d/post-tailscaled/dependencies.d/local-network
fi

# Disable forwarding service when userspace-networking is enabled
if ! bashio::config.has_value "userspace_networking" || \
bashio::config.true "userspace_networking";
then
if bashio::config.true "userspace_networking"; then
rm /etc/s6-overlay/s6-rc.d/user/contents.d/forwarding
fi

# Disable mss-clamping service when userspace-networking is enabled
if ! bashio::config.has_value "userspace_networking" || \
bashio::config.true "userspace_networking";
then
if bashio::config.true "userspace_networking"; then
rm /etc/s6-overlay/s6-rc.d/user/contents.d/mss-clamping
fi

Expand All @@ -91,9 +86,7 @@ if bashio::config.false 'taildrop'; then
rm /etc/s6-overlay/s6-rc.d/user/contents.d/taildrop
fi

# Disable share-homeassistant service when share_homeassistant has not been explicitly enabled
if ! bashio::config.has_value 'share_homeassistant' || \
bashio::config.equals 'share_homeassistant' 'disabled'
then
# Disable share-homeassistant service when it has been explicitly disabled
if bashio::config.equals 'share_homeassistant' 'disabled'; then
rm /etc/s6-overlay/s6-rc.d/user/contents.d/share-homeassistant
fi
Loading