proxy: add demo and README with the use cases

This commit is contained in:
Oliver Gugger
2019-11-01 16:59:54 +01:00
parent e704ba4aa9
commit 564deb6545
4 changed files with 244 additions and 1 deletions

57
README.md Normal file
View File

@@ -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 <macaroon>:<preimage>" \
https://test-staging.swap.lightning.today:8081/availability/v1/btc.json
```

View File

@@ -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)
}
}
}

View File

@@ -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
}

View File

@@ -1,5 +1,125 @@
<html>
<head>
<title>LSAT proxy demo page</title>
<style>
.row:after {
content: "";
display: table;
clear: both;
}
.col {
width: 45%;
float: left;
padding: 10px;
}
.max-height-scroll {
max-height: 500px;
overflow-y: scroll;
}
pre {
white-space: pre-wrap; /* Since CSS 2.1 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
</style>
</head>
<body>
<h1>LSAT auth server</h1>
<div class="row">
<div class="col">
<h1>LND node info</h1>
<pre id="getinfo-lnd"></pre>
<button id="reload-lnd">Reload</button>
</div>
<div class="col">
<h1>Bos Scores</h1>
<pre id="bos-scores" class="max-height-scroll"></pre>
<button id="reload-bos">Reload</button>
<button id="pay">Pay invoice with Joule</button>
<button id="add-preimage">Paste preimage of manual payment</button>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="https://unpkg.com/webln@0.2.1/dist/webln.min.js"
integrity="sha384-Enk2tnv6U0yPoFk7RasscZ5oQIG2fzVYaG4ledkAf7MdEXP9fMazV74tAgEwvxm7"
crossorigin="anonymous"></script>
<script>
let authorization = "";
let lastMacaroon = null;
let lastInvoice = null;
function parseInvoice(invoice) {
const rex = /LSAT macaroon='(.*?)' invoice='(.*?)'/i;
parts = invoice.match(rex);
lastMacaroon = parts[1];
lastInvoice = parts[2];
}
function loadJSON(url, elem) {
elem.text("");
$.ajax(url, {
cache: false,
dataType: 'json',
crossDomain: true,
headers: {
'Authorization': authorization,
},
success: (data, status) => {
elem.text(JSON.stringify(data, null, 2));
},
statusCode: {
402: (xhr, status, err) => {
var invoice = xhr.getResponseHeader('www-authenticate');
parseInvoice(invoice);
elem.text("payment required: " + invoice);
}
},
error: (xhr, status, err) => {
console.log("error: " + err + ", headers: " + xhr.getAllResponseHeaders())
}
});
}
function payInvoice() {
if (window.WebLN) {
WebLN.requestProvider()
.then((provider) => {
provider.sendPayment(lastInvoice)
.then((response) => {
authorization = "LSAT " + lastMacaroon + ":" + response.preimage;
$('#reload-bos').click();
$('#reload-lnd').click();
});
})
.catch((err) => {
alert(err);
});
} else {
alert("Joule not installed or failed to load WebLN library.");
}
}
function addPreimage() {
let preimage = prompt("Enter hex encoded preimage");
authorization = "LSAT " + lastMacaroon + ":" + preimage;
$('#reload-bos').click();
$('#reload-lnd').click();
}
$(document).ready(() => {
const host = document.location.host;
$('#reload-lnd').on('click', () => loadJSON('//alice.' + host + '/v1/getinfo', $('#getinfo-lnd'))).click();
$('#reload-bos').on('click', () => loadJSON('//' + host + '/availability/v1/btc.json', $('#bos-scores'))).click();
$('#pay').on('click', () => payInvoice());
$('#add-preimage').on('click', () => addPreimage());
});
</script>
</body>
</html>