mirror of https://github.com/jlelse/GoBlog
Add support for Tor
This commit is contained in:
parent
917dcdab48
commit
ded4294c45
|
@ -7,7 +7,7 @@ WORKDIR /app
|
|||
RUN go build --tags "libsqlite3 linux sqlite_fts5"
|
||||
|
||||
FROM alpine:3.13
|
||||
RUN apk add --no-cache sqlite-dev tzdata
|
||||
RUN apk add --no-cache sqlite-dev tzdata tor
|
||||
COPY templates/ /app/templates/
|
||||
COPY --from=build /app/GoBlog /bin/
|
||||
WORKDIR /app
|
||||
|
|
|
@ -30,6 +30,7 @@ type configServer struct {
|
|||
PublicAddress string `mapstructure:"publicAddress"`
|
||||
ShortPublicAddress string `mapstructure:"shortPublicAddress"`
|
||||
PublicHTTPS bool `mapstructure:"publicHttps"`
|
||||
Tor bool `mapstructure:"tor"`
|
||||
SecurityHeaders bool `mapstructure:"securityHeaders"`
|
||||
CSPDomains []string `mapstructure:"cspDomains"`
|
||||
LetsEncryptMail string `mapstructure:"letsEncryptMail"`
|
||||
|
|
|
@ -23,6 +23,8 @@ server:
|
|||
- media.example.com
|
||||
# Cookies
|
||||
jwtSecret: changeThisWeakSecret # JWT secret to use for Json Web Token in cookies (login and captcha)
|
||||
# Tor
|
||||
tor: true # Publish onion service, requires Tor to be installed and available in path
|
||||
|
||||
# Cache
|
||||
cache:
|
||||
|
|
5
go.mod
5
go.mod
|
@ -9,6 +9,7 @@ require (
|
|||
github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/caddyserver/certmagic v0.12.0
|
||||
github.com/cretz/bine v0.1.1-0.20200124154328-f9f678b84cca
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||
github.com/dgraph-io/ristretto v0.0.4-0.20210311064603-e4f298c8aa88
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
|
@ -36,7 +37,7 @@ require (
|
|||
github.com/mattn/go-sqlite3 v1.14.6
|
||||
github.com/mholt/acmez v0.1.3 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.4
|
||||
github.com/miekg/dns v1.1.40 // indirect
|
||||
github.com/miekg/dns v1.1.41 // indirect
|
||||
github.com/mitchellh/go-server-timing v1.0.1
|
||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||
github.com/pelletier/go-toml v1.8.1 // indirect
|
||||
|
@ -60,7 +61,7 @@ require (
|
|||
golang.org/x/mod v0.4.1 // indirect
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e // indirect
|
||||
golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d // indirect
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
|
||||
golang.org/x/text v0.3.5 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
|
|
11
go.sum
11
go.sum
|
@ -57,6 +57,8 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
|
|||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cretz/bine v0.1.1-0.20200124154328-f9f678b84cca h1:Q2r7AxHdJwWfLtBZwvW621M3sPqxPc6ITv2j1FGsYpw=
|
||||
github.com/cretz/bine v0.1.1-0.20200124154328-f9f678b84cca/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=
|
||||
|
@ -234,8 +236,8 @@ github.com/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDE
|
|||
github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
|
||||
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
|
||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-server-timing v1.0.1 h1:f00/aIe8T3MrnLhQHu3tSWvnwc5GV/p5eutuu3hF/tE=
|
||||
|
@ -449,9 +451,10 @@ golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e h1:XNp2Flc/1eWQGk5BLzqTAN7fQIwIbfyVTuVxXxZh73M=
|
||||
golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d h1:jbzgAvDZn8aEnytae+4ou0J0GwFZoHR0hOrTg4qH8GA=
|
||||
golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
|
29
http.go
29
http.go
|
@ -63,6 +63,14 @@ func startServer() (err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Start Onion service
|
||||
if appConfig.Server.Tor {
|
||||
go func() {
|
||||
torErr := startOnionService(finalHandler)
|
||||
log.Println("Tor failed:", torErr.Error())
|
||||
}()
|
||||
}
|
||||
// Start HTTP(s) server
|
||||
localAddress := ":" + strconv.Itoa(appConfig.Server.Port)
|
||||
if appConfig.Server.PublicHTTPS {
|
||||
certmagic.Default.Storage = &certmagic.FileStorage{Path: "data/https"}
|
||||
|
@ -74,8 +82,6 @@ func startServer() (err error) {
|
|||
hosts = append(hosts, appConfig.Server.shortPublicHostname)
|
||||
}
|
||||
err = certmagic.HTTPS(hosts, finalHandler)
|
||||
} else if appConfig.Server.SecurityHeaders {
|
||||
err = http.ListenAndServe(localAddress, finalHandler)
|
||||
} else {
|
||||
err = http.ListenAndServe(localAddress, finalHandler)
|
||||
}
|
||||
|
@ -457,23 +463,32 @@ func buildHandler() (http.Handler, error) {
|
|||
return r, nil
|
||||
}
|
||||
|
||||
func securityHeaders(next http.Handler) http.Handler {
|
||||
extraCSPDomains := ""
|
||||
var cspDomains = ""
|
||||
|
||||
func refreshCSPDomains() {
|
||||
cspDomains = ""
|
||||
if mp := appConfig.Micropub.MediaStorage; mp != nil && mp.MediaURL != "" {
|
||||
if u, err := url.Parse(mp.MediaURL); err == nil {
|
||||
extraCSPDomains += " " + u.Hostname()
|
||||
cspDomains += " " + u.Hostname()
|
||||
}
|
||||
}
|
||||
if len(appConfig.Server.CSPDomains) > 0 {
|
||||
extraCSPDomains += " " + strings.Join(appConfig.Server.CSPDomains, " ")
|
||||
cspDomains += " " + strings.Join(appConfig.Server.CSPDomains, " ")
|
||||
}
|
||||
}
|
||||
|
||||
func securityHeaders(next http.Handler) http.Handler {
|
||||
refreshCSPDomains()
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Strict-Transport-Security", "max-age=31536000;")
|
||||
w.Header().Set("Referrer-Policy", "no-referrer")
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
w.Header().Set("X-Frame-Options", "SAMEORIGIN")
|
||||
w.Header().Set("X-Xss-Protection", "1; mode=block")
|
||||
w.Header().Set("Content-Security-Policy", "default-src 'self'"+extraCSPDomains)
|
||||
w.Header().Set("Content-Security-Policy", "default-src 'self'"+cspDomains)
|
||||
if appConfig.Server.Tor && torAddress != "" {
|
||||
w.Header().Set("Onion-Location", fmt.Sprintf("http://%v%v", torAddress, r.URL.Path))
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ed25519"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/cretz/bine/tor"
|
||||
)
|
||||
|
||||
var (
|
||||
torAddress string
|
||||
)
|
||||
|
||||
func startOnionService(h http.Handler) error {
|
||||
torDataPath, err := filepath.Abs("data/tor")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(torDataPath, 0644)
|
||||
if err != nil {
|
||||
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, os.ModePerm)
|
||||
} else {
|
||||
d, _ := os.ReadFile(torKeyPath)
|
||||
block, _ := pem.Decode(d)
|
||||
x509Encoded := block.Bytes
|
||||
torKey, err = x509.ParsePKCS8PrivateKey(x509Encoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Start tor with default config (can set start conf's DebugWriter to os.Stdout for debug logs)
|
||||
log.Println("Starting and registering onion service, please wait a couple of minutes...")
|
||||
t, err := tor.Start(nil, &tor.StartConf{
|
||||
TempDataDirBase: os.TempDir(),
|
||||
})
|
||||
if err != nil {
|
||||
return 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{
|
||||
Version3: true,
|
||||
Key: torKey,
|
||||
LocalPort: 8888,
|
||||
RemotePorts: []int{80},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer onion.Close()
|
||||
torAddress = onion.String()
|
||||
log.Println("Onion service published on http://" + torAddress)
|
||||
// Serve handler
|
||||
return http.Serve(onion, h)
|
||||
}
|
Loading…
Reference in New Issue