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 // SPDX-License-Identifier: BSD-3-Clause
// The tsnet-services example demonstrates how to use tsnet with Services. // 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 // TODO:
// on defining a Service // - 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 // To use it, generate an auth key from the Tailscale admin panel and
// run the demo with the key: // run the demo with the key:
@ -15,7 +18,6 @@ import (
"flag" "flag"
"fmt" "fmt"
"log" "log"
"math"
"net/http" "net/http"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
@ -24,7 +26,6 @@ import (
var ( var (
svcName = flag.String("service", "", "the name of your Service, e.g. svc:demo-service") 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' // 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 == "" { if *svcName == "" {
log.Fatal("a Service name must be provided") log.Fatal("a Service name must be provided")
} }
if *port == 0 {
log.Fatal("the listening port must be provided") const port uint16 = 443
}
if *port > math.MaxUint16 {
log.Fatal("invalid port number")
}
s := &tsnet.Server{ s := &tsnet.Server{
Dir: "./services-demo-config", Dir: "./services-demo-config",
@ -47,16 +44,14 @@ func main() {
} }
defer s.Close() defer s.Close()
ln, err := s.ListenService(*svcName, uint16(*port)) ln, err := s.ListenService(*svcName, port, tsnet.ServiceOptionTerminateTLS())
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
defer ln.Close() 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) { err = http.Serve(ln, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "<html><body><h1>Hello, tailnet!</h1>") 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) return nil, fmt.Errorf("fetching node preferences: %w", err)
} }
if !slices.Contains(prefs.AdvertiseServices, name) { if !slices.Contains(prefs.AdvertiseServices, name) {
// TODO: do we need to undo this edit on error?
_, err = lc.EditPrefs(ctx, &ipn.MaskedPrefs{ _, err = lc.EditPrefs(ctx, &ipn.MaskedPrefs{
AdvertiseServicesSet: true, AdvertiseServicesSet: true,
Prefs: ipn.Prefs{ 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) return nil, fmt.Errorf("starting local listener: %w", err)
} }
// Forward all connections from service-hostname:port to our socket. // 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 { if err := lc.SetServeConfig(ctx, srvConfig); err != nil {
ln.Close() ln.Close()