Tor single hop mode option

This commit is contained in:
Jan-Lukas Else 2022-03-10 14:52:56 +01:00
parent e3eb3a5b4e
commit 5f52993a0c
4 changed files with 64 additions and 26 deletions

View File

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

View File

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

View File

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

74
tor.go
View File

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