Dockerized Tailscale forwarder for LAN/Tailscale traffic via macvlan & iptables.
1.2K
menggatot/tailforwarder)This Docker image allows you to forward network traffic between your local LAN and your Tailscale network, or vice-versa, by running one or more container instances. Each instance utilizes a macvlan network to obtain its own IP address on your LAN and employs iptables for granular traffic control based on its configured scenario.
Source code and issues: https://github.com/menggatot/tailforwarder
docker-compose.yml.eth0).Below is an example docker-compose.yml for running multiple instances, each for a different scenario.
version: '3.8'
services:
tailforwarder-one: # Scenario: Local LAN to a specific Tailscale IP
image: menggatot/tailforwarder:latest
container_name: tailforwarder-one
hostname: tailforwarder-one
restart: unless-stopped
cap_add: [NET_ADMIN]
devices: ["/dev/net/tun:/dev/net/tun"]
sysctls:
net.ipv4.ip_forward: 1
net.ipv6.conf.all.forwarding: 1
environment:
- TS_AUTHKEY=${TS_AUTHKEY} # From .env file
- TS_HOSTNAME=tailforwarder-one
- TS_USERSPACE=false
- TS_STATE_DIR=/var/lib/tailscale
- TS_ACCEPT_ROUTES=false
- MACVLAN_IFACE=eth0
- TS_PEER_UDP_PORT=41641 # Unique port
- ENABLE_LOCAL_TO_TS=true
- LISTEN_IP_ON_MACVLAN=10.10.0.10 # This instance's LAN IP
- TARGET_TS_IP_FROM_LOCAL=100.x.y.z # Target Tailscale IP
- ENABLE_TS_TO_LOCAL=false
volumes:
- ./tailscale_state_one:/var/lib/tailscale # Unique state volume
networks:
macvlan_net:
ipv4_address: 10.10.0.10 # Static LAN IP for this instance
tailforwarder-two: # Scenario: Local LAN to an IP in an advertised Tailscale route
image: menggatot/tailforwarder:latest
container_name: tailforwarder-two
hostname: tailforwarder-two
restart: unless-stopped
cap_add: [NET_ADMIN]
devices: ["/dev/net/tun:/dev/net/tun"]
sysctls:
net.ipv4.ip_forward: 1
net.ipv6.conf.all.forwarding: 1
environment:
- TS_AUTHKEY=${TS_AUTHKEY} # From .env file
- TS_HOSTNAME=tailforwarder-two
- TS_USERSPACE=false
- TS_STATE_DIR=/var/lib/tailscale
- TS_ACCEPT_ROUTES=true # Required for this scenario
- MACVLAN_IFACE=eth0
- TS_PEER_UDP_PORT=41642 # Unique port
- ENABLE_LOCAL_TO_TS=true
- LISTEN_IP_ON_MACVLAN=10.10.0.20 # This instance's LAN IP
- TARGET_TS_IP_FROM_LOCAL=192.168.A.B # Target IP in an advertised TS subnet
- ENABLE_TS_TO_LOCAL=false
volumes:
- ./tailscale_state_two:/var/lib/tailscale # Unique state volume
networks:
macvlan_net:
ipv4_address: 10.10.0.20 # Static LAN IP for this instance
tailforwarder-three: # Scenario: Tailscale Network to a specific Local LAN IP
image: menggatot/tailforwarder:latest
container_name: tailforwarder-three
hostname: tailforwarder-three
restart: unless-stopped
cap_add: [NET_ADMIN]
devices: ["/dev/net/tun:/dev/net/tun"]
sysctls:
net.ipv4.ip_forward: 1
net.ipv6.conf.all.forwarding: 1
environment:
- TS_AUTHKEY=${TS_AUTHKEY} # From .env file
- TS_HOSTNAME=tailforwarder-three
- TS_USERSPACE=false
- TS_STATE_DIR=/var/lib/tailscale
- MACVLAN_IFACE=eth0
- TS_PEER_UDP_PORT=41643 # Unique port
- ENABLE_TS_TO_LOCAL=true
- DESTINATION_IP_FROM_TS=10.10.0.50 # Target local device IP
- ENABLE_LOCAL_TO_TS=false
volumes:
- ./tailscale_state_three:/var/lib/tailscale # Unique state volume
networks:
macvlan_net:
ipv4_address: 10.10.0.30 # Static LAN IP for this instance
tailforwarder-exit: # Scenario: Exit Node for routing internet traffic
image: menggatot/tailforwarder:latest
container_name: tailforwarder-exit
hostname: tailforwarder-exit
restart: unless-stopped
cap_add: [NET_ADMIN]
devices: ["/dev/net/tun:/dev/net/tun"]
sysctls:
net.ipv4.ip_forward: 1
net.ipv6.conf.all.forwarding: 1
environment:
- TS_AUTHKEY=${TS_AUTHKEY} # From .env file
- TS_HOSTNAME=tailforwarder-exit
- TS_USERSPACE=false
- TS_STATE_DIR=/var/lib/tailscale
- TS_ACCEPT_DNS=true # Optional: Accept Tailscale DNS configuration
- TS_EXTRA_ARGS=--advertise-exit-node # Required for exit node
- MACVLAN_IFACE=eth0
- TS_PEER_UDP_PORT=41644 # Unique port
- ENABLE_EXIT_NODE=true # Enable exit node functionality
- ENABLE_LOCAL_TO_TS=false
- ENABLE_TS_TO_LOCAL=false
volumes:
- ./tailscale_state_exit:/var/lib/tailscale # Unique state volume
networks:
macvlan_net:
ipv4_address: 10.10.0.40 # Static LAN IP for this instance
networks:
macvlan_net:
driver: macvlan
driver_opts:
parent: eth0 # IMPORTANT: Change 'eth0' to your host's physical LAN interface
ipam:
config:
- subnet: 10.10.0.0/24 # IMPORTANT: Adjust to your LAN's subnet
gateway: 10.10.0.1 # IMPORTANT: Adjust to your LAN's gateway
# ip_range: 10.10.0.160/28 # Optional: if you want Docker to pick from a pool, but static is recommended per service.
# You would also create corresponding state directories on your host:
# ./tailscale_state_one
# ./tailscale_state_two
# ./tailscale_state_three
# ./tailscale_state_exit
Create a .env file in the same directory as your docker-compose.yml. This file is primarily for your TS_AUTHKEY:
# .env file
# --- Required Tailscale Authentication Key ---
# This key will be used by all tailforwarder instances.
TS_AUTHKEY=tskey-your-auth-key-goes-here
# Other variables like TS_HOSTNAME, TS_ROUTES_ADVERTISE, etc., are now set
# per-service directly in the docker-compose.yml file if needed.
Important docker-compose.yml notes:
menggatot/tailforwarder:latest is used.macvlan_net settings (parent, subnet, gateway) to match your host and LAN configuration.tailforwarder-one, tailforwarder-two, etc.) must have a unique ipv4_address on the macvlan_net.TS_PEER_UDP_PORT if running on the same Docker host.TS_STATE_DIR (e.g., ./tailscale_state_one, ./tailscale_state_two) to maintain separate Tailscale node identities.environment section of each service.The main configuration is done via environment variables. TS_AUTHKEY is global (set in .env), while most other variables are configured per instance within the docker-compose.yml.
.env file)TS_AUTHKEY: (Required) Your Tailscale authentication key. This is used by the entrypoint.sh script to log each instance into your Tailscale network.docker-compose.yml for each service)TS_HOSTNAME: The hostname this specific container instance will use on the Tailscale network (e.g., tailforwarder-one).TS_ACCEPT_ROUTES: Set to true if this instance should accept routes advertised by other nodes on your Tailscale network. Necessary for forwarding traffic to IPs within those advertised routes.TS_ACCEPT_DNS: Set to true to accept Tailscale's DNS configuration. Default: false. When enabled, the container will use Tailscale's DNS servers (including MagicDNS). Leave as false (default) for most forwarding scenarios.TS_USERSPACE: Determines if Tailscale runs in userspace or kernel mode. false (kernel mode) is recommended. Default: false.TS_STATE_DIR: Directory inside the container for Tailscale state. Default: /var/lib/tailscale. Mapped to a unique persistent host volume per instance.TS_PEER_UDP_PORT: Specifies the UDP port tailscaled for this instance should listen on. Must be unique per instance on the same host. Example: 41641.TS_ROUTES (or TS_ROUTES_ADVERTISE): Comma-separated list of local subnets this instance should advertise to your Tailscale network.TS_TAGS (or TS_TAGS_ADVERTISE): Comma-separated list of tags to apply to this instance on Tailscale.TS_EXTRA_ARGS: Allows passing additional flags to the tailscale up command for this instance.iptables-config.sh, per instance)MACVLAN_IFACE: (Required if any forwarding is enabled) The name of the macvlan network interface inside the container. Typically eth0.ENABLE_LOCAL_TO_TS: Set to true to enable forwarding from your Local LAN (via this instance's macvlan IP) to Tailscale.
LISTEN_IP_ON_MACVLAN: The macvlan IP of this container instance. Traffic from LAN to this IP gets forwarded. Must match the ipv4_address for this service.TARGET_TS_IP_FROM_LOCAL: The destination IP on the Tailscale network (a direct Tailscale node IP like 100.x.y.z, or an IP within an advertised subnet like 192.168.A.B).EXCLUDE_PORTS_TCP: Comma-separated TCP ports to exclude from this forwarding rule.EXCLUDE_PORTS_UDP: Comma-separated UDP ports to exclude from this forwarding rule.ENABLE_TS_TO_LOCAL: Set to true to enable forwarding from the Tailscale network (to this instance's Tailscale IP) to a specific IP on your Local LAN.
DESTINATION_IP_FROM_TS: The actual IP address of the target device on your local LAN that you want to expose to Tailscale.ENABLE_EXIT_NODE: Set to true to configure the container as a Tailscale exit node.
TS_EXTRA_ARGS: Should include --advertise-exit-node to advertise the exit node capability.TS_ACCEPT_DNS: Optionally set to true to accept Tailscale's DNS configuration.ENABLE_LOCAL_TO_TS and ENABLE_TS_TO_LOCAL: Should both be set to false when using exit node mode.Typically, for a given instance, only one of ENABLE_LOCAL_TO_TS, ENABLE_TS_TO_LOCAL, or ENABLE_EXIT_NODE will be true, to define its specific role.
Content type
Image
Digest
sha256:e23b829ba…
Size
58.8 MB
Last updated
8 days ago
docker pull menggatot/tailforwarder:dbf9917095a8ef6b1d68336bcfbbff869028ef31