less panic and more fixing

This commit is contained in:
6543 2023-02-10 01:57:19 +01:00
parent d5b9f255f5
commit 0d855ac441
6 changed files with 46 additions and 43 deletions

View File

@ -16,7 +16,7 @@ build:
CGO_ENABLED=1 go build -tags '{{TAGS}}' -ldflags '-s -w {{CGO_FLAGS}}' -v -o build/codeberg-pages-server ./ CGO_ENABLED=1 go build -tags '{{TAGS}}' -ldflags '-s -w {{CGO_FLAGS}}' -v -o build/codeberg-pages-server ./
build-tag VERSION: build-tag VERSION:
CGO_ENABLED=1 go build -tags '{{TAGS}}' '-ldflags '-s -w -X "codeberg.org/codeberg/pages/server/version.Version={{VERSION}}" {{CGO_FLAGS}}' -v -o build/codeberg-pages-server ./ CGO_ENABLED=1 go build -tags '{{TAGS}}' -ldflags '-s -w -X "codeberg.org/codeberg/pages/server/version.Version={{VERSION}}" {{CGO_FLAGS}}' -v -o build/codeberg-pages-server ./
lint: tool-golangci tool-gofumpt lint: tool-golangci tool-gofumpt
[ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }; \ [ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }; \

View File

@ -102,6 +102,7 @@ Thank you very much.
run `just dev` run `just dev`
now this pages should work: now this pages should work:
- https://magiclike.localhost.mock.directory:4430/ - https://cb_pages_tests.localhost.mock.directory:4430/images/827679288a.jpg
- https://momar.localhost.mock.directory:4430/ci-testing/ - https://momar.localhost.mock.directory:4430/ci-testing/
- https://momar.localhost.mock.directory:4430/pag/@master/ - https://momar.localhost.mock.directory:4430/pag/@master/
- https://mock-pages.codeberg-test.org:4430/README.md

2
go.mod
View File

