diff --git a/cmd/main.go b/cmd/main.go index be33940..a7d606d 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -15,6 +15,7 @@ import ( "github.com/valyala/fasthttp" "codeberg.org/codeberg/pages/server" + "codeberg.org/codeberg/pages/server/utils" ) // AllowedCorsDomains lists the domains for which Cross-Origin Resource Sharing is allowed. @@ -91,7 +92,7 @@ func Serve(ctx *cli.Context) error { challengePath := []byte("/.well-known/acme-challenge/") err := fasthttp.ListenAndServe("[::]:80", func(ctx *fasthttp.RequestCtx) { if bytes.HasPrefix(ctx.Path(), challengePath) { - challenge, ok := server.ChallengeCache.Get(string(server.TrimHostPort(ctx.Host())) + "/" + string(bytes.TrimPrefix(ctx.Path(), challengePath))) + challenge, ok := server.ChallengeCache.Get(string(utils.TrimHostPort(ctx.Host())) + "/" + string(bytes.TrimPrefix(ctx.Path(), challengePath))) if !ok || challenge == nil { ctx.SetStatusCode(http.StatusNotFound) ctx.SetBodyString("no challenge for this token") diff --git a/server/certificates.go b/server/certificates.go index 8ac158d..5339375 100644 --- a/server/certificates.go +++ b/server/certificates.go @@ -14,12 +14,6 @@ import ( "encoding/json" "encoding/pem" "errors" - "github.com/OrlovEvgeny/go-mcache" - "github.com/akrylysov/pogreb/fs" - "github.com/go-acme/lego/v4/certificate" - "github.com/go-acme/lego/v4/challenge" - "github.com/go-acme/lego/v4/challenge/tlsalpn01" - "github.com/go-acme/lego/v4/providers/dns" "io/ioutil" "log" "math/big" @@ -29,12 +23,20 @@ import ( "sync" "time" + "github.com/OrlovEvgeny/go-mcache" "github.com/akrylysov/pogreb" + "github.com/akrylysov/pogreb/fs" "github.com/reugn/equalizer" "github.com/go-acme/lego/v4/certcrypto" + "github.com/go-acme/lego/v4/certificate" + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/go-acme/lego/v4/lego" + "github.com/go-acme/lego/v4/providers/dns" "github.com/go-acme/lego/v4/registration" + + "codeberg.org/codeberg/pages/server/database" ) // TlsConfig returns the configuration for generating, serving and cleaning up Let's Encrypt certificates. @@ -212,7 +214,7 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool) (tls.Certificate, bool) { // parse certificate from database res := &certificate.Resource{} - if !PogrebGet(KeyDatabase, sni, res) { + if !database.PogrebGet(KeyDatabase, sni, res) { return tls.Certificate{}, false } @@ -317,7 +319,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re if err == nil && tlsCertificate.Leaf.NotAfter.After(time.Now()) { // avoid sending a mock cert instead of a still valid cert, instead abuse CSR field to store time to try again at renew.CSR = []byte(strconv.FormatInt(time.Now().Add(6*time.Hour).Unix(), 10)) - PogrebPut(KeyDatabase, []byte(name), renew) + database.PogrebPut(KeyDatabase, []byte(name), renew) return tlsCertificate, nil } } @@ -325,7 +327,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re } log.Printf("Obtained certificate for %v", domains) - PogrebPut(KeyDatabase, []byte(name), res) + database.PogrebPut(KeyDatabase, []byte(name), res) tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { return tls.Certificate{}, err @@ -390,7 +392,7 @@ func mockCert(domain, msg, mainDomainSuffix string) tls.Certificate { if domain == "*"+mainDomainSuffix || domain == mainDomainSuffix[1:] { databaseName = mainDomainSuffix } - PogrebPut(KeyDatabase, []byte(databaseName), res) + database.PogrebPut(KeyDatabase, []byte(databaseName), res) tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { @@ -578,7 +580,7 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, // update main cert res := &certificate.Resource{} - if !PogrebGet(KeyDatabase, mainDomainSuffix, res) { + if !database.PogrebGet(KeyDatabase, mainDomainSuffix, res) { log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", "expected main domain cert to exist, but it's missing - seems like the database is corrupted") } else { tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) diff --git a/server/database/helpers.go b/server/database/helpers.go new file mode 100644 index 0000000..b2eb017 --- /dev/null +++ b/server/database/helpers.go @@ -0,0 +1,38 @@ +package database + +import ( + "bytes" + "encoding/gob" + "github.com/akrylysov/pogreb" +) + +func PogrebPut(db *pogreb.DB, name []byte, obj interface{}) { + var resGob bytes.Buffer + resEnc := gob.NewEncoder(&resGob) + err := resEnc.Encode(obj) + if err != nil { + panic(err) + } + err = db.Put(name, resGob.Bytes()) + if err != nil { + panic(err) + } +} + +func PogrebGet(db *pogreb.DB, name []byte, obj interface{}) bool { + resBytes, err := db.Get(name) + if err != nil { + panic(err) + } + if resBytes == nil { + return false + } + + resGob := bytes.NewBuffer(resBytes) + resDec := gob.NewDecoder(resGob) + err = resDec.Decode(obj) + if err != nil { + panic(err) + } + return true +} diff --git a/server/handler.go b/server/handler.go index ca2872f..80c1c79 100644 --- a/server/handler.go +++ b/server/handler.go @@ -16,6 +16,7 @@ import ( "github.com/valyala/fastjson" "codeberg.org/codeberg/pages/html" + "codeberg.org/codeberg/pages/server/utils" ) // Handler handles a single HTTP request to the web server. @@ -31,7 +32,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp // Enable browser caching for up to 10 minutes ctx.Response.Header.Set("Cache-Control", "public, max-age=600") - trimmedHost := TrimHostPort(ctx.Request.Host()) + trimmedHost := utils.TrimHostPort(ctx.Request.Host()) // Add HSTS for RawDomain and MainDomainSuffix if hsts := GetHSTSHeader(trimmedHost, mainDomainSuffix, rawDomain); hsts != "" { diff --git a/server/helpers.go b/server/helpers.go index ecb4bf8..6d55ddf 100644 --- a/server/helpers.go +++ b/server/helpers.go @@ -2,8 +2,6 @@ package server import ( "bytes" - "encoding/gob" - "github.com/akrylysov/pogreb" ) // GetHSTSHeader returns a HSTS header with includeSubdomains & preload for MainDomainSuffix and RawDomain, or an empty @@ -15,42 +13,3 @@ func GetHSTSHeader(host, mainDomainSuffix, rawDomain []byte) string { return "" } } - -func TrimHostPort(host []byte) []byte { - i := bytes.IndexByte(host, ':') - if i >= 0 { - return host[:i] - } - return host -} - -func PogrebPut(db *pogreb.DB, name []byte, obj interface{}) { - var resGob bytes.Buffer - resEnc := gob.NewEncoder(&resGob) - err := resEnc.Encode(obj) - if err != nil { - panic(err) - } - err = db.Put(name, resGob.Bytes()) - if err != nil { - panic(err) - } -} - -func PogrebGet(db *pogreb.DB, name []byte, obj interface{}) bool { - resBytes, err := db.Get(name) - if err != nil { - panic(err) - } - if resBytes == nil { - return false - } - - resGob := bytes.NewBuffer(resBytes) - resDec := gob.NewDecoder(resGob) - err = resDec.Decode(obj) - if err != nil { - panic(err) - } - return true -} diff --git a/server/utils/utils.go b/server/utils/utils.go new file mode 100644 index 0000000..7be330f --- /dev/null +++ b/server/utils/utils.go @@ -0,0 +1,11 @@ +package utils + +import "bytes" + +func TrimHostPort(host []byte) []byte { + i := bytes.IndexByte(host, ':') + if i >= 0 { + return host[:i] + } + return host +}