working example using TLS termination

Signed-off-by: Harry Harpham <harry@tailscale.com>
This commit is contained in:
Harry Harpham 2025-12-19 15:32:23 -07:00
parent 180e100a82
commit 39b84281e0
No known key found for this signature in database
2 changed files with 13 additions and 15 deletions

View File

@ -2,8 +2,11 @@
// SPDX-License-Identifier: BSD-3-Clause
// The tsnet-services example demonstrates how to use tsnet with Services.
// TODO: explain that a Service must be defined for the tailent and link to KB
// on defining a Service
// TODO:
// - explain that a Service must be defined for the tailent and link to KB on
// defining a Service
// - recommend using an auth key with associated tags
// - recommend an auto-approval rule for service tags
//
// To use it, generate an auth key from the Tailscale admin panel and
// run the demo with the key:
@ -15,7 +18,6 @@ import (
"flag"
"fmt"
"log"
"math"
"net/http"
"tailscale.com/tailcfg"
@ -24,7 +26,6 @@ import (
var (
svcName = flag.String("service", "", "the name of your Service, e.g. svc:demo-service")
port = flag.Uint("port", 0, "the port to listen on")
)
// TODO: this worked several times, then my host got stuck in 'Partially configured: has-config, config-valid'
@ -34,12 +35,8 @@ func main() {
if *svcName == "" {
log.Fatal("a Service name must be provided")
}
if *port == 0 {
log.Fatal("the listening port must be provided")
}
if *port > math.MaxUint16 {
log.Fatal("invalid port number")
}
const port uint16 = 443
s := &tsnet.Server{
Dir: "./services-demo-config",
@ -47,16 +44,14 @@ func main() {
}
defer s.Close()
ln, err := s.ListenService(*svcName, uint16(*port))
ln, err := s.ListenService(*svcName, port, tsnet.ServiceOptionTerminateTLS())
if err != nil {
log.Fatal(err)
}
defer ln.Close()
fmt.Printf("Listening on http://%v\n", tailcfg.AsServiceName(*svcName).WithoutPrefix())
fmt.Printf("Listening on https://%v\n", tailcfg.AsServiceName(*svcName).WithoutPrefix())
// TODO: maybe just respond to TCP connections? (since we don't know the port)
// Actually, let's hard-code port 80 and provide an example Service definition to use
err = http.Serve(ln, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "<html><body><h1>Hello, tailnet!</h1>")
}))

View File

@ -1320,6 +1320,7 @@ func (s *Server) ListenService(name string, port uint16, opts ...ServiceOption)
return nil, fmt.Errorf("fetching node preferences: %w", err)
}
if !slices.Contains(prefs.AdvertiseServices, name) {
// TODO: do we need to undo this edit on error?
_, err = lc.EditPrefs(ctx, &ipn.MaskedPrefs{
AdvertiseServicesSet: true,
Prefs: ipn.Prefs{
@ -1345,7 +1346,9 @@ func (s *Server) ListenService(name string, port uint16, opts ...ServiceOption)
return nil, fmt.Errorf("starting local listener: %w", err)
}
// Forward all connections from service-hostname:port to our socket.
srvConfig.SetTCPForwarding(port, ln.Addr().String(), terminateTLS, proxyProtocol, name)
srvConfig.SetTCPForwardingForService( // TODO: tangent, but can we reduce the number of args here?
port, ln.Addr().String(), tailcfg.ServiceName(name),
terminateTLS, proxyProtocol, st.CurrentTailnet.MagicDNSSuffix)
if err := lc.SetServeConfig(ctx, srvConfig); err != nil {
ln.Close()