Expose local services to your Tailnet and beyond with Serve and Funnel
I am a big fan of Tailscale and I recently discovered that it allows you to expose local running services on your system to other devices on your Tailnet and even to the public internet complete with TLS. The 2 services I will be highlighting today are: Serve and Funnel
Both of these services require that you enable HTTPS Certificates for your Tailnet.
Tailscale Serve
The Serve function in Tailscale allows you to route traffic from other devices on your Tailnet to a local service running on your device. This means for example I can be running a docker container on my mac at home that only exposes port 8080 on localhost and provide an encrypted endpoint I can use to connect from my phone, ipad or any other device on my Tailnet.
Testing
An easy way to test this is with the whoami
docker container:
docker run -d --rm -p 8180:80 traefik/whoami
Curl the container endpoint to ensure it is running: curl http://localhost:8180
callisto :: ~ » curl http://localhost:8180
Hostname: ee16ef47f1c1
IP: 127.0.0.1
IP: ::1
IP: 172.17.0.2
RemoteAddr: 172.17.0.1:37056
GET / HTTP/1.1
Host: localhost:8180
User-Agent: curl/8.7.1
Accept: */*
To expose this service to our Tailnet we will run the serve command and specify the port, 8180
in this case:
callisto :: ~ » tailscale serve 8180
Available within your tailnet:
https://io.tail36707.ts.net/
|-- proxy http://127.0.0.1:8180
Press Ctrl+C to exit.
From another device connected to our Tailscale network (Tailnet), we can now browse or curl our TLS-enabled endpoint.
ryan@io:~|⇒ tailscale status | grep io
100.93.71.112 io 74wzsgmt6m@ macOS -
ryan@io:~|⇒ curl https://callisto.tail36707.ts.net/
Hostname: 5f1224ef7307
IP: 127.0.0.1
IP: ::1
IP: 172.17.0.2
RemoteAddr: 172.17.0.1:59798
GET / HTTP/1.1
Host: callisto.tail36707.ts.net
User-Agent: curl/8.7.1
Accept: */*
Accept-Encoding: gzip
Tailscale-Headers-Info: https://tailscale.com/s/serve-headers
Tailscale-User-Login: 74wzsgxxxxxxxx
Tailscale-User-Name: 74wxxxxxx
Tailscale-User-Profile-Pic:
X-Forwarded-For: 100.93.71.112
X-Forwarded-Host: callisto.tail36707.ts.net
X-Forwarded-Proto: https
If I disconnect from my Tailnet the device can no longer reach the endpoint:
ryan@io:~|⇒ sudo tailscale down
ryan@io:~|⇒ tailscale status
Tailscale is stopped.
ryan@io:~|⇒ curl https://callisto.tail36707.ts.net/
curl: (6) Could not resolve host: callisto.tail36707.ts.net
Thanks Tailscale Magic DNS!
Tailscale Funnel
While Serve exposed our local service to devices on our Tailnet, the Funnel service allows us to route traffic from the wider internet to the local service running in the Tailnet. This means anyone with the URL can interact with the service. This is really handy if you want to have a users/coworkers test something without adding more devices/users to your Tailnet.
Testing
Since serve and funnel can't be running at the same time, we will stop our serve command with ctrl+c
and then expose our container to the wider internet with funnel:
callisto :: ~ » tailscale serve 8180
Available within your tailnet:
https://callisto.tail36707.ts.net/
|-- proxy http://127.0.0.1:8180
Press Ctrl+C to exit.
^C%
callisto :: ~ » tailscale funnel 8180
Available on the internet:
https://callisto.tail36707.ts.net/
|-- proxy http://127.0.0.1:8180
Press Ctrl+C to exit.
Now anyone can get my local service securely for quick testing and feedback by visiting https://callisto.tail36707.ts.net/
.
While my services
in this case were extraordinarily simplistic, the serve and funnel commands in Tailscale are quite powerful. Find some more examples at the links below: