Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions cmd/commands/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,13 +212,14 @@ func getClientConn(ctx *cli.Context, skipMacaroons bool) *grpc.ClientConn {
// to connect to the grpc server.
if ctx.GlobalIsSet("socksproxy") {
socksProxy := ctx.GlobalString("socksproxy")
torNet := &tor.ClearNet{
SOCKS: socksProxy,
}

torDialer := func(_ context.Context, addr string) (net.Conn,
error) {

return tor.Dial(
addr, socksProxy, false, false,
tor.DefaultConnTimeout,
)
return torNet.Dial("tcp", addr, tor.DefaultConnTimeout)
}
opts = append(opts, grpc.WithContextDialer(torDialer))
} else {
Expand Down
74 changes: 63 additions & 11 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/signal"
"github.com/lightningnetwork/lnd/tor"
"golang.org/x/net/proxy"
)

const (
Expand Down Expand Up @@ -76,12 +77,16 @@
defaultLetsEncryptDirname = "letsencrypt"
defaultLetsEncryptListen = ":80"

defaultNetSOCKS = ""
defaultNetNoProxyTargets = "localhost,::1/128,127.0.0.0/8"

defaultTorSOCKSPort = 9050
defaultTorDNSHost = "soa.nodes.lightning.directory"
defaultTorDNSPort = 53
defaultTorControlPort = 9051
defaultTorV2PrivateKeyFilename = "v2_onion_private_key"
defaultTorV3PrivateKeyFilename = "v3_onion_private_key"
defaultTorNoProxyTargets = "localhost,::1/128,127.0.0.0/8"

// defaultZMQReadDeadline is the default read deadline to be used for
// both the block and tx ZMQ subscriptions.
Expand Down Expand Up @@ -356,6 +361,8 @@
MinBackoff time.Duration `long:"minbackoff" description:"Shortest backoff when reconnecting to persistent peers. Valid time units are {s, m, h}."`
MaxBackoff time.Duration `long:"maxbackoff" description:"Longest backoff when reconnecting to persistent peers. Valid time units are {s, m, h}."`
ConnectionTimeout time.Duration `long:"connectiontimeout" description:"The timeout value for network connections. Valid time units are {ms, s, m, h}."`
SOCKS string `long:"socks" description:"The SOCKS5 proxy to use for any outbound connections not going over Tor with format [user:pass]@host:port"`
NoProxyTargets []string `long:"no-proxy-targets" description:"Specify a target that should bypass the configured proxy. Each value is either an IP address, a CIDR range, a zone (*.example.com) or a hostname (localhost). A best effort is made to parse the string and errors are ignored. Can be specified multiple times. (default: localhost,127.0.0.0/8,::1/128)"`

DebugLevel string `short:"d" long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify <global-level>,<subsystem>=<level>,<subsystem2>=<level>,... to set the log level for individual subsystems -- Use show to list available subsystems"`

Expand Down Expand Up @@ -655,11 +662,15 @@
NumGraphSyncPeers: defaultMinPeers,
HistoricalSyncInterval: discovery.DefaultHistoricalSyncInterval,
Tor: &lncfg.Tor{
SOCKS: defaultTorSOCKS,
DNS: defaultTorDNS,
Control: defaultTorControl,
SOCKS: defaultTorSOCKS,
DNS: defaultTorDNS,
Control: defaultTorControl,
NoProxyTargets: strings.Split(defaultTorNoProxyTargets, ","),
},
net: &tor.ClearNet{
SOCKS: defaultNetSOCKS,
NoProxyTargets: defaultNetNoProxyTargets,
},
net: &tor.ClearNet{},
Workers: &lncfg.Workers{
Read: lncfg.DefaultReadWorkers,
Write: lncfg.DefaultWriteWorkers,
Expand Down Expand Up @@ -1113,15 +1124,35 @@
cfg.MaxCommitFeeRateAnchors)
}

socksAuth := &proxy.Auth{}
if cfg.SOCKS != "" {
socksAddress := cfg.SOCKS
if strings.Contains(socksAddress, "@") {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should mention this format in the flag description.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

parts := strings.Split(socksAddress, "@")
socksAddress = parts[1]
creds := strings.Split(parts[0], ":")
socksAuth.User = creds[0]
socksAuth.Password = creds[1]
}
socks, err := lncfg.ParseAddressString(
socksAddress, "",
cfg.net.ResolveTCPAddr,
)
if err != nil {
return nil, err
}
cfg.SOCKS = socks.String()
}

// Validate the Tor config parameters.
socks, err := lncfg.ParseAddressString(
torSocks, err := lncfg.ParseAddressString(
cfg.Tor.SOCKS, strconv.Itoa(defaultTorSOCKSPort),
cfg.net.ResolveTCPAddr,
)
if err != nil {
return nil, err
}
cfg.Tor.SOCKS = socks.String()
cfg.Tor.SOCKS = torSocks.String()

// We'll only attempt to normalize and resolve the DNS host if it hasn't
// changed, as it doesn't need to be done for the default.
Expand Down Expand Up @@ -1172,7 +1203,7 @@
cfg.Tor.PrivateKeyPath = filepath.Join(
lndDir, defaultTorV3PrivateKeyFilename,
)
}

Check failure on line 1206 in config.go

View workflow job for this annotation

GitHub Actions / Check commits

unknown field NoProxyTargets in struct literal of type tor.ProxyNet
}

if cfg.Tor.WatchtowerKeyPath == "" {
Expand All @@ -1195,13 +1226,34 @@
// default. If we should be proxying all traffic through Tor, then
// we'll use the Tor proxy specific functions in order to avoid leaking
// our real information.

clearNet := &tor.ClearNet{
SOCKS: cfg.SOCKS,
SOCKSAuth: socksAuth,
NoProxyTargets: strings.Join(cfg.NoProxyTargets, ","),
}
if cfg.Tor.Active {
cfg.net = &tor.ProxyNet{
SOCKS: cfg.Tor.SOCKS,
DNS: cfg.Tor.DNS,
StreamIsolation: cfg.Tor.StreamIsolation,
SkipProxyForClearNetTargets: cfg.Tor.SkipProxyForClearNetTargets,
torNet := &tor.ProxyNet{
SOCKS: cfg.Tor.SOCKS,
DNS: cfg.Tor.DNS,
StreamIsolation: cfg.Tor.StreamIsolation,
NoProxyTargets: strings.Join(
cfg.Tor.NoProxyTargets, ","),
ClearNet: clearNet,
}
if !cfg.Tor.SkipProxyForClearNetTargets {
torNet.ClearNet = &tor.ProxyNet{
SOCKS: cfg.Tor.SOCKS,
DNS: cfg.Tor.DNS,
StreamIsolation: cfg.Tor.StreamIsolation,
NoProxyTargets: strings.Join(
cfg.Tor.NoProxyTargets, ","),
ClearNet: clearNet,
}
}
cfg.net = torNet
} else {
cfg.net = clearNet
}

if cfg.DisableListen && cfg.NAT {
Expand Down
8 changes: 7 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ require (
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.17.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/net v0.39.0
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
Expand Down Expand Up @@ -220,6 +220,12 @@ replace github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2
// allows us to specify that as an option.
replace google.golang.org/protobuf => github.com/lightninglabs/protobuf-go-hex-display v1.33.0-hex-display

// Temporary replace until the next version of tor is tagged.
replace (
github.com/lightningnetwork/lnd/tor v0.0.0-local => ./tor
github.com/lightningnetwork/lnd/tor v1.1.6 => ./tor
)

// If you change this please also update docs/INSTALL.md and GO_VERSION in
// Makefile (then run `make lint` to see where else it needs to be updated as
// well).
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,6 @@ github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6
github.com/lightningnetwork/lnd/ticker v1.1.1/go.mod h1:waPTRAAcwtu7Ji3+3k+u/xH5GHovTsCoSVpho0KDvdA=
github.com/lightningnetwork/lnd/tlv v1.3.2 h1:MO4FCk7F4k5xPMqVZF6Nb/kOpxlwPrUQpYjmyKny5s0=
github.com/lightningnetwork/lnd/tlv v1.3.2/go.mod h1:pJuiBj1ecr1WWLOtcZ+2+hu9Ey25aJWFIsjmAoPPnmc=
github.com/lightningnetwork/lnd/tor v1.1.6 h1:WHUumk7WgU6BUFsqHuqszI9P6nfhMeIG+rjJBlVE6OE=
github.com/lightningnetwork/lnd/tor v1.1.6/go.mod h1:qSRB8llhAK+a6kaTPWOLLXSZc6Hg8ZC0mq1sUQ/8JfI=
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw=
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY=
github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA=
Expand Down
27 changes: 14 additions & 13 deletions lncfg/tor.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ package lncfg
//
//nolint:ll
type Tor struct {
Active bool `long:"active" description:"Allow outbound and inbound connections to be routed through Tor"`
SOCKS string `long:"socks" description:"The host:port that Tor's exposed SOCKS5 proxy is listening on"`
DNS string `long:"dns" description:"The DNS server as host:port that Tor will use for SRV queries - NOTE must have TCP resolution enabled"`
StreamIsolation bool `long:"streamisolation" description:"Enable Tor stream isolation by randomizing user credentials for each connection."`
SkipProxyForClearNetTargets bool `long:"skip-proxy-for-clearnet-targets" description:"Allow the node to establish direct connections to services not running behind Tor."`
Control string `long:"control" description:"The host:port that Tor is listening on for Tor control connections"`
TargetIPAddress string `long:"targetipaddress" description:"IP address that Tor should use as the target of the hidden service"`
Password string `long:"password" description:"The password used to arrive at the HashedControlPassword for the control port. If provided, the HASHEDPASSWORD authentication method will be used instead of the SAFECOOKIE one."`
V2 bool `long:"v2" description:"DEPRECATED: Tor v2 onion services are obsolete and support will be removed in v0.21.0. Use v3 instead." hidden:"true"`
V3 bool `long:"v3" description:"Automatically set up a v3 onion service to listen for inbound connections"`
PrivateKeyPath string `long:"privatekeypath" description:"The path to the private key of the onion service being created"`
EncryptKey bool `long:"encryptkey" description:"Encrypts the Tor private key file on disk"`
WatchtowerKeyPath string `long:"watchtowerkeypath" description:"The path to the private key of the watchtower onion service being created"`
Active bool `long:"active" description:"Allow outbound and inbound connections to be routed through Tor"`
SOCKS string `long:"socks" description:"The host:port that Tor's exposed SOCKS5 proxy is listening on"`
DNS string `long:"dns" description:"The DNS server as host:port that Tor will use for SRV queries - NOTE must have TCP resolution enabled"`
StreamIsolation bool `long:"streamisolation" description:"Enable Tor stream isolation by randomizing user credentials for each connection."`
SkipProxyForClearNetTargets bool `long:"skip-proxy-for-clearnet-targets" description:"Allow the node to establish direct connections to services not running behind Tor."`
NoProxyTargets []string `long:"no-proxy-targets" description:"Specify a target that should bypass the configured proxy. Each value is either an IP address, a CIDR range, a zone (*.example.com) or a hostname (localhost). A best effort is made to parse the string and errors are ignored. Can be specified multiple times. (default: localhost,127.0.0.0/8,::1/128)"`
Control string `long:"control" description:"The host:port that Tor is listening on for Tor control connections"`
TargetIPAddress string `long:"targetipaddress" description:"IP address that Tor should use as the target of the hidden service"`
Password string `long:"password" description:"The password used to arrive at the HashedControlPassword for the control port. If provided, the HASHEDPASSWORD authentication method will be used instead of the SAFECOOKIE one."`
V2 bool `long:"v2" description:"DEPRECATED: Tor v2 onion services are obsolete and support will be removed in v0.21.0. Use v3 instead." hidden:"true"`
V3 bool `long:"v3" description:"Automatically set up a v3 onion service to listen for inbound connections"`
PrivateKeyPath string `long:"privatekeypath" description:"The path to the private key of the onion service being created"`
EncryptKey bool `long:"encryptkey" description:"Encrypts the Tor private key file on disk"`
WatchtowerKeyPath string `long:"watchtowerkeypath" description:"The path to the private key of the watchtower onion service being created"`
}
25 changes: 10 additions & 15 deletions lnd.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,6 @@ type ListenerCfg struct {
RPCListeners []*ListenerWithSignal
}

var errStreamIsolationWithProxySkip = errors.New(
"while stream isolation is enabled, the TOR proxy may not be skipped",
)

// Main is the true entry point for lnd. It accepts a fully populated and
// validated main configuration struct and an optional listener config struct.
// This function starts all main system components then blocks until a signal
Expand Down Expand Up @@ -514,20 +510,19 @@ func Main(cfg *Config, lisCfg ListenerCfg, implCfg *ImplementationCfg,
}

if cfg.Tor.StreamIsolation && cfg.Tor.SkipProxyForClearNetTargets {
return errStreamIsolationWithProxySkip
srvrLog.Warn(
"Danger! Skipping Tor while Stream Isolation is on." +
"This has high risk of leaking your IP. " +
"Make sure this is what you want.")
}

if cfg.Tor.Active {
if cfg.Tor.SkipProxyForClearNetTargets {
srvrLog.InfoS(ctx, "Onion services are accessible "+
"via Tor! NOTE: Traffic to clearnet services "+
"is not routed via Tor.")
} else {
srvrLog.InfoS(ctx, "Proxying all network traffic "+
"via Tor! NOTE: Ensure the backend node is "+
"proxying over Tor as well",
"stream_isolation", cfg.Tor.StreamIsolation)
}
srvrLog.InfoS(ctx, "Proxying all network traffic "+
"via Tor! NOTE: Ensure the backend node is "+
"proxying over Tor as well",
"tor_no_proxy_targets", strings.Join(cfg.Tor.NoProxyTargets, ","),
"no_proxy_targets", strings.Join(cfg.NoProxyTargets, ","),
"stream_isolation", cfg.Tor.StreamIsolation)
}

// If tor is active and either v2 or v3 onion services have been
Expand Down
30 changes: 30 additions & 0 deletions sample-lnd.conf
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,24 @@
; support devices behind multiple NATs.
; nat=false

; The host:port of SOCKS5 proxy to use for any outbound connections not
; going over Tor.
; Example:
; socks=proxyuser:proxypassword@127.0.0.1:8888
; Default:
; socks=

; Specify a target that should bypass the clearnet proxy.
; Each value is either an IP address, a CIDR range, a zone (*.example.com) or a
; host name (localhost). A best effort is made to parse the string and errors
; are ignored.
; Default:
; no-proxy-targets=localhost
; no-proxy-targets=127.0.0.0/8
; no-proxy-targets=::1/128
; Example (option can be specified multiple times):
; no-proxy-targets=192.168.0.0/16

; Disable REST API.
; norest=false

Expand Down Expand Up @@ -959,6 +977,18 @@
; be used only if privacy is not a concern.
; tor.skip-proxy-for-clearnet-targets=false

; Specify a target that should not be routed over Tor.
; Each value is either an
; IP address, a CIDR range, a zone (*.example.com) or a host name
; (localhost). A best effort is made to parse the string and errors are
; ignored.
; Default:
; tor.no-proxy-targets=localhost
; tor.no-proxy-targets=127.0.0.0/8
; tor.no-proxy-targets=::1/128
; Example (option can be specified multiple times):
; tor.no-proxy-targets=192.168.0.0/16

; The port that Tor's exposed SOCKS5 proxy is listening on. Using Tor allows
; outbound-only connections (listening will be disabled) -- NOTE port must be
; between 1024 and 65535.
Expand Down
2 changes: 1 addition & 1 deletion scripts/check-sample-lnd-conf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ OPTIONS_NO_LND_DEFAULT_VALUE_CHECK="channel-max-fee-exposure adminmacaroonpath \
backupfilepath maxchansize bitcoin.chaindir bitcoin.defaultchanconfs \
bitcoin.defaultremotedelay bitcoin.dnsseed signrpc.signermacaroonpath \
walletrpc.walletkitmacaroonpath chainrpc.notifiermacaroonpath \
routerrpc.routermacaroonpath"
routerrpc.routermacaroonpath no-proxy-targets tor.no-proxy-targets"


# EXITCODE is returned at the end after all checks are performed and set to 1
Expand Down
Loading
Loading