diff --git a/README.md b/README.md new file mode 100644 index 0000000..d8b4a99 --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +# Lightning Service Authentication Token (LSAT) proxy + +Kirin is a HTTP reverse proxy that supports proxying requests for gRPC (HTTP/2) +and REST (HTTP/1 and HTTP/2) backends. + +## Installation + +See [INSTALL.md](install.md). + +## Demo + +There is a demo installation available at +[test-staging.swap.lightning.today:8081](https://test-staging.swap.lightning.today:8081). + +### Use Case 1: Web GUI + +If you visit the demo installation in the browser, you see a simple web GUI. +There you can request the current BOS scores for testnet. Notice that you can +only request the scores three times per IP addres. After the free requests have +been used up, you receive an LSAT token/macaroon and are challenged to pay an +invoice to authorize it. + +You have two options to pay for the invoice: + +1. If you have Joule installed in your browser and connected to a testnet node, + you can click the "Pay invoice with Joule" button to pay the invoice. After + successful payment the page should automatically refresh. +1. In case you want to pay the invoice manually, copy the payment request to + your wallet of choice that has the feature to reveal the preimage after a + successful payment. Copy the payment preimage in hex format, then click the + button "Paste preimage of manual payment" and paste it in the dialog box. + +### Use Case 2: cURL + +First, let's request the BOS scores until we hit the freebie limit: + +`curl -k -v https://test-staging.swap.lightning.today:8081/availability/v1/btc.json` + +At some point, we will get an answer 402 with an authorization header: + +``` +www-authenticate: LSAT macaroon='...' invoice='lntb10n1...' +``` + +We will need both these values, the `macaroon` and the `invoice` so copy them +to a text file somewhere (without the single quotes!). +Let's pay the invoice now, choose any LN wallet that displays the preimage after +a successful payment. Copy the hex encoded preimage to the text file too once +you get it from the wallet. + +Finally, you can issue the authenticated request with the following command: + +``` +curl -k -v \ +--header "Authorization: LSAT :" \ +https://test-staging.swap.lightning.today:8081/availability/v1/btc.json +``` diff --git a/proxy/proxy.go b/proxy/proxy.go index 0341169..793413b 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -173,6 +173,12 @@ func (p *Proxy) director(req *http.Request) { // Don't forward the authorization header since the // services won't know what it is. req.Header.Del("Authorization") + + // Now overwrite header fields of the client request + // with the fields from the configuration file. + for name, value := range target.Headers { + req.Header.Add(name, value) + } } } diff --git a/proxy/service.go b/proxy/service.go index 113a921..e74218a 100644 --- a/proxy/service.go +++ b/proxy/service.go @@ -1,10 +1,22 @@ package proxy import ( + "encoding/base64" + "encoding/hex" + "fmt" + "io/ioutil" + "strings" + "github.com/lightninglabs/kirin/auth" "github.com/lightninglabs/kirin/freebie" ) +var ( + filePrefix = "!file" + filePrefixHex = filePrefix + "+hex" + filePrefixBase64 = filePrefix + "+base64" +) + // Service generically specifies configuration data for backend services to the // Kirin proxy. type Service struct { @@ -32,6 +44,17 @@ type Service struct { // of the URL of a request to find out if this service should be used. PathRegexp string `long:"pathregexp" description:"Regular expression to match the path of the URL against"` + // Headers is a map of strings that defines header name and values that + // should always be passed to the backend service, overwriting any + // headers with the same name that might have been set by the client + // request. + // If the value of a header field starts with the prefix "!file+hex:", + // the rest of the value is treated as a path to a file and the content + // of that file is sent to the backend with each call (hex encoded). + // If the value starts with the prefix "!file+base64:", the content of + // the file is sent encoded as base64. + Headers map[string]string `long:"headers" description:"Header fields to always pass to the service"` + freebieDb freebie.DB } @@ -45,6 +68,43 @@ func prepareServices(services []*Service) error { service.Auth.FreebieCount(), ) } + + // Replace placeholders/directives in the header fields with the + // actual desired values. + for key, value := range service.Headers { + if !strings.HasPrefix(value, filePrefix) { + continue + } + + parts := strings.Split(value, ":") + if len(parts) != 2 { + return fmt.Errorf("invalid header config, " + + "must be '!file+hex:path'") + } + prefix, fileName := parts[0], parts[1] + bytes, err := ioutil.ReadFile(fileName) + if err != nil { + return err + } + + // There are two supported formats to encode the file + // content in: hex and base64. + switch { + case prefix == filePrefixHex: + newValue := hex.EncodeToString(bytes) + service.Headers[key] = newValue + + case prefix == filePrefixBase64: + newValue := base64.StdEncoding.EncodeToString( + bytes, + ) + service.Headers[key] = newValue + + default: + return fmt.Errorf("unsupported file prefix "+ + "format %s", value) + } + } } return nil } diff --git a/static/index.html b/static/index.html index 6cd02ad..7350b32 100644 --- a/static/index.html +++ b/static/index.html @@ -1,5 +1,125 @@ + + LSAT proxy demo page + + -

LSAT auth server

+
+
+

LND node info

+

+        
+    
+
+

Bos Scores

+

+        
+        
+        
+    
+
+ + + \ No newline at end of file