Securing gRPC services using Tailscale
- 2 minutes read - 266 wordsThis is so useful that it’s worth its own post.
I write many gRPC services. As these generally run securely, it’s best to test them that way too but, even with e.g. Let’s Encrypt, it can be challenging to generate appropriate TLS certs.
Tailscale makes this trivial.
Assuming there’s a gRPC service running on localhost:50051
, we want to avoid -plaintext
:
PORT="50051"
grpcurl \
-plaintext 0.0.0.0:${PORT} \
list
NOTE I’m using
list
and assuming your service has reflection enabled but you can, of course, use relevant methods.
Then, assuming you’ve enabled MagicDNS and HTTPs Certificates, you can use tailscale server
to make the service privately (!) accessible via TLS on 443:
PORT="50051"
tailscale serve --https=443 ${PORT}
And then:
MACHINE="..." # Your host's Tailscale machine name
TAILNET="..."
grpcurl \
${MACHINE}.${TAILNET}:443 \
list
And:
openssl s_client \
-connect ${MACHINE}.${TAILNET}:443 \
2>/dev/null \
</dev/null \
| openssl x509 \
-noout \
-subject \
-dates
subject=CN = {MACHNE}.{TAILNET}
notBefore=Apr 30 00:00:00 2024 GMT
notAfter=Jul 29 00:00:00 2024 GMT
To verify that the host isn’t Internet-accessible, e.g.:
gcloud cloud-shell ssh \
--command "ping ${MACHINE}.${TAILNET}"
ping: {MACHINE}.{TAILNET}: Name or service not known
If you want to make the service Internet|publicly-accessible, then simply use tailscale funnel
instead:
PORT="50051"
tailscale funnel --https=443 ${PORT}
To verify that the machine is Internet-accessible, e.g.:
gcloud cloud-shell ssh --command "ping ${MACHINE}.${TAILNET}"
PING {MACHINE}.{TAILNET} (208.111.34.11) 56(84) bytes of data.
64 bytes from ingress-sfo-01.tailscale.com (208.111.34.11): icmp_seq=1 ttl=59 time=18.6 ms
64 bytes from ingress-sfo-01.tailscale.com (208.111.34.11): icmp_seq=2 ttl=59 time=17.5 ms
64 bytes from ingress-sfo-01.tailscale.com (208.111.34.11): icmp_seq=3 ttl=59 time=17.5 ms
64 bytes from ingress-sfo-01.tailscale.com (208.111.34.11): icmp_seq=4 ttl=59 time=17.4 ms
...
How simple is that!