macaroons: add ip range checker

This commit is contained in:
Slyghtning 2025-03-07 09:56:13 +01:00
parent 6531d45050
commit ea9a5a2a71
No known key found for this signature in database
GPG Key ID: F82D456EA023C9BF
2 changed files with 66 additions and 4 deletions

View File

@ -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 {

View File

@ -3,6 +3,7 @@ package macaroons
import (
"bytes"
"context"
"errors"
"fmt"
"net"
"strings"
@ -21,6 +22,10 @@ const (
// in the serialized macaroon. We choose a single space as the delimiter
// between the because that is also used by the macaroon bakery library.
CondLndCustom = "lnd-custom"
// CondIPRange is the caveat condition name that is used for tying an IP
// range to a macaroon.
CondIPRange = "iprange"
)
// CustomCaveatAcceptor is an interface that contains a single method for
@ -80,9 +85,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 +98,32 @@ 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 != "" {
_, parsedNet, err := net.ParseCIDR(ipRange)
if err != nil {
return fmt.Errorf("incorrect macaroon IP "+
"range: %w", err)
}
caveat := checkers.Condition(
CondIPRange, parsedNet.String(),
)
return mac.AddFirstPartyCaveat([]byte(caveat))
}
return nil
}
}
@ -122,6 +151,39 @@ 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 CondIPRange, 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 errors.New("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: %w",
err)
}
_, ipNet, err := net.ParseCIDR(arg)
if err != nil {
return fmt.Errorf("unable to parse macaroon IP "+
"range: %w", err)
}
if !ipNet.Contains(net.ParseIP(peerAddr)) {
return errors.New("macaroon locked to different " +
"IP range")
}
return nil
}
}
// CustomConstraint returns a function that adds a custom caveat condition to
// a macaroon.
func CustomConstraint(name, condition string) func(*macaroon.Macaroon) error {