diff --git a/go.mod b/go.mod index e6a1104..c913fea 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/andybalholm/cascadia v1.2.0 // indirect 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/caddyserver/certmagic v0.13.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 @@ -31,12 +31,10 @@ require ( github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible github.com/lestrrat-go/strftime v1.0.4 // indirect github.com/lib/pq v1.9.0 // indirect - github.com/libdns/libdns v0.2.0 // indirect github.com/lopezator/migrator v0.3.0 github.com/magiconair/properties v1.8.5 // indirect 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/microcosm-cc/bluemonday v1.0.5 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 @@ -60,10 +58,11 @@ require ( golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect golang.org/x/mod v0.4.1 // indirect - golang.org/x/net v0.0.0-20210326060303-6b1517762897 // indirect + golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54 // indirect golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect - golang.org/x/text v0.3.5 // indirect + golang.org/x/text v0.3.6 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index bb7a7e3..eea526d 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBW github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= -github.com/caddyserver/certmagic v0.12.0 h1:1f7kxykaJkOVVpXJ8ZrC6RAO5F6+kKm9U7dBFbLNeug= -github.com/caddyserver/certmagic v0.12.0/go.mod h1:tr26xh+9fY5dN0J6IPAlMj07qpog22PJKa7Nw7j835U= +github.com/caddyserver/certmagic v0.13.0 h1:ky0rntZvIFiUKFdIikYxj31WN+Ts0Od6Wjz83iTzxfc= +github.com/caddyserver/certmagic v0.13.0/go.mod h1:dNOzF4iOB7H9E51xTooMB90vs+2XNVtpnx0liQNsQY4= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= @@ -210,7 +210,6 @@ github.com/lestrrat-go/strftime v1.0.4/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR7 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libdns/libdns v0.1.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= github.com/libdns/libdns v0.2.0 h1:ewg3ByWrdUrxrje8ChPVMBNcotg7H9LQYg+u5De2RzI= github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= github.com/lopezator/migrator v0.3.0 h1:VW/rR+J8NYwPdkBxjrFdjwejpgvP59LbmANJxXuNbuk= @@ -230,11 +229,10 @@ github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGw github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mholt/acmez v0.1.1/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM= github.com/mholt/acmez v0.1.3 h1:J7MmNIk4Qf9b8mAGqAh4XkNeowv3f1zW816yf4zt7Qk= github.com/mholt/acmez v0.1.3/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM= -github.com/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDEq1axMbGg= -github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w= +github.com/microcosm-cc/bluemonday v1.0.5 h1:cF59UCKMmmUgqN1baLvqU/B1ZsMori+duLVTLpgiG3w= +github.com/microcosm-cc/bluemonday v1.0.5/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.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= @@ -419,8 +417,8 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs= -golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c h1:KHUzaHIpjWVlVVNh65G3hhuj3KB1HnjY6Cq5cTvRQT8= +golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -455,8 +453,9 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w 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-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54 h1:rF3Ohx8DRyl8h2zw9qojyLHLhrJpEMgyPOImREEryf0= +golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/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= @@ -464,8 +463,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/http.go b/http.go index 3131b40..8f07105 100644 --- a/http.go +++ b/http.go @@ -4,6 +4,7 @@ import ( "compress/flate" "fmt" "log" + "net" "net/http" "net/url" "strconv" @@ -74,30 +75,56 @@ func startServer() (err error) { log.Println("Tor failed:", torErr.Error()) }() } - // Start HTTP(s) server - localAddress := ":" + strconv.Itoa(appConfig.Server.Port) + // Start server + s := &http.Server{ + Handler: finalHandler, + ReadTimeout: 5 * time.Minute, + WriteTimeout: 5 * time.Minute, + } if appConfig.Server.PublicHTTPS { + // Configure certmagic.Default.Storage = &certmagic.FileStorage{Path: "data/https"} - certmagic.DefaultACME.Agreed = true certmagic.DefaultACME.Email = appConfig.Server.LetsEncryptMail certmagic.DefaultACME.CA = certmagic.LetsEncryptProductionCA + // Start HTTP server for TLS verification and redirect + httpServer := &http.Server{ + Addr: ":http", + Handler: http.HandlerFunc(redirectToHttps), + ReadTimeout: 5 * time.Minute, + WriteTimeout: 5 * time.Minute, + } + go func() { + if err := httpServer.ListenAndServe(); err != nil { + log.Println("Failed to start HTTP server:", err.Error()) + } + }() + // Start HTTPS + s.Addr = ":https" hosts := []string{appConfig.Server.publicHostname} if appConfig.Server.shortPublicHostname != "" { hosts = append(hosts, appConfig.Server.shortPublicHostname) } - err = certmagic.HTTPS(hosts, finalHandler) - } else { - s := &http.Server{ - Addr: localAddress, - Handler: finalHandler, - ReadTimeout: 5 * time.Minute, - WriteTimeout: 5 * time.Minute, + listener, e := certmagic.Listen(hosts) + if e != nil { + return e } + err = s.Serve(listener) + } else { + s.Addr = ":" + strconv.Itoa(appConfig.Server.Port) err = s.ListenAndServe() } return } +func redirectToHttps(w http.ResponseWriter, r *http.Request) { + requestHost, _, err := net.SplitHostPort(r.Host) + if err != nil { + requestHost = r.Host + } + w.Header().Set("Connection", "close") + http.Redirect(w, r, fmt.Sprintf("https://%s%s", requestHost, r.URL.RequestURI()), http.StatusMovedPermanently) +} + func reloadRouter() error { h, err := buildDynamicRouter() if err != nil { diff --git a/render.go b/render.go index cf1d75a..025bfe9 100644 --- a/render.go +++ b/render.go @@ -116,8 +116,9 @@ func initRendering() error { } return d.Before(b) }, - "asset": assetFileName, - "string": getTemplateStringVariant, + "asset": assetFileName, + "assetsri": assetSRI, + "string": getTemplateStringVariant, "include": func(templateName string, data ...interface{}) (template.HTML, error) { if len(data) == 0 || len(data) > 2 { return "", errors.New("wrong argument count") diff --git a/templateAssets.go b/templateAssets.go index 7fb467a..c22a3f5 100644 --- a/templateAssets.go +++ b/templateAssets.go @@ -2,7 +2,10 @@ package main import ( "crypto/sha1" + "crypto/sha512" + "encoding/base64" "fmt" + "io" "mime" "net/http" "os" @@ -18,6 +21,7 @@ var assetFiles map[string]*assetFile = map[string]*assetFile{} type assetFile struct { contentType string + sri string body []byte } @@ -41,37 +45,41 @@ func initTemplateAssets() (err error) { } func compileAsset(name string) (string, error) { - originalContent, err := os.ReadFile(name) + content, err := os.ReadFile(name) if err != nil { return "", err } ext := path.Ext(name) - var compiledContent []byte compiledExt := ext switch ext { case ".js": - compiledContent, err = minifier.Bytes("application/javascript", originalContent) + content, err = minifier.Bytes("application/javascript", content) if err != nil { return "", err } case ".css": - compiledContent, err = minifier.Bytes("text/css", originalContent) + content, err = minifier.Bytes("text/css", content) if err != nil { return "", err } default: - // Just copy the file - compiledContent = originalContent + // Do nothing } - sha := sha1.New() - if _, err := sha.Write(compiledContent); err != nil { + // Hashes + sha1Hash := sha1.New() + sha512Hash := sha512.New() + if _, err := io.MultiWriter(sha1Hash, sha512Hash).Write(content); err != nil { return "", err } - hash := fmt.Sprintf("%x", sha.Sum(nil)) - compiledFileName := hash + compiledExt + // File name + compiledFileName := fmt.Sprintf("%x", sha1Hash.Sum(nil)) + compiledExt + // SRI + sriHash := fmt.Sprintf("sha512-%s", base64.StdEncoding.EncodeToString(sha512Hash.Sum(nil))) + // Create struct assetFiles[compiledFileName] = &assetFile{ contentType: mime.TypeByExtension(compiledExt), - body: compiledContent, + sri: sriHash, + body: content, } return compiledFileName, err } @@ -81,6 +89,10 @@ func assetFileName(fileName string) string { return "/" + assetFileNames[fileName] } +func assetSRI(fileName string) string { + return assetFiles[assetFileNames[fileName]].sri +} + func allAssetPaths() []string { var paths []string for _, name := range assetFileNames { diff --git a/templateStrings.go b/templateStrings.go index 138ea25..3ed679a 100644 --- a/templateStrings.go +++ b/templateStrings.go @@ -2,7 +2,7 @@ package main import ( "os" - "path" + "path/filepath" "gopkg.in/yaml.v3" ) @@ -21,14 +21,14 @@ func initTemplateStrings() error { } for _, variant := range variants { variantStrings := map[string]string{} - fileContent, err := os.ReadFile(path.Join(stringsDir, variant+variantFileExt)) + f, err := os.Open(filepath.Join(stringsDir, variant+variantFileExt)) if err != nil { if os.IsNotExist(err) { continue } return err } - err = yaml.Unmarshal(fileContent, variantStrings) + err = yaml.NewDecoder(f).Decode(variantStrings) if err != nil { return err } diff --git a/templates/postactions.gohtml b/templates/postactions.gohtml index dd6622c..4e152b6 100644 --- a/templates/postactions.gohtml +++ b/templates/postactions.gohtml @@ -2,8 +2,8 @@
{{ string .Blog.Lang "share" }}  {{ string .Blog.Lang "translate" }}  - + - +
{{ end }} \ No newline at end of file