mirror of
https://codeberg.org/Codeberg/pages-server.git
synced 2025-04-24 22:06:57 +00:00
Merge branch 'main' into refactor_acme
This commit is contained in:
commit
5f5fd09ed3
15 changed files with 168 additions and 291 deletions
|
@ -65,19 +65,6 @@ pipeline:
|
||||||
- RAW_DOMAIN=raw.localhost.mock.directory
|
- RAW_DOMAIN=raw.localhost.mock.directory
|
||||||
- PORT=4430
|
- PORT=4430
|
||||||
|
|
||||||
# TODO: remove in next version
|
|
||||||
integration-tests-legacy:
|
|
||||||
group: test
|
|
||||||
image: codeberg.org/6543/docker-images/golang_just
|
|
||||||
commands:
|
|
||||||
- just integration
|
|
||||||
environment:
|
|
||||||
- ACME_API=https://acme.mock.directory
|
|
||||||
- PAGES_DOMAIN=localhost.mock.directory
|
|
||||||
- RAW_DOMAIN=raw.localhost.mock.directory
|
|
||||||
- PORT=4430
|
|
||||||
- DB_TYPE=
|
|
||||||
|
|
||||||
release:
|
release:
|
||||||
image: plugins/gitea-release
|
image: plugins/gitea-release
|
||||||
settings:
|
settings:
|
||||||
|
|
2
Justfile
2
Justfile
|
@ -27,7 +27,7 @@ fmt: tool-gofumpt
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
go clean ./...
|
go clean ./...
|
||||||
rm -rf build/ integration/certs.sqlite integration/key-database.pogreb/ integration/acme-account.json
|
rm -rf build/ integration/certs.sqlite integration/acme-account.json
|
||||||
|
|
||||||
tool-golangci:
|
tool-golangci:
|
||||||
@hash golangci-lint> /dev/null 2>&1; if [ $? -ne 0 ]; then \
|
@hash golangci-lint> /dev/null 2>&1; if [ $? -ne 0 ]; then \
|
||||||
|
|
64
cmd/certs.go
64
cmd/certs.go
|
@ -4,11 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
"codeberg.org/codeberg/pages/server/database"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var Certs = &cli.Command{
|
var Certs = &cli.Command{
|
||||||
|
@ -25,63 +21,8 @@ var Certs = &cli.Command{
|
||||||
Usage: "remove a certificate from the database",
|
Usage: "remove a certificate from the database",
|
||||||
Action: removeCert,
|
Action: removeCert,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Name: "migrate",
|
|
||||||
Usage: "migrate from \"pogreb\" driver to dbms driver",
|
|
||||||
Action: migrateCerts,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Flags: append(CertStorageFlags, []cli.Flag{
|
Flags: CertStorageFlags,
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "verbose",
|
|
||||||
Usage: "print trace info",
|
|
||||||
EnvVars: []string{"VERBOSE"},
|
|
||||||
Value: false,
|
|
||||||
},
|
|
||||||
}...),
|
|
||||||
}
|
|
||||||
|
|
||||||
func migrateCerts(ctx *cli.Context) error {
|
|
||||||
dbType := ctx.String("db-type")
|
|
||||||
if dbType == "" {
|
|
||||||
dbType = "sqlite3"
|
|
||||||
}
|
|
||||||
dbConn := ctx.String("db-conn")
|
|
||||||
dbPogrebConn := ctx.String("db-pogreb")
|
|
||||||
verbose := ctx.Bool("verbose")
|
|
||||||
|
|
||||||
log.Level(zerolog.InfoLevel)
|
|
||||||
if verbose {
|
|
||||||
log.Level(zerolog.TraceLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
xormDB, err := database.NewXormDB(dbType, dbConn)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not connect to database: %w", err)
|
|
||||||
}
|
|
||||||
defer xormDB.Close()
|
|
||||||
|
|
||||||
pogrebDB, err := database.NewPogreb(dbPogrebConn)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not open database: %w", err)
|
|
||||||
}
|
|
||||||
defer pogrebDB.Close()
|
|
||||||
|
|
||||||
fmt.Printf("Start migration from \"%s\" to \"%s:%s\" ...\n", dbPogrebConn, dbType, dbConn)
|
|
||||||
|
|
||||||
certs, err := pogrebDB.Items(0, 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, cert := range certs {
|
|
||||||
if err := xormDB.Put(cert.Domain, cert.Raw()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("... done")
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func listCerts(ctx *cli.Context) error {
|
func listCerts(ctx *cli.Context) error {
|
||||||
|
@ -98,9 +39,6 @@ func listCerts(ctx *cli.Context) error {
|
||||||
|
|
||||||
fmt.Printf("Domain\tValidTill\n\n")
|
fmt.Printf("Domain\tValidTill\n\n")
|
||||||
for _, cert := range items {
|
for _, cert := range items {
|
||||||
if cert.Domain[0] == '.' {
|
|
||||||
cert.Domain = "*" + cert.Domain
|
|
||||||
}
|
|
||||||
fmt.Printf("%s\t%s\n",
|
fmt.Printf("%s\t%s\n",
|
||||||
cert.Domain,
|
cert.Domain,
|
||||||
time.Unix(cert.ValidTill, 0).Format(time.RFC3339))
|
time.Unix(cert.ValidTill, 0).Format(time.RFC3339))
|
||||||
|
|
42
cmd/flags.go
42
cmd/flags.go
|
@ -6,16 +6,9 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
CertStorageFlags = []cli.Flag{
|
CertStorageFlags = []cli.Flag{
|
||||||
&cli.StringFlag{
|
|
||||||
// TODO: remove in next version
|
|
||||||
// DEPRICATED
|
|
||||||
Name: "db-pogreb",
|
|
||||||
Value: "key-database.pogreb",
|
|
||||||
EnvVars: []string{"DB_POGREB"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "db-type",
|
Name: "db-type",
|
||||||
Value: "", // TODO: "sqlite3" in next version
|
Value: "sqlite3",
|
||||||
EnvVars: []string{"DB_TYPE"},
|
EnvVars: []string{"DB_TYPE"},
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
|
@ -43,6 +36,18 @@ var (
|
||||||
EnvVars: []string{"GITEA_API_TOKEN"},
|
EnvVars: []string{"GITEA_API_TOKEN"},
|
||||||
Value: "",
|
Value: "",
|
||||||
},
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "enable-lfs-support",
|
||||||
|
Usage: "enable lfs support, require gitea >= v1.17.0 as backend",
|
||||||
|
EnvVars: []string{"ENABLE_LFS_SUPPORT"},
|
||||||
|
Value: true,
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "enable-symlink-support",
|
||||||
|
Usage: "follow symlinks if enabled, require gitea >= v1.18.0 as backend",
|
||||||
|
EnvVars: []string{"ENABLE_SYMLINK_SUPPORT"},
|
||||||
|
Value: true,
|
||||||
|
},
|
||||||
|
|
||||||
// ###########################
|
// ###########################
|
||||||
// ### Page Server Domains ###
|
// ### Page Server Domains ###
|
||||||
|
@ -73,7 +78,9 @@ var (
|
||||||
Value: "https://docs.codeberg.org/codeberg-pages/raw-content/",
|
Value: "https://docs.codeberg.org/codeberg-pages/raw-content/",
|
||||||
},
|
},
|
||||||
|
|
||||||
// Server
|
// #########################
|
||||||
|
// ### Page Server Setup ###
|
||||||
|
// #########################
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "host",
|
Name: "host",
|
||||||
Usage: "specifies host of listening address",
|
Usage: "specifies host of listening address",
|
||||||
|
@ -91,19 +98,6 @@ var (
|
||||||
// TODO: desc
|
// TODO: desc
|
||||||
EnvVars: []string{"ENABLE_HTTP_SERVER"},
|
EnvVars: []string{"ENABLE_HTTP_SERVER"},
|
||||||
},
|
},
|
||||||
// Server Options
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "enable-lfs-support",
|
|
||||||
Usage: "enable lfs support, require gitea v1.17.0 as backend",
|
|
||||||
EnvVars: []string{"ENABLE_LFS_SUPPORT"},
|
|
||||||
Value: true,
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "enable-symlink-support",
|
|
||||||
Usage: "follow symlinks if enabled, require gitea v1.18.0 as backend",
|
|
||||||
EnvVars: []string{"ENABLE_SYMLINK_SUPPORT"},
|
|
||||||
Value: true,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "log-level",
|
Name: "log-level",
|
||||||
Value: "warn",
|
Value: "warn",
|
||||||
|
@ -111,7 +105,9 @@ var (
|
||||||
EnvVars: []string{"LOG_LEVEL"},
|
EnvVars: []string{"LOG_LEVEL"},
|
||||||
},
|
},
|
||||||
|
|
||||||
// ACME
|
// ############################
|
||||||
|
// ### ACME Client Settings ###
|
||||||
|
// ############################
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "acme-api-endpoint",
|
Name: "acme-api-endpoint",
|
||||||
EnvVars: []string{"ACME_API"},
|
EnvVars: []string{"ACME_API"},
|
||||||
|
|
26
cmd/setup.go
26
cmd/setup.go
|
@ -15,29 +15,9 @@ import (
|
||||||
var ErrAcmeMissConfig = errors.New("ACME client has wrong config")
|
var ErrAcmeMissConfig = errors.New("ACME client has wrong config")
|
||||||
|
|
||||||
func openCertDB(ctx *cli.Context) (certDB database.CertDB, closeFn func(), err error) {
|
func openCertDB(ctx *cli.Context) (certDB database.CertDB, closeFn func(), err error) {
|
||||||
if ctx.String("db-type") != "" {
|
certDB, err = database.NewXormDB(ctx.String("db-type"), ctx.String("db-conn"))
|
||||||
log.Trace().Msg("use xorm mode")
|
if err != nil {
|
||||||
certDB, err = database.NewXormDB(ctx.String("db-type"), ctx.String("db-conn"))
|
return nil, nil, fmt.Errorf("could not connect to database: %w", err)
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("could not connect to database: %w", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO: remove in next version
|
|
||||||
fmt.Println(`
|
|
||||||
######################
|
|
||||||
## W A R N I N G !!! #
|
|
||||||
######################
|
|
||||||
|
|
||||||
You use "pogreb" witch is deprecated and will be removed in the next version.
|
|
||||||
Please switch to sqlite, mysql or postgres !!!
|
|
||||||
|
|
||||||
The simplest way is, to use './pages certs migrate' and set environment var DB_TYPE to 'sqlite' on next start.`)
|
|
||||||
log.Error().Msg("depricated \"pogreb\" used\n")
|
|
||||||
|
|
||||||
certDB, err = database.NewPogreb(ctx.String("db-pogreb"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("could not create database: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closeFn = func() {
|
closeFn = func() {
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -5,7 +5,6 @@ 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
|
||||||
github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a
|
github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a
|
||||||
github.com/akrylysov/pogreb v0.10.1
|
|
||||||
github.com/go-acme/lego/v4 v4.5.3
|
github.com/go-acme/lego/v4 v4.5.3
|
||||||
github.com/go-sql-driver/mysql v1.6.0
|
github.com/go-sql-driver/mysql v1.6.0
|
||||||
github.com/joho/godotenv v1.4.0
|
github.com/joho/godotenv v1.4.0
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -71,8 +71,6 @@ github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/
|
||||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 h1:bLzehmpyCwQiqCE1Qe9Ny6fbFqs7hPlmo9vKv2orUxs=
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 h1:bLzehmpyCwQiqCE1Qe9Ny6fbFqs7hPlmo9vKv2orUxs=
|
||||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8=
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8=
|
||||||
github.com/akrylysov/pogreb v0.10.1 h1:FqlR8VR7uCbJdfUob916tPM+idpKgeESDXOA1K0DK4w=
|
|
||||||
github.com/akrylysov/pogreb v0.10.1/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI=
|
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
|
|
@ -20,7 +20,9 @@ func TestGetRedirect(t *testing.T) {
|
||||||
log.Println("=== TestGetRedirect ===")
|
log.Println("=== TestGetRedirect ===")
|
||||||
// test custom domain redirect
|
// test custom domain redirect
|
||||||
resp, err := getTestHTTPSClient().Get("https://calciumdibromid.localhost.mock.directory:4430")
|
resp, err := getTestHTTPSClient().Get("https://calciumdibromid.localhost.mock.directory:4430")
|
||||||
assert.NoError(t, err)
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
if !assert.EqualValues(t, http.StatusTemporaryRedirect, resp.StatusCode) {
|
if !assert.EqualValues(t, http.StatusTemporaryRedirect, resp.StatusCode) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
|
@ -332,14 +332,6 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, acmeClient *Acm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Debug().Msgf("Removed %d expired certificates from the database", expiredCertCount)
|
log.Debug().Msgf("Removed %d expired certificates from the database", expiredCertCount)
|
||||||
|
|
||||||
// compact the database
|
|
||||||
msg, err := certDB.Compact()
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("Compacting key database failed")
|
|
||||||
} else {
|
|
||||||
log.Debug().Msgf("Compacted key database: %s", msg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update main cert
|
// update main cert
|
||||||
|
|
|
@ -14,8 +14,6 @@ type CertDB interface {
|
||||||
Get(name string) (*certificate.Resource, error)
|
Get(name string) (*certificate.Resource, error)
|
||||||
Delete(key string) error
|
Delete(key string) error
|
||||||
Items(page, pageSize int) ([]*Cert, error)
|
Items(page, pageSize int) ([]*Cert, error)
|
||||||
// Compact deprecated // TODO: remove in next version
|
|
||||||
Compact() (string, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Cert struct {
|
type Cert struct {
|
||||||
|
@ -54,10 +52,14 @@ func toCert(name string, c *certificate.Resource) (*Cert, error) {
|
||||||
}
|
}
|
||||||
validTill := tlsCertificates[0].NotAfter.Unix()
|
validTill := tlsCertificates[0].NotAfter.Unix()
|
||||||
|
|
||||||
// TODO: do we need this or can we just go with domain name for wildcard cert
|
// handle wildcard certs
|
||||||
// default *.mock cert is prefixed with '.'
|
if name[:1] == "." {
|
||||||
if name != c.Domain && name[1:] != c.Domain && name[0] != '.' {
|
name = "*" + name
|
||||||
return nil, fmt.Errorf("domain key and cert domain not equal")
|
}
|
||||||
|
if name != c.Domain {
|
||||||
|
err := fmt.Errorf("domain key '%s' and cert domain '%s' not equal", name, c.Domain)
|
||||||
|
log.Error().Err(err).Msg("toCert conversion did discover mismatch")
|
||||||
|
// TODO: fail hard: return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Cert{
|
return &Cert{
|
||||||
|
|
|
@ -37,11 +37,6 @@ func (p tmpDB) Delete(key string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p tmpDB) Compact() (string, error) {
|
|
||||||
p.intern.Truncate()
|
|
||||||
return "Truncate done", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p tmpDB) Items(page, pageSize int) ([]*Cert, error) {
|
func (p tmpDB) Items(page, pageSize int) ([]*Cert, error) {
|
||||||
return nil, fmt.Errorf("items not implemented for tmpDB")
|
return nil, fmt.Errorf("items not implemented for tmpDB")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,134 +0,0 @@
|
||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"encoding/gob"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/akrylysov/pogreb"
|
|
||||||
"github.com/akrylysov/pogreb/fs"
|
|
||||||
"github.com/go-acme/lego/v4/certificate"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ CertDB = aDB{}
|
|
||||||
|
|
||||||
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(name string, cert *certificate.Resource) error {
|
|
||||||
var resGob bytes.Buffer
|
|
||||||
if err := gob.NewEncoder(&resGob).Encode(cert); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return p.intern.Put([]byte(name), resGob.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p aDB) Get(name string) (*certificate.Resource, error) {
|
|
||||||
cert := &certificate.Resource{}
|
|
||||||
resBytes, err := p.intern.Get([]byte(name))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if resBytes == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
if err := gob.NewDecoder(bytes.NewBuffer(resBytes)).Decode(cert); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return cert, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p aDB) Delete(key string) error {
|
|
||||||
return p.intern.Delete([]byte(key))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p aDB) Compact() (string, error) {
|
|
||||||
result, err := p.intern.Compact()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%+v", result), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p aDB) Items(_, _ int) ([]*Cert, error) {
|
|
||||||
items := make([]*Cert, 0, p.intern.Count())
|
|
||||||
iterator := p.intern.Items()
|
|
||||||
for {
|
|
||||||
key, resBytes, err := iterator.Next()
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, pogreb.ErrIterationDone) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res := &certificate.Resource{}
|
|
||||||
if err := gob.NewDecoder(bytes.NewBuffer(resBytes)).Decode(res); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
cert, err := toCert(string(key), res)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
items = append(items, cert)
|
|
||||||
}
|
|
||||||
|
|
||||||
return items, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ CertDB = &aDB{}
|
|
||||||
|
|
||||||
func (p aDB) sync() {
|
|
||||||
for {
|
|
||||||
err := p.intern.Sync()
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("Syncing cert database failed")
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-p.ctx.Done():
|
|
||||||
return
|
|
||||||
case <-time.After(p.syncInterval):
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPogreb(path string) (CertDB, error) {
|
|
||||||
if path == "" {
|
|
||||||
return nil, fmt.Errorf("path not set")
|
|
||||||
}
|
|
||||||
db, err := pogreb.Open(path, &pogreb.Options{
|
|
||||||
BackgroundSyncInterval: 30 * time.Second,
|
|
||||||
BackgroundCompactionInterval: 6 * time.Hour,
|
|
||||||
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
|
|
||||||
}
|
|
|
@ -3,7 +3,6 @@ package database
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
|
@ -52,18 +51,38 @@ func (x xDB) Close() error {
|
||||||
|
|
||||||
func (x xDB) Put(domain string, cert *certificate.Resource) error {
|
func (x xDB) Put(domain string, cert *certificate.Resource) error {
|
||||||
log.Trace().Str("domain", cert.Domain).Msg("inserting cert to db")
|
log.Trace().Str("domain", cert.Domain).Msg("inserting cert to db")
|
||||||
|
|
||||||
|
domain = integrationTestReplacements(domain)
|
||||||
c, err := toCert(domain, cert)
|
c, err := toCert(domain, cert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = x.engine.Insert(c)
|
sess := x.engine.NewSession()
|
||||||
return err
|
if err := sess.Begin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer sess.Close()
|
||||||
|
|
||||||
|
if exist, _ := sess.ID(c.Domain).Exist(); exist {
|
||||||
|
if _, err := sess.ID(c.Domain).Update(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err = sess.Insert(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x xDB) Get(domain string) (*certificate.Resource, error) {
|
func (x xDB) Get(domain string) (*certificate.Resource, error) {
|
||||||
// TODO: do we need this or can we just go with domain name for wildcard cert
|
// handle wildcard certs
|
||||||
domain = strings.TrimPrefix(domain, ".")
|
if domain[:1] == "." {
|
||||||
|
domain = "*" + domain
|
||||||
|
}
|
||||||
|
domain = integrationTestReplacements(domain)
|
||||||
|
|
||||||
cert := new(Cert)
|
cert := new(Cert)
|
||||||
log.Trace().Str("domain", domain).Msg("get cert from db")
|
log.Trace().Str("domain", domain).Msg("get cert from db")
|
||||||
|
@ -76,16 +95,17 @@ func (x xDB) Get(domain string) (*certificate.Resource, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x xDB) Delete(domain string) error {
|
func (x xDB) Delete(domain string) error {
|
||||||
|
// handle wildcard certs
|
||||||
|
if domain[:1] == "." {
|
||||||
|
domain = "*" + domain
|
||||||
|
}
|
||||||
|
domain = integrationTestReplacements(domain)
|
||||||
|
|
||||||
log.Trace().Str("domain", domain).Msg("delete cert from db")
|
log.Trace().Str("domain", domain).Msg("delete cert from db")
|
||||||
_, err := x.engine.ID(domain).Delete(new(Cert))
|
_, err := x.engine.ID(domain).Delete(new(Cert))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x xDB) Compact() (string, error) {
|
|
||||||
// not needed
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Items return al certs from db, if pageSize is 0 it does not use limit
|
// Items return al certs from db, if pageSize is 0 it does not use limit
|
||||||
func (x xDB) Items(page, pageSize int) ([]*Cert, error) {
|
func (x xDB) Items(page, pageSize int) ([]*Cert, error) {
|
||||||
// paginated return
|
// paginated return
|
||||||
|
@ -119,3 +139,13 @@ func supportedDriver(driver string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// integrationTestReplacements is needed because integration tests use a single domain cert,
|
||||||
|
// while production use a wildcard cert
|
||||||
|
// TODO: find a better way to handle this
|
||||||
|
func integrationTestReplacements(domainKey string) string {
|
||||||
|
if domainKey == "*.localhost.mock.directory" {
|
||||||
|
return "localhost.mock.directory"
|
||||||
|
}
|
||||||
|
return domainKey
|
||||||
|
}
|
||||||
|
|
92
server/database/xorm_test.go
Normal file
92
server/database/xorm_test.go
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/certificate"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newTestDB(t *testing.T) *xDB {
|
||||||
|
e, err := xorm.NewEngine("sqlite3", ":memory:")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, e.Sync2(new(Cert)))
|
||||||
|
return &xDB{engine: e}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSanitizeWildcardCerts(t *testing.T) {
|
||||||
|
certDB := newTestDB(t)
|
||||||
|
|
||||||
|
_, err := certDB.Get(".not.found")
|
||||||
|
assert.True(t, errors.Is(err, ErrNotFound))
|
||||||
|
|
||||||
|
// TODO: cert key and domain mismatch are don not fail hard jet
|
||||||
|
// https://codeberg.org/Codeberg/pages-server/src/commit/d8595cee882e53d7f44f1ddc4ef8a1f7b8f31d8d/server/database/interface.go#L64
|
||||||
|
//
|
||||||
|
// assert.Error(t, certDB.Put(".wildcard.de", &certificate.Resource{
|
||||||
|
// Domain: "*.localhost.mock.directory",
|
||||||
|
// Certificate: localhost_mock_directory_certificate,
|
||||||
|
// }))
|
||||||
|
|
||||||
|
// insert new wildcard cert
|
||||||
|
assert.NoError(t, certDB.Put(".wildcard.de", &certificate.Resource{
|
||||||
|
Domain: "*.wildcard.de",
|
||||||
|
Certificate: localhost_mock_directory_certificate,
|
||||||
|
}))
|
||||||
|
|
||||||
|
// update existing cert
|
||||||
|
assert.Error(t, certDB.Put(".wildcard.de", &certificate.Resource{
|
||||||
|
Domain: "*.wildcard.de",
|
||||||
|
Certificate: localhost_mock_directory_certificate,
|
||||||
|
}))
|
||||||
|
|
||||||
|
c1, err := certDB.Get(".wildcard.de")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
c2, err := certDB.Get("*.wildcard.de")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, c1, c2)
|
||||||
|
}
|
||||||
|
|
||||||
|
var localhost_mock_directory_certificate = []byte(`-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDczCCAlugAwIBAgIIJyBaXHmLk6gwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UE
|
||||||
|
AxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSA0OWE0ZmIwHhcNMjMwMjEwMDEwOTA2
|
||||||
|
WhcNMjgwMjEwMDEwOTA2WjAjMSEwHwYDVQQDExhsb2NhbGhvc3QubW9jay5kaXJl
|
||||||
|
Y3RvcnkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIU/CjzS7t62Gj
|
||||||
|
neEMqvP7sn99ULT7AEUzEfWL05fWG2z714qcUg1hXkZLgdVDgmsCpplyddip7+2t
|
||||||
|
ZH/9rLPLMqJphzvOL4CF6jDLbeifETtKyjnt9vUZFnnNWcP3tu8lo8iYSl08qsUI
|
||||||
|
Pp/hiEriAQzCDjTbR5m9xUPNPYqxzcS4ALzmmCX9Qfc4CuuhMkdv2G4TT7rylWrA
|
||||||
|
SCSRPnGjeA7pCByfNrO/uXbxmzl3sMO3k5sqgMkx1QIHEN412V8+vtx88mt2sM6k
|
||||||
|
xjzGZWWKXlRq+oufIKX9KPplhsCjMH6E3VNAzgOPYDqXagtUcGmLWghURltO8Mt2
|
||||||
|
zwM6OgjjAgMBAAGjgaUwgaIwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsG
|
||||||
|
AQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSMQvlJ1755
|
||||||
|
sarf8i1KNqj7s5o/aDAfBgNVHSMEGDAWgBTcZcxJMhWdP7MecHCCpNkFURC/YzAj
|
||||||
|
BgNVHREEHDAaghhsb2NhbGhvc3QubW9jay5kaXJlY3RvcnkwDQYJKoZIhvcNAQEL
|
||||||
|
BQADggEBACcd7TT28OWwzQN2PcH0aG38JX5Wp2iOS/unDCfWjNAztXHW7nBDMxza
|
||||||
|
VtyebkJfccexpuVuOsjOX+bww0vtEYIvKX3/GbkhogksBrNkE0sJZtMnZWMR33wa
|
||||||
|
YxAy/kJBTmLi02r8fX9ZhwjldStHKBav4USuP7DXZjrgX7LFQhR4LIDrPaYqQRZ8
|
||||||
|
ltC3mM9LDQ9rQyIFP5cSBMO3RUAm4I8JyLoOdb/9G2uxjHr7r6eG1g8DmLYSKBsQ
|
||||||
|
mWGQDOYgR3cGltDe2yMxM++yHY+b1uhxGOWMrDA1+1k7yI19LL8Ifi2FMovDfu/X
|
||||||
|
JxYk1NNNtdctwaYJFenmGQvDaIq1KgE=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDUDCCAjigAwIBAgIIKBJ7IIA6W1swDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
|
||||||
|
AxMVUGViYmxlIFJvb3QgQ0EgNTdmZjE2MCAXDTIzMDIwOTA1MzMxMloYDzIwNTMw
|
||||||
|
MjA5MDUzMzEyWjAoMSYwJAYDVQQDEx1QZWJibGUgSW50ZXJtZWRpYXRlIENBIDQ5
|
||||||
|
YTRmYjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOvlqRx8SXQFWo2
|
||||||
|
gFCiXxls53eENcyr8+meFyjgnS853eEvplaPxoa2MREKd+ZYxM8EMMfj2XGvR3UI
|
||||||
|
aqR5QyLQ9ihuRqvQo4fG91usBHgH+vDbGPdMX8gDmm9HgnmtOVhSKJU+M2jfE1SW
|
||||||
|
UuWB9xOa3LMreTXbTNfZEMoXf+GcWZMbx5WPgEga3DvfmV+RsfNvB55eD7YAyZgF
|
||||||
|
ZnQ3Dskmnxxlkz0EGgd7rqhFHHNB9jARlL22gITADwoWZidlr3ciM9DISymRKQ0c
|
||||||
|
mRN15fQjNWdtuREgJlpXecbYQMGhdTOmFrqdHkveD1o63rGSC4z+s/APV6xIbcRp
|
||||||
|
aNpO7L8CAwEAAaOBgzCBgDAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYB
|
||||||
|
BQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNxlzEky
|
||||||
|
FZ0/sx5wcIKk2QVREL9jMB8GA1UdIwQYMBaAFOqfkm9rebIz4z0SDIKW5edLg5JM
|
||||||
|
MA0GCSqGSIb3DQEBCwUAA4IBAQBRG9AHEnyj2fKzVDDbQaKHjAF5jh0gwyHoIeRK
|
||||||
|
FkP9mQNSWxhvPWI0tK/E49LopzmVuzSbDd5kZsaii73rAs6f6Rf9W5veo3AFSEad
|
||||||
|
stM+Zv0f2vWB38nuvkoCRLXMX+QUeuL65rKxdEpyArBju4L3/PqAZRgMLcrH+ak8
|
||||||
|
nvw5RdAq+Km/ZWyJgGikK6cfMmh91YALCDFnoWUWrCjkBaBFKrG59ONV9f0IQX07
|
||||||
|
aNfFXFCF5l466xw9dHjw5iaFib10cpY3iq4kyPYIMs6uaewkCtxWKKjiozM4g4w3
|
||||||
|
HqwyUyZ52WUJOJ/6G9DJLDtN3fgGR+IAp8BhYd5CqOscnt3h
|
||||||
|
-----END CERTIFICATE-----`)
|
|
@ -49,7 +49,7 @@ func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add [owner].[pages-domain] as valid domnain.
|
// Add [owner].[pages-domain] as valid domain.
|
||||||
domains = append(domains, o.TargetOwner+mainDomainSuffix)
|
domains = append(domains, o.TargetOwner+mainDomainSuffix)
|
||||||
if domains[len(domains)-1] == actualDomain {
|
if domains[len(domains)-1] == actualDomain {
|
||||||
valid = true
|
valid = true
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue