mirror of
https://codeberg.org/Codeberg/pages-server.git
synced 2024-11-23 06:18:59 +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>
144 lines
4.4 KiB
Go
144 lines
4.4 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog"
|
|
"github.com/rs/zerolog/log"
|
|
"github.com/urfave/cli/v2"
|
|
|
|
cmd "codeberg.org/codeberg/pages/cli"
|
|
"codeberg.org/codeberg/pages/config"
|
|
"codeberg.org/codeberg/pages/server/acme"
|
|
"codeberg.org/codeberg/pages/server/cache"
|
|
"codeberg.org/codeberg/pages/server/certificates"
|
|
"codeberg.org/codeberg/pages/server/gitea"
|
|
"codeberg.org/codeberg/pages/server/handler"
|
|
)
|
|
|
|
// Serve sets up and starts the web server.
|
|
func Serve(ctx *cli.Context) error {
|
|
// initialize logger with Trace, overridden later with actual level
|
|
log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Logger().Level(zerolog.TraceLevel)
|
|
|
|
cfg, err := config.ReadConfig(ctx)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("could not read config")
|
|
}
|
|
|
|
config.MergeConfig(ctx, cfg)
|
|
|
|
// Initialize the logger.
|
|
logLevel, err := zerolog.ParseLevel(cfg.LogLevel)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Logger().Level(logLevel)
|
|
|
|
foo, _ := json.Marshal(cfg)
|
|
log.Trace().RawJSON("config", foo).Msg("starting server with config")
|
|
|
|
listeningSSLAddress := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)
|
|
listeningHTTPAddress := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.HttpPort)
|
|
|
|
if cfg.Server.RawDomain != "" {
|
|
cfg.Server.AllowedCorsDomains = append(cfg.Server.AllowedCorsDomains, cfg.Server.RawDomain)
|
|
}
|
|
|
|
// Make sure MainDomain has a leading dot
|
|
if !strings.HasPrefix(cfg.Server.MainDomain, ".") {
|
|
// TODO make this better
|
|
cfg.Server.MainDomain = "." + cfg.Server.MainDomain
|
|
}
|
|
|
|
if len(cfg.Server.PagesBranches) == 0 {
|
|
return fmt.Errorf("no default branches set (PAGES_BRANCHES)")
|
|
}
|
|
|
|
// Init ssl cert database
|
|
certDB, closeFn, err := cmd.OpenCertDB(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer closeFn()
|
|
|
|
keyCache := cache.NewInMemoryCache()
|
|
challengeCache := cache.NewInMemoryCache()
|
|
// canonicalDomainCache stores canonical domains
|
|
canonicalDomainCache := cache.NewInMemoryCache()
|
|
// dnsLookupCache stores DNS lookups for custom domains
|
|
dnsLookupCache := cache.NewInMemoryCache()
|
|
// redirectsCache stores redirects in _redirects files
|
|
redirectsCache := cache.NewInMemoryCache()
|
|
// clientResponseCache stores responses from the Gitea server
|
|
clientResponseCache := cache.NewInMemoryCache()
|
|
|
|
giteaClient, err := gitea.NewClient(cfg.Gitea, clientResponseCache)
|
|
if err != nil {
|
|
return fmt.Errorf("could not create new gitea client: %v", err)
|
|
}
|
|
|
|
acmeClient, err := acme.CreateAcmeClient(cfg.ACME, cfg.Server.HttpServerEnabled, challengeCache)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := certificates.SetupMainDomainCertificates(cfg.Server.MainDomain, acmeClient, certDB); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create listener for SSL connections
|
|
log.Info().Msgf("Create TCP listener for SSL on %s", listeningSSLAddress)
|
|
listener, err := net.Listen("tcp", listeningSSLAddress)
|
|
if err != nil {
|
|
return fmt.Errorf("couldn't create listener: %v", err)
|
|
}
|
|
|
|
// Setup listener for SSL connections
|
|
listener = tls.NewListener(listener, certificates.TLSConfig(
|
|
cfg.Server.MainDomain,
|
|
giteaClient,
|
|
acmeClient,
|
|
cfg.Server.PagesBranches[0],
|
|
keyCache, challengeCache, dnsLookupCache, canonicalDomainCache,
|
|
certDB,
|
|
cfg.ACME.NoDNS01,
|
|
cfg.Server.RawDomain,
|
|
))
|
|
|
|
interval := 12 * time.Hour
|
|
certMaintainCtx, cancelCertMaintain := context.WithCancel(context.Background())
|
|
defer cancelCertMaintain()
|
|
go certificates.MaintainCertDB(certMaintainCtx, interval, acmeClient, cfg.Server.MainDomain, certDB)
|
|
|
|
if cfg.Server.HttpServerEnabled {
|
|
// Create handler for http->https redirect and http acme challenges
|
|
httpHandler := certificates.SetupHTTPACMEChallengeServer(challengeCache, uint(cfg.Server.Port))
|
|
|
|
// Create listener for http and start listening
|
|
go func() {
|
|
log.Info().Msgf("Start HTTP server listening on %s", listeningHTTPAddress)
|
|
err := http.ListenAndServe(listeningHTTPAddress, httpHandler)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Couldn't start HTTP server")
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Create ssl handler based on settings
|
|
sslHandler := handler.Handler(cfg.Server, giteaClient, dnsLookupCache, canonicalDomainCache, redirectsCache)
|
|
|
|
// Start the ssl listener
|
|
log.Info().Msgf("Start SSL server using TCP listener on %s", listener.Addr())
|
|
|
|
return http.Serve(listener, sslHandler)
|
|
}
|