@ -1,6 +1,6 @@
module codeberg.org/codeberg/pages module codeberg.org/codeberg/pages
go 1.19 go 1.20
require ( require (
code.gitea.io/sdk/gitea v0.15.1-0.20220729105105-cc14c63cccfa code.gitea.io/sdk/gitea v0.15.1-0.20220729105105-cc14c63cccfa

View File

@ -95,10 +95,9 @@ func TLSConfig(mainDomainSuffix string,
return tlsCertificate.(*tls.Certificate), nil return tlsCertificate.(*tls.Certificate), nil
} }
var tlsCertificate tls.Certificate var tlsCertificate *tls.Certificate
var err error var err error
var ok bool if tlsCertificate, err = retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider, acmeUseRateLimits, certDB); err != nil {
if tlsCertificate, ok = retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider, acmeUseRateLimits, certDB); !ok {
// request a new certificate // request a new certificate
if strings.EqualFold(sni, mainDomainSuffix) { if strings.EqualFold(sni, mainDomainSuffix) {
return nil, errors.New("won't request certificate for main domain, something really bad has happened") return nil, errors.New("won't request certificate for main domain, something really bad has happened")
@ -110,12 +109,11 @@ func TLSConfig(mainDomainSuffix string,
} }
} }
if err := keyCache.Set(sni, &tlsCertificate, 15*time.Minute); err != nil { if err := keyCache.Set(sni, tlsCertificate, 15*time.Minute); err != nil {
return nil, err return nil, err
} }
return &tlsCertificate, nil return tlsCertificate, nil
}, },
PreferServerCipherSuites: true,
NextProtos: []string{ NextProtos: []string{
"h2", "h2",
"http/1.1", "http/1.1",
@ -196,54 +194,53 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error {
return nil return nil
} }
func retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider string, acmeUseRateLimits bool, certDB database.CertDB) (tls.Certificate, bool) { func retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider string, acmeUseRateLimits bool, certDB database.CertDB) (*tls.Certificate, error) {
// parse certificate from database // parse certificate from database
res, err := certDB.Get(sni) res, err := certDB.Get(sni)
if err != nil { if err != nil {
panic(err) // TODO: no panic return nil, err
} } else if res == nil {
if res == nil { return nil, database.ErrNotFound
return tls.Certificate{}, false
} }
tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey)
if err != nil { if err != nil {
log.Error().Err(err).Msgf("could not create tlsCert from key pair: %v", res) return nil, err
} }
// TODO: document & put into own function // TODO: document & put into own function
if !strings.EqualFold(sni, mainDomainSuffix) { if !strings.EqualFold(sni, mainDomainSuffix) {
tlsCertificate.Leaf, err = x509.ParseCertificate(tlsCertificate.Certificate[0]) tlsCertificate.Leaf, err = x509.ParseCertificate(tlsCertificate.Certificate[0])
if err != nil { if err != nil {
panic(err) return nil, fmt.Errorf("error parsin leaf tlsCert: %w", err)
} }
// renew certificates 7 days before they expire // renew certificates 7 days before they expire
if tlsCertificate.Leaf.NotAfter.Before(time.Now().Add(7 * 24 * time.Hour)) { if tlsCertificate.Leaf.NotAfter.Before(time.Now().Add(7 * 24 * time.Hour)) {
// TODO: add ValidUntil to custom res struct // TODO: use ValidTill of custom cert struct
if res.CSR != nil && len(res.CSR) > 0 { if res.CSR != nil && len(res.CSR) > 0 {
// CSR stores the time when the renewal shall be tried again // CSR stores the time when the renewal shall be tried again
nextTryUnix, err := strconv.ParseInt(string(res.CSR), 10, 64) nextTryUnix, err := strconv.ParseInt(string(res.CSR), 10, 64)
if err == nil && time.Now().Before(time.Unix(nextTryUnix, 0)) { if err == nil && time.Now().Before(time.Unix(nextTryUnix, 0)) {
return tlsCertificate, true return &tlsCertificate, nil
} }
} }
// TODO: make a queue ?
go (func() { go (func() {
res.CSR = nil // acme client doesn't like CSR to be set res.CSR = nil // acme client doesn't like CSR to be set
tlsCertificate, err = obtainCert(acmeClient, []string{sni}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) if _, err := obtainCert(acmeClient, []string{sni}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB); err != nil {
if err != nil {
log.Error().Msgf("Couldn't renew certificate for %s: %v", sni, err) log.Error().Msgf("Couldn't renew certificate for %s: %v", sni, err)
} }
})() })()
} }
} }
return tlsCertificate, true return &tlsCertificate, nil
} }
var obtainLocks = sync.Map{} var obtainLocks = sync.Map{}
func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user, dnsProvider, mainDomainSuffix string, acmeUseRateLimits bool, keyDatabase database.CertDB) (tls.Certificate, error) { func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user, dnsProvider, mainDomainSuffix string, acmeUseRateLimits bool, keyDatabase database.CertDB) (*tls.Certificate, error) {
name := strings.TrimPrefix(domains[0], "*") name := strings.TrimPrefix(domains[0], "*")
if dnsProvider == "" && len(domains[0]) > 0 && domains[0][0] == '*' { if dnsProvider == "" && len(domains[0]) > 0 && domains[0][0] == '*' {
domains = domains[1:] domains = domains[1:]
@ -256,16 +253,16 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
_, working = obtainLocks.Load(name) _, working = obtainLocks.Load(name)
} }
cert, ok := retrieveCertFromDB(name, mainDomainSuffix, dnsProvider, acmeUseRateLimits, keyDatabase) cert, err := retrieveCertFromDB(name, mainDomainSuffix, dnsProvider, acmeUseRateLimits, keyDatabase)
if !ok { if err != nil {
return tls.Certificate{}, errors.New("certificate failed in synchronous request") return nil, fmt.Errorf("certificate failed in synchronous request: %w", err)
} }
return cert, nil return cert, nil
} }
defer obtainLocks.Delete(name) defer obtainLocks.Delete(name)
if acmeClient == nil { if acmeClient == nil {
return mockCert(domains[0], "ACME client uninitialized. This is a server error, please report!", mainDomainSuffix, keyDatabase), nil return mockCert(domains[0], "ACME client uninitialized. This is a server error, please report!", mainDomainSuffix, keyDatabase)
} }
// request actual cert // request actual cert
@ -288,7 +285,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re
if res == nil { if res == nil {
if user != "" { if user != "" {
if err := checkUserLimit(user); err != nil { if err := checkUserLimit(user); err != nil {
return tls.Certificate{}, err return nil, err
} }
} }
@ -311,30 +308,38 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re
if renew != nil && renew.CertURL != "" { if renew != nil && renew.CertURL != "" {
tlsCertificate, err := tls.X509KeyPair(renew.Certificate, renew.PrivateKey) tlsCertificate, err := tls.X509KeyPair(renew.Certificate, renew.PrivateKey)
if err != nil { if err != nil {
return mockCert(domains[0], err.Error(), mainDomainSuffix, keyDatabase), err mockC, err2 := mockCert(domains[0], err.Error(), mainDomainSuffix, keyDatabase)
if err2 != nil {
return nil, errors.Join(err, err2)
}
return mockC, err
} }
leaf, err := leaf(&tlsCertificate) leaf, err := leaf(&tlsCertificate)
if err == nil && leaf.NotAfter.After(time.Now()) { if err == nil && 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 // 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)) renew.CSR = []byte(strconv.FormatInt(time.Now().Add(6*time.Hour).Unix(), 10))
if err := keyDatabase.Put(name, renew); err != nil { if err := keyDatabase.Put(name, renew); err != nil {
return mockCert(domains[0], err.Error(), mainDomainSuffix, keyDatabase), err mockC, err2 := mockCert(domains[0], err.Error(), mainDomainSuffix, keyDatabase)
if err2 != nil {
return nil, errors.Join(err, err2)
}
return mockC, err
} }
return tlsCertificate, nil return &tlsCertificate, nil
} }
} }
return mockCert(domains[0], err.Error(), mainDomainSuffix, keyDatabase), err return mockCert(domains[0], err.Error(), mainDomainSuffix, keyDatabase)
} }
log.Debug().Msgf("Obtained certificate for %v", domains) log.Debug().Msgf("Obtained certificate for %v", domains)
if err := keyDatabase.Put(name, res); err != nil { if err := keyDatabase.Put(name, res); err != nil {
return tls.Certificate{}, err return nil, err
} }
tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey)
if err != nil { if err != nil {
return tls.Certificate{}, err return nil, err
} }
return tlsCertificate, nil return &tlsCertificate, nil
} }
func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcceptTerms bool) (*lego.Config, error) { func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcceptTerms bool) (*lego.Config, error) {

View File

@ -17,10 +17,10 @@ import (
"codeberg.org/codeberg/pages/server/database" "codeberg.org/codeberg/pages/server/database"
) )
func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.CertDB) tls.Certificate { func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.CertDB) (*tls.Certificate, error) {
key, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048) key, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048)
if err != nil { if err != nil {
panic(err) return nil, err
} }
template := x509.Certificate{ template := x509.Certificate{
@ -52,7 +52,7 @@ func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.CertDB)
key, key,
) )
if err != nil { if err != nil {
panic(err) return nil, err
} }
out := &bytes.Buffer{} out := &bytes.Buffer{}
@ -61,7 +61,7 @@ func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.CertDB)
Type: "CERTIFICATE", Type: "CERTIFICATE",
}) })
if err != nil { if err != nil {
panic(err) return nil, err
} }
outBytes := out.Bytes() outBytes := out.Bytes()
res := &certificate.Resource{ res := &certificate.Resource{
@ -80,7 +80,7 @@ func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.CertDB)
tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey)
if err != nil { if err != nil {
panic(err) return nil, err
} }
return tlsCertificate return &tlsCertificate, nil
} }

