Previously on the Tailscale blog, I walked through how authentication works with Tailscale for Grafana and even for Minecraft. Today we’re going to take that basic concept and show how to extend it to services that you have proxied behind NGINX.
The Grafana/Minecraft authentication proxy trick works because we set up a whole new node on your tailnet to proxy traffic directly to Grafana or Minecraft. This does work, but there’s a nonzero setup cost every time you add a new service to the mix. If you have an existing NGINX configuration for all your internal services, it may be easier to just use NGINX to proxy access to everything.
I have created
nginx-auth
to implement the NGINX HTTP subrequest authentication
protocol.
You can use this to authenticate every request to your internal services and
then decorate requests to them with the right HTTP headers.
Setup
I have added nginx-auth
to the Tailscale repositories for x86_64
Linux
machines. To install it on an Ubuntu machine, run this command:
sudo apt-get update && sudo apt-get install tailscale-nginx-auth
cmd/nginx-auth
’s
documentation
on how to build packages.This will automatically download the nginx-auth
tool and other metadata needed
to run it in systemd.
Once it is installed, you need to activate it in systemd with the following command:
sudo systemctl enable --now tailscale.nginx-auth.socket
This uses systemd socket
activation to automatically
start the service when it is needed. This lets systemd dynamically activate
tailscale.nginx-auth.service
on-demand instead of having it always run. This
also automatically restarts the service if it crashes.
NGINX configuration
Once the tailscale.nginx-auth
service is set up in your system, you need to
configure NGINX to use it. This works in the server
block, meaning that you
can set up separate authentication logic for services listening over Tailscale
vs services listening to the internet.
Set up an internal route named /auth
(you can change this path should you need
to):
location /auth {
internal;
proxy_pass http://unix:/run/tailscale.nginx-auth.sock;
proxy_pass_request_body off;
proxy_set_header Host $http_host;
proxy_set_header Remote-Addr $remote_addr;
proxy_set_header Remote-Port $remote_port;
proxy_set_header Original-URI $request_uri;
}
And then in your location
block that reverse proxies to your service, add this
just before the proxy_pass
instruction to use it:
auth_request /auth;
auth_request_set $auth_user $upstream_http_tailscale_user;
auth_request_set $auth_name $upstream_http_tailscale_name;
auth_request_set $auth_login $upstream_http_tailscale_login;
auth_request_set $auth_tailnet $upstream_http_tailscale_tailnet;
auth_request_set $auth_profile_picture $upstream_http_tailscale_profile_picture;
proxy_set_header X-Webauth-User "$auth_user";
proxy_set_header X-Webauth-Name "$auth_name";
proxy_set_header X-Webauth-Login "$auth_login";
proxy_set_header X-Webauth-Tailnet "$auth_tailnet";
proxy_set_header X-Webauth-Profile-Picture "$auth_profile_picture";
From here you can configure your applications to use the contents of
X-Webauth-User
or the other headers to use that for authentication logic. For
a full list of options that are exposed in these response headers, check the
documentation
table
which includes header names, example values and descriptions.
Security benefits
Overall the security benefits of doing this are similar to setting up a corporate SSO system. By moving authentication into the Tailscale level, you no longer need to handle authentication yourself. You don’t need to have internal tools maintain their own account systems with their own passwords. People connect to the server over Tailscale, and Tailscale already knows who they are.
This will give you a Single-Sign-On (SSO) experience without having the installation and maintenance overhead of setting up a new OAuth2 client, nor enduring the toil involved with getting administrative approval to try things out.
This proxy listens over a UNIX socket instead of a network-facing TCP socket. This makes it impossible to accidentally expose the proxy to the network. When you have the ability to, you should have your services listen over UNIX sockets like this. It allows you to use normal filesystem permissions to limit access to services instead of having to configure firewall rules.
Get in touch
Did you try this? How did you like it? Ping us on Twitter @Tailscale or let us know on the forum. If you want to check out the source code of this authentication sidecar, check it out on GitHub.
Many thanks to @zrail for pointing out this NGINX feature. Their examples helped guide the development of this service.