macaroons: add macaroons package and update glide
This commit is contained in:
parent
5ef077e5c8
commit
662731e719
14
glide.lock
generated
14
glide.lock
generated
@ -1,5 +1,5 @@
|
||||
hash: d40636a5875b6dedd06b34514ea3430717c02153ecd16f32aa5bcedeedd6a833
|
||||
updated: 2017-08-02T21:19:34.129016558-07:00
|
||||
hash: 6569f51a7172f16b26c374cdcf3cae0abea234207ac99bfa6dc4712406bfc4d2
|
||||
updated: 2017-08-09T14:51:02.638659953-06:00
|
||||
imports:
|
||||
- name: github.com/aead/chacha20
|
||||
version: d31a916ded42d1640b9d89a26f8abd53cc96790c
|
||||
@ -180,4 +180,14 @@ imports:
|
||||
- stats
|
||||
- tap
|
||||
- transport
|
||||
- name: gopkg.in/macaroon.v1
|
||||
version: d8fd13e6951f2ce46f0964a58149cf2f103cac9a
|
||||
- name: gopkg.in/macaroon-bakery.v1
|
||||
version: 8e14f8b0f5e286ad100ca8d6ce5bde0f0dfb8b21
|
||||
- name: github.com/juju/loggo
|
||||
version: 8232ab8918d91c72af1a9fb94d3edbe31d88b790
|
||||
- name: github.com/rogpeppe/fastuuid
|
||||
version: 6724a57986aff9bff1a1770e9347036def7c89f6
|
||||
- name: gopkg.in/errgo.v1
|
||||
version: 442357a80af5c6bf9b6d51ae791a39c3421004f3
|
||||
testImports: []
|
||||
|
@ -71,3 +71,8 @@ import:
|
||||
- chaincfg
|
||||
- package: github.com/lightninglabs/neutrino
|
||||
version: 807d3e267a2863654e99dda39ac4daac78943f7e
|
||||
- package: gopkg.in/macaroon.v1
|
||||
- package: gopkg.in/macaroon-bakery.v1
|
||||
- package: github.com/juju/loggo
|
||||
- package: github.com/rogpeppe/fastuuid
|
||||
- package: gopkg.in/errgo.v1
|
||||
|
76
macaroons/auth.go
Normal file
76
macaroons/auth.go
Normal file
@ -0,0 +1,76 @@
|
||||
package macaroons
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"gopkg.in/macaroon-bakery.v1/bakery"
|
||||
"gopkg.in/macaroon-bakery.v1/bakery/checkers"
|
||||
macaroon "gopkg.in/macaroon.v1"
|
||||
)
|
||||
|
||||
// MacaroonCredential wraps a macaroon to implement the
|
||||
// credentials.PerRPCCredentials interface.
|
||||
type MacaroonCredential struct {
|
||||
*macaroon.Macaroon
|
||||
}
|
||||
|
||||
// RequireTransportSecurity implements the PerRPCCredentials interface.
|
||||
func (m MacaroonCredential) RequireTransportSecurity() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetRequestMetadata implements the PerRPCCredentials interface.
|
||||
func (m MacaroonCredential) GetRequestMetadata(ctx context.Context,
|
||||
uri ...string) (map[string]string, error) {
|
||||
macBytes, err := m.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
md := make(map[string]string)
|
||||
md["macaroon"] = hex.EncodeToString(macBytes)
|
||||
return md, nil
|
||||
}
|
||||
|
||||
// NewMacaroonCredential returns a copy of the passed macaroon wrapped in a
|
||||
// MacaroonCredential struct which implements PerRPCCredentials.
|
||||
func NewMacaroonCredential(m *macaroon.Macaroon) MacaroonCredential {
|
||||
ms := MacaroonCredential{}
|
||||
ms.Macaroon = m.Clone()
|
||||
return ms
|
||||
}
|
||||
|
||||
// ValidateMacaroon validates auth given a bakery service, context, and uri.
|
||||
func ValidateMacaroon(ctx context.Context, method string,
|
||||
svc *bakery.Service) error {
|
||||
// Get macaroon bytes from context and unmarshal into macaroon.
|
||||
// TODO(aakselrod): use FromIncomingContext after grpc update in glide.
|
||||
md, ok := metadata.FromContext(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"]))
|
||||
}
|
||||
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 return the result.
|
||||
// TODO(aakselrod): Add more checks as required.
|
||||
return svc.Check(macaroon.Slice{mac}, checkers.New(
|
||||
checkers.OperationChecker(method),
|
||||
checkers.TimeBefore,
|
||||
))
|
||||
}
|
44
macaroons/service.go
Normal file
44
macaroons/service.go
Normal file
@ -0,0 +1,44 @@
|
||||
package macaroons
|
||||
|
||||
import (
|
||||
"path"
|
||||
|
||||
"gopkg.in/macaroon-bakery.v1/bakery"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
)
|
||||
|
||||
var (
|
||||
// dbFileName is the filename within the data directory which contains
|
||||
// the macaroon stores.
|
||||
dbFilename = "macaroons.db"
|
||||
)
|
||||
|
||||
// NewService returns a service backed by the macaroon Bolt DB stored in the
|
||||
// passed directory.
|
||||
func NewService(dir string) (*bakery.Service, error) {
|
||||
// Open the database.
|
||||
macaroonDB, err := bolt.Open(path.Join(dir, dbFilename), 0600,
|
||||
bolt.DefaultOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rootKeyStore, err := NewRootKeyStorage(macaroonDB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
macaroonStore, err := NewStorage(macaroonDB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
macaroonParams := bakery.NewServiceParams{
|
||||
Location: "lnd",
|
||||
Store: macaroonStore,
|
||||
RootKeyStore: rootKeyStore,
|
||||
// No third-party caveat support for now.
|
||||
// TODO(aakselrod): Add third-party caveat support.
|
||||
Locator: nil,
|
||||
Key: nil,
|
||||
}
|
||||
return bakery.NewService(macaroonParams)
|
||||
}
|
145
macaroons/store.go
Normal file
145
macaroons/store.go
Normal file
@ -0,0 +1,145 @@
|
||||
package macaroons
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
)
|
||||
|
||||
const (
|
||||
// RootKeyLen is the length of a root key.
|
||||
RootKeyLen = 32
|
||||
)
|
||||
|
||||
var (
|
||||
// rootKeyBucketName is the name of the root key store bucket.
|
||||
rootKeyBucketName = []byte("macrootkeys")
|
||||
|
||||
// defaultRootKeyID is the ID of the default root key. The first is
|
||||
// just 0, to emulate the memory storage that comes with bakery.
|
||||
// TODO(aakselrod): Add support for key rotation.
|
||||
defaultRootKeyID = "0"
|
||||
|
||||
// macaroonBucketName is the name of the macaroon store bucket.
|
||||
macaroonBucketName = []byte("macaroons")
|
||||
)
|
||||
|
||||
// RootKeyStorage implements the bakery.RootKeyStorage interface.
|
||||
type RootKeyStorage struct {
|
||||
*bolt.DB
|
||||
}
|
||||
|
||||
// NewRootKeyStorage creates a RootKeyStorage instance.
|
||||
// TODO(aakselrod): Add support for encryption of data with passphrase.
|
||||
func NewRootKeyStorage(db *bolt.DB) (*RootKeyStorage, error) {
|
||||
// If the store's bucket doesn't exist, create it.
|
||||
err := db.Update(func(tx *bolt.Tx) error {
|
||||
_, err := tx.CreateBucketIfNotExists(rootKeyBucketName)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Return the DB wrapped in a RootKeyStorage object.
|
||||
return &RootKeyStorage{db}, nil
|
||||
}
|
||||
|
||||
// Get implements the Get method for the bakery.RootKeyStorage interface.
|
||||
func (r *RootKeyStorage) Get(id string) ([]byte, error) {
|
||||
var rootKey []byte
|
||||
err := r.View(func(tx *bolt.Tx) error {
|
||||
rootKey = tx.Bucket(rootKeyBucketName).Get([]byte(id))
|
||||
if len(rootKey) == 0 {
|
||||
return fmt.Errorf("root key with id %s doesn't exist",
|
||||
id)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rootKey, nil
|
||||
}
|
||||
|
||||
// RootKey implements the RootKey method for the bakery.RootKeyStorage
|
||||
// interface.
|
||||
// TODO(aakselrod): Add support for key rotation.
|
||||
func (r *RootKeyStorage) RootKey() ([]byte, string, error) {
|
||||
var rootKey []byte
|
||||
id := defaultRootKeyID
|
||||
err := r.Update(func(tx *bolt.Tx) error {
|
||||
ns := tx.Bucket(rootKeyBucketName)
|
||||
rootKey = ns.Get([]byte(id))
|
||||
|
||||
// If there's no root key stored in the bucket yet, create one.
|
||||
if len(rootKey) != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a RootKeyLen-byte root key.
|
||||
rootKey = make([]byte, RootKeyLen)
|
||||
if _, err := io.ReadFull(rand.Reader, rootKey[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
return ns.Put([]byte(id), rootKey)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return rootKey, id, nil
|
||||
}
|
||||
|
||||
// Storage implements the bakery.Storage interface.
|
||||
type Storage struct {
|
||||
*bolt.DB
|
||||
}
|
||||
|
||||
// NewStorage creates a Storage instance.
|
||||
// TODO(aakselrod): Add support for encryption of data with passphrase.
|
||||
func NewStorage(db *bolt.DB) (*Storage, error) {
|
||||
// If the store's bucket doesn't exist, create it.
|
||||
err := db.Update(func(tx *bolt.Tx) error {
|
||||
_, err := tx.CreateBucketIfNotExists(macaroonBucketName)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Return the DB wrapped in a Storage object.
|
||||
return &Storage{db}, nil
|
||||
}
|
||||
|
||||
// Put implements the Put method for the bakery.Storage interface.
|
||||
func (s *Storage) Put(location string, item string) error {
|
||||
return s.Update(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(macaroonBucketName).Put([]byte(location),
|
||||
[]byte(item))
|
||||
})
|
||||
}
|
||||
|
||||
// Get implements the Get method for the bakery.Storage interface.
|
||||
func (s *Storage) Get(location string) (string, error) {
|
||||
var item string
|
||||
err := s.View(func(tx *bolt.Tx) error {
|
||||
itemBytes := tx.Bucket(macaroonBucketName).Get([]byte(location))
|
||||
if len(itemBytes) == 0 {
|
||||
return fmt.Errorf("couldn't get item for location %s",
|
||||
location)
|
||||
}
|
||||
item = string(itemBytes)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// Del implements the Del method for the bakery.Storage interface.
|
||||
func (s *Storage) Del(location string) error {
|
||||
return s.Update(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(macaroonBucketName).Delete([]byte(location))
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user