mirror of
https://codeberg.org/Codeberg/pages-server.git
synced 2024-11-18 10:29:43 +00:00
Fix xorm regressions by handle wildcard certs correctly (#177)
close #176 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/177
This commit is contained in:
parent
d8d119b0b3
commit
272c7ca76f
7 changed files with 162 additions and 29 deletions
|
@ -98,9 +98,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))
|
||||||
|
|
33
cmd/flags.go
33
cmd/flags.go
|
@ -43,6 +43,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 +85,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 +105,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 +112,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"},
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,10 +54,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{
|
||||||
|
|
|
@ -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,6 +95,12 @@ 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
|
||||||
|
@ -119,3 +144,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…
Reference in a new issue