Skip to content

Commit

Permalink
Add tor support
Browse files Browse the repository at this point in the history
  • Loading branch information
clementauger committed Aug 25, 2020
1 parent 81f9e36 commit 9ed932c
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 4 deletions.
1 change: 1 addition & 0 deletions config.toml.sample
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[app]
# Address to listen, use "tor" to run an hidden service.
address = "0.0.0.0:9000"

# No trailing slashes.
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module github.com/knadh/niltalk
go 1.13

require (
github.com/clementauger/tor-prebuilt v0.0.0-20200815153310-0d7058794224
github.com/cretz/bine v0.1.0
github.com/go-chi/chi v4.1.0+incompatible
github.com/gomodule/redigo v2.0.0+incompatible
github.com/gorilla/websocket v1.4.2
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/clementauger/tor-prebuilt v0.0.0-20200815153310-0d7058794224 h1:Bm1RJ6O3xTpatREKCjtK3kCmG8SYDgHNGp/qUy0fYek=
github.com/clementauger/tor-prebuilt v0.0.0-20200815153310-0d7058794224/go.mod h1:QVD8AVR2PuMTcxIbUiUBgexRjeVUtymMMVNrGagoVA4=
github.com/cretz/bine v0.1.0 h1:1/fvhLE+fk0bPzjdO5Ci+0ComYxEMuB1JhM4X5skT3g=
github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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=
Expand Down Expand Up @@ -38,6 +42,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8 h1:fpnn/HnJONpIu6hkXi1u/7rR0NzilgWr4T0JmWkEitk=
golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down
38 changes: 34 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func loadConfig() {
f.StringSlice("config", []string{"config.toml"},
"Path to one or more TOML config files to load in order")
f.Bool("new-config", false, "generate sample config file")
f.Bool("onion", false, "Show the onion URL")
f.String("static-dir", "", "(optional) path to directory with static files")
f.Bool("version", false, "Show build version")
f.Parse(os.Args[1:])
Expand Down Expand Up @@ -238,6 +239,16 @@ func main() {
} else {
logger.Fatal("app.storage must be one of redis|memory|fs")
}

if ko.Bool("onion") {
pk, err := getOrCreatePK(store)
if err != nil {
logger.Fatal(err)
}
fmt.Printf("http://%v.onion\n", onionAddr(pk))
os.Exit(0)
}

app.hub = hub.NewHub(app.cfg, store, logger)

// Compile static templates.
Expand All @@ -264,11 +275,30 @@ func main() {
})

// Start the app.
srv := &http.Server{
Addr: ko.String("app.address"),
Handler: r,
var srv interface {
ListenAndServe() error
}

if appAddress := ko.String("app.address"); appAddress == "tor" {
pk, err := getOrCreatePK(store)
if err != nil {
logger.Fatalf("could not create the private key file: %v", err)
}

srv = &torServer{
PrivateKey: pk,
Handler: r,
}
logger.Printf("starting server on http://%v.onion", onionAddr(pk))

} else {
srv = &http.Server{
Addr: appAddress,
Handler: r,
}
logger.Printf("starting server on http://%v", appAddress)
}
logger.Printf("starting server on %v", ko.String("app.address"))

if err := srv.ListenAndServe(); err != nil {
logger.Fatalf("couldn't start server: %v", err)
}
Expand Down
91 changes: 91 additions & 0 deletions tor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package main

import (
"context"
"crypto/ed25519"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http"
"time"

"github.com/clementauger/tor-prebuilt/embedded"
"github.com/cretz/bine/tor"
"github.com/cretz/bine/torutil"
tued25519 "github.com/cretz/bine/torutil/ed25519"
"github.com/knadh/niltalk/store"
)

func getOrCreatePK(store store.Store) (privateKey ed25519.PrivateKey, err error) {
key := "onionkey"
d, err := store.Get(key)
if len(d) == 0 || err != nil {
_, privateKey, err = ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
var x509Encoded []byte
x509Encoded, err = x509.MarshalPKCS8PrivateKey(privateKey)
if err != nil {
return nil, err
}
pemEncoded := pem.EncodeToMemory(&pem.Block{Type: "ED25519 PRIVATE KEY", Bytes: x509Encoded})
err = store.Set(key, pemEncoded)
} else {
block, _ := pem.Decode(d)
x509Encoded := block.Bytes
var tPk interface{}
tPk, err = x509.ParsePKCS8PrivateKey(x509Encoded)
if err != nil {
return nil, err
}
if x, ok := tPk.(ed25519.PrivateKey); ok {
privateKey = x
} else {
err = fmt.Errorf("invalid key type %T wanted ed25519.PrivateKey", tPk)
}
}
return privateKey, err
}

type torServer struct {
Handler http.Handler
// PrivateKey path to a pem encoded ed25519 private key
PrivateKey ed25519.PrivateKey
}

func onionAddr(pk ed25519.PrivateKey) string {
return torutil.OnionServiceIDFromV3PublicKey(tued25519.PublicKey([]byte(pk.Public().(ed25519.PublicKey))))
}

func (ts *torServer) ListenAndServe() error {

d, err := ioutil.TempDir("", "")
if err != nil {
return err
}

// Start tor with default config (can set start conf's DebugWriter to os.Stdout for debug logs)
// fmt.Println("Starting and registering onion service, please wait a couple of minutes...")
t, err := tor.Start(nil, &tor.StartConf{TempDataDirBase: d, ProcessCreator: embedded.NewCreator(), NoHush: true})
if err != nil {
return fmt.Errorf("unable to start Tor: %v", err)
}
defer t.Close()

// Wait at most a few minutes to publish the service
listenCtx, listenCancel := context.WithTimeout(context.Background(), 3*time.Minute)
defer listenCancel()
// Create a v3 onion service to listen on any port but show as 80
onion, err := t.Listen(listenCtx, &tor.ListenConf{Key: ts.PrivateKey, Version3: true, RemotePorts: []int{80}})
if err != nil {
return fmt.Errorf("unable to create onion service: %v", err)
}
defer onion.Close()

// fmt.Printf("server listening at http://%v.onion\n", onion.ID)

return http.Serve(onion, ts.Handler)
}

0 comments on commit 9ed932c

Please sign in to comment.