From 5f52993a0c577499fb417080032dae90dc63cf3f Mon Sep 17 00:00:00 2001 From: Jan-Lukas Else Date: Thu, 10 Mar 2022 14:52:56 +0100 Subject: [PATCH] Tor single hop mode option --- config.go | 1 + docs/usage.md | 6 +++- example-config.yml | 9 ++++++ tor.go | 74 ++++++++++++++++++++++++++++++---------------- 4 files changed, 64 insertions(+), 26 deletions(-) diff --git a/config.go b/config.go index 39a5d84..849f01a 100644 --- a/config.go +++ b/config.go @@ -44,6 +44,7 @@ type configServer struct { TailscaleHTTPS bool `mapstructure:"tailscaleHttps"` Tailscale *configTailscale `mapstructure:"tailscale"` Tor bool `mapstructure:"tor"` + TorSingleHop bool `mapstructure:"torSingleHop"` SecurityHeaders bool `mapstructure:"securityHeaders"` CSPDomains []string `mapstructure:"cspDomains"` publicHostname string diff --git a/docs/usage.md b/docs/usage.md index ab14214..e5bec8b 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -40,4 +40,8 @@ There's also the possibility to configure GoBlog to use Google Cloud's Text-to-S On receiving a webmention, a new comment or a contact form submission, GoBlog will create a new notification. Notifications are displayed on `/notifications` and can be deleted by the user. -If configured, GoBlog will also send a notification using a Telegram Bot or [Ntfy.sh](https://ntfy.sh/). See the `example-config.yml` file for how to configure the notification providers. \ No newline at end of file +If configured, GoBlog will also send a notification using a Telegram Bot or [Ntfy.sh](https://ntfy.sh/). See the `example-config.yml` file for how to configure the notification providers. + +## Tor Hidden Services + +GoBlog can be configured to provide a Tor Hidden Service. This is useful if you want to offer your visitors a way to connect to your blog from censored networks or countries. See the `example-config.yml` file for how to enable the Tor Hidden Service. If you don't need to hide your server, you can enable the Single Hop mode. \ No newline at end of file diff --git a/example-config.yml b/example-config.yml index c18893d..24b3362 100644 --- a/example-config.yml +++ b/example-config.yml @@ -2,6 +2,14 @@ # Until there's an official release configuration may change # Keep a look at the commit history +# Debug +debug: true # Enable more verbose logging + +# Pprof - Option to enable pprof profiling +pprof: + enabled: true # Enable pprof profiling + address: ":6060" # Address to listen on + # Database database: file: data/db.sqlite # File for the SQLite database @@ -25,6 +33,7 @@ server: - media.example.com # Tor tor: true # Publish onion service, requires Tor to be installed and available in path + torSingleHop: true # Enable single hop mode (non-anonymous) # Tailscale (see https://tailscale.com) tailscaleHttps: true # Use an Let's Encrypt certificate from Tailscale (see https://goblog.app/s/7), requires publicHttps to be disabled tailscale: diff --git a/tor.go b/tor.go index 3d8f002..49ed60e 100644 --- a/tor.go +++ b/tor.go @@ -29,32 +29,16 @@ func (a *goBlog) startOnionService(h http.Handler) error { return err } // Initialize private key - torKeyPath := filepath.Join(torDataPath, "onion.pk") - var torKey crypto.PrivateKey - if _, err := os.Stat(torKeyPath); os.IsNotExist(err) { - _, torKey, err = ed25519.GenerateKey(nil) - if err != nil { - return err - } - x509Encoded, err := x509.MarshalPKCS8PrivateKey(torKey) - if err != nil { - return err - } - pemEncoded := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: x509Encoded}) - _ = os.WriteFile(torKeyPath, pemEncoded, 0600) - } else { - d, _ := os.ReadFile(torKeyPath) - block, _ := pem.Decode(d) - x509Encoded := block.Bytes - torKey, err = x509.ParsePKCS8PrivateKey(x509Encoded) - if err != nil { - return err - } + torKey, err := a.createTorPrivateKey(torDataPath) + if err != nil { + return err } - // Start tor with default config (can set start conf's DebugWriter to os.Stdout for debug logs) + // Start tor log.Println("Starting and registering onion service, please wait a couple of minutes...") t, err := tor.Start(context.Background(), &tor.StartConf{ TempDataDirBase: os.TempDir(), + NoAutoSocksPort: true, + ExtraArgs: a.torExtraArgs(), }) if err != nil { return err @@ -65,9 +49,10 @@ func (a *goBlog) startOnionService(h http.Handler) error { defer listenCancel() // Create a v3 onion service to listen on any port but show as 80 onion, err := t.Listen(listenCtx, &tor.ListenConf{ - Version3: true, - Key: torKey, - RemotePorts: []int{80}, + Version3: true, + Key: torKey, + RemotePorts: []int{80}, + NonAnonymous: a.cfg.Server.TorSingleHop, }) if err != nil { return err @@ -91,3 +76,42 @@ func (a *goBlog) startOnionService(h http.Handler) error { } return nil } + +func (a *goBlog) createTorPrivateKey(torDataPath string) (crypto.PrivateKey, error) { + torKeyPath := filepath.Join(torDataPath, "onion.pk") + var torKey crypto.PrivateKey + if _, err := os.Stat(torKeyPath); os.IsNotExist(err) { + // Tor private key not found, create it + _, torKey, err = ed25519.GenerateKey(nil) + if err != nil { + return nil, err + } + x509Encoded, err := x509.MarshalPKCS8PrivateKey(torKey) + if err != nil { + return nil, err + } + pemEncoded := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: x509Encoded}) + err = os.WriteFile(torKeyPath, pemEncoded, 0600) + if err != nil { + return nil, err + } + } else { + // Tor private key found, load it + d, _ := os.ReadFile(torKeyPath) + block, _ := pem.Decode(d) + x509Encoded := block.Bytes + torKey, err = x509.ParsePKCS8PrivateKey(x509Encoded) + if err != nil { + return nil, err + } + } + return torKey, nil +} + +func (a *goBlog) torExtraArgs() []string { + s := []string{"--SocksPort", "0"} + if a.cfg.Server.TorSingleHop { + s = append(s, "--HiddenServiceNonAnonymousMode", "1", "--HiddenServiceSingleHopMode", "1") + } + return s +}