Skip to content

Commit 9126875

Browse files
authored
Merge pull request #438 from ellemouton/fine-grained-perms
multi: custom URI sessions
2 parents 11c4a09 + b38acfa commit 9126875

File tree

6 files changed

+466
-153
lines changed

6 files changed

+466
-153
lines changed

cmd/litcli/sessions.go

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88

99
"github.com/lightninglabs/lightning-terminal/litrpc"
10+
"github.com/lightningnetwork/lnd/macaroons"
1011
"github.com/urfave/cli"
1112
)
1213

@@ -62,9 +63,18 @@ var addSessionCommand = cli.Command{
6263
Usage: "session type to be created which will " +
6364
"determine the permissions a user has when " +
6465
"connecting with the session. Options " +
65-
"include readonly|admin",
66+
"include readonly|admin|custom",
6667
Value: "readonly",
6768
},
69+
cli.StringSliceFlag{
70+
Name: "uri",
71+
Usage: "A URI that should be included in the " +
72+
"macaroon of a custom session. Note that " +
73+
"this flag will only be used if the 'type' " +
74+
"flag is set to 'custom'. This flag can be " +
75+
"specified multiple times if multiple URIs " +
76+
"should be included",
77+
},
6878
},
6979
}
7080

@@ -87,17 +97,26 @@ func addSession(ctx *cli.Context) error {
8797
return err
8898
}
8999

100+
var macPerms []*litrpc.MacaroonPermission
101+
for _, uri := range ctx.StringSlice("uri") {
102+
macPerms = append(macPerms, &litrpc.MacaroonPermission{
103+
Entity: macaroons.PermissionEntityCustomURI,
104+
Action: uri,
105+
})
106+
}
107+
90108
sessionLength := time.Second * time.Duration(ctx.Uint64("expiry"))
91109
sessionExpiry := time.Now().Add(sessionLength).Unix()
92110

93111
ctxb := context.Background()
94112
resp, err := client.AddSession(
95113
ctxb, &litrpc.AddSessionRequest{
96-
Label: label,
97-
SessionType: sessType,
98-
ExpiryTimestampSeconds: uint64(sessionExpiry),
99-
MailboxServerAddr: ctx.String("mailboxserveraddr"),
100-
DevServer: ctx.Bool("devserver"),
114+
Label: label,
115+
SessionType: sessType,
116+
ExpiryTimestampSeconds: uint64(sessionExpiry),
117+
MailboxServerAddr: ctx.String("mailboxserveraddr"),
118+
DevServer: ctx.Bool("devserver"),
119+
MacaroonCustomPermissions: macPerms,
101120
},
102121
)
103122
if err != nil {
@@ -115,6 +134,8 @@ func parseSessionType(sessionType string) (litrpc.SessionType, error) {
115134
return litrpc.SessionType_TYPE_MACAROON_ADMIN, nil
116135
case "readonly":
117136
return litrpc.SessionType_TYPE_MACAROON_READONLY, nil
137+
case "custom":
138+
return litrpc.SessionType_TYPE_MACAROON_CUSTOM, nil
118139
default:
119140
return 0, fmt.Errorf("unsupported session type %s", sessionType)
120141
}

itest/litd_mode_integrated_test.go

Lines changed: 106 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/lightningnetwork/lnd/lnrpc"
2929
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
3030
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
31+
"github.com/lightningnetwork/lnd/macaroons"
3132
"github.com/stretchr/testify/require"
3233
"golang.org/x/net/http2"
3334
"google.golang.org/grpc"
@@ -224,6 +225,13 @@ var (
224225
allowedThroughLNC: false,
225226
grpcWebURI: "/litrpc.Sessions/ListSessions",
226227
}}
228+
229+
// customURIs is a map of endpoint URIs that we want to allow via a
230+
// custom-macaroon session type.
231+
customURIs = map[string]bool{
232+
"/lnrpc.Lightning/GetInfo": true,
233+
"/frdrpc.FaradayServer/RevenueReport": true,
234+
}
227235
)
228236

229237
// testModeIntegrated makes sure that in integrated mode all daemons work
@@ -367,20 +375,109 @@ func testModeIntegrated(net *NetworkHarness, t *harnessTest) {
367375
t.t.Run("lnc auth", func(tt *testing.T) {
368376
cfg := net.Alice.Cfg
369377

378+
ctx := context.Background()
379+
ctxt, cancel := context.WithTimeout(ctx, defaultTimeout)
380+
defer cancel()
381+
382+
rawLNCConn := setUpLNCConn(
383+
ctxt, t.t, cfg.LitAddr(), cfg.TLSCertPath,
384+
cfg.LitMacPath,
385+
litrpc.SessionType_TYPE_MACAROON_READONLY, nil,
386+
)
387+
defer rawLNCConn.Close()
388+
370389
for _, endpoint := range endpoints {
371390
endpoint := endpoint
372391
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
373392
runLNCAuthTest(
374-
ttt, cfg.LitAddr(), cfg.TLSCertPath,
375-
cfg.LitMacPath, endpoint.requestFn,
393+
ttt, rawLNCConn, endpoint.requestFn,
376394
endpoint.successPattern,
377395
endpoint.allowedThroughLNC,
396+
"unknown service",
397+
)
398+
})
399+
}
400+
})
401+
402+
t.t.Run("lnc auth custom mac perms", func(tt *testing.T) {
403+
cfg := net.Alice.Cfg
404+
405+
ctx := context.Background()
406+
ctxt, cancel := context.WithTimeout(ctx, defaultTimeout)
407+
defer cancel()
408+
409+
customPerms := make(
410+
[]*litrpc.MacaroonPermission, 0, len(customURIs),
411+
)
412+
413+
customURIKeyword := macaroons.PermissionEntityCustomURI
414+
for uri := range customURIs {
415+
customPerms = append(
416+
customPerms, &litrpc.MacaroonPermission{
417+
Entity: customURIKeyword,
418+
Action: uri,
419+
},
420+
)
421+
}
422+
423+
rawLNCConn := setUpLNCConn(
424+
ctxt, t.t, cfg.LitAddr(), cfg.TLSCertPath,
425+
cfg.LitMacPath,
426+
litrpc.SessionType_TYPE_MACAROON_CUSTOM, customPerms,
427+
)
428+
defer rawLNCConn.Close()
429+
430+
for _, endpoint := range endpoints {
431+
endpoint := endpoint
432+
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
433+
allowed := customURIs[endpoint.grpcWebURI]
434+
runLNCAuthTest(
435+
ttt, rawLNCConn, endpoint.requestFn,
436+
endpoint.successPattern,
437+
allowed, "permission denied",
378438
)
379439
})
380440
}
381441
})
382442
}
383443

