mirror of
https://codeberg.org/Codeberg/pages-server.git
synced 2024-11-18 10:29:43 +00:00
wrap cert db and make sync gracefull
This commit is contained in:
parent
11fa729686
commit
de439f9bec
7 changed files with 95 additions and 14 deletions
|
@ -35,7 +35,7 @@ func certs(ctx *cli.Context) error {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := keyDatabase.Sync(); err != nil {
|
if err := keyDatabase.Close(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
|
|
|
@ -94,7 +94,7 @@ func Serve(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not create database: %v", err)
|
return fmt.Errorf("could not create database: %v", err)
|
||||||
}
|
}
|
||||||
defer keyDatabase.Sync() //nolint:errcheck // database has no close ... sync behave like it
|
defer keyDatabase.Close() //nolint:errcheck // database has no close ... sync behave like it
|
||||||
|
|
||||||
listener = tls.NewListener(listener, certificates.TLSConfig(mainDomainSuffix,
|
listener = tls.NewListener(listener, certificates.TLSConfig(mainDomainSuffix,
|
||||||
giteaRoot, giteaAPIToken, dnsProvider,
|
giteaRoot, giteaAPIToken, dnsProvider,
|
||||||
|
|
|
@ -38,7 +38,7 @@ func TLSConfig(mainDomainSuffix []byte,
|
||||||
giteaRoot, giteaApiToken, dnsProvider string,
|
giteaRoot, giteaApiToken, dnsProvider string,
|
||||||
acmeUseRateLimits bool,
|
acmeUseRateLimits bool,
|
||||||
keyCache, challengeCache, dnsLookupCache, canonicalDomainCache cache.SetGetKey,
|
keyCache, challengeCache, dnsLookupCache, canonicalDomainCache cache.SetGetKey,
|
||||||
keyDatabase database.KeyDB) *tls.Config {
|
keyDatabase database.CertDB) *tls.Config {
|
||||||
return &tls.Config{
|
return &tls.Config{
|
||||||
// check DNS name & get certificate from Let's Encrypt
|
// check DNS name & get certificate from Let's Encrypt
|
||||||
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
|
@ -185,7 +185,7 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, keyDatabase database.KeyDB) (tls.Certificate, bool) {
|
func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, keyDatabase database.CertDB) (tls.Certificate, bool) {
|
||||||
// parse certificate from database
|
// parse certificate from database
|
||||||
res := &certificate.Resource{}
|
res := &certificate.Resource{}
|
||||||
if !database.PogrebGet(keyDatabase, sni, res) {
|
if !database.PogrebGet(keyDatabase, sni, res) {
|
||||||
|
@ -229,7 +229,7 @@ func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUs
|
||||||
|
|
||||||
var obtainLocks = sync.Map{}
|
var obtainLocks = sync.Map{}
|
||||||
|
|
||||||
func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user, dnsProvider string, mainDomainSuffix []byte, acmeUseRateLimits bool, keyDatabase database.KeyDB) (tls.Certificate, error) {
|
func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user, dnsProvider string, mainDomainSuffix []byte, 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:]
|
||||||
|
@ -392,7 +392,7 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce
|
||||||
return myAcmeConfig, nil
|
return myAcmeConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig *lego.Config, acmeUseRateLimits, enableHTTPServer bool, challengeCache cache.SetGetKey, keyDatabase database.KeyDB) {
|
func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig *lego.Config, acmeUseRateLimits, enableHTTPServer bool, challengeCache cache.SetGetKey, keyDatabase database.CertDB) {
|
||||||
// getting main cert before ACME account so that we can panic here on database failure without hitting rate limits
|
// getting main cert before ACME account so that we can panic here on database failure without hitting rate limits
|
||||||
mainCertBytes, err := keyDatabase.Get(mainDomainSuffix)
|
mainCertBytes, err := keyDatabase.Get(mainDomainSuffix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
"codeberg.org/codeberg/pages/server/database"
|
"codeberg.org/codeberg/pages/server/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.KeyDB) tls.Certificate {
|
func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.CertDB) tls.Certificate {
|
||||||
key, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048)
|
key, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PogrebPut(db KeyDB, name []byte, obj interface{}) {
|
func PogrebPut(db CertDB, name []byte, obj interface{}) {
|
||||||
var resGob bytes.Buffer
|
var resGob bytes.Buffer
|
||||||
resEnc := gob.NewEncoder(&resGob)
|
resEnc := gob.NewEncoder(&resGob)
|
||||||
err := resEnc.Encode(obj)
|
err := resEnc.Encode(obj)
|
||||||
|
@ -18,7 +18,7 @@ func PogrebPut(db KeyDB, name []byte, obj interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func PogrebGet(db KeyDB, name []byte, obj interface{}) bool {
|
func PogrebGet(db CertDB, name []byte, obj interface{}) bool {
|
||||||
resBytes, err := db.Get(name)
|
resBytes, err := db.Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
@ -2,8 +2,8 @@ package database
|
||||||
|
|
||||||
import "github.com/akrylysov/pogreb"
|
import "github.com/akrylysov/pogreb"
|
||||||
|
|
||||||
type KeyDB interface {
|
type CertDB interface {
|
||||||
Sync() error
|
Close() error
|
||||||
Put(key []byte, value []byte) error
|
Put(key []byte, value []byte) error
|
||||||
Get(key []byte) ([]byte, error)
|
Get(key []byte) ([]byte, error)
|
||||||
Delete(key []byte) error
|
Delete(key []byte) error
|
||||||
|
|
|
@ -1,19 +1,100 @@
|
||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"github.com/akrylysov/pogreb"
|
"github.com/akrylysov/pogreb"
|
||||||
"github.com/akrylysov/pogreb/fs"
|
"github.com/akrylysov/pogreb/fs"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(path string) (KeyDB, error) {
|
type aDB struct {
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
intern *pogreb.DB
|
||||||
|
syncInterval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p aDB) Close() error {
|
||||||
|
p.cancel()
|
||||||
|
return p.intern.Sync()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p aDB) Put(key []byte, value []byte) error {
|
||||||
|
return p.intern.Put(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p aDB) Get(key []byte) ([]byte, error) {
|
||||||
|
return p.intern.Get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p aDB) Delete(key []byte) error {
|
||||||
|
return p.intern.Delete(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p aDB) Compact() (pogreb.CompactionResult, error) {
|
||||||
|
return p.intern.Compact()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p aDB) Items() *pogreb.ItemIterator {
|
||||||
|
return p.intern.Items()
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ CertDB = &aDB{}
|
||||||
|
|
||||||
|
func (p aDB) sync() {
|
||||||
|
for {
|
||||||
|
err := p.intern.Sync()
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("Syncing cert database failed")
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-p.ctx.Done():
|
||||||
|
return
|
||||||
|
case <-time.After(p.syncInterval):
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p aDB) compact() {
|
||||||
|
for {
|
||||||
|
err := p.intern.Sync()
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("Syncing cert database failed")
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-p.ctx.Done():
|
||||||
|
return
|
||||||
|
case <-time.After(p.syncInterval):
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(path string) (CertDB, error) {
|
||||||
if path == "" {
|
if path == "" {
|
||||||
return nil, fmt.Errorf("path not set")
|
return nil, fmt.Errorf("path not set")
|
||||||
}
|
}
|
||||||
return pogreb.Open(path, &pogreb.Options{
|
db, err := pogreb.Open(path, &pogreb.Options{
|
||||||
BackgroundSyncInterval: 30 * time.Second,
|
BackgroundSyncInterval: 30 * time.Second,
|
||||||
BackgroundCompactionInterval: 6 * time.Hour,
|
BackgroundCompactionInterval: 6 * time.Hour,
|
||||||
FileSystem: fs.OSMMap,
|
FileSystem: fs.OSMMap,
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
result := &aDB{
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
intern: db,
|
||||||
|
syncInterval: 5 * time.Minute,
|
||||||
|
}
|
||||||
|
|
||||||
|
go result.sync()
|
||||||
|
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue