Skip to content

Commit c9df71e

Browse files
authored
Merge pull request #1321 from bitromortac/2603-faraday-prep-5
firewall: enable and obfuscate `faraday.ForwardingAbility`
2 parents c9a5dbb + 526a057 commit c9df71e

5 files changed

Lines changed: 231 additions & 37 deletions

File tree

docs/release-notes/release-notes-0.17.0.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
added](https://github.com/lightninglabs/lightning-terminal/pull/1271) to be
3232
able to apply privacy mapping for non-LND sub-daemon requests (e.g. Faraday)
3333
within LNC sessions. Non-mapped subservers calls are blocked.
34+
* The Faraday `ForwardingAbility` endpoint [is enabled and its peer pubkeys
35+
obfuscated](https://github.com/lightninglabs/lightning-terminal/pull/1321) in
36+
the firewall's privacy mapper.
3437

3538
## RPC Updates
3639

firewall/privacy_mapper.go

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"time"
1414

1515
"github.com/btcsuite/btcd/chaincfg/chainhash"
16+
"github.com/lightninglabs/faraday/frdrpc"
1617
"github.com/lightninglabs/lightning-terminal/firewalldb"
1718
mid "github.com/lightninglabs/lightning-terminal/rpcmiddleware"
1819
"github.com/lightninglabs/lightning-terminal/session"
@@ -535,6 +536,13 @@ func (p *PrivacyMapper) checkers(db firewalldb.PrivacyMapDB,
535536
flags session.PrivacyFlags) map[string]mid.RoundTripChecker {
536537

537538
return map[string]mid.RoundTripChecker{
539+
//nolint:ll
540+
"/frdrpc.FaradayServer/ForwardingAbility": mid.NewResponseRewriter(
541+
&frdrpc.ForwardingAbilityRequest{},
542+
&frdrpc.ForwardingAbilityResponse{},
543+
handleForwardingAbilityResponse(db, flags),
544+
mid.PassThroughErrorHandler,
545+
),
538546
"/lnrpc.Lightning/GetInfo": mid.NewResponseRewriter(
539547
&lnrpc.GetInfoRequest{}, &lnrpc.GetInfoResponse{},
540548
handleGetInfoResponse(db, flags),
@@ -585,7 +593,6 @@ func (p *PrivacyMapper) checkers(db firewalldb.PrivacyMapDB,
585593
handlePendingChannelsResponse(db, flags, p.randIntn),
586594
mid.PassThroughErrorHandler,
587595
),
588-
589596
"/lnrpc.Lightning/BatchOpenChannel": mid.NewFullRewriter(
590597
&lnrpc.BatchOpenChannelRequest{},
591598
&lnrpc.BatchOpenChannelResponse{},
@@ -600,7 +607,6 @@ func (p *PrivacyMapper) checkers(db firewalldb.PrivacyMapDB,
600607
handleChannelOpenResponse(db, flags),
601608
mid.PassThroughErrorHandler,
602609
),
603-
604610
"/lnrpc.Lightning/ConnectPeer": mid.NewRequestRewriter(
605611
&lnrpc.ConnectPeerRequest{},
606612
&lnrpc.ConnectPeerResponse{},
@@ -609,6 +615,69 @@ func (p *PrivacyMapper) checkers(db firewalldb.PrivacyMapDB,
609615
}
610616
}
611617

618+
func handleForwardingAbilityResponse(db firewalldb.PrivacyMapDB,
619+
flags session.PrivacyFlags) func(ctx context.Context,
620+
r *frdrpc.ForwardingAbilityResponse) (proto.Message, error) {
621+
622+
return func(ctx context.Context, r *frdrpc.ForwardingAbilityResponse) (
623+
proto.Message, error) {
624+
625+
// When pubkey hiding is disabled or there are no peers to
626+
// obfuscate, pass the response through untouched and avoid
627+
// opening a write transaction.
628+
if flags.Contains(session.ClearPubkeys) || len(r.Peers) == 0 {
629+
return r, nil
630+
}
631+
632+
// The response addresses peers by index from a shared peer
633+
// list, so obfuscate each peer in place to keep the entries'
634+
// packed indices valid. The list need not stay sorted because
635+
// decoding resolves peers purely by index.
636+
peers := make([][]byte, len(r.Peers))
637+
638+
err := db.Update(ctx, func(ctx context.Context,
639+
tx firewalldb.PrivacyMapTx) error {
640+
641+
for i, p := range r.Peers {
642+
// Skip empty pubkeys to avoid storing a
643+
// degenerate empty-to-empty mapping.
644+
if len(p) == 0 {
645+
peers[i] = p
646+
647+
continue
648+
}
649+
650+
// Hide the pubkey so a peer maps to the same
651+
// pseudonym across RPCs.
652+
pseudoBytes, err := firewalldb.HideBytes(
653+
ctx, tx, p,
654+
)
655+
if err != nil {
656+
return err
657+
}
658+
659+
peers[i] = pseudoBytes
660+
}
661+
662+
return nil
663+
})
664+
if err != nil {
665+
return nil, err
666+
}
667+
668+
// Peers are obfuscated in place, so the index-keyed entries and
669+
// up-but-idle bitmask stay valid and pass through unchanged.
670+
return &frdrpc.ForwardingAbilityResponse{
671+
Peers: peers,
672+
Entries: r.Entries,
673+
StartTime: r.StartTime,
674+
EndTime: r.EndTime,
675+
UpButIdleBitmask: r.UpButIdleBitmask,
676+
UptimeThreshold: r.UptimeThreshold,
677+
}, nil
678+
}
679+
}
680+
612681
func handleGetInfoResponse(db firewalldb.PrivacyMapDB,
613682
flags session.PrivacyFlags) func(ctx context.Context,
614683
r *lnrpc.GetInfoResponse) (proto.Message, error) {

firewall/privacy_mapper_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ func TestPrivacyMapper(t *testing.T) {
8585
outPoint(clearTxID, 0): outPoint(obfusTxID0, obfusOut0),
8686
outPoint(clearTxID, 1): outPoint(obfusTxID1, obfusOut1),
8787
"01020304": "c8134495",
88+
"05060708": "a1b2c3d4",
8889
"secret-host.com": "sksiuekalkdoowurekdf",
8990
}
9091

@@ -151,6 +152,115 @@ func TestPrivacyMapper(t *testing.T) {
151152
msg proto.Message
152153
expectedReplacement proto.Message
153154
}{
155+
{
156+
name: "ForwardingAbility Response",
157+
uri: "/frdrpc.FaradayServer/ForwardingAbility",
158+
msgType: rpcperms.TypeResponse,
159+
msg: &frdrpc.ForwardingAbilityResponse{
160+
Peers: [][]byte{{0x01, 0x02, 0x03, 0x04}},
161+
Entries: []*frdrpc.ForwardingAbilityEntry{{
162+
PackedIdx: 0,
163+
EffectiveUptimeS: 90,
164+
ForwardedSat: 100,
165+
}},
166+
},
167+
expectedReplacement: &frdrpc.ForwardingAbilityResponse{
168+
Peers: [][]byte{{0xc8, 0x13, 0x44, 0x95}},
169+
Entries: []*frdrpc.ForwardingAbilityEntry{{
170+
PackedIdx: 0,
171+
EffectiveUptimeS: 90,
172+
ForwardedSat: 100,
173+
}},
174+
},
175+
},
176+
{
177+
// Multiple peers, including a repeated one, must map to
178+
// the same pseudonym so the index-keyed entries stay
179+
// valid.
180+
name: "ForwardingAbility Response multiple peers",
181+
uri: "/frdrpc.FaradayServer/ForwardingAbility",
182+
msgType: rpcperms.TypeResponse,
183+
msg: &frdrpc.ForwardingAbilityResponse{
184+
Peers: [][]byte{
185+
{0x01, 0x02, 0x03, 0x04},
186+
{0x05, 0x06, 0x07, 0x08},
187+
{0x01, 0x02, 0x03, 0x04},
188+
},
189+
Entries: []*frdrpc.ForwardingAbilityEntry{
190+
{
191+
PackedIdx: 0,
192+
EffectiveUptimeS: 90,
193+
ForwardedSat: 100,
194+
},
195+
{
196+
PackedIdx: 1,
197+
EffectiveUptimeS: 80,
198+
ForwardedSat: 200,
199+
},
200+
{
201+
PackedIdx: 2,
202+
EffectiveUptimeS: 70,
203+
ForwardedSat: 300,
204+
},
205+
},
206+
},
207+
expectedReplacement: &frdrpc.ForwardingAbilityResponse{
208+
Peers: [][]byte{
209+
{0xc8, 0x13, 0x44, 0x95},
210+
{0xa1, 0xb2, 0xc3, 0xd4},
211+
{0xc8, 0x13, 0x44, 0x95},
212+
},
213+
Entries: []*frdrpc.ForwardingAbilityEntry{
214+
{
215+
PackedIdx: 0,
216+
EffectiveUptimeS: 90,
217+
ForwardedSat: 100,
218+
},
219+
{
220+
PackedIdx: 1,
221+
EffectiveUptimeS: 80,
222+
ForwardedSat: 200,
223+
},
224+
{
225+
PackedIdx: 2,
226+
EffectiveUptimeS: 70,
227+
ForwardedSat: 300,
228+
},
229+
},
230+
},
231+
},
232+
{
233+
// With ClearPubkeys set the peers must pass through
234+
// without obfuscation.
235+
name: "ForwardingAbility Response clear pubkey",
236+
uri: "/frdrpc.FaradayServer/ForwardingAbility",
237+
msgType: rpcperms.TypeResponse,
238+
privacyFlags: session.PrivacyFlags{
239+
session.ClearPubkeys,
240+
},
241+
msg: &frdrpc.ForwardingAbilityResponse{
242+
Peers: [][]byte{
243+
{0x01, 0x02, 0x03, 0x04},
244+
{0x05, 0x06, 0x07, 0x08},
245+
},
246+
Entries: []*frdrpc.ForwardingAbilityEntry{{
247+
PackedIdx: 0,
248+
EffectiveUptimeS: 90,
249+
ForwardedSat: 100,
250+
}},
251+
},
252+
expectedReplacement: &frdrpc.ForwardingAbilityResponse{
253+
Peers: [][]byte{
254+
{0x01, 0x02, 0x03, 0x04},
255+
{0x05, 0x06, 0x07, 0x08},
256+
},
257+
Entries: []*frdrpc.ForwardingAbilityEntry{{
258+
PackedIdx: 0,
259+
EffectiveUptimeS: 90,
260+
ForwardedSat: 100,
261+
}},
262+
},
263+
},
154264
{
155265
name: "GetInfo Response",
156266
uri: "/lnrpc.Lightning/GetInfo",

go.mod

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ require (
5454
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b
5555
golang.org/x/net v0.47.0
5656
golang.org/x/sync v0.18.0
57-
google.golang.org/grpc v1.65.0
57+
google.golang.org/grpc v1.71.0
5858
google.golang.org/protobuf v1.36.11
5959
gopkg.in/macaroon-bakery.v2 v2.3.0
6060
gopkg.in/macaroon.v2 v2.1.0
@@ -85,20 +85,20 @@ require (
8585
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect
8686
github.com/btcsuite/winsvc v1.0.0 // indirect
8787
github.com/caddyserver/certmagic v0.17.2 // indirect
88-
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
88+
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
8989
github.com/cespare/xxhash/v2 v2.3.0 // indirect
9090
github.com/coder/websocket v1.8.13 // indirect
9191
github.com/containerd/continuity v0.3.0 // indirect
9292
github.com/coreos/go-semver v0.3.0 // indirect
9393
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect
9494
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
95-
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
95+
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
9696
github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect
9797
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
9898
github.com/decred/dcrd/lru v1.1.2 // indirect
9999
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
100100
github.com/docker/cli v28.1.1+incompatible // indirect
101-
github.com/docker/docker v28.1.1+incompatible // indirect
101+
github.com/docker/docker v28.3.3+incompatible // indirect
102102
github.com/docker/go-connections v0.5.0 // indirect
103103
github.com/docker/go-units v0.5.0 // indirect
104104
github.com/dustin/go-humanize v1.0.1 // indirect
@@ -151,6 +151,7 @@ require (
151151
github.com/lightningnetwork/lightning-onion v1.2.1-0.20240815225420-8b40adf04ab9 // indirect
152152
github.com/lightningnetwork/lnd/healthcheck v1.2.6 // indirect
153153
github.com/lightningnetwork/lnd/queue v1.1.1 // indirect
154+
github.com/lightningnetwork/lnd/sqldb/v2 v2.0.0-20260326184657-f7cc56305bae // indirect
154155
github.com/lightningnetwork/lnd/ticker v1.1.1 // indirect
155156
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 // indirect
156157
github.com/mattn/go-isatty v0.0.20 // indirect
@@ -169,7 +170,7 @@ require (
169170
github.com/opencontainers/runc v1.2.8 // indirect
170171
github.com/pkg/errors v0.9.1 // indirect
171172
github.com/prometheus/client_golang v1.14.0 // indirect
172-
github.com/prometheus/client_model v0.4.0 // indirect
173+
github.com/prometheus/client_model v0.6.0 // indirect
173174
github.com/prometheus/common v0.37.0 // indirect
174175
github.com/prometheus/procfs v0.8.0 // indirect
175176
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
@@ -213,15 +214,15 @@ require (
213214
go.uber.org/mock v0.4.0 // indirect
214215
go.uber.org/multierr v1.6.0 // indirect
215216
go.uber.org/zap v1.24.0 // indirect
216-
golang.org/x/mod v0.29.0 // indirect
217+
golang.org/x/mod v0.30.0 // indirect
217218
golang.org/x/sys v0.40.0 // indirect
218219
golang.org/x/term v0.37.0 // indirect
219220
golang.org/x/text v0.31.0 // indirect
220221
golang.org/x/time v0.5.0 // indirect
221-
golang.org/x/tools v0.38.0 // indirect
222-
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
223-
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect
224-
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
222+
golang.org/x/tools v0.39.0 // indirect
223+
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
224+
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
225+
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
225226
gopkg.in/errgo.v1 v1.0.1 // indirect
226227
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
227228
gopkg.in/yaml.v2 v2.4.0 // indirect
@@ -258,3 +259,7 @@ replace nhooyr.io/websocket => github.com/coder/websocket v1.8.7
258259
replace github.com/lightninglabs/taproot-assets/taprpc => github.com/lightninglabs/taproot-assets/taprpc v1.0.12-0.20260223100136-c6e45b478517
259260

260261
replace github.com/lightningnetwork/lnd => github.com/lightningnetwork/lnd v0.20.0-beta.rc4.0.20260223110936-dd65ba2b0106
262+
263+
replace github.com/lightninglabs/faraday => github.com/bitromortac/faraday v0.0.0-20260611104839-edc3a405e6ec
264+
265+
replace github.com/lightninglabs/faraday/frdrpc => github.com/bitromortac/faraday/frdrpc v0.0.0-20260611104839-edc3a405e6ec

0 commit comments

Comments
 (0)