444+
// setUpLNCConn creates a new LNC session and then creates a connection to that
445+
// session via the mailbox that the session was created with.
446+
func setUpLNCConn(ctx context.Context, t *testing.T, hostPort, tlsCertPath,
447+
macPath string, sessType litrpc.SessionType,
448+
customMacPerms []*litrpc.MacaroonPermission) *grpc.ClientConn {
449+
450+
rawConn, err := connectRPC(ctx, hostPort, tlsCertPath)
451+
require.NoError(t, err)
452+
453+
macBytes, err := ioutil.ReadFile(macPath)
454+
require.NoError(t, err)
455+
ctxm := macaroonContext(ctx, macBytes)
456+
457+
// We first need to create an LNC session that we can use to connect.
458+
litClient := litrpc.NewSessionsClient(rawConn)
459+
sessResp, err := litClient.AddSession(ctxm, &litrpc.AddSessionRequest{
460+
Label: "integration-test",
461+
SessionType: sessType,
462+
ExpiryTimestampSeconds: uint64(
463+
time.Now().Add(5 * time.Minute).Unix(),
464+
),
465+
MailboxServerAddr: mailboxServerAddr,
466+
MacaroonCustomPermissions: customMacPerms,
467+
})
468+
require.NoError(t, err)
469+
470+
// Try the LNC connection now.
471+
connectPhrase := strings.Split(
472+
sessResp.Session.PairingSecretMnemonic, " ",
473+
)
474+
475+
rawLNCConn, err := connectMailbox(ctx, connectPhrase)
476+
require.NoError(t, err)
477+
478+
return rawLNCConn
479+
}
480+
384481
// runCertificateCheck checks that the TLS certificates presented to clients are
385482
// what we expect them to be.
386483
func runCertificateCheck(t *testing.T, node *HarnessNode) {
@@ -624,44 +721,15 @@ func runRESTAuthTest(t *testing.T, hostPort, uiPassword, macaroonPath, restURI,
624721

625722
// runLNCAuthTest tests authentication of the given interface when connecting
626723
// through Lightning Node Connect.
627-
func runLNCAuthTest(t *testing.T, hostPort, tlsCertPath, macPath string,
628-
makeRequest requestFn, successContent string, callAllowed bool) {
629-
630-
ctxb := context.Background()
631-
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
632-
defer cancel()
633-
634-
rawConn, err := connectRPC(ctxt, hostPort, tlsCertPath)
635-
require.NoError(t, err)
724+
func runLNCAuthTest(t *testing.T, rawLNCConn grpc.ClientConnInterface,
725+
makeRequest requestFn, successContent string, callAllowed bool,
726+
expectErrContains string) {
636727

637-
macBytes, err := ioutil.ReadFile(macPath)
638-
require.NoError(t, err)
639-
ctxm := macaroonContext(ctxt, macBytes)
640-
641-
// We first need to create an LNC session that we can use to connect.
642-
// We use the UI password to create the session.
643-
litClient := litrpc.NewSessionsClient(rawConn)
644-
sessResp, err := litClient.AddSession(ctxm, &litrpc.AddSessionRequest{
645-
Label: "integration-test",
646-
SessionType: litrpc.SessionType_TYPE_MACAROON_READONLY,
647-
ExpiryTimestampSeconds: uint64(
648-
time.Now().Add(5 * time.Minute).Unix(),
649-
),
650-
MailboxServerAddr: mailboxServerAddr,
651-
})
652-
require.NoError(t, err)
653-
654-
// Try the LNC connection now.
655-
connectPhrase := strings.Split(
656-
sessResp.Session.PairingSecretMnemonic, " ",
728+
ctxt, cancel := context.WithTimeout(
729+
context.Background(), defaultTimeout,
657730
)
658-
659-
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
660731
defer cancel()
661732

662-
rawLNCConn, err := connectMailbox(ctxt, connectPhrase)
663-
require.NoError(t, err)
664-
665733
// We should be able to make a request via LNC to the given RPC
666734
// endpoint, unless it is explicitly disallowed (we currently don't want
667735
// to support creating more sessions through LNC until we have all
@@ -671,7 +739,7 @@ func runLNCAuthTest(t *testing.T, hostPort, tlsCertPath, macPath string,
671739
// Is this a disallowed call?
672740
if !callAllowed {
673741
require.Error(t, err)
674-
require.Contains(t, err.Error(), "unknown service")
742+
require.Contains(t, err.Error(), expectErrContains)
675743

676744
return
677745
}
@@ -767,7 +835,7 @@ func getServerCertificates(hostPort string) ([]*x509.Certificate, error) {
767835
// connectMailbox tries to establish a connection through LNC using the given
768836
// connect phrase and the test mailbox server.
769837
func connectMailbox(ctx context.Context,
770-
connectPhrase []string) (grpc.ClientConnInterface, error) {
838+
connectPhrase []string) (*grpc.ClientConn, error) {
771839

772840
var mnemonicWords [mailbox.NumPassphraseWords]string
773841
copy(mnemonicWords[:], connectPhrase)

itest/litd_mode_remote_test.go

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import (
66
"testing"
77

88
"github.com/btcsuite/btcd/btcutil"
9+
"github.com/lightninglabs/lightning-terminal/litrpc"
910
"github.com/lightningnetwork/lnd/lnrpc"
11+
"github.com/lightningnetwork/lnd/macaroons"
1012
"github.com/stretchr/testify/require"
1113
)
1214

@@ -134,14 +136,66 @@ func testModeRemote(net *NetworkHarness, t *harnessTest) {
134136
t.t.Run("lnc auth", func(tt *testing.T) {
135137
cfg := net.Bob.Cfg
136138

139+
ctx := context.Background()
140+
ctxt, cancel := context.WithTimeout(ctx, defaultTimeout)
141+
defer cancel()
142+
143+
rawLNCConn := setUpLNCConn(
144+
ctxt, tt, cfg.LitAddr(), cfg.LitTLSCertPath,
145+
cfg.LitMacPath,
146+
litrpc.SessionType_TYPE_MACAROON_READONLY, nil,
147+
)
148+
defer rawLNCConn.Close()
149+
137150
for _, endpoint := range endpoints {
138151
endpoint := endpoint
139152
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
140153
runLNCAuthTest(
141-
ttt, cfg.LitAddr(), cfg.LitTLSCertPath,
142-
cfg.LitMacPath, endpoint.requestFn,
154+
ttt, rawLNCConn, endpoint.requestFn,
143155
endpoint.successPattern,
144156
endpoint.allowedThroughLNC,
157+
"unknown service",
158+
)
159+
})
160+
}
161+
})
162+
163+
t.t.Run("lnc auth custom mac perms", func(tt *testing.T) {
164+
cfg := net.Bob.Cfg
165+
166+
ctx := context.Background()
167+
ctxt, cancel := context.WithTimeout(ctx, defaultTimeout)
168+
defer cancel()
169+
170+
customPerms := make(
171+
[]*litrpc.MacaroonPermission, 0, len(customURIs),
172+
)
173+
174+
customURIKeyword := macaroons.PermissionEntityCustomURI
175+
for uri := range customURIs {
176+
customPerms = append(
177+
customPerms, &litrpc.MacaroonPermission{
178+
Entity: customURIKeyword,
179+
Action: uri,
180+
},
181+
)
182+
}
183+
184+
rawLNCConn := setUpLNCConn(
185+
ctxt, tt, cfg.LitAddr(), cfg.LitTLSCertPath,
186+
cfg.LitMacPath,
187+
litrpc.SessionType_TYPE_MACAROON_CUSTOM, customPerms,
188+
)
189+
defer rawLNCConn.Close()
190+
191+
for _, endpoint := range endpoints {
192+
endpoint := endpoint
193+
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
194+
allowed := customURIs[endpoint.grpcWebURI]
195+
runLNCAuthTest(
196+
ttt, rawLNCConn, endpoint.requestFn,
197+
endpoint.successPattern,
198+
allowed, "permission denied",
145199
)
146200
})
147201
}

0 commit comments

Comments
 (0)