@@ -11,6 +11,7 @@ import (
1111 "io/ioutil"
1212 "net/http"
1313 "os"
14+ "path/filepath"
1415 "strings"
1516 "testing"
1617 "time"
@@ -1481,3 +1482,224 @@ func bakeSuperMacaroon(t *testing.T, cfg *LitNodeConfig,
14811482
14821483 return tempFile .Name ()
14831484}
1485+
1486+ // testSuperMacaroonOnStartup tests that the super macaroon is successfully
1487+ // baked on startup if configured.
1488+ func testSuperMacaroonOnStartup (ctx context.Context , net * NetworkHarness ,
1489+ t * harnessTest ) {
1490+
1491+ superMacPath := filepath .Join (
1492+ net .Alice .Cfg .LitDir , "startup-super.macaroon" ,
1493+ )
1494+
1495+ verifyMacaroonPermissions := func (path string , expectWrite bool ) {
1496+ ctxTimeout , cancel := context .WithTimeout (ctx , defaultTimeout )
1497+ defer cancel ()
1498+
1499+ rawConn , err := connectRPC (
1500+ ctxTimeout , net .Alice .Cfg .LitAddr (),
1501+ net .Alice .Cfg .LitTLSCertPath ,
1502+ )
1503+ require .NoError (t .t , err )
1504+ defer rawConn .Close ()
1505+
1506+ macBytes , err := os .ReadFile (path )
1507+ require .NoError (t .t , err )
1508+
1509+ ctxm := macaroonContext (ctxTimeout , macBytes )
1510+ lnrpcConn := lnrpc .NewLightningClient (rawConn )
1511+
1512+ _ , err = lnrpcConn .GetInfo (ctxm , & lnrpc.GetInfoRequest {})
1513+ require .NoError (t .t , err )
1514+
1515+ _ , err = lnrpcConn .NewAddress (ctxm , & lnrpc.NewAddressRequest {
1516+ Type : lnrpc .AddressType_WITNESS_PUBKEY_HASH ,
1517+ })
1518+ if expectWrite {
1519+ require .NoError (t .t , err )
1520+ } else {
1521+ require .Error (t .t , err )
1522+ require .Contains (t .t , err .Error (), "permission denied" )
1523+ }
1524+ }
1525+
1526+ verifyMacaroonSubserverPermissions := func (path string ,
1527+ expectSubservers bool ) {
1528+
1529+ macBytes , err := os .ReadFile (path )
1530+ require .NoError (t .t , err )
1531+
1532+ mac := & macaroon.Macaroon {}
1533+ err = mac .UnmarshalBinary (macBytes )
1534+ require .NoError (t .t , err )
1535+
1536+ rawID := mac .Id ()
1537+ require .NotEmpty (t .t , rawID )
1538+
1539+ decodedID := & lnrpc.MacaroonId {}
1540+ err = proto .Unmarshal (rawID [1 :], decodedID )
1541+ require .NoError (t .t , err )
1542+
1543+ hasSubserver := false
1544+ for _ , op := range decodedID .Ops {
1545+ if op == nil {
1546+ continue
1547+ }
1548+ if op .Entity == "loop" || op .Entity == "pool" ||
1549+ op .Entity == "faraday" {
1550+
1551+ hasSubserver = true
1552+ }
1553+ }
1554+
1555+ if expectSubservers {
1556+ require .True (
1557+ t .t , hasSubserver ,
1558+ "expected macaroon to contain " +
1559+ "subserver permissions" ,
1560+ )
1561+ } else {
1562+ require .False (
1563+ t .t , hasSubserver ,
1564+ "expected macaroon NOT to contain " +
1565+ "subserver permissions" ,
1566+ )
1567+ }
1568+ }
1569+
1570+ // Ensure any old file is removed first.
1571+ _ = os .Remove (superMacPath )
1572+
1573+ // Test that starting Alice with an invalid super-macaroon-path (not
1574+ // ending with .macaroon) fails.
1575+ invalidMacPath := filepath .Join (
1576+ net .Alice .Cfg .LitDir , "invalid-path.mac" ,
1577+ )
1578+ err := net .RestartNode (
1579+ net .Alice , nil , []LitArgOption {
1580+ WithLitArg ("bake-super-macaroon" , "read-only" ),
1581+ WithLitArg ("super-macaroon-path" , invalidMacPath ),
1582+ },
1583+ )
1584+ require .Error (t .t , err )
1585+
1586+ // Drain the expected process exit error from the error channel to
1587+ // prevent it from failing the test runner at the end of the test.
1588+ select {
1589+ case <- net .lndErrorChan :
1590+ case <- time .After (defaultTimeout ):
1591+ t .t .Fatalf ("expected process exit error in lndErrorChan" )
1592+ }
1593+
1594+ // Restart Alice with read-only super macaroon baking enabled.
1595+ err = net .RestartNode (
1596+ net .Alice , nil , []LitArgOption {
1597+ WithLitArg ("bake-super-macaroon" , "read-only" ),
1598+ WithLitArg ("super-macaroon-path" , superMacPath ),
1599+ },
1600+ )
1601+ require .NoError (t .t , err )
1602+
1603+ net .ConnectNodes (t .t , net .Alice , net .Bob )
1604+
1605+ // Verify that the super macaroon was created on startup.
1606+ require .FileExists (t .t , superMacPath )
1607+
1608+ // Verify permissions: write should be blocked.
1609+ verifyMacaroonPermissions (superMacPath , false )
1610+
1611+ // Restart Alice with a read-write super macaroon, WITHOUT
1612+ // deleting the file. This will test the overwrite behavior when
1613+ // permissions differ.
1614+ err = net .RestartNode (
1615+ net .Alice , nil , []LitArgOption {
1616+ WithLitArg ("bake-super-macaroon" , "read-write" ),
1617+ WithLitArg ("super-macaroon-path" , superMacPath ),
1618+ },
1619+ )
1620+ require .NoError (t .t , err )
1621+ net .ConnectNodes (t .t , net .Alice , net .Bob )
1622+
1623+ require .FileExists (t .t , superMacPath )
1624+
1625+ // Verify permissions: write should succeed.
1626+ verifyMacaroonPermissions (superMacPath , true )
1627+
1628+ // Restart Alice back with a read-only super macaroon, WITHOUT
1629+ // deleting the file. This will test the overwrite behavior when
1630+ // switching back to read-only.
1631+ err = net .RestartNode (
1632+ net .Alice , nil , []LitArgOption {
1633+ WithLitArg ("bake-super-macaroon" , "read-only" ),
1634+ WithLitArg ("super-macaroon-path" , superMacPath ),
1635+ },
1636+ )
1637+ require .NoError (t .t , err )
1638+ net .ConnectNodes (t .t , net .Alice , net .Bob )
1639+
1640+ require .FileExists (t .t , superMacPath )
1641+
1642+ // Verify permissions: write should be blocked again.
1643+ verifyMacaroonPermissions (superMacPath , false )
1644+
1645+ // Restart Alice with a sub-server disabled to bake a super macaroon
1646+ // with a subset of permissions.
1647+ err = net .RestartNode (
1648+ net .Alice , nil , []LitArgOption {
1649+ WithLitArg ("bake-super-macaroon" , "read-write" ),
1650+ WithLitArg ("super-macaroon-path" , superMacPath ),
1651+ WithLitArg ("loop-mode" , "disable" ),
1652+ WithLitArg ("pool-mode" , "disable" ),
1653+ WithLitArg ("faraday-mode" , "disable" ),
1654+ },
1655+ )
1656+ require .NoError (t .t , err )
1657+ net .ConnectNodes (t .t , net .Alice , net .Bob )
1658+
1659+ require .FileExists (t .t , superMacPath )
1660+ verifyMacaroonSubserverPermissions (superMacPath , false )
1661+
1662+ // Restart Alice with the sub-servers re-enabled. This will add
1663+ // permissions and trigger macaroon regeneration on startup.
1664+ err = net .RestartNode (
1665+ net .Alice , nil , []LitArgOption {
1666+ WithLitArg ("bake-super-macaroon" , "read-write" ),
1667+ WithLitArg ("super-macaroon-path" , superMacPath ),
1668+ },
1669+ )
1670+ require .NoError (t .t , err )
1671+ net .ConnectNodes (t .t , net .Alice , net .Bob )
1672+
1673+ require .FileExists (t .t , superMacPath )
1674+ verifyMacaroonSubserverPermissions (superMacPath , true )
1675+
1676+ // Verify permissions: write should succeed.
1677+ verifyMacaroonPermissions (superMacPath , true )
1678+
1679+ // Clean up the super macaroon file.
1680+ _ = os .Remove (superMacPath )
1681+
1682+ // Restart Alice with super macaroon baking disabled and verify that
1683+ // no super macaroon is created.
1684+ err = net .RestartNode (
1685+ net .Alice , nil , []LitArgOption {
1686+ WithLitArg ("bake-super-macaroon" , "none" ),
1687+ WithLitArg ("super-macaroon-path" , superMacPath ),
1688+ },
1689+ )
1690+ require .NoError (t .t , err )
1691+ net .ConnectNodes (t .t , net .Alice , net .Bob )
1692+
1693+ _ , err = os .Stat (superMacPath )
1694+ require .True (t .t , os .IsNotExist (err ))
1695+
1696+ // Clean up after ourselves.
1697+ err = net .RestartNode (
1698+ net .Alice , nil , []LitArgOption {
1699+ WithoutLitArg ("bake-super-macaroon" ),
1700+ WithoutLitArg ("super-macaroon-path" ),
1701+ },
1702+ )
1703+ require .NoError (t .t , err )
1704+ net .ConnectNodes (t .t , net .Alice , net .Bob )
1705+ }
0 commit comments