@ -1101,13 +1101,33 @@ type FunnelOption interface {
funnelOption ( )
}
type funnelOnly int
type funnelOnly struct { }
func ( funnelOnly ) funnelOption ( ) { }
// FunnelOnly configures the listener to only respond to connections from Tailscale Funnel.
// The local tailnet will not be able to connect to the listener.
func FunnelOnly ( ) FunnelOption { return funnelOnly ( 1 ) }
func FunnelOnly ( ) FunnelOption { return funnelOnly { } }
type funnelTLSConfig struct { conf * tls . Config }
func ( f funnelTLSConfig ) funnelOption ( ) { }
// FunnelTLSConfig configures the TLS configuration for [Server.ListenFunnel]
//
// This is rarely needed but can permit requiring client certificates, specific
// ciphers suites, etc.
//
// The provided conf should at least be able to get a certificate, setting
// GetCertificate, Certificates or GetConfigForClient appropriately.
// The most common configuration is to set GetCertificate to
// Server.LocalClient's GetCertificate method.
//
// Unless [FunnelOnly] is also used, the configuration is also used for
// in-tailnet connections that don't arrive over Funnel.
func FunnelTLSConfig ( conf * tls . Config ) FunnelOption {
return funnelTLSConfig { conf : conf }
}
// ListenFunnel announces on the public internet using Tailscale Funnel.
//
@ -1140,6 +1160,26 @@ func (s *Server) ListenFunnel(network, addr string, opts ...FunnelOption) (net.L
return nil , err
}
// Process, validate opts.
lnOn := listenOnBoth
var tlsConfig * tls . Config
for _ , opt := range opts {
switch v := opt . ( type ) {
case funnelTLSConfig :
if v . conf == nil {
return nil , errors . New ( "invalid nil FunnelTLSConfig" )
}
tlsConfig = v . conf
case funnelOnly :
lnOn = listenOnFunnel
default :
return nil , fmt . Errorf ( "unknown opts FunnelOption type %T" , v )
}
}
if tlsConfig == nil {
tlsConfig = & tls . Config { GetCertificate : s . getCert }
}
ctx := context . Background ( )
st , err := s . Up ( ctx )
if err != nil {
@ -1177,19 +1217,11 @@ func (s *Server) ListenFunnel(network, addr string, opts ...FunnelOption) (net.L
}
// Start a funnel listener.
lnOn := listenOnBoth
for _ , opt := range opts {
if _ , ok := opt . ( funnelOnly ) ; ok {
lnOn = listenOnFunnel
}
}
ln , err := s . listen ( network , addr , lnOn )
if err != nil {
return nil , err
}
return tls . NewListener ( ln , & tls . Config {
GetCertificate : s . getCert ,
} ) , nil
return tls . NewListener ( ln , tlsConfig ) , nil
}
type listenOn string