Skip to content

Commit 5a3b8b7

Browse files
committed
proxy: implement basic proxy functionality
1 parent 4d1fbbf commit 5a3b8b7

File tree

11 files changed

+694
-61
lines changed

11 files changed

+694
-61
lines changed

auth/config.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package auth
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"strings"
7+
)
8+
9+
type Config struct {
10+
// LndHost is the hostname of the LND instance to connect to.
11+
LndHost string `long:"lndhost" description:"Hostname of the LND instance to connect to"`
12+
13+
TlsPath string `long:"tlspath"`
14+
15+
MacDir string `long:"macdir"`
16+
17+
Network string `long:"network"`
18+
}
19+
20+
type Level string
21+
22+
func (l Level) lower() string {
23+
return strings.ToLower(string(l))
24+
}
25+
26+
func (l Level) IsOn() bool {
27+
lower := l.lower()
28+
return lower == "" || lower == "on" || lower == "true"
29+
}
30+
31+
func (l Level) IsFreebie() bool {
32+
return strings.HasPrefix(l.lower(), "freebie")
33+
}
34+
35+
func (l Level) FreebieCount() uint8 {
36+
parts := strings.Split(l.lower(), " ")
37+
if len(parts) != 2 {
38+
panic(fmt.Errorf("invalid auth value: %s", l.lower()))
39+
}
40+
count, err := strconv.Atoi(parts[1])
41+
if err != nil {
42+
panic(err)
43+
}
44+
return uint8(count)
45+
}
46+
47+
func (l Level) IsOff() bool {
48+
lower := l.lower()
49+
return lower == "off" || lower == "false"
50+
}

auth/lnd_authenticator.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package auth
2+
3+
import (
4+
"context"
5+
"encoding/base64"
6+
"encoding/hex"
7+
"fmt"
8+
"net/http"
9+
"regexp"
10+
11+
"github.com/lightninglabs/kirin/macaroons"
12+
"github.com/lightninglabs/loop/lndclient"
13+
"github.com/lightningnetwork/lnd/lnrpc"
14+
"gopkg.in/macaroon-bakery.v2/bakery"
15+
"gopkg.in/macaroon-bakery.v2/bakery/checkers"
16+
)
17+
18+
var (
19+
authRegex = regexp.MustCompile("LSAT (.*?):([a-f0-9]{64})")
20+
opWildcard = "*"
21+
)
22+
23+
type LndAuthenticator struct {
24+
client lnrpc.LightningClient
25+
macService *macaroons.Service
26+
}
27+
28+
// A compile time flag to ensure the LndAuthenticator satisfies the
29+
// Authenticator interface.
30+
var _ Authenticator = (*LndAuthenticator)(nil)
31+
32+
// NewLndAuthenticator creates a new authenticator that is connected to an lnd
33+
// backend and can create new invoices if required.
34+
func NewLndAuthenticator(cfg *Config) (*LndAuthenticator, error) {
35+
client, err := lndclient.NewBasicClient(
36+
cfg.LndHost, cfg.TlsPath, cfg.MacDir, cfg.Network,
37+
)
38+
if err != nil {
39+
return nil, err
40+
}
41+
macService, err := macaroons.NewService()
42+
if err != nil {
43+
return nil, err
44+
}
45+
46+
return &LndAuthenticator{
47+
client: client,
48+
macService: macService,
49+
}, nil
50+
}
51+
52+
// Accept returns whether or not the header successfully authenticates the user
53+
// to a given backend service.
54+
//
55+
// NOTE: This is part of the Authenticator interface.
56+
func (l *LndAuthenticator) Accept(header *http.Header) bool {
57+
authHeader := header.Get("Authorization")
58+
if authHeader == "" {
59+
return false
60+
}
61+
62+
if !authRegex.MatchString(authHeader) {
63+
return false
64+
}
65+
66+
matches := authRegex.FindStringSubmatch(authHeader)
67+
if len(matches) != 3 {
68+
return false
69+
}
70+
71+
macBase64, preimageHex := matches[1], matches[2]
72+
macBytes, err := base64.StdEncoding.DecodeString(macBase64)
73+
if err != nil {
74+
return false
75+
}
76+
77+
preimageBytes, err := hex.DecodeString(preimageHex)
78+
if err != nil {
79+
return false
80+
}
81+
82+
// TODO(guggero): check preimage against payment hash caveat in the
83+
// macaroon.
84+
if len(preimageBytes) != 32 {
85+
return false
86+
}
87+
88+
err = l.macService.ValidateMacaroon(macBytes, []bakery.Op{})
89+
if err != nil {
90+
return false
91+
}
92+
return true
93+
}
94+
95+
// FreshChallengeHeader returns a header containing a challenge for the user to
96+
// complete.
97+
//
98+
// NOTE: This is part of the Authenticator interface.
99+
func (l *LndAuthenticator) FreshChallengeHeader(r *http.Request) (
100+
http.Header, error) {
101+
102+
// Obtain a new invoice from lnd first. We need to know the payment hash
103+
// so we can add it as a caveat to the macaroon.
104+
ctx := context.Background()
105+
invoice := &lnrpc.Invoice{
106+
Memo: "LSAT",
107+
Value: 1,
108+
}
109+
response, err := l.client.AddInvoice(ctx, invoice)
110+
if err != nil {
111+
fmt.Printf("error adding invoice: %v\n", err)
112+
return nil, err
113+
}
114+
paymentHashHex := hex.EncodeToString(response.RHash)
115+
116+
// Create a new macaroon and add the payment hash as a caveat.
117+
// The bakery requires at least one operation so we add an "allow all"
118+
// permission set for now.
119+
mac, err := l.macService.NewMacaroon(
120+
[]bakery.Op{{Entity: opWildcard, Action: opWildcard}}, []string{
121+
checkers.Condition(macaroons.CondRHash, paymentHashHex),
122+
},
123+
)
124+
if err != nil {
125+
fmt.Printf("error creating macaroon: %v\n", err)
126+
return nil, err
127+
}
128+
129+
str := "LSAT macaroon='%s' invoice='%s'"
130+
str = fmt.Sprintf(
131+
str, base64.StdEncoding.EncodeToString(mac),
132+
response.GetPaymentRequest(),
133+
)
134+
header := r.Header
135+
header.Set("WWW-Authenticate", str)
136+
return header, nil
137+
}

