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
|
hash: 6569f51a7172f16b26c374cdcf3cae0abea234207ac99bfa6dc4712406bfc4d2
|
||||||
updated: 2017-08-02T21:19:34.129016558-07:00
|
updated: 2017-08-09T14:51:02.638659953-06:00
|
||||||
imports:
|
imports:
|
||||||
- name: github.com/aead/chacha20
|
- name: github.com/aead/chacha20
|
||||||
version: d31a916ded42d1640b9d89a26f8abd53cc96790c
|
version: d31a916ded42d1640b9d89a26f8abd53cc96790c
|
||||||
@ -180,4 +180,14 @@ imports:
|
|||||||
- stats
|
- stats
|
||||||
- tap
|
- tap
|
||||||
- transport
|
- 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: []
|
testImports: []
|
||||||
|
@ -71,3 +71,8 @@ import:
|
|||||||
- chaincfg
|
- chaincfg
|
||||||
- package: github.com/lightninglabs/neutrino
|
- package: github.com/lightninglabs/neutrino
|
||||||
version: 807d3e267a2863654e99dda39ac4daac78943f7e
|
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