@@ -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,203 @@ 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+ // Restart Alice with read-only super macaroon baking enabled.
1574+ err := net .RestartNode (
1575+ net .Alice , nil , []LitArgOption {
1576+ WithLitArg ("bake-super-macaroon" , "read-only" ),
1577+ WithLitArg ("super-macaroon-path" , superMacPath ),
1578+ },
1579+ )
1580+ require .NoError (t .t , err )
1581+
1582+ net .ConnectNodes (t .t , net .Alice , net .Bob )
1583+
1584+ // Verify that the super macaroon was created on startup.
1585+ require .FileExists (t .t , superMacPath )
1586+
1587+ // Verify permissions: write should be blocked.
1588+ verifyMacaroonPermissions (superMacPath , false )
1589+
1590+ // Restart Alice with a read-write super macaroon, WITHOUT
1591+ // deleting the file. This will test the overwrite behavior when
1592+ // permissions differ.
1593+ err = net .RestartNode (
1594+ net .Alice , nil , []LitArgOption {
1595+ WithLitArg ("bake-super-macaroon" , "read-write" ),
1596+ WithLitArg ("super-macaroon-path" , superMacPath ),
1597+ },
1598+ )
1599+ require .NoError (t .t , err )
1600+ net .ConnectNodes (t .t , net .Alice , net .Bob )
1601+
1602+ require .FileExists (t .t , superMacPath )
1603+
1604+ // Verify permissions: write should succeed.
1605+ verifyMacaroonPermissions (superMacPath , true )
1606+
1607+ // Restart Alice back with a read-only super macaroon, WITHOUT
1608+ // deleting the file. This will test the overwrite behavior when
1609+ // switching back to read-only.
1610+ err = net .RestartNode (
1611+ net .Alice , nil , []LitArgOption {
1612+ WithLitArg ("bake-super-macaroon" , "read-only" ),
1613+ WithLitArg ("super-macaroon-path" , superMacPath ),
1614+ },
1615+ )
1616+ require .NoError (t .t , err )
1617+ net .ConnectNodes (t .t , net .Alice , net .Bob )
1618+
1619+ require .FileExists (t .t , superMacPath )
1620+
1621+ // Verify permissions: write should be blocked again.
1622+ verifyMacaroonPermissions (superMacPath , false )
1623+
1624+ // Restart Alice with a sub-server disabled to bake a super macaroon
1625+ // with a subset of permissions.
1626+ err = net .RestartNode (
1627+ net .Alice , nil , []LitArgOption {
1628+ WithLitArg ("bake-super-macaroon" , "read-write" ),
1629+ WithLitArg ("super-macaroon-path" , superMacPath ),
1630+ WithLitArg ("loop-mode" , "disable" ),
1631+ WithLitArg ("pool-mode" , "disable" ),
1632+ WithLitArg ("faraday-mode" , "disable" ),
1633+ },
1634+ )
1635+ require .NoError (t .t , err )
1636+ net .ConnectNodes (t .t , net .Alice , net .Bob )
1637+
1638+ require .FileExists (t .t , superMacPath )
1639+ verifyMacaroonSubserverPermissions (superMacPath , false )
1640+
1641+ // Restart Alice with the sub-servers re-enabled. This will add
1642+ // permissions and trigger macaroon regeneration on startup.
1643+ err = net .RestartNode (
1644+ net .Alice , nil , []LitArgOption {
1645+ WithLitArg ("bake-super-macaroon" , "read-write" ),
1646+ WithLitArg ("super-macaroon-path" , superMacPath ),
1647+ },
1648+ )
1649+ require .NoError (t .t , err )
1650+ net .ConnectNodes (t .t , net .Alice , net .Bob )
1651+
1652+ require .FileExists (t .t , superMacPath )
1653+ verifyMacaroonSubserverPermissions (superMacPath , true )
1654+
1655+ // Verify permissions: write should succeed.
1656+ verifyMacaroonPermissions (superMacPath , true )
1657+
1658+ // Clean up the super macaroon file.
1659+ _ = os .Remove (superMacPath )
1660+
1661+ // Restart Alice with super macaroon baking disabled and verify that
1662+ // no super macaroon is created.
1663+ err = net .RestartNode (
1664+ net .Alice , nil , []LitArgOption {
1665+ WithLitArg ("bake-super-macaroon" , "none" ),
1666+ WithLitArg ("super-macaroon-path" , superMacPath ),
1667+ },
1668+ )
1669+ require .NoError (t .t , err )
1670+ net .ConnectNodes (t .t , net .Alice , net .Bob )
1671+
1672+ _ , err = os .Stat (superMacPath )
1673+ require .True (t .t , os .IsNotExist (err ))
1674+
1675+ // Clean up after ourselves.
1676+ err = net .RestartNode (
1677+ net .Alice , nil , []LitArgOption {
1678+ WithoutLitArg ("bake-super-macaroon" ),
1679+ WithoutLitArg ("super-macaroon-path" ),
1680+ },
1681+ )
1682+ require .NoError (t .t , err )
1683+ net .ConnectNodes (t .t , net .Alice , net .Bob )
1684+ }
0 commit comments