config.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package kirin
22

33
import (
44
"github.com/btcsuite/btcutil"
5+
"github.com/lightninglabs/kirin/auth"
56
"github.com/lightninglabs/kirin/proxy"
67
)
78

@@ -17,7 +18,9 @@ type config struct {
1718
// to listen for requests.
1819
ListenAddr string `long:"listenaddr" description:"The interface we should listen on for client requests"`
1920

21+
Authenticator *auth.Config `long:"authenticator" description:"Configuration for the authenticator."`
22+
2023
// Services is a list of JSON objects in string format, which specify
2124
// each backend service to Kirin.
22-
Services []*proxy.Service `long:"service" description:"JSON configurations for each Kirin backend service."`
25+
Services []*proxy.Service `long:"service" description:"Configurations for each Kirin backend service."`
2326
}

go.mod

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ module github.com/lightninglabs/kirin
33
go 1.13
44

55
require (
6-
github.com/btcsuite/btcd v0.0.0-20190629003639-c26ffa870fd8 // indirect
76
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d
8-
github.com/davecgh/go-spew v1.1.1 // indirect
9-
github.com/kr/pretty v0.1.0 // indirect
10-
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 // indirect
11-
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
7+
github.com/lightninglabs/loop v0.2.3-alpha
8+
github.com/lightningnetwork/lnd v0.8.0-beta-rc3.0.20191029004703-c069bdd4c7c1
9+
gopkg.in/macaroon-bakery.v2 v2.1.0
10+
gopkg.in/macaroon.v2 v2.1.0
1211
gopkg.in/yaml.v2 v2.2.2
1312
)

0 commit comments

Comments
 (0)