kirin: allow handling client requests over Tor onion services

In this commit, we integrate Tor onion services into the proxy. Clients
can now make their requests through Tor's encrypted network. To make
this possible, there were a few quirks, the most important being that
clients were unable to establish encrypted HTTP/2 connections due to
TLS certificates not being able to verify onion services. To work around
this, we now spin up an additional HTTP/2 server _without TLS_ that's
not exposed to the outside world and can only be accessed through the
onion services, which already provide encryption.

Once the onion services are created, we store their private keys within
etcd to ensure we can recover them later on as the proxy is intended to
be long-lived.
This commit is contained in:
Wilmer Paulino
2020-03-06 17:46:08 -08:00
parent 34b4e1f6a5
commit 494fdcc0a3
7 changed files with 287 additions and 11 deletions

81
onion_store_test.go Normal file
View File

@@ -0,0 +1,81 @@
package kirin
import (
"bytes"
"testing"
"github.com/lightningnetwork/lnd/tor"
)
// assertPrivateKeyExists is a helper to determine if the private key for an
// onion service exists in the store. If it does, it's compared against what's
// expected.
func assertPrivateKeyExists(t *testing.T, store *onionStore,
onionType tor.OnionType, expPrivateKey *[]byte) {
t.Helper()
exists := expPrivateKey != nil
privateKey, err := store.PrivateKey(onionType)
switch {
case exists && err != nil:
t.Fatalf("unable to retrieve private key: %v", err)
case !exists && err != tor.ErrNoPrivateKey:
t.Fatalf("expected error ErrNoPrivateKey, got \"%v\"", err)
case exists:
if !bytes.Equal(privateKey, *expPrivateKey) {
t.Fatalf("expected private key %v, got %v",
string(*expPrivateKey), string(privateKey))
}
default:
return
}
}
// TestOnionStore ensures the different operations of the onionStore behave as
// espected.
func TestOnionStore(t *testing.T) {
etcdClient, serverCleanup := etcdSetup(t)
defer etcdClient.Close()
defer serverCleanup()
// Upon a fresh initialization of the store, no private keys should
// exist for any onion service type.
store := newOnionStore(etcdClient)
assertPrivateKeyExists(t, store, tor.V2, nil)
assertPrivateKeyExists(t, store, tor.V3, nil)
// Store a private key for a V2 onion service and check it was stored
// correctly.
privateKeyV2 := []byte("hide_me_plz_v2")
if err := store.StorePrivateKey(tor.V2, privateKeyV2); err != nil {
t.Fatalf("unable to store private key for v2 onion service: %v",
err)
}
assertPrivateKeyExists(t, store, tor.V2, &privateKeyV2)
// Store a private key for a V3 onion service and check it was stored
// correctly.
privateKeyV3 := []byte("hide_me_plz_v3")
if err := store.StorePrivateKey(tor.V3, privateKeyV3); err != nil {
t.Fatalf("unable to store private key for v3 onion service: %v",
err)
}
assertPrivateKeyExists(t, store, tor.V3, &privateKeyV3)
// Delete the private key for the V2 onion service and check that it was
// indeed successful.
if err := store.DeletePrivateKey(tor.V2); err != nil {
t.Fatalf("unable to remove private key for v2 onion service: %v",
err)
}
assertPrivateKeyExists(t, store, tor.V2, nil)
// Delete the private key for the V3 onion service and check that it was
// indeed successful.
if err := store.DeletePrivateKey(tor.V3); err != nil {
t.Fatalf("unable to remove private key for v3 onion service: %v",
err)
}
assertPrivateKeyExists(t, store, tor.V3, nil)
}