tailscale: Revert dialer deprecation and remove control_http_client

This commit is contained in:
世界
2026-05-20 14:50:36 +08:00
parent cf06742014
commit 8785b39b15
7 changed files with 55 additions and 120 deletions
+2 -16
View File
@@ -2,11 +2,6 @@
icon: material/new-box icon: material/new-box
--- ---
!!! quote "Changes in sing-box 1.14.0"
:material-plus: [control_http_client](#control_http_client)
:material-delete-clock: [Dial Fields](#dial-fields)
!!! quote "Changes in sing-box 1.13.0" !!! quote "Changes in sing-box 1.13.0"
:material-plus: [relay_server_port](#relay_server_port) :material-plus: [relay_server_port](#relay_server_port)
@@ -27,7 +22,6 @@ icon: material/new-box
"state_directory": "", "state_directory": "",
"auth_key": "", "auth_key": "",
"control_url": "", "control_url": "",
"control_http_client": {}, // or ""
"ephemeral": false, "ephemeral": false,
"hostname": "", "hostname": "",
"accept_routes": false, "accept_routes": false,
@@ -154,18 +148,10 @@ UDP NAT expiration time.
`5m` will be used by default. `5m` will be used by default.
#### control_http_client
!!! question "Since sing-box 1.14.0"
HTTP Client for connecting to the Tailscale control plane.
See [HTTP Client Fields](/configuration/shared/http-client/) for details.
### Dial Fields ### Dial Fields
!!! failure "Deprecated in sing-box 1.14.0" !!! note
Dial Fields in Tailscale endpoints are deprecated in sing-box 1.14.0 and will be removed in sing-box 1.16.0, use `control_http_client` instead. Dial Fields in Tailscale endpoints only control how it connects to the control plane and have nothing to do with actual connections.
See [Dial Fields](/configuration/shared/dial/) for details. See [Dial Fields](/configuration/shared/dial/) for details.
+2 -16
View File
@@ -2,11 +2,6 @@
icon: material/new-box icon: material/new-box
--- ---
!!! quote "sing-box 1.14.0 中的更改"
:material-plus: [control_http_client](#control_http_client)
:material-delete-clock: [拨号字段](#拨号字段)
!!! quote "sing-box 1.13.0 中的更改" !!! quote "sing-box 1.13.0 中的更改"
:material-plus: [relay_server_port](#relay_server_port) :material-plus: [relay_server_port](#relay_server_port)
@@ -27,7 +22,6 @@ icon: material/new-box
"state_directory": "", "state_directory": "",
"auth_key": "", "auth_key": "",
"control_url": "", "control_url": "",
"control_http_client": {}, // 或 ""
"ephemeral": false, "ephemeral": false,
"hostname": "", "hostname": "",
"accept_routes": false, "accept_routes": false,
@@ -153,18 +147,10 @@ UDP NAT 过期时间。
默认使用 `5m` 默认使用 `5m`
#### control_http_client
!!! question "自 sing-box 1.14.0 起"
用于连接 Tailscale 控制平面的 HTTP 客户端。
参阅 [HTTP 客户端字段](/zh/configuration/shared/http-client/) 了解详情。
### 拨号字段 ### 拨号字段
!!! failure "已在 sing-box 1.14.0 废弃" !!! note
Tailscale 端点中的拨号字段已在 sing-box 1.14.0 废弃且将在 sing-box 1.16.0 中被移除,请使用 `control_http_client` 代替 Tailscale 端点中的拨号字段仅控制它如何连接到控制平面,与实际连接无关
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。 参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。
-7
View File
@@ -20,13 +20,6 @@ Configure `http_clients` and `route.default_http_client` explicitly.
Old behavior will be removed in sing-box 1.16.0. Old behavior will be removed in sing-box 1.16.0.
#### Legacy dialer options in Tailscale endpoint
Legacy dialer options in Tailscale endpoints are deprecated,
use `control_http_client` instead.
Old fields will be removed in sing-box 1.16.0.
#### Inline ACME options in TLS #### Inline ACME options in TLS
Inline ACME options (`tls.acme`) are deprecated Inline ACME options (`tls.acme`) are deprecated
-7
View File
@@ -20,13 +20,6 @@ icon: material/delete-alert
旧行为将在 sing-box 1.16.0 中被移除。 旧行为将在 sing-box 1.16.0 中被移除。
#### Tailscale 端点中的旧版拨号选项
Tailscale 端点中的旧版拨号选项已废弃,
请使用 `control_http_client` 代替。
旧字段将在 sing-box 1.16.0 中被移除。
#### TLS 中的内联 ACME 选项 #### TLS 中的内联 ACME 选项
TLS 中的内联 ACME 选项(`tls.acme`)已废弃, TLS 中的内联 ACME 选项(`tls.acme`)已废弃,
-9
View File
@@ -101,14 +101,6 @@ var OptionLegacyRuleSetDownloadDetour = Note{
EnvName: "LEGACY_RULE_SET_DOWNLOAD_DETOUR", EnvName: "LEGACY_RULE_SET_DOWNLOAD_DETOUR",
} }
var OptionLegacyTailscaleEndpointDialer = Note{
Name: "legacy-tailscale-endpoint-dialer",
Description: "legacy dialer options in Tailscale endpoint",
DeprecatedVersion: "1.14.0",
ScheduledVersion: "1.16.0",
EnvName: "LEGACY_TAILSCALE_ENDPOINT_DIALER",
}
var OptionRuleSetIPCIDRAcceptEmpty = Note{ var OptionRuleSetIPCIDRAcceptEmpty = Note{
Name: "dns-rule-rule-set-ip-cidr-accept-empty", Name: "dns-rule-rule-set-ip-cidr-accept-empty",
Description: "Legacy `rule_set_ip_cidr_accept_empty` DNS rule item", Description: "Legacy `rule_set_ip_cidr_accept_empty` DNS rule item",
@@ -168,7 +160,6 @@ var Options = []Note{
OptionLegacyDomainStrategyOptions, OptionLegacyDomainStrategyOptions,
OptionInlineACME, OptionInlineACME,
OptionLegacyRuleSetDownloadDetour, OptionLegacyRuleSetDownloadDetour,
OptionLegacyTailscaleEndpointDialer,
OptionRuleSetIPCIDRAcceptEmpty, OptionRuleSetIPCIDRAcceptEmpty,
OptionLegacyDNSAddressFilter, OptionLegacyDNSAddressFilter,
OptionLegacyDNSRuleStrategy, OptionLegacyDNSRuleStrategy,
-2
View File
@@ -11,12 +11,10 @@ import (
) )
type TailscaleEndpointOptions struct { type TailscaleEndpointOptions struct {
// Deprecated: use control_http_client instead
DialerOptions DialerOptions
StateDirectory string `json:"state_directory,omitempty"` StateDirectory string `json:"state_directory,omitempty"`
AuthKey string `json:"auth_key,omitempty"` AuthKey string `json:"auth_key,omitempty"`
ControlURL string `json:"control_url,omitempty"` ControlURL string `json:"control_url,omitempty"`
ControlHTTPClient *HTTPClientOptions `json:"control_http_client,omitempty"`
Ephemeral bool `json:"ephemeral,omitempty"` Ephemeral bool `json:"ephemeral,omitempty"`
Hostname string `json:"hostname,omitempty"` Hostname string `json:"hostname,omitempty"`
AcceptRoutes bool `json:"accept_routes,omitempty"` AcceptRoutes bool `json:"accept_routes,omitempty"`
+51 -63
View File
@@ -4,6 +4,7 @@ package tailscale
import ( import (
"context" "context"
"crypto/tls"
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
@@ -27,7 +28,6 @@ import (
"github.com/sagernet/sing-box/adapter/endpoint" "github.com/sagernet/sing-box/adapter/endpoint"
"github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/dialer"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental/deprecated"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/route/rule" "github.com/sagernet/sing-box/route/rule"
@@ -41,6 +41,7 @@ import (
"github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/ntp"
"github.com/sagernet/sing/service" "github.com/sagernet/sing/service"
"github.com/sagernet/sing/service/filemanager" "github.com/sagernet/sing/service/filemanager"
_ "github.com/sagernet/tailscale/feature/relayserver" _ "github.com/sagernet/tailscale/feature/relayserver"
@@ -116,45 +117,6 @@ type Endpoint struct {
fallbackTCPCloser func() fallbackTCPCloser func()
} }
func (t *Endpoint) registerNetstackHandlers() {
netstack := t.server.ExportNetstack()
if netstack == nil {
return
}
previousTCP := netstack.GetTCPHandlerForFlow
netstack.GetTCPHandlerForFlow = func(src, dst netip.AddrPort) (handler func(net.Conn), intercept bool) {
if previousTCP != nil {
handler, intercept = previousTCP(src, dst)
if handler != nil || !intercept {
return handler, intercept
}
}
return func(conn net.Conn) {
ctx := log.ContextWithNewID(t.ctx)
source := M.SocksaddrFrom(src.Addr(), src.Port())
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
t.NewConnectionEx(ctx, conn, source, destination, nil)
}, true
}
previousUDP := netstack.GetUDPHandlerForFlow
netstack.GetUDPHandlerForFlow = func(src, dst netip.AddrPort) (handler func(nettype.ConnPacketConn), intercept bool) {
if previousUDP != nil {
handler, intercept = previousUDP(src, dst)
if handler != nil || !intercept {
return handler, intercept
}
}
return func(conn nettype.ConnPacketConn) {
ctx := log.ContextWithNewID(t.ctx)
source := M.SocksaddrFrom(src.Addr(), src.Port())
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
packetConn := bufio.NewUnbindPacketConnWithAddr(conn, destination)
t.NewPacketConnectionEx(ctx, packetConn, source, destination, nil)
}, true
}
}
func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TailscaleEndpointOptions) (adapter.Endpoint, error) { func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TailscaleEndpointOptions) (adapter.Endpoint, error) {
stateDirectory := options.StateDirectory stateDirectory := options.StateDirectory
if stateDirectory == "" { if stateDirectory == "" {
@@ -196,19 +158,6 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
// controlplane.tailscale.com // controlplane.tailscale.com
remoteIsDomain = true remoteIsDomain = true
} }
hasLegacyDialer := !reflect.DeepEqual(options.DialerOptions, option.DialerOptions{})
hasControlHTTPClient := options.ControlHTTPClient != nil && !options.ControlHTTPClient.IsEmpty()
if hasLegacyDialer && hasControlHTTPClient {
return nil, E.New("control_http_client is conflict with deprecated dialer options")
}
controlHTTPClientOptions := common.PtrValueOrDefault(options.ControlHTTPClient)
if hasLegacyDialer {
deprecated.Report(ctx, deprecated.OptionLegacyTailscaleEndpointDialer)
controlHTTPClientOptions.DialerOptions = options.DialerOptions
}
if remoteIsDomain {
controlHTTPClientOptions.ResolveOnDetour = true
}
outboundDialer, err := dialer.NewWithOptions(dialer.Options{ outboundDialer, err := dialer.NewWithOptions(dialer.Options{
Context: ctx, Context: ctx,
Options: options.DialerOptions, Options: options.DialerOptions,
@@ -220,12 +169,6 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
return nil, err return nil, err
} }
dnsRouter := service.FromContext[adapter.DNSRouter](ctx) dnsRouter := service.FromContext[adapter.DNSRouter](ctx)
httpClientManager := service.FromContext[adapter.HTTPClientManager](ctx)
controlTransport, err := httpClientManager.ResolveTransport(ctx, logger, controlHTTPClientOptions)
if err != nil {
return nil, E.Cause(err, "create control HTTP client")
}
controlHTTPClient := &http.Client{Transport: controlTransport}
server := &tsnet.Server{ server := &tsnet.Server{
Dir: stateDirectory, Dir: stateDirectory,
Hostname: hostname, Hostname: hostname,
@@ -243,11 +186,22 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
LookupHook: func(ctx context.Context, host string) ([]netip.Addr, error) { LookupHook: func(ctx context.Context, host string) ([]netip.Addr, error) {
return dnsRouter.Lookup(ctx, host, outboundDialer.(dialer.ResolveDialer).QueryOptions()) return dnsRouter.Lookup(ctx, host, outboundDialer.(dialer.ResolveDialer).QueryOptions())
}, },
DNS: &dnsConfigurtor{}, DNS: &dnsConfigurtor{},
HTTPClient: controlHTTPClient, HTTPClient: &http.Client{
Transport: &http.Transport{
ForceAttemptHTTP2: true,
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
return outboundDialer.DialContext(ctx, network, M.ParseSocksaddr(address))
},
TLSClientConfig: &tls.Config{
RootCAs: adapter.RootPoolFromContext(ctx),
Time: ntp.TimeFuncFromContext(ctx),
},
},
},
} }
return &Endpoint{ return &Endpoint{
Adapter: endpoint.NewAdapterWithDialerOptions(C.TypeTailscale, tag, []string{N.NetworkTCP, N.NetworkUDP, N.NetworkICMP}, controlHTTPClientOptions.DialerOptions), Adapter: endpoint.NewAdapter(C.TypeTailscale, tag, []string{N.NetworkTCP, N.NetworkUDP, N.NetworkICMP}, nil),
ctx: ctx, ctx: ctx,
router: router, router: router,
logger: logger, logger: logger,
@@ -401,7 +355,41 @@ func (t *Endpoint) postStart() error {
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket) ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
t.stack = ipStack t.stack = ipStack
t.icmpForwarder = icmpForwarder t.icmpForwarder = icmpForwarder
t.registerNetstackHandlers() netstack := t.server.ExportNetstack()
if netstack != nil {
previousTCP := netstack.GetTCPHandlerForFlow
netstack.GetTCPHandlerForFlow = func(src, dst netip.AddrPort) (handler func(net.Conn), intercept bool) {
if previousTCP != nil {
handler, intercept = previousTCP(src, dst)
if handler != nil || !intercept {
return handler, intercept
}
}
return func(conn net.Conn) {
ctx := log.ContextWithNewID(t.ctx)
source := M.SocksaddrFrom(src.Addr(), src.Port())
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
t.NewConnectionEx(ctx, conn, source, destination, nil)
}, true
}
previousUDP := netstack.GetUDPHandlerForFlow
netstack.GetUDPHandlerForFlow = func(src, dst netip.AddrPort) (handler func(nettype.ConnPacketConn), intercept bool) {
if previousUDP != nil {
handler, intercept = previousUDP(src, dst)
if handler != nil || !intercept {
return handler, intercept
}
}
return func(conn nettype.ConnPacketConn) {
ctx := log.ContextWithNewID(t.ctx)
source := M.SocksaddrFrom(src.Addr(), src.Port())
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
packetConn := bufio.NewUnbindPacketConnWithAddr(conn, destination)
t.NewPacketConnectionEx(ctx, packetConn, source, destination, nil)
}, true
}
}
localBackend := t.server.ExportLocalBackend() localBackend := t.server.ExportLocalBackend()
perfs := &ipn.MaskedPrefs{ perfs := &ipn.MaskedPrefs{