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