Merge pull request #10 from guggero/loop-ready

prepare for loop integration
This commit is contained in:
Olaoluwa Osuntokun
2019-11-18 17:55:08 -08:00
committed by GitHub
11 changed files with 976 additions and 90 deletions

View File

@@ -10,10 +10,26 @@ import (
"github.com/lightninglabs/kirin/macaroons"
"gopkg.in/macaroon-bakery.v2/bakery"
"gopkg.in/macaroon-bakery.v2/bakery/checkers"
"gopkg.in/macaroon.v2"
)
const (
// HeaderAuthorization is the HTTP header field name that is used to
// send the LSAT by REST clients.
HeaderAuthorization = "Authorization"
// HeaderMacaroonMD is the HTTP header field name that is used to send
// the LSAT by certain REST and gRPC clients.
HeaderMacaroonMD = "Grpc-Metadata-Macaroon"
// HeaderMacaroon is the HTTP header field name that is used to send the
// LSAT by our own gRPC clients.
HeaderMacaroon = "Macaroon"
)
var (
authRegex = regexp.MustCompile("LSAT (.*?):([a-f0-9]{64})")
authFormat = "LSAT %s:%s"
opWildcard = "*"
)
@@ -47,33 +63,12 @@ func NewLsatAuthenticator(challenger Challenger) (*LsatAuthenticator, error) {
//
// NOTE: This is part of the Authenticator interface.
func (l *LsatAuthenticator) Accept(header *http.Header) bool {
authHeader := header.Get("Authorization")
log.Debugf("Trying to authorize with header value [%s].", authHeader)
if authHeader == "" {
return false
}
if !authRegex.MatchString(authHeader) {
log.Debugf("Deny: Auth header in invalid format.")
return false
}
matches := authRegex.FindStringSubmatch(authHeader)
if len(matches) != 3 {
log.Debugf("Deny: Auth header in invalid format.")
return false
}
macBase64, preimageHex := matches[1], matches[2]
macBytes, err := base64.StdEncoding.DecodeString(macBase64)
// Try reading the macaroon and preimage from the HTTP header. This can
// be in different header fields depending on the implementation and/or
// protocol.
mac, preimageBytes, err := FromHeader(header)
if err != nil {
log.Debugf("Deny: Base64 decode of macaroon failed: %v", err)
return false
}
preimageBytes, err := hex.DecodeString(preimageHex)
if err != nil {
log.Debugf("Deny: Hex decode of preimage failed: %v", err)
log.Debugf("Deny: %v", err)
return false
}
@@ -84,7 +79,7 @@ func (l *LsatAuthenticator) Accept(header *http.Header) bool {
return false
}
err = l.macService.ValidateMacaroon(macBytes, []bakery.Op{})
err = l.macService.ValidateMacaroon(mac, []bakery.Op{})
if err != nil {
log.Debugf("Deny: Macaroon validation failed: %v", err)
return false
@@ -130,3 +125,112 @@ func (l *LsatAuthenticator) FreshChallengeHeader(r *http.Request) (
log.Debugf("Created new challenge header: [%s]", str)
return header, nil
}
// FromHeader tries to extract authentication information from HTTP headers.
// There are two supported formats that can be sent in three different header
// fields:
// 1. Authorization: LSAT <macBase64>:<preimageHex>
// 2. Grpc-Metadata-Macaroon: <macHex>
// 3. Macaroon: <macHex>
// If only the macaroon is sent in header 2 or three then it is expected to have
// a caveat with the preimage attached to it.
func FromHeader(header *http.Header) (*macaroon.Macaroon, []byte, error) {
var authHeader string
switch {
// Header field 1 contains the macaroon and the preimage as distinct
// values separated by a colon.
case header.Get(HeaderAuthorization) != "":
// Parse the content of the header field and check that it is in
// the correct format.
authHeader = header.Get(HeaderAuthorization)
log.Debugf("Trying to authorize with header value [%s].",
authHeader)
if !authRegex.MatchString(authHeader) {
return nil, nil, fmt.Errorf("invalid auth header "+
"format: %s", authHeader)
}
matches := authRegex.FindStringSubmatch(authHeader)
if len(matches) != 3 {
return nil, nil, fmt.Errorf("invalid auth header "+
"format: %s", authHeader)
}
// Decode the content of the two parts of the header value.
macBase64, preimageHex := matches[1], matches[2]
macBytes, err := base64.StdEncoding.DecodeString(macBase64)
if err != nil {
return nil, nil, fmt.Errorf("base64 decode of "+
"macaroon failed: %v", err)
}
mac := &macaroon.Macaroon{}
err = mac.UnmarshalBinary(macBytes)
if err != nil {
return nil, nil, fmt.Errorf("unable to unmarshal "+
"macaroon: %v", err)
}
preimageBytes, err := hex.DecodeString(preimageHex)
if err != nil {
return nil, nil, fmt.Errorf("hex decode of preimage "+
"failed: %v", err)
}
// All done, we don't need to extract anything from the
// macaroon since the preimage was presented separately.
return mac, preimageBytes, nil
// Header field 2: Contains only the macaroon.
case header.Get(HeaderMacaroonMD) != "":
authHeader = header.Get(HeaderMacaroonMD)
// Header field 3: Contains only the macaroon.
case header.Get(HeaderMacaroon) != "":
authHeader = header.Get(HeaderMacaroon)
default:
return nil, nil, fmt.Errorf("no auth header provided")
}
// For case 2 and 3, we need to actually unmarshal the macaroon to
// extract the preimage.
macBytes, err := hex.DecodeString(authHeader)
if err != nil {
return nil, nil, fmt.Errorf("hex decode of macaroon "+
"failed: %v", err)
}
mac := &macaroon.Macaroon{}
err = mac.UnmarshalBinary(macBytes)
if err != nil {
return nil, nil, fmt.Errorf("unable to unmarshal macaroon: "+
"%v", err)
}
preimageHex, err := macaroons.ExtractCaveat(mac, macaroons.CondPreimage)
if err != nil {
return nil, nil, fmt.Errorf("unable to extract preimage from "+
"macaroon: %v", err)
}
preimageBytes, err := hex.DecodeString(preimageHex)
if err != nil {
return nil, nil, fmt.Errorf("hex decode of preimage "+
"failed: %v", err)
}
return mac, preimageBytes, nil
}
// SetHeader sets the provided authentication elements as the default/standard
// HTTP header for the LSAT protocol.
func SetHeader(header *http.Header, mac *macaroon.Macaroon,
preimage []byte) error {
macBytes, err := mac.MarshalBinary()
if err != nil {
return err
}
value := fmt.Sprintf(
authFormat, base64.StdEncoding.EncodeToString(macBytes),
hex.EncodeToString(preimage),
)
header.Set(HeaderAuthorization, value)
return nil
}

150
auth/authenticator_test.go Normal file
View File

@@ -0,0 +1,150 @@
package auth_test
import (
"encoding/base64"
"encoding/hex"
"net/http"
"testing"
"github.com/lightninglabs/kirin/auth"
"github.com/lightninglabs/kirin/macaroons"
"github.com/lightningnetwork/lnd/lntypes"
"gopkg.in/macaroon.v2"
)
type mockChallenger struct{}
func (c *mockChallenger) NewChallenge() (string, lntypes.Hash, error) {
return "lnt1xxxx", lntypes.ZeroHash, nil
}
// createDummyMacHex creates a valid macaroon with dummy content for our tests.
func createDummyMacHex(preimage string) string {
dummyMac, err := macaroon.New(
[]byte("aabbccddeeff00112233445566778899"), []byte("AA=="),
"kirin", macaroon.LatestVersion,
)
if err != nil {
panic(err)
}
err = dummyMac.AddFirstPartyCaveat(
[]byte(macaroons.CondPreimage + " " + preimage),
)
if err != nil {
panic(err)
}
macBytes, err := dummyMac.MarshalBinary()
if err != nil {
panic(err)
}
return hex.EncodeToString(macBytes)
}
// TestLsatAuthenticator tests that the authenticator properly handles auth
// headers and the tokens contained in them.
func TestLsatAuthenticator(t *testing.T) {
var (
testPreimage = "49349dfea4abed3cd14f6d356afa83de" +
"9787b609f088c8df09bacc7b4bd21b39"
testMacHex = createDummyMacHex(testPreimage)
testMacBytes, _ = hex.DecodeString(testMacHex)
testMacBase64 = base64.StdEncoding.EncodeToString(
testMacBytes,
)
headerTests = []struct {
id string
header *http.Header
result bool
}{
{
id: "empty header",
header: &http.Header{},
result: false,
},
{
id: "no auth header",
header: &http.Header{
"Test": []string{"foo"},
},
result: false,
},
{
id: "empty auth header",
header: &http.Header{
auth.HeaderAuthorization: []string{},
},
result: false,
},
{
id: "zero length auth header",
header: &http.Header{
auth.HeaderAuthorization: []string{""},
},
result: false,
},
{
id: "invalid auth header",
header: &http.Header{
auth.HeaderAuthorization: []string{
"foo",
},
},
result: false,
},
{
id: "invalid macaroon metadata header",
header: &http.Header{
auth.HeaderMacaroonMD: []string{"foo"},
},
result: false,
},
{
id: "invalid macaroon header",
header: &http.Header{
auth.HeaderMacaroon: []string{"foo"},
},
result: false,
},
{
id: "valid auth header",
header: &http.Header{
auth.HeaderAuthorization: []string{
"LSAT " + testMacBase64 + ":" +
testPreimage,
},
},
result: true,
},
{
id: "valid macaroon metadata header",
header: &http.Header{
auth.HeaderMacaroonMD: []string{
testMacHex,
}},
result: true,
},
{
id: "valid macaroon header",
header: &http.Header{
auth.HeaderMacaroon: []string{
testMacHex,
},
},
result: true,
},
}
)
a, err := auth.NewLsatAuthenticator(&mockChallenger{})
if err != nil {
t.Fatalf("Could not create authenticator: %v", err)
}
for _, testCase := range headerTests {
result := a.Accept(testCase.header)
if result != testCase.result {
t.Fatalf("test case %s failed. got %v expected %v",
testCase.id, result, testCase.result)
}
}
}

View File

@@ -16,6 +16,12 @@ func (a MockAuthenticator) Accept(header *http.Header) bool {
if header.Get("Authorization") != "" {
return true
}
if header.Get("Grpc-Metadata-macaroon") != "" {
return true
}
if header.Get("Macaroon") != "" {
return true
}
return false
}

5
go.mod
View File

@@ -5,8 +5,13 @@ go 1.13
require (
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d
github.com/golang/protobuf v1.3.2
github.com/lightninglabs/loop v0.2.3-alpha
github.com/lightningnetwork/lnd v0.8.0-beta-rc3.0.20191029004703-c069bdd4c7c1
golang.org/x/net v0.0.0-20191112182307-2180aed22343 // indirect
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea // indirect
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a // indirect
google.golang.org/grpc v1.25.1
gopkg.in/macaroon-bakery.v2 v2.1.0
gopkg.in/macaroon.v2 v2.1.0
gopkg.in/yaml.v2 v2.2.2

13
go.sum
View File

@@ -57,6 +57,7 @@ github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
@@ -65,6 +66,8 @@ github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k=
github.com/frankban/quicktest v1.2.2 h1:xfmOhhoH5fGPgbEAlhLpJH9p0z/0Qizio9osmvn9IUY=
@@ -78,6 +81,7 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -175,6 +179,7 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
@@ -220,6 +225,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191112182307-2180aed22343 h1:00ohfJ4K98s3m6BGUoBd8nyfp4Yl0GoIKvw5abItTjI=
golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -235,6 +242,8 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea h1:Mz1TMnfJDRJLk8S8OPCoJYgrsp/Se/2TBre2+vwX128=
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
@@ -253,11 +262,15 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=

View File

@@ -3,6 +3,7 @@ package macaroons
import (
"context"
"encoding/hex"
"fmt"
"github.com/lightningnetwork/lnd/macaroons"
"gopkg.in/macaroon-bakery.v2/bakery"
@@ -11,7 +12,11 @@ import (
)
const (
// CondRHash is the macaroon caveat condition for a payment hash.
CondRHash = "r-hash"
// CondPreimage is the macaroon caveat condition for a payment preimage.
CondPreimage = "preimage"
)
var (
@@ -21,6 +26,10 @@ var (
type rootKeyStore struct{}
// A compile time flag to ensure the rootKeyStore satisfies the
// bakery.RootKeyStore interface.
var _ bakery.RootKeyStore = (*rootKeyStore)(nil)
func (r *rootKeyStore) Get(_ context.Context, id []byte) ([]byte, error) {
return hex.DecodeString(rootKey)
}
@@ -35,10 +44,37 @@ func (r *rootKeyStore) RootKey(_ context.Context) (rootKey, id []byte,
return key, rootKeyId, nil
}
// Service can bake and validate macaroons.
type Service struct {
bakery.Bakery
}
// NewService creates a new macaroon service with the given checker functions
// that should be supported when validating a macaroon.
func NewService(checks ...macaroons.Checker) (*Service, error) {
macaroonParams := bakery.BakeryParams{
Location: "kirin",
RootKeyStore: &rootKeyStore{},
Locator: nil,
Key: nil,
}
svc := bakery.New(macaroonParams)
// Register all custom caveat checkers with the bakery's checker.
checker := svc.Checker.FirstPartyCaveatChecker.(*checkers.Checker)
for _, check := range checks {
cond, fun := check()
if !isRegistered(checker, cond) {
checker.Register(cond, "std", fun)
}
}
return &Service{*svc}, nil
}
// NewMacaroon bakes a new macaroon with the given allowed operations and
// optional first-party caveats.
func (s *Service) NewMacaroon(operations []bakery.Op, caveats []string) (
[]byte, error) {
@@ -64,42 +100,36 @@ func (s *Service) NewMacaroon(operations []bakery.Op, caveats []string) (
return macBytes, nil
}
func (s *Service) ValidateMacaroon(macBytes []byte,
requiredPermissions []bakery.Op) error {
mac := &macaroon.Macaroon{}
err := mac.UnmarshalBinary(macBytes)
if err != nil {
return err
}
// ValidateMacaroon verifies the signature chain of a macaroon and then
// checks that none of the applied restrictions are violated.
func (s *Service) ValidateMacaroon(mac *macaroon.Macaroon,
perms []bakery.Op) error {
// Check the method being called against the permitted operation and
// the expiration time and IP address and return the result.
authChecker := s.Checker.Auth(macaroon.Slice{mac})
_, err = authChecker.Allow(context.Background(), requiredPermissions...)
_, err := authChecker.Allow(context.Background(), perms...)
return err
}
func NewService(checks ...macaroons.Checker) (*Service, error) {
macaroonParams := bakery.BakeryParams{
Location: "kirin",
RootKeyStore: &rootKeyStore{},
Locator: nil,
Key: nil,
// ExtractCaveat extracts the value of a given caveat condition or returns an
// empty string if that caveat does not exist.
func ExtractCaveat(mac *macaroon.Macaroon, cond string) (string, error) {
if mac == nil {
return "", fmt.Errorf("macaroon cannot be nil")
}
svc := bakery.New(macaroonParams)
// Register all custom caveat checkers with the bakery's checker.
checker := svc.Checker.FirstPartyCaveatChecker.(*checkers.Checker)
for _, check := range checks {
cond, fun := check()
if !isRegistered(checker, cond) {
checker.Register(cond, "std", fun)
for _, caveat := range mac.Caveats() {
cavStr := string(caveat.Id)
cavCond, cavArg, err := checkers.ParseCaveat(cavStr)
if err != nil {
continue
}
if cavCond == cond {
return cavArg, nil
}
}
return &Service{*svc}, nil
return "", nil
}
// isRegistered checks to see if the required checker has already been

View File

@@ -8,17 +8,22 @@ import (
"net/http"
"net/http/httputil"
"regexp"
"strconv"
"strings"
"github.com/lightninglabs/kirin/auth"
"google.golang.org/grpc/codes"
)
const (
// formatPattern is the pattern in which the request log will be
// printed. This is loosely oriented on the apache log format.
// An example entry would look like this:
// 2019-11-09 04:07:55.072 [INF] PRXY: 66.249.69.89 - -
// 2019-11-09 04:07:55.072 [INF] PRXY: 66.249.69.89 - -
// "GET /availability/v1/btc.json HTTP/1.1" "" "Mozilla/5.0 ..."
formatPattern = "- - \"%s %s %s\" \"%s\" \"%s\""
formatPattern = "- - \"%s %s %s\" \"%s\" \"%s\""
hdrContentType = "Content-Type"
hdrTypeGrpc = "application/grpc"
)
// Proxy is a HTTP, HTTP/2 and gRPC handler that takes an incoming request,
@@ -68,7 +73,7 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// any content;
if r.Method == "OPTIONS" {
addCorsHeaders(w.Header())
w.WriteHeader(http.StatusOK)
sendDirectResponse(w, r, http.StatusOK, "")
return
}
@@ -101,7 +106,10 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err != nil {
prefixLog.Errorf("Error querying freebie db: "+
"%v", err)
w.WriteHeader(http.StatusInternalServerError)
sendDirectResponse(
w, r, http.StatusInternalServerError,
"freebie DB failure",
)
return
}
if !ok {
@@ -112,7 +120,10 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err != nil {
prefixLog.Errorf("Error updating freebie db: "+
"%v", err)
w.WriteHeader(http.StatusInternalServerError)
sendDirectResponse(
w, r, http.StatusInternalServerError,
"freebie DB failure",
)
return
}
}
@@ -170,9 +181,18 @@ func (p *Proxy) director(req *http.Request) {
req.URL.Host = target.Address
req.URL.Scheme = target.Protocol
// Don't forward the authorization header since the
// services won't know what it is.
req.Header.Del("Authorization")
// Make sure we always forward the authorization in the correct/
// default format so the backend knows what to do with it.
mac, preimage, err := auth.FromHeader(&req.Header)
if err == nil {
// It could be that there is no auth information because
// none is needed for this particular request. So we
// only continue if no error is set.
err := auth.SetHeader(&req.Header, mac, preimage)
if err != nil {
log.Errorf("could not set header: %v", err)
}
}
// Now overwrite header fields of the client request
// with the fields from the configuration file.
@@ -263,8 +283,11 @@ func (p *Proxy) handlePaymentRequired(w http.ResponseWriter, r *http.Request) {
header, err := p.authenticator.FreshChallengeHeader(r)
if err != nil {
log.Errorf("Error creating new challenge header, response 500.")
w.WriteHeader(http.StatusInternalServerError)
log.Errorf("Error creating new challenge header: %v", err)
sendDirectResponse(
w, r, http.StatusInternalServerError,
"challenge failure",
)
return
}
@@ -275,8 +298,26 @@ func (p *Proxy) handlePaymentRequired(w http.ResponseWriter, r *http.Request) {
}
}
w.WriteHeader(http.StatusPaymentRequired)
if _, err := w.Write([]byte("payment required")); err != nil {
log.Errorf("Error writing response: %v", err)
sendDirectResponse(w, r, http.StatusPaymentRequired, "payment required")
}
// sendDirectResponse sends a response directly to the client without proxying
// anything to a backend. The given error is transported in a way the client can
// understand. This means, for a gRPC client it is sent as specific header
// fields.
func sendDirectResponse(w http.ResponseWriter, r *http.Request,
statusCode int, errInfo string) {
// Find out if the client is a normal HTTP or a gRPC client. Every gRPC
// request should have the Content-Type header field set accordingly
// so we can use that.
switch {
case strings.HasPrefix(r.Header.Get(hdrContentType), hdrTypeGrpc):
w.Header().Set("Grpc-Status", strconv.Itoa(int(codes.Internal)))
w.Header().Set("Grpc-Message", errInfo)
w.WriteHeader(statusCode)
default:
http.Error(w, errInfo, statusCode)
}
}

View File

@@ -1,58 +1,98 @@
package proxy_test
import (
"bytes"
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"io/ioutil"
"math/big"
"net"
"net/http"
"os"
"path"
"strings"
"testing"
"time"
"github.com/lightninglabs/kirin/auth"
"github.com/lightninglabs/kirin/proxy"
proxytest "github.com/lightninglabs/kirin/proxy/testdata"
"github.com/lightningnetwork/lnd/macaroons"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/status"
"gopkg.in/macaroon.v2"
)
const (
testAddr = "localhost:10019"
testProxyAddr = "localhost:10019"
testHostRegexp = "^localhost:.*$"
testPathRegexp = "^/grpc/.*$"
testPathRegexpHTTP = "^/http/.*$"
testPathRegexpGRPC = "^/proxy_test.*$"
testTargetServiceAddress = "localhost:8082"
testHTTPResponseBody = "HTTP Hello"
)
func TestProxy(t *testing.T) {
var (
serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128)
tlsCipherSuites = []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
}
)
// helloServer is a simple server that implements the GreeterServer interface.
type helloServer struct{}
// SayHello returns a simple string that also contains a string from the
// request.
func (s *helloServer) SayHello(ctx context.Context,
req *proxytest.HelloRequest) (*proxytest.HelloReply, error) {
return &proxytest.HelloReply{
Message: fmt.Sprintf("Hello %s", req.Name),
}, nil
}
// TestProxyHTTP tests that the proxy can forward HTTP requests to a backend
// service and handle LSAT authentication correctly.
func TestProxyHTTP(t *testing.T) {
// Create a list of services to proxy between.
services := []*proxy.Service{{
Address: testTargetServiceAddress,
HostRegexp: testHostRegexp,
PathRegexp: testPathRegexp,
PathRegexp: testPathRegexpHTTP,
Protocol: "http",
}}
auth := auth.NewMockAuthenticator()
proxy, err := proxy.New(auth, services, "static")
mockAuth := auth.NewMockAuthenticator()
p, err := proxy.New(mockAuth, services, "static")
if err != nil {
t.Fatalf("failed to create new proxy: %v", err)
}
// Start server that gives requests to the proxy.
server := &http.Server{
Addr: testAddr,
Handler: http.HandlerFunc(proxy.ServeHTTP),
Addr: testProxyAddr,
Handler: http.HandlerFunc(p.ServeHTTP),
}
go func() {
if err := server.ListenAndServe(); err != nil {
t.Fatalf("failed to serve to proxy: %v", err)
}
}()
go server.ListenAndServe()
defer server.Close()
// Start the target backend service.
go func() {
if err := startHTTPHello(); err != nil {
t.Fatalf("failed to start backend service: %v", err)
}
}()
backendService := &http.Server{Addr: testTargetServiceAddress}
go startBackendHTTP(backendService)
defer backendService.Close()
// Wait for servers to start.
time.Sleep(100 * time.Millisecond)
@@ -60,7 +100,7 @@ func TestProxy(t *testing.T) {
// Test making a request to the backend service without the
// Authorization header set.
client := &http.Client{}
url := fmt.Sprintf("http://%s/grpc/test", testAddr)
url := fmt.Sprintf("http://%s/http/test", testProxyAddr)
resp, err := client.Get(url)
if err != nil {
t.Fatalf("errored making http request: %v", err)
@@ -79,6 +119,9 @@ func TestProxy(t *testing.T) {
// Make sure that if the Auth header is set, the client's request is
// proxied to the backend service.
req, err := http.NewRequest("GET", url, nil)
if err != nil {
t.Fatalf("error creating request: %v", err)
}
req.Header.Add("Authorization", "foobar")
resp, err = client.Do(req)
@@ -103,10 +146,287 @@ func TestProxy(t *testing.T) {
}
}
func startHTTPHello() error {
sayHello := func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(testHTTPResponseBody))
// TestProxyHTTP tests that the proxy can forward gRPC requests to a backend
// service and handle LSAT authentication correctly.
func TestProxyGRPC(t *testing.T) {
// Since gRPC only really works over TLS, we need to generate a
// certificate and key pair first.
tempDirName, err := ioutil.TempDir("", "proxytest")
if err != nil {
t.Fatalf("unable to create temp dir: %v", err)
}
certFile := path.Join(tempDirName, "proxy.cert")
keyFile := path.Join(tempDirName, "proxy.key")
certPool, creds, cert, err := genCertPair(certFile, keyFile)
if err != nil {
t.Fatalf("unable to create cert pair: %v", err)
}
// Create a list of services to proxy between.
services := []*proxy.Service{{
Address: testTargetServiceAddress,
HostRegexp: testHostRegexp,
PathRegexp: testPathRegexpGRPC,
Protocol: "https",
TLSCertPath: certFile,
}}
// Create the proxy server and start serving on TLS.
mockAuth := auth.NewMockAuthenticator()
p, err := proxy.New(mockAuth, services, "static")
if err != nil {
t.Fatalf("failed to create new proxy: %v", err)
}
server := &http.Server{
Addr: testProxyAddr,
Handler: http.HandlerFunc(p.ServeHTTP),
TLSConfig: &tls.Config{
RootCAs: certPool,
InsecureSkipVerify: true,
},
}
go server.ListenAndServeTLS(certFile, keyFile)
defer server.Close()
// Start the target backend service also on TLS.
tlsConf := &tls.Config{
Certificates: []tls.Certificate{cert},
CipherSuites: tlsCipherSuites,
MinVersion: tls.VersionTLS12,
}
serverOpts := []grpc.ServerOption{
grpc.Creds(credentials.NewTLS(tlsConf)),
}
backendService := grpc.NewServer(serverOpts...)
go startBackendGRPC(backendService)
defer backendService.Stop()
// Dial to the proxy now, without any authentication.
opts := []grpc.DialOption{grpc.WithTransportCredentials(creds)}
conn, err := grpc.Dial(testProxyAddr, opts...)
if err != nil {
t.Fatalf("unable to connect to RPC server: %v", err)
}
client := proxytest.NewGreeterClient(conn)
// Make request without authentication. We expect an error that can
// be parsed by gRPC.
req := &proxytest.HelloRequest{Name: "foo"}
res, err := client.SayHello(
context.Background(), req, grpc.WaitForReady(true),
)
if err == nil {
t.Fatalf("expected error to be returned without auth")
}
statusErr, ok := status.FromError(err)
if !ok {
t.Fatalf("expected error to be status.Status")
}
if statusErr.Code() != codes.Internal {
t.Fatalf("unexpected code. wanted %d, got %d",
codes.Internal, statusErr.Code())
}
if statusErr.Message() != "payment required" {
t.Fatalf("invalid error. expected [%s] got [%s]",
"payment required", err.Error())
}
// Dial to the proxy again, this time with a dummy macaroon.
dummyMac, err := macaroon.New(
[]byte("key"), []byte("id"), "loc", macaroon.LatestVersion,
)
opts = []grpc.DialOption{
grpc.WithTransportCredentials(creds),
grpc.WithPerRPCCredentials(macaroons.NewMacaroonCredential(
dummyMac,
)),
}
conn, err = grpc.Dial(testProxyAddr, opts...)
if err != nil {
t.Fatalf("unable to connect to RPC server: %v", err)
}
client = proxytest.NewGreeterClient(conn)
// Make the request. This time no error should be returned.
req = &proxytest.HelloRequest{Name: "foo"}
res, err = client.SayHello(context.Background(), req)
if err != nil {
t.Fatalf("unable to call service: %v", err)
}
if res.Message != "Hello foo" {
t.Fatalf("unexpected reply, wanted %s, got %s",
"Hello foo", res.Message)
}
http.HandleFunc("/", sayHello)
return http.ListenAndServe(testTargetServiceAddress, nil)
}
// startBackendHTTP starts the given HTTP server and blocks until the server
// is shut down.
func startBackendHTTP(server *http.Server) error {
sayHello := func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(testHTTPResponseBody))
if err != nil {
panic(err)
}
}
server.Handler = http.HandlerFunc(sayHello)
return server.ListenAndServe()
}
// startBackendGRPC starts the given RPC server and blocks until the server is
// shut down.
func startBackendGRPC(grpcServer *grpc.Server) error {
server := helloServer{}
proxytest.RegisterGreeterServer(grpcServer, &server)
grpcListener, err := net.Listen("tcp", testTargetServiceAddress)
if err != nil {
return fmt.Errorf("RPC server unable to listen on %s",
testTargetServiceAddress)
}
return grpcServer.Serve(grpcListener)
}
// genCertPair generates a pair of private key and certificate and returns them
// in different formats needed to spin up test servers and clients.
func genCertPair(certFile, keyFile string) (*x509.CertPool,
credentials.TransportCredentials, tls.Certificate, error) {
org := "kirin autogenerated cert"
cert := tls.Certificate{}
now := time.Now()
validUntil := now.Add(1 * time.Hour)
// Generate a serial number that's below the serialNumberLimit.
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return nil, nil, cert, fmt.Errorf("failed to generate serial "+
"number: %s", err)
}
// Collect the host's IP addresses, including loopback, in a slice.
ipAddresses := []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")}
// addIP appends an IP address only if it isn't already in the slice.
addIP := func(ipAddr net.IP) {
for _, ip := range ipAddresses {
if ip.Equal(ipAddr) {
return
}
}
ipAddresses = append(ipAddresses, ipAddr)
}
// Add all the interface IPs that aren't already in the slice.
addrs, err := net.InterfaceAddrs()
if err != nil {
return nil, nil, cert, err
}
for _, a := range addrs {
ipAddr, _, err := net.ParseCIDR(a.String())
if err == nil {
addIP(ipAddr)
}
}
// Collect the host's names into a slice.
host, err := os.Hostname()
if err != nil {
return nil, nil, cert, err
}
dnsNames := []string{host}
if host != "localhost" {
dnsNames = append(dnsNames, "localhost")
}
// Generate a private key for the certificate.
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, nil, cert, err
}
// Construct the certificate template.
template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
Organization: []string{org},
CommonName: host,
},
NotBefore: now.Add(-time.Hour * 24),
NotAfter: validUntil,
KeyUsage: x509.KeyUsageKeyEncipherment |
x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
IsCA: true, // so can sign self.
BasicConstraintsValid: true,
DNSNames: dnsNames,
IPAddresses: ipAddresses,
}
derBytes, err := x509.CreateCertificate(
rand.Reader, &template, &template, &priv.PublicKey, priv,
)
if err != nil {
return nil, nil, cert, fmt.Errorf("failed to create "+
"certificate: %v", err)
}
certBuf := &bytes.Buffer{}
err = pem.Encode(
certBuf,
&pem.Block{Type: "CERTIFICATE",
Bytes: derBytes,
},
)
if err != nil {
return nil, nil, cert, fmt.Errorf("failed to encode "+
"certificate: %v", err)
}
keybytes, err := x509.MarshalECPrivateKey(priv)
if err != nil {
return nil, nil, cert, fmt.Errorf("unable to encode privkey: "+
"%v", err)
}
keyBuf := &bytes.Buffer{}
err = pem.Encode(
keyBuf,
&pem.Block{
Type: "EC PRIVATE KEY",
Bytes: keybytes,
},
)
if err != nil {
return nil, nil, cert, fmt.Errorf("failed to encode private "+
"key: %v", err)
}
cert, err = tls.X509KeyPair(certBuf.Bytes(), keyBuf.Bytes())
if err != nil {
return nil, nil, cert, fmt.Errorf("failed to create key pair: "+
"%v", err)
}
// Write cert and key files.
if err = ioutil.WriteFile(certFile, certBuf.Bytes(), 0644); err != nil {
return nil, nil, cert, fmt.Errorf("unable to write cert file "+
"at %v: %v", certFile, err)
}
if err = ioutil.WriteFile(keyFile, keyBuf.Bytes(), 0600); err != nil {
os.Remove(certFile)
return nil, nil, cert, fmt.Errorf("unable to write key file "+
"at %v: %v", keyFile, err)
}
cp := x509.NewCertPool()
if !cp.AppendCertsFromPEM(certBuf.Bytes()) {
return nil, nil, cert, fmt.Errorf("credentials: failed to " +
"append certificate")
}
creds, err := credentials.NewClientTLSFromFile(certFile, "")
if err != nil {
return nil, nil, cert, fmt.Errorf("unable to load cert file: "+
"%v", err)
}
return cp, creds, cert, nil
}

8
proxy/testdata/gen_protos.sh vendored Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/sh
set -e
protoc -I/usr/local/include -I. \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--go_out=plugins=grpc,paths=source_relative:. \
hello.proto

194
proxy/testdata/hello.pb.go vendored Normal file
View File

@@ -0,0 +1,194 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: hello.proto
package proxy_test
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type HelloRequest struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HelloRequest) Reset() { *m = HelloRequest{} }
func (m *HelloRequest) String() string { return proto.CompactTextString(m) }
func (*HelloRequest) ProtoMessage() {}
func (*HelloRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_61ef911816e0a8ce, []int{0}
}
func (m *HelloRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HelloRequest.Unmarshal(m, b)
}
func (m *HelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HelloRequest.Marshal(b, m, deterministic)
}
func (m *HelloRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_HelloRequest.Merge(m, src)
}
func (m *HelloRequest) XXX_Size() int {
return xxx_messageInfo_HelloRequest.Size(m)
}
func (m *HelloRequest) XXX_DiscardUnknown() {
xxx_messageInfo_HelloRequest.DiscardUnknown(m)
}
var xxx_messageInfo_HelloRequest proto.InternalMessageInfo
func (m *HelloRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
type HelloReply struct {
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HelloReply) Reset() { *m = HelloReply{} }
func (m *HelloReply) String() string { return proto.CompactTextString(m) }
func (*HelloReply) ProtoMessage() {}
func (*HelloReply) Descriptor() ([]byte, []int) {
return fileDescriptor_61ef911816e0a8ce, []int{1}
}
func (m *HelloReply) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HelloReply.Unmarshal(m, b)
}
func (m *HelloReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HelloReply.Marshal(b, m, deterministic)
}
func (m *HelloReply) XXX_Merge(src proto.Message) {
xxx_messageInfo_HelloReply.Merge(m, src)
}
func (m *HelloReply) XXX_Size() int {
return xxx_messageInfo_HelloReply.Size(m)
}
func (m *HelloReply) XXX_DiscardUnknown() {
xxx_messageInfo_HelloReply.DiscardUnknown(m)
}
var xxx_messageInfo_HelloReply proto.InternalMessageInfo
func (m *HelloReply) GetMessage() string {
if m != nil {
return m.Message
}
return ""
}
func init() {
proto.RegisterType((*HelloRequest)(nil), "proxy_test.HelloRequest")
proto.RegisterType((*HelloReply)(nil), "proxy_test.HelloReply")
}
func init() { proto.RegisterFile("hello.proto", fileDescriptor_61ef911816e0a8ce) }
var fileDescriptor_61ef911816e0a8ce = []byte{
// 145 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xce, 0x48, 0xcd, 0xc9,
0xc9, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x2a, 0x28, 0xca, 0xaf, 0xa8, 0x8c, 0x2f,
0x49, 0x2d, 0x2e, 0x51, 0x52, 0xe2, 0xe2, 0xf1, 0x00, 0x49, 0x05, 0xa5, 0x16, 0x96, 0xa6, 0x16,
0x97, 0x08, 0x09, 0x71, 0xb1, 0xe4, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06,
0x81, 0xd9, 0x4a, 0x6a, 0x5c, 0x5c, 0x50, 0x35, 0x05, 0x39, 0x95, 0x42, 0x12, 0x5c, 0xec, 0xb9,
0xa9, 0xc5, 0xc5, 0x89, 0xe9, 0x30, 0x45, 0x30, 0xae, 0x91, 0x27, 0x17, 0xbb, 0x7b, 0x51, 0x6a,
0x6a, 0x49, 0x6a, 0x91, 0x90, 0x1d, 0x17, 0x47, 0x70, 0x62, 0x25, 0x58, 0x97, 0x90, 0x84, 0x1e,
0xc2, 0x3e, 0x3d, 0x64, 0xcb, 0xa4, 0xc4, 0xb0, 0xc8, 0x14, 0xe4, 0x54, 0x2a, 0x31, 0x24, 0xb1,
0x81, 0x5d, 0x6a, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xe3, 0x7f, 0xe6, 0xbe, 0xb8, 0x00, 0x00,
0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// GreeterClient is the client API for Greeter service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type GreeterClient interface {
SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
}
type greeterClient struct {
cc *grpc.ClientConn
}
func NewGreeterClient(cc *grpc.ClientConn) GreeterClient {
return &greeterClient{cc}
}
func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
out := new(HelloReply)
err := c.cc.Invoke(ctx, "/proxy_test.Greeter/SayHello", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// GreeterServer is the server API for Greeter service.
type GreeterServer interface {
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}
func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
s.RegisterService(&_Greeter_serviceDesc, srv)
}
func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HelloRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GreeterServer).SayHello(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/proxy_test.Greeter/SayHello",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Greeter_serviceDesc = grpc.ServiceDesc{
ServiceName: "proxy_test.Greeter",
HandlerType: (*GreeterServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "SayHello",
Handler: _Greeter_SayHello_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "hello.proto",
}

15
proxy/testdata/hello.proto vendored Normal file
View File

@@ -0,0 +1,15 @@
syntax = "proto3";
package proxy_test;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}