21c29c33d7
This commit reworks the macaroon authentication framework to use the v2 macaroon format and bakery API. It also replaces the code in each RPC method which calls the macaroon verifier with interceptors which call the macaroon verifier instead. In addition, the operation permissions are reworked to fit the new format of "allow" commands (specifically, entity/operation permissions instead of method permissions).
91 lines
2.9 KiB
Go
91 lines
2.9 KiB
Go
package macaroons
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"time"
|
|
|
|
"google.golang.org/grpc/peer"
|
|
|
|
"gopkg.in/macaroon-bakery.v2/bakery/checkers"
|
|
macaroon "gopkg.in/macaroon.v2"
|
|
|
|
"golang.org/x/net/context"
|
|
)
|
|
|
|
// Constraint type adds a layer of indirection over macaroon caveats.
|
|
type Constraint func(*macaroon.Macaroon) error
|
|
|
|
// Checker type adds a layer of indirection over macaroon checkers. A Checker
|
|
// returns the name of the checker and the checker function; these are used to
|
|
// register the function with the bakery service's compound checker.
|
|
type Checker func() (string, checkers.Func)
|
|
|
|
// AddConstraints returns new derived macaroon by applying every passed
|
|
// constraint and tightening its restrictions.
|
|
func AddConstraints(mac *macaroon.Macaroon, cs ...Constraint) (*macaroon.Macaroon, error) {
|
|
newMac := mac.Clone()
|
|
for _, constraint := range cs {
|
|
if err := constraint(newMac); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return newMac, nil
|
|
}
|
|
|
|
// Each *Constraint function is a functional option, which takes a pointer
|
|
// to the macaroon and adds another restriction to it. For each *Constraint,
|
|
// the corresponding *Checker is provided if not provided by default.
|
|
|
|
// TimeoutConstraint restricts the lifetime of the macaroon
|
|
// to the amount of seconds given.
|
|
func TimeoutConstraint(seconds int64) func(*macaroon.Macaroon) error {
|
|
return func(mac *macaroon.Macaroon) error {
|
|
macaroonTimeout := time.Duration(seconds)
|
|
requestTimeout := time.Now().Add(time.Second * macaroonTimeout)
|
|
caveat := checkers.TimeBeforeCaveat(requestTimeout)
|
|
return mac.AddFirstPartyCaveat([]byte(caveat.Condition))
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
func IPLockConstraint(ipAddr string) func(*macaroon.Macaroon) error {
|
|
return func(mac *macaroon.Macaroon) error {
|
|
if ipAddr != "" {
|
|
macaroonIPAddr := net.ParseIP(ipAddr)
|
|
if macaroonIPAddr == nil {
|
|
return fmt.Errorf("incorrect macaroon IP-lock address")
|
|
}
|
|
caveat := checkers.Condition("ipaddr",
|
|
macaroonIPAddr.String())
|
|
return mac.AddFirstPartyCaveat([]byte(caveat))
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// IPLockChecker accepts client IP from the validation context and compares it
|
|
// with IP locked in the macaroon. It is of the `Checker` type.
|
|
func IPLockChecker() (string, checkers.Func) {
|
|
return "ipaddr", func(ctx context.Context, cond, arg string) error {
|
|
// Get peer info and extract IP address 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")
|
|
}
|
|
|
|
if !net.ParseIP(arg).Equal(net.ParseIP(peerAddr)) {
|
|
msg := "macaroon locked to different IP address"
|
|
return fmt.Errorf(msg)
|
|
}
|
|
return nil
|
|
}
|
|
}
|