View File

@ -29,7 +29,6 @@ type Cert struct {
PrivateKey []byte `xorm:"'private_key'"` PrivateKey []byte `xorm:"'private_key'"`
Certificate []byte `xorm:"'certificate'"` Certificate []byte `xorm:"'certificate'"`
IssuerCertificate []byte `xorm:"'issuer_certificate'"` IssuerCertificate []byte `xorm:"'issuer_certificate'"`
CSR []byte `xorm:"'csr'"`
} }
func (c Cert) Raw() *certificate.Resource { func (c Cert) Raw() *certificate.Resource {
@ -40,7 +39,6 @@ func (c Cert) Raw() *certificate.Resource {
PrivateKey: c.PrivateKey, PrivateKey: c.PrivateKey,
Certificate: c.Certificate, Certificate: c.Certificate,
IssuerCertificate: c.IssuerCertificate, IssuerCertificate: c.IssuerCertificate,
CSR: c.CSR,
} }
} }
@ -71,6 +69,5 @@ func toCert(name string, c *certificate.Resource) (*Cert, error) {
PrivateKey: c.PrivateKey, PrivateKey: c.PrivateKey,
Certificate: c.Certificate, Certificate: c.Certificate,
IssuerCertificate: c.IssuerCertificate, IssuerCertificate: c.IssuerCertificate,
CSR: c.CSR,
}, nil }, nil
} }