mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-03 17:26:57 +01:00
macaroons: ip range constraint
This commit is contained in:
parent
5fe900d18d
commit
0b78ea05fe
4 changed files with 116 additions and 4 deletions
|
@ -30,6 +30,10 @@ var (
|
|||
Name: "ip_address",
|
||||
Usage: "the IP address the macaroon will be bound to",
|
||||
}
|
||||
macIPRangeFlag = cli.StringFlag{
|
||||
Name: "ip_range",
|
||||
Usage: "the IP range the macaroon will be bound to",
|
||||
}
|
||||
macCustomCaveatNameFlag = cli.StringFlag{
|
||||
Name: "custom_caveat_name",
|
||||
Usage: "the name of the custom caveat to add",
|
||||
|
@ -557,6 +561,19 @@ func applyMacaroonConstraints(ctx *cli.Context,
|
|||
)
|
||||
}
|
||||
|
||||
if ctx.IsSet(macIPRangeFlag.Name) {
|
||||
_, net, err := net.ParseCIDR(ctx.String(macIPRangeFlag.Name))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse ip_range: %s",
|
||||
ctx.String("ip_range"))
|
||||
}
|
||||
|
||||
macConstraints = append(
|
||||
macConstraints,
|
||||
macaroons.IPLockConstraint(net.String()),
|
||||
)
|
||||
}
|
||||
|
||||
if ctx.IsSet(macCustomCaveatNameFlag.Name) {
|
||||
customCaveatName := ctx.String(macCustomCaveatNameFlag.Name)
|
||||
if containsWhiteSpace(customCaveatName) {
|
||||
|
|
|
@ -472,7 +472,7 @@ func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context,
|
|||
}
|
||||
macaroonService, err = macaroons.NewService(
|
||||
rootKeyStore, "lnd", walletInitParams.StatelessInit,
|
||||
macaroons.IPLockChecker,
|
||||
macaroons.IPLockChecker, macaroons.IPRangeLockChecker,
|
||||
macaroons.CustomChecker(interceptorChain),
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
@ -148,6 +148,49 @@ func testMacaroonAuthentication(ht *lntest.HarnessTest) {
|
|||
require.NoError(t, err, "get new address")
|
||||
assert.Contains(t, res.Address, "bcrt1")
|
||||
},
|
||||
}, {
|
||||
// Fifth test: Check first-party caveat with invalid IP range.
|
||||
name: "invalid IP range macaroon",
|
||||
run: func(ctxt context.Context, t *testing.T) {
|
||||
readonlyMac, err := testNode.ReadMacaroon(
|
||||
testNode.Cfg.ReadMacPath, defaultTimeout,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
invalidIPRangeMac, err := macaroons.AddConstraints(
|
||||
readonlyMac, macaroons.IPRangeLockConstraint(
|
||||
"1.1.1.1/32",
|
||||
),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
cleanup, client := macaroonClient(
|
||||
t, testNode, invalidIPRangeMac,
|
||||
)
|
||||
defer cleanup()
|
||||
_, err = client.GetInfo(ctxt, infoReq)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "different IP range")
|
||||
},
|
||||
}, {
|
||||
// Sixth test: Make sure that if we do everything correct and
|
||||
// send the admin macaroon with first-party caveats that we can
|
||||
// satisfy, we get a correct answer.
|
||||
name: "correct macaroon",
|
||||
run: func(ctxt context.Context, t *testing.T) {
|
||||
adminMac, err := testNode.ReadMacaroon(
|
||||
testNode.Cfg.AdminMacPath, defaultTimeout,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
adminMac, err = macaroons.AddConstraints(
|
||||
adminMac, macaroons.TimeoutConstraint(30),
|
||||
macaroons.IPRangeLockConstraint("127.0.0.1/32"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
cleanup, client := macaroonClient(t, testNode, adminMac)
|
||||
defer cleanup()
|
||||
res, err := client.NewAddress(ctxt, newAddrReq)
|
||||
require.NoError(t, err, "get new address")
|
||||
assert.Contains(t, res.Address, "bcrt1")
|
||||
},
|
||||
}, {
|
||||
// Seventh test: Bake a macaroon that can only access exactly
|
||||
// two RPCs and make sure it works as expected.
|
||||
|
|
|
@ -80,9 +80,9 @@ func TimeoutConstraint(seconds int64) func(*macaroon.Macaroon) error {
|
|||
}
|
||||
}
|
||||
|
||||
// IPLockConstraint locks macaroon to a specific IP address.
|
||||
// If address is an empty string, this constraint does nothing to
|
||||
// accommodate default value's desired behavior.
|
||||
// IPLockConstraint locks a macaroon to a specific IP address. If ipAddr is an
|
||||
// empty string, this constraint does nothing to accommodate default value's
|
||||
// desired behavior.
|
||||
func IPLockConstraint(ipAddr string) func(*macaroon.Macaroon) error {
|
||||
return func(mac *macaroon.Macaroon) error {
|
||||
if ipAddr != "" {
|
||||
|
@ -93,8 +93,30 @@ func IPLockConstraint(ipAddr string) func(*macaroon.Macaroon) error {
|
|||
}
|
||||
caveat := checkers.Condition("ipaddr",
|
||||
macaroonIPAddr.String())
|
||||
|
||||
return mac.AddFirstPartyCaveat([]byte(caveat))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// IPRangeLockConstraint locks a macaroon to a specific IP address range. If
|
||||
// ipRange is an empty string, this constraint does nothing to accommodate
|
||||
// default value's desired behavior.
|
||||
func IPRangeLockConstraint(ipRange string) func(*macaroon.Macaroon) error {
|
||||
return func(mac *macaroon.Macaroon) error {
|
||||
if ipRange != "" {
|
||||
_, net, err := net.ParseCIDR(ipRange)
|
||||
if err != nil {
|
||||
return fmt.Errorf("incorrect macaroon IP " +
|
||||
"range")
|
||||
}
|
||||
caveat := checkers.Condition("iprange", net.String())
|
||||
|
||||
return mac.AddFirstPartyCaveat([]byte(caveat))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -122,6 +144,36 @@ func IPLockChecker() (string, checkers.Func) {
|
|||
}
|
||||
}
|
||||
|
||||
// IPRangeLockChecker accepts client IP range from the validation context and
|
||||
// compares it with the IP range locked in the macaroon. It is of the `Checker`
|
||||
// type.
|
||||
func IPRangeLockChecker() (string, checkers.Func) {
|
||||
return "iprange", func(ctx context.Context, cond, arg string) error {
|
||||
// Get peer info and extract IP range from it for macaroon
|
||||
// check.
|
||||
pr, ok := peer.FromContext(ctx)
|
||||
if !ok {
|
||||
return fmt.Errorf("unable to get peer info from context")
|
||||
}
|
||||
peerAddr, _, err := net.SplitHostPort(pr.Addr.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse peer address")
|
||||
}
|
||||
|
||||
_, ipNet, err := net.ParseCIDR(arg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse macaroon IP range")
|
||||
}
|
||||
|
||||
if !ipNet.Contains(net.ParseIP(peerAddr)) {
|
||||
msg := "macaroon locked to different IP range"
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// CustomConstraint returns a function that adds a custom caveat condition to
|
||||
// a macaroon.
|
||||
func CustomConstraint(name, condition string) func(*macaroon.Macaroon) error {
|
||||
|
|
Loading…
Add table
Reference in a new issue