mirror of
https://codeberg.org/Codeberg/pages-server.git
synced 2024-11-18 10:29:43 +00:00
03881382a4
This PR add the `$NO_DNS_01` option (disabled by default) that removes the DNS ACME provider, and replaces the wildcard certificate by individual certificates obtained using the TLS ACME provider. This option allows an instance to work without having to manage access tokens for the DNS provider. On the flip side, this means that a certificate can be requested for each subdomains. To limit the risk of DOS, the existence of the user/org corresponding to a subdomain is checked before requesting a cert, however, this limitation is not enough for an forge with a high number of users/orgs. Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/290 Reviewed-by: Moritz Marquardt <momar@noreply.codeberg.org> Co-authored-by: Jean-Marie 'Histausse' Mineau <histausse@protonmail.com> Co-committed-by: Jean-Marie 'Histausse' Mineau <histausse@protonmail.com>
603 lines
17 KiB
Go
603 lines
17 KiB
Go
package config
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/pelletier/go-toml/v2"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/urfave/cli/v2"
|
|
|
|
cmd "codeberg.org/codeberg/pages/cli"
|
|
)
|
|
|
|
func runApp(t *testing.T, fn func(*cli.Context) error, args []string) {
|
|
app := cmd.CreatePagesApp()
|
|
app.Action = fn
|
|
|
|
appCtx, appCancel := context.WithCancel(context.Background())
|
|
defer appCancel()
|
|
|
|
// os.Args always contains the binary name
|
|
args = append([]string{"testing"}, args...)
|
|
|
|
err := app.RunContext(appCtx, args)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
// fixArrayFromCtx fixes the number of "changed" strings in a string slice according to the number of values in the context.
|
|
// This is a workaround because the cli library has a bug where the number of values in the context gets bigger the more tests are run.
|
|
func fixArrayFromCtx(ctx *cli.Context, key string, expected []string) []string {
|
|
if ctx.IsSet(key) {
|
|
ctxSlice := ctx.StringSlice(key)
|
|
|
|
if len(ctxSlice) > 1 {
|
|
for i := 1; i < len(ctxSlice); i++ {
|
|
expected = append([]string{"changed"}, expected...)
|
|
}
|
|
}
|
|
}
|
|
|
|
return expected
|
|
}
|
|
|
|
func readTestConfig() (*Config, error) {
|
|
content, err := os.ReadFile("assets/test_config.toml")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
expectedConfig := NewDefaultConfig()
|
|
err = toml.Unmarshal(content, &expectedConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &expectedConfig, nil
|
|
}
|
|
|
|
func TestReadConfigShouldReturnEmptyConfigWhenConfigArgEmpty(t *testing.T) {
|
|
runApp(
|
|
t,
|
|
func(ctx *cli.Context) error {
|
|
cfg, err := ReadConfig(ctx)
|
|
expected := NewDefaultConfig()
|
|
assert.Equal(t, &expected, cfg)
|
|
|
|
return err
|
|
},
|
|
[]string{},
|
|
)
|
|
}
|
|
|
|
func TestReadConfigShouldReturnConfigFromFileWhenConfigArgPresent(t *testing.T) {
|
|
runApp(
|
|
t,
|
|
func(ctx *cli.Context) error {
|
|
cfg, err := ReadConfig(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
expectedConfig, err := readTestConfig()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
assert.Equal(t, expectedConfig, cfg)
|
|
|
|
return nil
|
|
},
|
|
[]string{"--config-file", "assets/test_config.toml"},
|
|
)
|
|
}
|
|
|
|
func TestValuesReadFromConfigFileShouldBeOverwrittenByArgs(t *testing.T) {
|
|
runApp(
|
|
t,
|
|
func(ctx *cli.Context) error {
|
|
cfg, err := ReadConfig(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
MergeConfig(ctx, cfg)
|
|
|
|
expectedConfig, err := readTestConfig()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
expectedConfig.LogLevel = "debug"
|
|
expectedConfig.Gitea.Root = "not-codeberg.org"
|
|
expectedConfig.ACME.AcceptTerms = true
|
|
expectedConfig.Server.Host = "172.17.0.2"
|
|
expectedConfig.Server.BlacklistedPaths = append(expectedConfig.Server.BlacklistedPaths, ALWAYS_BLACKLISTED_PATHS...)
|
|
|
|
assert.Equal(t, expectedConfig, cfg)
|
|
|
|
return nil
|
|
},
|
|
[]string{
|
|
"--config-file", "assets/test_config.toml",
|
|
"--log-level", "debug",
|
|
"--gitea-root", "not-codeberg.org",
|
|
"--acme-accept-terms",
|
|
"--host", "172.17.0.2",
|
|
},
|
|
)
|
|
}
|
|
|
|
func TestMergeConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T) {
|
|
runApp(
|
|
t,
|
|
func(ctx *cli.Context) error {
|
|
cfg := &Config{
|
|
LogLevel: "original",
|
|
Server: ServerConfig{
|
|
Host: "original",
|
|
Port: 8080,
|
|
HttpPort: 80,
|
|
HttpServerEnabled: false,
|
|
MainDomain: "original",
|
|
RawDomain: "original",
|
|
PagesBranches: []string{"original"},
|
|
AllowedCorsDomains: []string{"original"},
|
|
BlacklistedPaths: []string{"original"},
|
|
},
|
|
Gitea: GiteaConfig{
|
|
Root: "original",
|
|
Token: "original",
|
|
LFSEnabled: false,
|
|
FollowSymlinks: false,
|
|
DefaultMimeType: "original",
|
|
ForbiddenMimeTypes: []string{"original"},
|
|
},
|
|
Database: DatabaseConfig{
|
|
Type: "original",
|
|
Conn: "original",
|
|
},
|
|
ACME: ACMEConfig{
|
|
Email: "original",
|
|
APIEndpoint: "original",
|
|
AcceptTerms: false,
|
|
UseRateLimits: false,
|
|
EAB_HMAC: "original",
|
|
EAB_KID: "original",
|
|
DNSProvider: "original",
|
|
NoDNS01: false,
|
|
AccountConfigFile: "original",
|
|
},
|
|
}
|
|
|
|
MergeConfig(ctx, cfg)
|
|
|
|
expectedConfig := &Config{
|
|
LogLevel: "changed",
|
|
Server: ServerConfig{
|
|
Host: "changed",
|
|
Port: 8443,
|
|
HttpPort: 443,
|
|
HttpServerEnabled: true,
|
|
MainDomain: "changed",
|
|
RawDomain: "changed",
|
|
PagesBranches: []string{"changed"},
|
|
AllowedCorsDomains: []string{"changed"},
|
|
BlacklistedPaths: append([]string{"changed"}, ALWAYS_BLACKLISTED_PATHS...),
|
|
},
|
|
Gitea: GiteaConfig{
|
|
Root: "changed",
|
|
Token: "changed",
|
|
LFSEnabled: true,
|
|
FollowSymlinks: true,
|
|
DefaultMimeType: "changed",
|
|
ForbiddenMimeTypes: []string{"changed"},
|
|
},
|
|
Database: DatabaseConfig{
|
|
Type: "changed",
|
|
Conn: "changed",
|
|
},
|
|
ACME: ACMEConfig{
|
|
Email: "changed",
|
|
APIEndpoint: "changed",
|
|
AcceptTerms: true,
|
|
UseRateLimits: true,
|
|
EAB_HMAC: "changed",
|
|
EAB_KID: "changed",
|
|
DNSProvider: "changed",
|
|
NoDNS01: true,
|
|
AccountConfigFile: "changed",
|
|
},
|
|
}
|
|
|
|
assert.Equal(t, expectedConfig, cfg)
|
|
|
|
return nil
|
|
},
|
|
[]string{
|
|
"--log-level", "changed",
|
|
// Server
|
|
"--pages-domain", "changed",
|
|
"--raw-domain", "changed",
|
|
"--allowed-cors-domains", "changed",
|
|
"--blacklisted-paths", "changed",
|
|
"--pages-branch", "changed",
|
|
"--host", "changed",
|
|
"--port", "8443",
|
|
"--http-port", "443",
|
|
"--enable-http-server",
|
|
// Gitea
|
|
"--gitea-root", "changed",
|
|
"--gitea-api-token", "changed",
|
|
"--enable-lfs-support",
|
|
"--enable-symlink-support",
|
|
"--default-mime-type", "changed",
|
|
"--forbidden-mime-types", "changed",
|
|
// Database
|
|
"--db-type", "changed",
|
|
"--db-conn", "changed",
|
|
// ACME
|
|
"--acme-email", "changed",
|
|
"--acme-api-endpoint", "changed",
|
|
"--acme-accept-terms",
|
|
"--acme-use-rate-limits",
|
|
"--acme-eab-hmac", "changed",
|
|
"--acme-eab-kid", "changed",
|
|
"--dns-provider", "changed",
|
|
"--no-dns-01",
|
|
"--acme-account-config", "changed",
|
|
},
|
|
)
|
|
}
|
|
|
|
func TestMergeServerConfigShouldAddDefaultBlacklistedPathsToBlacklistedPaths(t *testing.T) {
|
|
runApp(
|
|
t,
|
|
func(ctx *cli.Context) error {
|
|
cfg := &ServerConfig{}
|
|
mergeServerConfig(ctx, cfg)
|
|
|
|
expected := ALWAYS_BLACKLISTED_PATHS
|
|
assert.Equal(t, expected, cfg.BlacklistedPaths)
|
|
|
|
return nil
|
|
},
|
|
[]string{},
|
|
)
|
|
}
|
|
|
|
func TestMergeServerConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T) {
|
|
for range []uint8{0, 1} {
|
|
runApp(
|
|
t,
|
|
func(ctx *cli.Context) error {
|
|
cfg := &ServerConfig{
|
|
Host: "original",
|
|
Port: 8080,
|
|
HttpPort: 80,
|
|
HttpServerEnabled: false,
|
|
MainDomain: "original",
|
|
RawDomain: "original",
|
|
AllowedCorsDomains: []string{"original"},
|
|
BlacklistedPaths: []string{"original"},
|
|
}
|
|
|
|
mergeServerConfig(ctx, cfg)
|
|
|
|
expectedConfig := &ServerConfig{
|
|
Host: "changed",
|
|
Port: 8443,
|
|
HttpPort: 443,
|
|
HttpServerEnabled: true,
|
|
MainDomain: "changed",
|
|
RawDomain: "changed",
|
|
AllowedCorsDomains: fixArrayFromCtx(ctx, "allowed-cors-domains", []string{"changed"}),
|
|
BlacklistedPaths: fixArrayFromCtx(ctx, "blacklisted-paths", append([]string{"changed"}, ALWAYS_BLACKLISTED_PATHS...)),
|
|
}
|
|
|
|
assert.Equal(t, expectedConfig, cfg)
|
|
|
|
return nil
|
|
},
|
|
[]string{
|
|
"--pages-domain", "changed",
|
|
"--raw-domain", "changed",
|
|
"--allowed-cors-domains", "changed",
|
|
"--blacklisted-paths", "changed",
|
|
"--host", "changed",
|
|
"--port", "8443",
|
|
"--http-port", "443",
|
|
"--enable-http-server",
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestMergeServerConfigShouldReplaceOnlyOneValueExistingValueGivenOnlyOneArgExists(t *testing.T) {
|
|
type testValuePair struct {
|
|
args []string
|
|
callback func(*ServerConfig)
|
|
}
|
|
testValuePairs := []testValuePair{
|
|
{args: []string{"--host", "changed"}, callback: func(sc *ServerConfig) { sc.Host = "changed" }},
|
|
{args: []string{"--port", "8443"}, callback: func(sc *ServerConfig) { sc.Port = 8443 }},
|
|
{args: []string{"--http-port", "443"}, callback: func(sc *ServerConfig) { sc.HttpPort = 443 }},
|
|
{args: []string{"--enable-http-server"}, callback: func(sc *ServerConfig) { sc.HttpServerEnabled = true }},
|
|
{args: []string{"--pages-domain", "changed"}, callback: func(sc *ServerConfig) { sc.MainDomain = "changed" }},
|
|
{args: []string{"--raw-domain", "changed"}, callback: func(sc *ServerConfig) { sc.RawDomain = "changed" }},
|
|
{args: []string{"--pages-branch", "changed"}, callback: func(sc *ServerConfig) { sc.PagesBranches = []string{"changed"} }},
|
|
{args: []string{"--allowed-cors-domains", "changed"}, callback: func(sc *ServerConfig) { sc.AllowedCorsDomains = []string{"changed"} }},
|
|
{args: []string{"--blacklisted-paths", "changed"}, callback: func(sc *ServerConfig) { sc.BlacklistedPaths = []string{"changed"} }},
|
|
}
|
|
|
|
for _, pair := range testValuePairs {
|
|
runApp(
|
|
t,
|
|
func(ctx *cli.Context) error {
|
|
cfg := ServerConfig{
|
|
Host: "original",
|
|
Port: 8080,
|
|
HttpPort: 80,
|
|
HttpServerEnabled: false,
|
|
MainDomain: "original",
|
|
RawDomain: "original",
|
|
PagesBranches: []string{"original"},
|
|
AllowedCorsDomains: []string{"original"},
|
|
BlacklistedPaths: []string{"original"},
|
|
}
|
|
|
|
expectedConfig := cfg
|
|
pair.callback(&expectedConfig)
|
|
expectedConfig.BlacklistedPaths = append(expectedConfig.BlacklistedPaths, ALWAYS_BLACKLISTED_PATHS...)
|
|
|
|
expectedConfig.PagesBranches = fixArrayFromCtx(ctx, "pages-branch", expectedConfig.PagesBranches)
|
|
expectedConfig.AllowedCorsDomains = fixArrayFromCtx(ctx, "allowed-cors-domains", expectedConfig.AllowedCorsDomains)
|
|
expectedConfig.BlacklistedPaths = fixArrayFromCtx(ctx, "blacklisted-paths", expectedConfig.BlacklistedPaths)
|
|
|
|
mergeServerConfig(ctx, &cfg)
|
|
|
|
assert.Equal(t, expectedConfig, cfg)
|
|
|
|
return nil
|
|
},
|
|
pair.args,
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestMergeGiteaConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T) {
|
|
runApp(
|
|
t,
|
|
func(ctx *cli.Context) error {
|
|
cfg := &GiteaConfig{
|
|
Root: "original",
|
|
Token: "original",
|
|
LFSEnabled: false,
|
|
FollowSymlinks: false,
|
|
DefaultMimeType: "original",
|
|
ForbiddenMimeTypes: []string{"original"},
|
|
}
|
|
|
|
mergeGiteaConfig(ctx, cfg)
|
|
|
|
expectedConfig := &GiteaConfig{
|
|
Root: "changed",
|
|
Token: "changed",
|
|
LFSEnabled: true,
|
|
FollowSymlinks: true,
|
|
DefaultMimeType: "changed",
|
|
ForbiddenMimeTypes: fixArrayFromCtx(ctx, "forbidden-mime-types", []string{"changed"}),
|
|
}
|
|
|
|
assert.Equal(t, expectedConfig, cfg)
|
|
|
|
return nil
|
|
},
|
|
[]string{
|
|
"--gitea-root", "changed",
|
|
"--gitea-api-token", "changed",
|
|
"--enable-lfs-support",
|
|
"--enable-symlink-support",
|
|
"--default-mime-type", "changed",
|
|
"--forbidden-mime-types", "changed",
|
|
},
|
|
)
|
|
}
|
|
|
|
func TestMergeGiteaConfigShouldReplaceOnlyOneValueExistingValueGivenOnlyOneArgExists(t *testing.T) {
|
|
type testValuePair struct {
|
|
args []string
|
|
callback func(*GiteaConfig)
|
|
}
|
|
testValuePairs := []testValuePair{
|
|
{args: []string{"--gitea-root", "changed"}, callback: func(gc *GiteaConfig) { gc.Root = "changed" }},
|
|
{args: []string{"--gitea-api-token", "changed"}, callback: func(gc *GiteaConfig) { gc.Token = "changed" }},
|
|
{args: []string{"--enable-lfs-support"}, callback: func(gc *GiteaConfig) { gc.LFSEnabled = true }},
|
|
{args: []string{"--enable-symlink-support"}, callback: func(gc *GiteaConfig) { gc.FollowSymlinks = true }},
|
|
{args: []string{"--default-mime-type", "changed"}, callback: func(gc *GiteaConfig) { gc.DefaultMimeType = "changed" }},
|
|
{args: []string{"--forbidden-mime-types", "changed"}, callback: func(gc *GiteaConfig) { gc.ForbiddenMimeTypes = []string{"changed"} }},
|
|
}
|
|
|
|
for _, pair := range testValuePairs {
|
|
runApp(
|
|
t,
|
|
func(ctx *cli.Context) error {
|
|
cfg := GiteaConfig{
|
|
Root: "original",
|
|
Token: "original",
|
|
LFSEnabled: false,
|
|
FollowSymlinks: false,
|
|
DefaultMimeType: "original",
|
|
ForbiddenMimeTypes: []string{"original"},
|
|
}
|
|
|
|
expectedConfig := cfg
|
|
pair.callback(&expectedConfig)
|
|
|
|
mergeGiteaConfig(ctx, &cfg)
|
|
|
|
expectedConfig.ForbiddenMimeTypes = fixArrayFromCtx(ctx, "forbidden-mime-types", expectedConfig.ForbiddenMimeTypes)
|
|
|
|
assert.Equal(t, expectedConfig, cfg)
|
|
|
|
return nil
|
|
},
|
|
pair.args,
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestMergeDatabaseConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T) {
|
|
runApp(
|
|
t,
|
|
func(ctx *cli.Context) error {
|
|
cfg := &DatabaseConfig{
|
|
Type: "original",
|
|
Conn: "original",
|
|
}
|
|
|
|
mergeDatabaseConfig(ctx, cfg)
|
|
|
|
expectedConfig := &DatabaseConfig{
|
|
Type: "changed",
|
|
Conn: "changed",
|
|
}
|
|
|
|
assert.Equal(t, expectedConfig, cfg)
|
|
|
|
return nil
|
|
},
|
|
[]string{
|
|
"--db-type", "changed",
|
|
"--db-conn", "changed",
|
|
},
|
|
)
|
|
}
|
|
|
|
func TestMergeDatabaseConfigShouldReplaceOnlyOneValueExistingValueGivenOnlyOneArgExists(t *testing.T) {
|
|
type testValuePair struct {
|
|
args []string
|
|
callback func(*DatabaseConfig)
|
|
}
|
|
testValuePairs := []testValuePair{
|
|
{args: []string{"--db-type", "changed"}, callback: func(gc *DatabaseConfig) { gc.Type = "changed" }},
|
|
{args: []string{"--db-conn", "changed"}, callback: func(gc *DatabaseConfig) { gc.Conn = "changed" }},
|
|
}
|
|
|
|
for _, pair := range testValuePairs {
|
|
runApp(
|
|
t,
|
|
func(ctx *cli.Context) error {
|
|
cfg := DatabaseConfig{
|
|
Type: "original",
|
|
Conn: "original",
|
|
}
|
|
|
|
expectedConfig := cfg
|
|
pair.callback(&expectedConfig)
|
|
|
|
mergeDatabaseConfig(ctx, &cfg)
|
|
|
|
assert.Equal(t, expectedConfig, cfg)
|
|
|
|
return nil
|
|
},
|
|
pair.args,
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestMergeACMEConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T) {
|
|
runApp(
|
|
t,
|
|
func(ctx *cli.Context) error {
|
|
cfg := &ACMEConfig{
|
|
Email: "original",
|
|
APIEndpoint: "original",
|
|
AcceptTerms: false,
|
|
UseRateLimits: false,
|
|
EAB_HMAC: "original",
|
|
EAB_KID: "original",
|
|
DNSProvider: "original",
|
|
NoDNS01: false,
|
|
AccountConfigFile: "original",
|
|
}
|
|
|
|
mergeACMEConfig(ctx, cfg)
|
|
|
|
expectedConfig := &ACMEConfig{
|
|
Email: "changed",
|
|
APIEndpoint: "changed",
|
|
AcceptTerms: true,
|
|
UseRateLimits: true,
|
|
EAB_HMAC: "changed",
|
|
EAB_KID: "changed",
|
|
DNSProvider: "changed",
|
|
NoDNS01: true,
|
|
AccountConfigFile: "changed",
|
|
}
|
|
|
|
assert.Equal(t, expectedConfig, cfg)
|
|
|
|
return nil
|
|
},
|
|
[]string{
|
|
"--acme-email", "changed",
|
|
"--acme-api-endpoint", "changed",
|
|
"--acme-accept-terms",
|
|
"--acme-use-rate-limits",
|
|
"--acme-eab-hmac", "changed",
|
|
"--acme-eab-kid", "changed",
|
|
"--dns-provider", "changed",
|
|
"--no-dns-01",
|
|
"--acme-account-config", "changed",
|
|
},
|
|
)
|
|
}
|
|
|
|
func TestMergeACMEConfigShouldReplaceOnlyOneValueExistingValueGivenOnlyOneArgExists(t *testing.T) {
|
|
type testValuePair struct {
|
|
args []string
|
|
callback func(*ACMEConfig)
|
|
}
|
|
testValuePairs := []testValuePair{
|
|
{args: []string{"--acme-email", "changed"}, callback: func(gc *ACMEConfig) { gc.Email = "changed" }},
|
|
{args: []string{"--acme-api-endpoint", "changed"}, callback: func(gc *ACMEConfig) { gc.APIEndpoint = "changed" }},
|
|
{args: []string{"--acme-accept-terms"}, callback: func(gc *ACMEConfig) { gc.AcceptTerms = true }},
|
|
{args: []string{"--acme-use-rate-limits"}, callback: func(gc *ACMEConfig) { gc.UseRateLimits = true }},
|
|
{args: []string{"--acme-eab-hmac", "changed"}, callback: func(gc *ACMEConfig) { gc.EAB_HMAC = "changed" }},
|
|
{args: []string{"--acme-eab-kid", "changed"}, callback: func(gc *ACMEConfig) { gc.EAB_KID = "changed" }},
|
|
{args: []string{"--dns-provider", "changed"}, callback: func(gc *ACMEConfig) { gc.DNSProvider = "changed" }},
|
|
{args: []string{"--no-dns-01"}, callback: func(gc *ACMEConfig) { gc.NoDNS01 = true }},
|
|
{args: []string{"--acme-account-config", "changed"}, callback: func(gc *ACMEConfig) { gc.AccountConfigFile = "changed" }},
|
|
}
|
|
|
|
for _, pair := range testValuePairs {
|
|
runApp(
|
|
t,
|
|
func(ctx *cli.Context) error {
|
|
cfg := ACMEConfig{
|
|
Email: "original",
|
|
APIEndpoint: "original",
|
|
AcceptTerms: false,
|
|
UseRateLimits: false,
|
|
EAB_HMAC: "original",
|
|
EAB_KID: "original",
|
|
DNSProvider: "original",
|
|
AccountConfigFile: "original",
|
|
}
|
|
|
|
expectedConfig := cfg
|
|
pair.callback(&expectedConfig)
|
|
|
|
mergeACMEConfig(ctx, &cfg)
|
|
|
|
assert.Equal(t, expectedConfig, cfg)
|
|
|
|
return nil
|
|
},
|
|
pair.args,
|
|
)
|
|
}
|
|
}
|