multi: macaroon root key encryption

This commit is contained in:
Alex authored and Olaoluwa Osuntokun committed 2018-02-05 21:44:06 -08:00
1 parent 4b1cc98808
commit de6efbd1a1
7 files changed
+381 -94

No files matched your search

+68 -8
View File
@@ -1,13 +1,16 @@
package macaroons
import (
"encoding/hex"
"fmt"
"path"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"gopkg.in/macaroon-bakery.v2/bakery"
"gopkg.in/macaroon-bakery.v2/bakery/checkers"
macaroon "gopkg.in/macaroon.v2"
"golang.org/x/net/context"
@@ -20,6 +23,15 @@ var (
dbFilename = "macaroons.db"
)
// Service encapsulates bakery.Bakery and adds a Close() method that zeroes the
// root key service encryption keys, as well as utility methods to validate a
// macaroon against the bakery and gRPC middleware for macaroon-based auth.
type Service struct {
bakery.Bakery
rks *RootKeyStorage
}
// NewService returns a service backed by the macaroon Bolt DB stored in the
// passed directory. The `checks` argument can be any of the `Checker` type
// functions defined in this package, or a custom checker if desired. This
@@ -27,7 +39,7 @@ var (
// listing the same checker more than once is not harmful. Default checkers,
// such as those for `allow`, `time-before`, `declared`, and `error` caveats
// are registered automatically and don't need to be added.
func NewService(dir string, checks ...Checker) (*bakery.Bakery, error) {
func NewService(dir string, checks ...Checker) (*Service, error) {
// Open the database that we'll use to store the primary macaroon key,
// and all generated macaroons+caveats.
macaroonDB, err := bolt.Open(path.Join(dir, dbFilename), 0600,
@@ -62,7 +74,7 @@ func NewService(dir string, checks ...Checker) (*bakery.Bakery, error) {
}
}
return svc, nil
return &Service{*svc, rootKeyStore}, nil
}
// isRegistered checks to see if the required checker has already been
@@ -83,7 +95,7 @@ func isRegistered(c *checkers.Checker, name string) bool {
// UnaryServerInterceptor is a GRPC interceptor that checks whether the
// request is authorized by the included macaroons.
func UnaryServerInterceptor(svc *bakery.Bakery,
func (svc *Service) UnaryServerInterceptor(
permissionMap map[string][]bakery.Op) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{},
@@ -95,8 +107,7 @@ func UnaryServerInterceptor(svc *bakery.Bakery,
"required for method", info.FullMethod)
}
err := ValidateMacaroon(ctx, permissionMap[info.FullMethod],
svc)
err := svc.ValidateMacaroon(ctx, permissionMap[info.FullMethod])
if err != nil {
return nil, err
}
@@ -107,7 +118,7 @@ func UnaryServerInterceptor(svc *bakery.Bakery,
// StreamServerInterceptor is a GRPC interceptor that checks whether the
// request is authorized by the included macaroons.
func StreamServerInterceptor(svc *bakery.Bakery,
func (svc *Service) StreamServerInterceptor(
permissionMap map[string][]bakery.Op) grpc.StreamServerInterceptor {
return func(srv interface{}, ss grpc.ServerStream,
@@ -118,8 +129,8 @@ func StreamServerInterceptor(svc *bakery.Bakery,
"for method", info.FullMethod)
}
err := ValidateMacaroon(ss.Context(),
permissionMap[info.FullMethod], svc)
err := svc.ValidateMacaroon(ss.Context(),
permissionMap[info.FullMethod])
if err != nil {
return err
}
@@ -127,3 +138,52 @@ func StreamServerInterceptor(svc *bakery.Bakery,
return handler(srv, ss)
}
}
// ValidateMacaroon validates the capabilities of a given request given a
// bakery service, context, and uri. Within the passed context.Context, we
// expect a macaroon to be encoded as request metadata using the key
// "macaroon".
func (svc *Service) ValidateMacaroon(ctx context.Context,
requiredPermissions []bakery.Op) error {
// Get macaroon bytes from context and unmarshal into macaroon.
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return fmt.Errorf("unable to get metadata from context")
}
if len(md["macaroon"]) != 1 {
return fmt.Errorf("expected 1 macaroon, got %d",
len(md["macaroon"]))
}
// With the macaroon obtained, we'll now decode the hex-string
// encoding, then unmarshal it from binary into its concrete struct
// representation.
macBytes, err := hex.DecodeString(md["macaroon"][0])
if err != nil {
return err
}
mac := &macaroon.Macaroon{}
err = mac.UnmarshalBinary(macBytes)
if err != nil {
return err
}
// Check the method being called against the permitted operation and
// the expiration time and IP address and return the result.
authChecker := svc.Checker.Auth(macaroon.Slice{mac})
_, err = authChecker.Allow(ctx, requiredPermissions...)
return err
}
// Close closes the database that underlies the RootKeyStore and zeroes the
// encryption keys.
func (svc *Service) Close() error {
return svc.rks.Close()
}
// CreateUnlock calls the underlying root key store's CreateUnlock and returns
// the result.
func (svc *Service) CreateUnlock(password *[]byte) error {
return svc.rks.CreateUnlock(password)
}