Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-pages.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaime Martinez <jmartinez@gitlab.com>2021-03-02 01:52:50 +0300
committerJaime Martinez <jmartinez@gitlab.com>2021-03-02 01:52:50 +0300
commit7c1d5db80c552d2031361a7a8498538b286e952d (patch)
tree48cee9ef470699df0f28df0b9a7ca726887f252f
parent01da18ea5717658eb98f539f921ed02fd35bd3d1 (diff)
parent2e9503fff8dfbc34399264e28a0738eab565fe9d (diff)
Merge branch 'move-auth-settings-to-config-pkg' into 'master'
Move configuration parsing to config package See merge request gitlab-org/gitlab-pages!431
-rw-r--r--app.go80
-rw-r--r--app_config.go74
-rw-r--r--app_test.go12
-rw-r--r--daemon.go24
-rw-r--r--helpers.go9
-rw-r--r--internal/config/config.go394
-rw-r--r--internal/config/config_test.go (renamed from config_test.go)2
-rw-r--r--internal/config/flags.go81
-rw-r--r--internal/config/multi_string_flag.go (renamed from multi_string_flag.go)2
-rw-r--r--internal/config/multi_string_flag_test.go (renamed from multi_string_flag_test.go)2
-rw-r--r--internal/serving/disk/zip/serving_test.go2
-rw-r--r--internal/vfs/zip/archive_test.go12
-rw-r--r--internal/vfs/zip/vfs_test.go12
-rw-r--r--main.go342
14 files changed, 581 insertions, 467 deletions
diff --git a/app.go b/app.go
index 40bfd153..9bfe3fb3 100644
--- a/app.go
+++ b/app.go
@@ -47,7 +47,7 @@ var (
)
type theApp struct {
- appConfig
+ config *cfg.Config
domains *source.Domains
Artifact *artifact.Artifact
Auth *auth.Auth
@@ -117,7 +117,7 @@ func (a *theApp) checkAuthAndServeNotFound(domain *domain.Domain, w http.Respons
func (a *theApp) tryAuxiliaryHandlers(w http.ResponseWriter, r *http.Request, https bool, host string, domain *domain.Domain) bool {
// Add auto redirect
- if !https && a.RedirectHTTP {
+ if !https && a.config.General.RedirectHTTP {
a.redirectToHTTPS(w, r, http.StatusTemporaryRedirect)
return true
}
@@ -173,13 +173,13 @@ func (a *theApp) healthCheckMiddleware(handler http.Handler) (http.Handler, erro
a.healthCheck(w, r, request.IsHTTPS(r))
})
- loggedHealthCheck, err := logging.BasicAccessLogger(healthCheck, a.LogFormat, nil)
+ loggedHealthCheck, err := logging.BasicAccessLogger(healthCheck, a.config.Log.Format, nil)
if err != nil {
return nil, err
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.RequestURI == a.appConfig.StatusPath {
+ if r.RequestURI == a.config.General.StatusPath {
loggedHealthCheck.ServeHTTP(w, r)
return
}
@@ -313,14 +313,14 @@ func setRequestScheme(r *http.Request) *http.Request {
func (a *theApp) buildHandlerPipeline() (http.Handler, error) {
// Handlers should be applied in a reverse order
handler := a.serveFileOrNotFoundHandler()
- if !a.DisableCrossOriginRequests {
+ if !a.config.General.DisableCrossOriginRequests {
handler = corsHandler.Handler(handler)
}
handler = a.accessControlMiddleware(handler)
handler = a.auxiliaryMiddleware(handler)
handler = a.authMiddleware(handler)
handler = a.acmeMiddleware(handler)
- handler, err := logging.AccessLogger(handler, a.LogFormat)
+ handler, err := logging.AccessLogger(handler, a.config.Log.Format)
if err != nil {
return nil, err
}
@@ -342,7 +342,7 @@ func (a *theApp) buildHandlerPipeline() (http.Handler, error) {
// Correlation ID injection middleware
var correlationOpts []correlation.InboundHandlerOption
- if a.appConfig.PropagateCorrelationID {
+ if a.config.General.PropagateCorrelationID {
correlationOpts = append(correlationOpts, correlation.WithPropagation())
}
handler = correlation.InjectCorrelationID(handler, correlationOpts...)
@@ -359,7 +359,7 @@ func (a *theApp) buildHandlerPipeline() (http.Handler, error) {
func (a *theApp) Run() {
var wg sync.WaitGroup
- limiter := netutil.NewLimiter(a.MaxConns)
+ limiter := netutil.NewLimiter(a.config.General.MaxConns)
// Use a common pipeline to use a single instance of each handler,
// instead of making two nearly identical pipelines
@@ -373,31 +373,31 @@ func (a *theApp) Run() {
httpHandler := a.httpInitialMiddleware(commonHandlerPipeline)
// Listen for HTTP
- for _, fd := range a.ListenHTTP {
+ for _, fd := range a.config.Listeners.HTTP {
a.listenHTTPFD(&wg, fd, httpHandler, limiter)
}
// Listen for HTTPS
- for _, fd := range a.ListenHTTPS {
+ for _, fd := range a.config.Listeners.HTTPS {
a.listenHTTPSFD(&wg, fd, httpHandler, limiter)
}
// Listen for HTTP proxy requests
- for _, fd := range a.ListenProxy {
+ for _, fd := range a.config.Listeners.Proxy {
a.listenProxyFD(&wg, fd, proxyHandler, limiter)
}
// Listen for HTTPS PROXYv2 requests
- for _, fd := range a.ListenHTTPSProxyv2 {
+ for _, fd := range a.config.Listeners.HTTPSProxyv2 {
a.ListenHTTPSProxyv2FD(&wg, fd, httpHandler, limiter)
}
// Serve metrics for Prometheus
- if a.ListenMetrics != 0 {
- a.listenMetricsFD(&wg, a.ListenMetrics)
+ if a.config.ListenMetrics != 0 {
+ a.listenMetricsFD(&wg, a.config.ListenMetrics)
}
- a.domains.Read(a.Domain)
+ a.domains.Read(a.config.General.Domain)
wg.Wait()
}
@@ -406,7 +406,7 @@ func (a *theApp) listenHTTPFD(wg *sync.WaitGroup, fd uintptr, httpHandler http.H
wg.Add(1)
go func() {
defer wg.Done()
- err := listenAndServe(fd, httpHandler, a.HTTP2, nil, limiter, false)
+ err := listenAndServe(fd, httpHandler, a.config.General.HTTP2, nil, limiter, false)
if err != nil {
capturingFatal(err, errortracking.WithField("listener", request.SchemeHTTP))
}
@@ -422,7 +422,7 @@ func (a *theApp) listenHTTPSFD(wg *sync.WaitGroup, fd uintptr, httpHandler http.
capturingFatal(err, errortracking.WithField("listener", request.SchemeHTTPS))
}
- err = listenAndServe(fd, httpHandler, a.HTTP2, tlsConfig, limiter, false)
+ err = listenAndServe(fd, httpHandler, a.config.General.HTTP2, tlsConfig, limiter, false)
if err != nil {
capturingFatal(err, errortracking.WithField("listener", request.SchemeHTTPS))
}
@@ -435,7 +435,7 @@ func (a *theApp) listenProxyFD(wg *sync.WaitGroup, fd uintptr, proxyHandler http
wg.Add(1)
go func(fd uintptr) {
defer wg.Done()
- err := listenAndServe(fd, proxyHandler, a.HTTP2, nil, limiter, false)
+ err := listenAndServe(fd, proxyHandler, a.config.General.HTTP2, nil, limiter, false)
if err != nil {
capturingFatal(err, errortracking.WithField("listener", "http proxy"))
}
@@ -453,7 +453,7 @@ func (a *theApp) ListenHTTPSProxyv2FD(wg *sync.WaitGroup, fd uintptr, httpHandle
capturingFatal(err, errortracking.WithField("listener", request.SchemeHTTPS))
}
- err = listenAndServe(fd, httpHandler, a.HTTP2, tlsConfig, limiter, true)
+ err = listenAndServe(fd, httpHandler, a.config.General.HTTP2, tlsConfig, limiter, true)
if err != nil {
capturingFatal(err, errortracking.WithField("listener", request.SchemeHTTPS))
}
@@ -482,33 +482,33 @@ func (a *theApp) listenMetricsFD(wg *sync.WaitGroup, fd uintptr) {
}()
}
-func runApp(config appConfig) {
+func runApp(config *cfg.Config) {
domains, err := source.NewDomains(config)
if err != nil {
log.WithError(err).Fatal("could not create domains config source")
}
- a := theApp{appConfig: config, domains: domains}
+ a := theApp{config: config, domains: domains}
- err = logging.ConfigureLogging(a.LogFormat, a.LogVerbose)
+ err = logging.ConfigureLogging(a.config.Log.Format, a.config.Log.Verbose)
if err != nil {
log.WithError(err).Fatal("Failed to initialize logging")
}
- if config.ArtifactsServer != "" {
- a.Artifact = artifact.New(config.ArtifactsServer, config.ArtifactsServerTimeout, config.Domain)
+ if config.ArtifactsServer.URL != "" {
+ a.Artifact = artifact.New(config.ArtifactsServer.URL, config.ArtifactsServer.TimeoutSeconds, config.General.Domain)
}
a.setAuth(config)
a.Handlers = handlers.New(a.Auth, a.Artifact)
- if config.GitLabServer != "" {
- a.AcmeMiddleware = &acme.Middleware{GitlabURL: config.GitLabServer}
+ if config.GitLab.Server != "" {
+ a.AcmeMiddleware = &acme.Middleware{GitlabURL: config.GitLab.Server}
}
- if len(config.CustomHeaders) != 0 {
- customHeaders, err := middleware.ParseHeaderString(config.CustomHeaders)
+ if len(config.General.CustomHeaders) != 0 {
+ customHeaders, err := middleware.ParseHeaderString(config.General.CustomHeaders)
if err != nil {
log.WithError(err).Fatal("Unable to parse header string")
}
@@ -519,33 +519,23 @@ func runApp(config appConfig) {
log.WithError(err).Warn("Loading extended MIME database failed")
}
- c := &cfg.Config{
- Zip: &cfg.ZipServing{
- ExpirationInterval: config.ZipCacheExpiry,
- CleanupInterval: config.ZipCacheCleanup,
- RefreshInterval: config.ZipCacheRefresh,
- OpenTimeout: config.ZipeOpenTimeout,
- AllowedPaths: []string{config.PagesRoot},
- },
- }
-
// TODO: reconfigure all VFS'
// https://gitlab.com/gitlab-org/gitlab-pages/-/issues/512
- if err := zip.Instance().Reconfigure(c); err != nil {
+ if err := zip.Instance().Reconfigure(config); err != nil {
fatal(err, "failed to reconfigure zip VFS")
}
a.Run()
}
-func (a *theApp) setAuth(config appConfig) {
- if config.ClientID == "" {
+func (a *theApp) setAuth(config *cfg.Config) {
+ if config.Authentication.ClientID == "" {
return
}
var err error
- a.Auth, err = auth.New(config.Domain, config.StoreSecret, config.ClientID, config.ClientSecret,
- config.RedirectURI, config.GitLabServer, config.AuthScope)
+ a.Auth, err = auth.New(config.General.Domain, config.Authentication.Secret, config.Authentication.ClientID, config.Authentication.ClientSecret,
+ config.Authentication.RedirectURI, config.GitLab.Server, config.Authentication.Scope)
if err != nil {
log.WithError(err).Fatal("could not initialize auth package")
}
@@ -557,6 +547,6 @@ func fatal(err error, message string) {
}
func (a *theApp) TLSConfig() (*tls.Config, error) {
- return tlsconfig.Create(a.RootCertificate, a.RootKey, a.ServeTLS,
- a.InsecureCiphers, a.TLSMinVersion, a.TLSMaxVersion)
+ return tlsconfig.Create(a.config.General.RootCertificate, a.config.General.RootKey, a.ServeTLS,
+ a.config.General.InsecureCiphers, a.config.TLS.MinVersion, a.config.TLS.MaxVersion)
}
diff --git a/app_config.go b/app_config.go
deleted file mode 100644
index f3048915..00000000
--- a/app_config.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package main
-
-import "time"
-
-type appConfig struct {
- PagesRoot string
- Domain string
- ArtifactsServer string
- ArtifactsServerTimeout int
- RootCertificate []byte
- RootKey []byte
- MaxConns int
-
- ListenHTTP []uintptr
- ListenHTTPS []uintptr
- ListenProxy []uintptr
- ListenHTTPSProxyv2 []uintptr
- ListenMetrics uintptr
- InsecureCiphers bool
- TLSMinVersion uint16
- TLSMaxVersion uint16
-
- HTTP2 bool
- RedirectHTTP bool
- StatusPath string
-
- DisableCrossOriginRequests bool
- PropagateCorrelationID bool
-
- LogFormat string
- LogVerbose bool
-
- StoreSecret string
- GitLabServer string
- InternalGitLabServer string
- GitLabAPISecretKey []byte
- GitlabClientHTTPTimeout time.Duration
- GitlabJWTTokenExpiration time.Duration
- DomainConfigurationSource string
- ClientID string
- ClientSecret string
- RedirectURI string
- AuthScope string
- SentryDSN string
- SentryEnvironment string
- CustomHeaders []string
-
- ZipCacheExpiry time.Duration
- ZipCacheRefresh time.Duration
- ZipCacheCleanup time.Duration
- ZipeOpenTimeout time.Duration
-}
-
-// InternalGitLabServerURL returns URL to a GitLab instance.
-func (config appConfig) InternalGitLabServerURL() string {
- return config.InternalGitLabServer
-}
-
-// GitlabClientSecret returns GitLab server access token.
-func (config appConfig) GitlabAPISecret() []byte {
- return config.GitLabAPISecretKey
-}
-
-func (config appConfig) GitlabClientConnectionTimeout() time.Duration {
- return config.GitlabClientHTTPTimeout
-}
-
-func (config appConfig) GitlabJWTTokenExpiry() time.Duration {
- return config.GitlabJWTTokenExpiration
-}
-
-func (config appConfig) DomainConfigSource() string {
- return config.DomainConfigurationSource
-}
diff --git a/app_test.go b/app_test.go
index f35e90c5..b7e9a01c 100644
--- a/app_test.go
+++ b/app_test.go
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
+ "gitlab.com/gitlab-org/gitlab-pages/internal/config"
"gitlab.com/gitlab-org/gitlab-pages/internal/request"
"gitlab.com/gitlab-org/gitlab-pages/internal/source"
)
@@ -83,14 +84,15 @@ func TestHealthCheckMiddleware(t *testing.T) {
},
}
+ cfg := config.LoadConfig()
+ cfg.General.StatusPath = "/-/healthcheck"
+ cfg.General.DomainConfigurationSource = "auto"
+
app := theApp{
- appConfig: appConfig{
- StatusPath: "/-/healthcheck",
- DomainConfigurationSource: "auto",
- },
+ config: cfg,
}
- domains, err := source.NewDomains(app.appConfig)
+ domains, err := source.NewDomains(app.config)
require.NoError(t, err)
app.domains = domains
diff --git a/daemon.go b/daemon.go
index e73a0e95..c2a995d8 100644
--- a/daemon.go
+++ b/daemon.go
@@ -15,6 +15,7 @@ import (
"github.com/kardianos/osext"
log "github.com/sirupsen/logrus"
+ "gitlab.com/gitlab-org/gitlab-pages/internal/config"
"gitlab.com/gitlab-org/gitlab-pages/internal/jail"
)
@@ -43,11 +44,11 @@ func daemonMain() {
}).Info("starting the daemon as unprivileged user")
// read the configuration from the pipe "ExtraFiles"
- var config appConfig
+ var config config.Config
if err := json.NewDecoder(os.NewFile(3, "options")).Decode(&config); err != nil {
fatal(err, "could not decode app config")
}
- runApp(config)
+ runApp(&config)
os.Exit(0)
}
@@ -266,7 +267,12 @@ func jailDaemon(pagesRoot string, cmd *exec.Cmd) (*jail.Jail, error) {
return cage, nil
}
-func daemonize(config appConfig, uid, gid uint, inPlace bool, pagesRoot string) error {
+func daemonize(config *config.Config) error {
+ uid := config.Daemon.UID
+ gid := config.Daemon.GID
+ inPlace := config.Daemon.InplaceChroot
+ pagesRoot := config.General.RootDir
+
// Ensure pagesRoot is an absolute path. This will produce a different path
// if any component of pagesRoot is a symlink (not likely). For example,
// -pages-root=/some-path where ln -s /other-path /some-path
@@ -323,7 +329,7 @@ func daemonize(config appConfig, uid, gid uint, inPlace bool, pagesRoot string)
defer configWriter.Close()
cmd.ExtraFiles = append(cmd.ExtraFiles, configReader)
- updateFds(&config, cmd)
+ updateFds(config, cmd)
// Start the process
if err := cmd.Start(); err != nil {
@@ -344,12 +350,12 @@ func daemonize(config appConfig, uid, gid uint, inPlace bool, pagesRoot string)
return cmd.Wait()
}
-func updateFds(config *appConfig, cmd *exec.Cmd) {
+func updateFds(config *config.Config, cmd *exec.Cmd) {
for _, fds := range [][]uintptr{
- config.ListenHTTP,
- config.ListenHTTPS,
- config.ListenProxy,
- config.ListenHTTPSProxyv2,
+ config.Listeners.HTTP,
+ config.Listeners.HTTPS,
+ config.Listeners.Proxy,
+ config.Listeners.HTTPSProxyv2,
} {
daemonUpdateFds(cmd, fds)
}
diff --git a/helpers.go b/helpers.go
index d71f2b2c..17b464d0 100644
--- a/helpers.go
+++ b/helpers.go
@@ -1,21 +1,12 @@
package main
import (
- "io/ioutil"
"net"
"os"
"gitlab.com/gitlab-org/labkit/errortracking"
)
-func readFile(file string) (result []byte) {
- result, err := ioutil.ReadFile(file)
- if err != nil {
- fatal(err, "could not read file")
- }
- return
-}
-
// Be careful: if you let either of the return values get garbage
// collected by Go they will be closed automatically.
func createSocket(addr string) (net.Listener, *os.File) {
diff --git a/internal/config/config.go b/internal/config/config.go
index e2956bb6..7e44abbc 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -1,15 +1,133 @@
package config
import (
+ "encoding/base64"
+ "fmt"
+ "io/ioutil"
+ "net/url"
+ "strings"
"time"
+
+ "github.com/namsral/flag"
+ log "github.com/sirupsen/logrus"
+
+ "gitlab.com/gitlab-org/gitlab-pages/internal/tlsconfig"
)
+// Config stores all the config options relevant to GitLab Pages.
type Config struct {
- Zip *ZipServing
+ General General
+ ArtifactsServer ArtifactsServer
+ Authentication Auth
+ Daemon Daemon
+ GitLab GitLab
+ Listeners Listeners
+ Log Log
+ Sentry Sentry
+ TLS TLS
+ Zip ZipServing
+
+ // Fields used to share information between files. These are not directly
+ // set by command line flags, but rather populated based on info from them.
+ // ListenMetrics points to a file descriptor of a socket, whose address is
+ // specified by `Config.General.MetricsAddress`.
+ ListenMetrics uintptr
+
+ // These fields contain the raw strings passed for listen-http,
+ // listen-https, listen-proxy and listen-https-proxyv2 settings. It is used
+ // by appmain() to create listeners, and the pointers to these listeners
+ // gets assigned to Config.Listeners.* fields
+ ListenHTTPStrings MultiStringFlag
+ ListenHTTPSStrings MultiStringFlag
+ ListenProxyStrings MultiStringFlag
+ ListenHTTPSProxyv2Strings MultiStringFlag
+}
+
+// General groups settings that are general to GitLab Pages and can not
+// be categorized under other head.
+type General struct {
+ Domain string
+ DomainConfigurationSource string
+ HTTP2 bool
+ MaxConns int
+ MetricsAddress string
+ RedirectHTTP bool
+ RootCertificate []byte
+ RootDir string
+ RootKey []byte
+ StatusPath string
+
+ DisableCrossOriginRequests bool
+ InsecureCiphers bool
+ PropagateCorrelationID bool
+
+ ShowVersion bool
+
+ CustomHeaders []string
+}
+
+// ArtifactsServer groups settings related to configuring Artifacts
+// server
+type ArtifactsServer struct {
+ URL string
+ TimeoutSeconds int
+}
+
+// Auth groups settings related to configuring Authentication with
+// GitLab
+type Auth struct {
+ Secret string
+ ClientID string
+ ClientSecret string
+ RedirectURI string
+ Scope string
+}
+
+// Daemon groups settings related to configuring GitLab Pages daemon
+type Daemon struct {
+ UID uint
+ GID uint
+ InplaceChroot bool
+}
+
+// GitLab groups settings related to configuring GitLab client used to
+// interact with GitLab API
+type GitLab struct {
+ Server string
+ InternalServer string
+ APISecretKey []byte
+ ClientHTTPTimeout time.Duration
+ JWTTokenExpiration time.Duration
+}
+
+// Listeners groups settings related to configuring various listeners
+// (HTTP, HTTPS, Proxy, HTTPSProxyv2)
+type Listeners struct {
+ HTTP []uintptr
+ HTTPS []uintptr
+ Proxy []uintptr
+ HTTPSProxyv2 []uintptr
}
-// ZipServing stores all configuration values to be used by the zip VFS opening and
-// caching
+// Log groups settings related to configuring logging
+type Log struct {
+ Format string
+ Verbose bool
+}
+
+// Sentry groups settings related to configuring Sentry
+type Sentry struct {
+ DSN string
+ Environment string
+}
+
+// TLS groups settings related to configuring TLS
+type TLS struct {
+ MinVersion uint16
+ MaxVersion uint16
+}
+
+// ZipServing groups settings to be used by the zip VFS opening and caching
type ZipServing struct {
ExpirationInterval time.Duration
CleanupInterval time.Duration
@@ -17,3 +135,273 @@ type ZipServing struct {
OpenTimeout time.Duration
AllowedPaths []string
}
+
+func gitlabServerFromFlags() string {
+ if *gitLabServer != "" {
+ return *gitLabServer
+ }
+
+ if *gitLabAuthServer != "" {
+ log.Warn("auth-server parameter is deprecated, use gitlab-server instead")
+ return *gitLabAuthServer
+ }
+
+ u, err := url.Parse(*artifactsServer)
+ if err != nil {
+ return ""
+ }
+
+ u.Path = ""
+ return u.String()
+}
+
+func internalGitlabServerFromFlags() string {
+ if *internalGitLabServer != "" {
+ return *internalGitLabServer
+ }
+
+ return gitlabServerFromFlags()
+}
+
+func setGitLabAPISecretKey(secretFile string, config *Config) {
+ encoded := readFile(secretFile)
+
+ decoded := make([]byte, base64.StdEncoding.DecodedLen(len(encoded)))
+ secretLength, err := base64.StdEncoding.Decode(decoded, encoded)
+ if err != nil {
+ log.WithError(err).Fatal("Failed to decode GitLab API secret")
+ }
+
+ if secretLength != 32 {
+ log.WithError(fmt.Errorf("expected 32 bytes GitLab API secret but got %d bytes", secretLength)).Fatal("Failed to decode GitLab API secret")
+ }
+
+ config.GitLab.APISecretKey = decoded
+}
+
+func checkAuthenticationConfig(config *Config) {
+ if config.Authentication.Secret == "" && config.Authentication.ClientID == "" &&
+ config.Authentication.ClientSecret == "" && config.Authentication.RedirectURI == "" {
+ return
+ }
+ assertAuthConfig(config)
+}
+
+func assertAuthConfig(config *Config) {
+ if config.Authentication.Secret == "" {
+ log.Fatal("auth-secret must be defined if authentication is supported")
+ }
+ if config.Authentication.ClientID == "" {
+ log.Fatal("auth-client-id must be defined if authentication is supported")
+ }
+ if config.Authentication.ClientSecret == "" {
+ log.Fatal("auth-client-secret must be defined if authentication is supported")
+ }
+ if config.GitLab.Server == "" {
+ log.Fatal("gitlab-server must be defined if authentication is supported")
+ }
+ if config.Authentication.RedirectURI == "" {
+ log.Fatal("auth-redirect-uri must be defined if authentication is supported")
+ }
+}
+
+func validateArtifactsServer(artifactsServer string, artifactsServerTimeoutSeconds int) {
+ u, err := url.Parse(artifactsServer)
+ if err != nil {
+ log.Fatal(err)
+ }
+ // url.Parse ensures that the Scheme attribute is always lower case.
+ if u.Scheme != "http" && u.Scheme != "https" {
+ log.Fatal("artifacts-server scheme must be either http:// or https://")
+ }
+
+ if artifactsServerTimeoutSeconds < 1 {
+ log.Fatal("artifacts-server-timeout must be greater than or equal to 1")
+ }
+}
+
+// fatal will log a fatal error and exit.
+func fatal(err error, message string) {
+ log.WithError(err).Fatal(message)
+}
+
+func readFile(file string) (result []byte) {
+ result, err := ioutil.ReadFile(file)
+ if err != nil {
+ fatal(err, "could not read file")
+ }
+ return
+}
+
+// InternalGitLabServerURL returns URL to a GitLab instance.
+func (config Config) InternalGitLabServerURL() string {
+ return config.GitLab.InternalServer
+}
+
+// GitlabClientSecret returns GitLab server access token.
+func (config Config) GitlabAPISecret() []byte {
+ return config.GitLab.APISecretKey
+}
+
+func (config Config) GitlabClientConnectionTimeout() time.Duration {
+ return config.GitLab.ClientHTTPTimeout
+}
+
+func (config Config) GitlabJWTTokenExpiry() time.Duration {
+ return config.GitLab.JWTTokenExpiration
+}
+
+func (config Config) DomainConfigSource() string {
+ return config.General.DomainConfigurationSource
+}
+
+func loadConfig() *Config {
+ config := &Config{
+ General: General{
+ Domain: strings.ToLower(*pagesDomain),
+ DomainConfigurationSource: *domainConfigSource,
+ HTTP2: *useHTTP2,
+ MaxConns: *maxConns,
+ MetricsAddress: *metricsAddress,
+ RedirectHTTP: *redirectHTTP,
+ RootDir: *pagesRoot,
+ StatusPath: *pagesStatus,
+ DisableCrossOriginRequests: *disableCrossOriginRequests,
+ InsecureCiphers: *insecureCiphers,
+ PropagateCorrelationID: *propagateCorrelationID,
+ CustomHeaders: header.Split(),
+ ShowVersion: *showVersion,
+ },
+ GitLab: GitLab{
+ ClientHTTPTimeout: *gitlabClientHTTPTimeout,
+ JWTTokenExpiration: *gitlabClientJWTExpiry,
+ },
+ ArtifactsServer: ArtifactsServer{
+ TimeoutSeconds: *artifactsServerTimeout,
+ URL: *artifactsServer,
+ },
+ Authentication: Auth{
+ Secret: *secret,
+ ClientID: *clientID,
+ ClientSecret: *clientSecret,
+ RedirectURI: *redirectURI,
+ Scope: *authScope,
+ },
+ Daemon: Daemon{
+ UID: *daemonUID,
+ GID: *daemonGID,
+ InplaceChroot: *daemonInplaceChroot,
+ },
+ Log: Log{
+ Format: *logFormat,
+ Verbose: *logVerbose,
+ },
+ Sentry: Sentry{
+ DSN: *sentryDSN,
+ Environment: *sentryEnvironment,
+ },
+ TLS: TLS{
+ MinVersion: tlsconfig.AllTLSVersions[*tlsMinVersion],
+ MaxVersion: tlsconfig.AllTLSVersions[*tlsMaxVersion],
+ },
+ Zip: ZipServing{
+ ExpirationInterval: *zipCacheExpiration,
+ CleanupInterval: *zipCacheCleanup,
+ RefreshInterval: *zipCacheRefresh,
+ OpenTimeout: *zipOpenTimeout,
+ AllowedPaths: []string{*pagesRoot},
+ },
+
+ // Actual listener pointers will be populated in appMain. We populate the
+ // raw strings here so that they are available in appMain
+ ListenHTTPStrings: listenHTTP,
+ ListenHTTPSStrings: listenHTTPS,
+ ListenProxyStrings: listenProxy,
+ ListenHTTPSProxyv2Strings: listenHTTPSProxyv2,
+ Listeners: Listeners{},
+ }
+
+ // Populating remaining General settings
+ for _, file := range []struct {
+ contents *[]byte
+ path string
+ }{
+ {&config.General.RootCertificate, *pagesRootCert},
+ {&config.General.RootKey, *pagesRootKey},
+ } {
+ if file.path != "" {
+ *file.contents = readFile(file.path)
+ }
+ }
+
+ // Populating remaining GitLab settings
+ config.GitLab.Server = gitlabServerFromFlags()
+ config.GitLab.InternalServer = internalGitlabServerFromFlags()
+ if *gitLabAPISecretKey != "" {
+ setGitLabAPISecretKey(*gitLabAPISecretKey, config)
+ }
+
+ // Validating Artifacts server settings
+ if *artifactsServer != "" {
+ validateArtifactsServer(*artifactsServer, *artifactsServerTimeout)
+ }
+
+ // Validating Authentication settings
+ checkAuthenticationConfig(config)
+
+ // Validating TLS settings
+ if err := tlsconfig.ValidateTLSVersions(*tlsMinVersion, *tlsMaxVersion); err != nil {
+ fatal(err, "invalid TLS version")
+ }
+
+ return config
+}
+
+func LogConfig(config *Config) {
+ log.WithFields(log.Fields{
+ "artifacts-server": *artifactsServer,
+ "artifacts-server-timeout": *artifactsServerTimeout,
+ "daemon-gid": *daemonGID,
+ "daemon-uid": *daemonUID,
+ "daemon-inplace-chroot": *daemonInplaceChroot,
+ "default-config-filename": flag.DefaultConfigFlagname,
+ "disable-cross-origin-requests": *disableCrossOriginRequests,
+ "domain": config.General.Domain,
+ "insecure-ciphers": config.General.InsecureCiphers,
+ "listen-http": listenHTTP,
+ "listen-https": listenHTTPS,
+ "listen-proxy": listenProxy,
+ "listen-https-proxyv2": listenHTTPSProxyv2,
+ "log-format": *logFormat,
+ "metrics-address": *metricsAddress,
+ "pages-domain": *pagesDomain,
+ "pages-root": *pagesRoot,
+ "pages-status": *pagesStatus,
+ "propagate-correlation-id": *propagateCorrelationID,
+ "redirect-http": config.General.RedirectHTTP,
+ "root-cert": *pagesRootKey,
+ "root-key": *pagesRootCert,
+ "status_path": config.General.StatusPath,
+ "tls-min-version": *tlsMinVersion,
+ "tls-max-version": *tlsMaxVersion,
+ "use-http-2": config.General.HTTP2,
+ "gitlab-server": config.GitLab.Server,
+ "internal-gitlab-server": config.GitLab.InternalServer,
+ "api-secret-key": *gitLabAPISecretKey,
+ "domain-config-source": config.General.DomainConfigurationSource,
+ "auth-redirect-uri": config.Authentication.RedirectURI,
+ "auth-scope": config.Authentication.Scope,
+ "zip-cache-expiration": config.Zip.ExpirationInterval,
+ "zip-cache-cleanup": config.Zip.CleanupInterval,
+ "zip-cache-refresh": config.Zip.RefreshInterval,
+ "zip-open-timeout": config.Zip.OpenTimeout,
+ }).Debug("Start daemon with configuration")
+}
+
+// LoadConfig parses configuration settings passed as command line arguments or
+// via config file, and populates a Config object with those values
+func LoadConfig() *Config {
+ initFlags()
+
+ return loadConfig()
+}
diff --git a/config_test.go b/internal/config/config_test.go
index dc200d1c..7fa2b253 100644
--- a/config_test.go
+++ b/internal/config/config_test.go
@@ -1,4 +1,4 @@
-package main
+package config
import (
"testing"
diff --git a/internal/config/flags.go b/internal/config/flags.go
new file mode 100644
index 00000000..d2cee32d
--- /dev/null
+++ b/internal/config/flags.go
@@ -0,0 +1,81 @@
+package config
+
+import (
+ "time"
+
+ "github.com/namsral/flag"
+
+ "gitlab.com/gitlab-org/gitlab-pages/internal/tlsconfig"
+)
+
+var (
+ pagesRootCert = flag.String("root-cert", "", "The default path to file certificate to serve static pages")
+ pagesRootKey = flag.String("root-key", "", "The default path to file certificate to serve static pages")
+ redirectHTTP = flag.Bool("redirect-http", false, "Redirect pages from HTTP to HTTPS")
+ useHTTP2 = flag.Bool("use-http2", true, "Enable HTTP2 support")
+ pagesRoot = flag.String("pages-root", "shared/pages", "The directory where pages are stored")
+ pagesDomain = flag.String("pages-domain", "gitlab-example.com", "The domain to serve static pages")
+ artifactsServer = flag.String("artifacts-server", "", "API URL to proxy artifact requests to, e.g.: 'https://gitlab.com/api/v4'")
+ artifactsServerTimeout = flag.Int("artifacts-server-timeout", 10, "Timeout (in seconds) for a proxied request to the artifacts server")
+ pagesStatus = flag.String("pages-status", "", "The url path for a status page, e.g., /@status")
+ metricsAddress = flag.String("metrics-address", "", "The address to listen on for metrics requests")
+ sentryDSN = flag.String("sentry-dsn", "", "The address for sending sentry crash reporting to")
+ sentryEnvironment = flag.String("sentry-environment", "", "The environment for sentry crash reporting")
+ daemonUID = flag.Uint("daemon-uid", 0, "Drop privileges to this user")
+ daemonGID = flag.Uint("daemon-gid", 0, "Drop privileges to this group")
+ daemonInplaceChroot = flag.Bool("daemon-inplace-chroot", false, "Fall back to a non-bind-mount chroot of -pages-root when daemonizing")
+ propagateCorrelationID = flag.Bool("propagate-correlation-id", false, "Reuse existing Correlation-ID from the incoming request header `X-Request-ID` if present")
+ logFormat = flag.String("log-format", "text", "The log output format: 'text' or 'json'")
+ logVerbose = flag.Bool("log-verbose", false, "Verbose logging")
+ _ = flag.String("admin-secret-path", "", "DEPRECATED")
+ _ = flag.String("admin-unix-listener", "", "DEPRECATED")
+ _ = flag.String("admin-https-listener", "", "DEPRECATED")
+ _ = flag.String("admin-https-cert", "", "DEPRECATED")
+ _ = flag.String("admin-https-key", "", "DEPRECATED")
+ secret = flag.String("auth-secret", "", "Cookie store hash key, should be at least 32 bytes long")
+ gitLabAuthServer = flag.String("auth-server", "", "DEPRECATED, use gitlab-server instead. GitLab server, for example https://www.gitlab.com")
+ gitLabServer = flag.String("gitlab-server", "", "GitLab server, for example https://www.gitlab.com")
+ internalGitLabServer = flag.String("internal-gitlab-server", "", "Internal GitLab server used for API requests, useful if you want to send that traffic over an internal load balancer, example value https://www.gitlab.com (defaults to value of gitlab-server)")
+ gitLabAPISecretKey = flag.String("api-secret-key", "", "File with secret key used to authenticate with the GitLab API")
+ gitlabClientHTTPTimeout = flag.Duration("gitlab-client-http-timeout", 10*time.Second, "GitLab API HTTP client connection timeout in seconds (default: 10s)")
+ gitlabClientJWTExpiry = flag.Duration("gitlab-client-jwt-expiry", 30*time.Second, "JWT Token expiry time in seconds (default: 30s)")
+ domainConfigSource = flag.String("domain-config-source", "auto", "Domain configuration source 'disk', 'auto' or 'gitlab' (default: 'auto'). DEPRECATED: gitlab-pages will use the API-based configuration starting from 14.0 see https://gitlab.com/gitlab-org/gitlab-pages/-/issues/382")
+ clientID = flag.String("auth-client-id", "", "GitLab application Client ID")
+ clientSecret = flag.String("auth-client-secret", "", "GitLab application Client Secret")
+ redirectURI = flag.String("auth-redirect-uri", "", "GitLab application redirect URI")
+ authScope = flag.String("auth-scope", "api", "Scope to be used for authentication (must match GitLab Pages OAuth application settings)")
+ maxConns = flag.Int("max-conns", 5000, "Limit on the number of concurrent connections to the HTTP, HTTPS or proxy listeners")
+ insecureCiphers = flag.Bool("insecure-ciphers", false, "Use default list of cipher suites, may contain insecure ones like 3DES and RC4")
+ tlsMinVersion = flag.String("tls-min-version", "tls1.2", tlsconfig.FlagUsage("min"))
+ tlsMaxVersion = flag.String("tls-max-version", "", tlsconfig.FlagUsage("max"))
+ zipCacheExpiration = flag.Duration("zip-cache-expiration", 60*time.Second, "Zip serving archive cache expiration interval")
+ zipCacheCleanup = flag.Duration("zip-cache-cleanup", 30*time.Second, "Zip serving archive cache cleanup interval")
+ zipCacheRefresh = flag.Duration("zip-cache-refresh", 30*time.Second, "Zip serving archive cache refresh interval")
+ zipOpenTimeout = flag.Duration("zip-open-timeout", 30*time.Second, "Zip archive open timeout")
+
+ disableCrossOriginRequests = flag.Bool("disable-cross-origin-requests", false, "Disable cross-origin requests")
+
+ showVersion = flag.Bool("version", false, "Show version")
+
+ // See initFlags()
+ listenHTTP = MultiStringFlag{separator: ","}
+ listenHTTPS = MultiStringFlag{separator: ","}
+ listenProxy = MultiStringFlag{separator: ","}
+ listenHTTPSProxyv2 = MultiStringFlag{separator: ","}
+
+ header = MultiStringFlag{separator: ";;"}
+)
+
+// initFlags will be called from LoadConfig
+func initFlags() {
+ flag.Var(&listenHTTP, "listen-http", "The address(es) to listen on for HTTP requests")
+ flag.Var(&listenHTTPS, "listen-https", "The address(es) to listen on for HTTPS requests")
+ flag.Var(&listenProxy, "listen-proxy", "The address(es) to listen on for proxy requests")
+ flag.Var(&listenHTTPSProxyv2, "listen-https-proxyv2", "The address(es) to listen on for HTTPS PROXYv2 requests (https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)")
+ flag.Var(&header, "header", "The additional http header(s) that should be send to the client")
+
+ // read from -config=/path/to/gitlab-pages-config
+ flag.String(flag.DefaultConfigFlagname, "", "path to config file")
+
+ flag.Parse()
+}
diff --git a/multi_string_flag.go b/internal/config/multi_string_flag.go
index 1be02ef1..fc63299b 100644
--- a/multi_string_flag.go
+++ b/internal/config/multi_string_flag.go
@@ -1,4 +1,4 @@
-package main
+package config
import (
"errors"
diff --git a/multi_string_flag_test.go b/internal/config/multi_string_flag_test.go
index 9c9c7d48..a79247a9 100644
--- a/multi_string_flag_test.go
+++ b/internal/config/multi_string_flag_test.go
@@ -1,4 +1,4 @@
-package main
+package config
import (
"strings"
diff --git a/internal/serving/disk/zip/serving_test.go b/internal/serving/disk/zip/serving_test.go
index 249dae1f..2cbee18d 100644
--- a/internal/serving/disk/zip/serving_test.go
+++ b/internal/serving/disk/zip/serving_test.go
@@ -86,7 +86,7 @@ func TestZip_ServeFileHTTP(t *testing.T) {
}
cfg := &config.Config{
- Zip: &config.ZipServing{
+ Zip: config.ZipServing{
ExpirationInterval: 10 * time.Second,
CleanupInterval: 5 * time.Second,
RefreshInterval: 5 * time.Second,
diff --git a/internal/vfs/zip/archive_test.go b/internal/vfs/zip/archive_test.go
index 421990db..23fe0583 100644
--- a/internal/vfs/zip/archive_test.go
+++ b/internal/vfs/zip/archive_test.go
@@ -24,7 +24,7 @@ import (
var (
chdirSet = false
- zipCfg = &config.ZipServing{
+ zipCfg = config.ZipServing{
ExpirationInterval: 10 * time.Second,
CleanupInterval: 5 * time.Second,
RefreshInterval: 5 * time.Second,
@@ -91,7 +91,7 @@ func TestOpenCached(t *testing.T) {
testServerURL, cleanup := newZipFileServerURL(t, "group/zip.gitlab.io/public-without-dirs.zip", &requests)
defer cleanup()
- fs := New(zipCfg)
+ fs := New(&zipCfg)
// We use array instead of map to ensure
// predictable ordering of test execution
@@ -342,7 +342,7 @@ func TestArchiveCanBeReadAfterOpenCtxCanceled(t *testing.T) {
testServerURL, cleanup := newZipFileServerURL(t, "group/zip.gitlab.io/public.zip", nil)
defer cleanup()
- fs := New(zipCfg).(*zipVFS)
+ fs := New(&zipCfg).(*zipVFS)
zip := newArchive(fs, time.Second)
ctx, cancel := context.WithCancel(context.Background())
cancel()
@@ -365,7 +365,7 @@ func TestReadArchiveFails(t *testing.T) {
testServerURL, cleanup := newZipFileServerURL(t, "group/zip.gitlab.io/public.zip", nil)
defer cleanup()
- fs := New(zipCfg).(*zipVFS)
+ fs := New(&zipCfg).(*zipVFS)
zip := newArchive(fs, time.Second)
err := zip.openArchive(context.Background(), testServerURL+"/unkown.html")
@@ -390,7 +390,7 @@ func openZipArchive(t *testing.T, requests *int64, fromDisk bool) (*zipArchive,
zipCfg.AllowedPaths = []string{wd}
- fs := New(zipCfg).(*zipVFS)
+ fs := New(&zipCfg).(*zipVFS)
err = fs.Reconfigure(&config.Config{Zip: zipCfg})
require.NoError(t, err)
@@ -466,7 +466,7 @@ func benchmarkArchiveRead(b *testing.B, size int64) {
ts := httptest.NewServer(m)
defer ts.Close()
- fs := New(zipCfg).(*zipVFS)
+ fs := New(&zipCfg).(*zipVFS)
b.ReportAllocs()
b.ResetTimer()
diff --git a/internal/vfs/zip/vfs_test.go b/internal/vfs/zip/vfs_test.go
index 5d7015f5..22cc5c6b 100644
--- a/internal/vfs/zip/vfs_test.go
+++ b/internal/vfs/zip/vfs_test.go
@@ -38,7 +38,7 @@ func TestVFSRoot(t *testing.T) {
},
}
- vfs := New(zipCfg)
+ vfs := New(&zipCfg)
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
@@ -76,7 +76,7 @@ func TestVFSFindOrOpenArchiveConcurrentAccess(t *testing.T) {
path := testServerURL + "/public.zip"
- vfs := New(zipCfg).(*zipVFS)
+ vfs := New(&zipCfg).(*zipVFS)
root, err := vfs.Root(context.Background(), path)
require.NoError(t, err)
@@ -161,7 +161,7 @@ func TestVFSFindOrOpenArchiveRefresh(t *testing.T) {
for name, test := range tests {
t.Run(name, func(t *testing.T) {
withExpectedArchiveCount(t, 1, func(t *testing.T) {
- cfg := *zipCfg
+ cfg := *&zipCfg
cfg.ExpirationInterval = test.expirationInterval
cfg.RefreshInterval = test.refreshInterval
@@ -223,7 +223,7 @@ func TestVFSReconfigureTransport(t *testing.T) {
fileURL := testhelpers.ToFileProtocol(t, "group/zip.gitlab.io/public.zip")
- vfs := New(zipCfg)
+ vfs := New(&zipCfg)
// try to open a file URL without registering the file protocol
_, err := vfs.Root(context.Background(), fileURL)
@@ -231,10 +231,10 @@ func TestVFSReconfigureTransport(t *testing.T) {
require.Contains(t, err.Error(), "unsupported protocol scheme \"file\"")
// reconfigure VFS with allowed paths and try to open file://
- cfg := *zipCfg
+ cfg := zipCfg
cfg.AllowedPaths = []string{testhelpers.Getwd(t)}
- err = vfs.Reconfigure(&config.Config{Zip: &cfg})
+ err = vfs.Reconfigure(&config.Config{Zip: cfg})
require.NoError(t, err)
root, err := vfs.Root(context.Background(), fileURL)
diff --git a/main.go b/main.go
index 6f758244..441b3a74 100644
--- a/main.go
+++ b/main.go
@@ -1,23 +1,18 @@
package main
import (
- "encoding/base64"
"fmt"
"io"
"math/rand"
- "net/url"
"os"
- "strings"
"time"
- "github.com/namsral/flag"
log "github.com/sirupsen/logrus"
"gitlab.com/gitlab-org/labkit/errortracking"
+ cfg "gitlab.com/gitlab-org/gitlab-pages/internal/config"
"gitlab.com/gitlab-org/gitlab-pages/internal/logging"
- "gitlab.com/gitlab-org/gitlab-pages/internal/request"
- "gitlab.com/gitlab-org/gitlab-pages/internal/tlsconfig"
"gitlab.com/gitlab-org/gitlab-pages/internal/validateargs"
"gitlab.com/gitlab-org/gitlab-pages/metrics"
)
@@ -28,229 +23,6 @@ var VERSION = "dev"
// REVISION stores the information about the git revision of application
var REVISION = "HEAD"
-func init() {
- // TODO: move all flags to config pkg https://gitlab.com/gitlab-org/gitlab-pages/-/issues/507
- flag.Var(&listenHTTP, "listen-http", "The address(es) to listen on for HTTP requests")
- flag.Var(&listenHTTPS, "listen-https", "The address(es) to listen on for HTTPS requests")
- flag.Var(&listenProxy, "listen-proxy", "The address(es) to listen on for proxy requests")
- flag.Var(&listenHTTPSProxyv2, "listen-https-proxyv2", "The address(es) to listen on for HTTPS PROXYv2 requests (https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)")
- flag.Var(&header, "header", "The additional http header(s) that should be send to the client")
-}
-
-var (
- // TODO: move all flags to config pkg https://gitlab.com/gitlab-org/gitlab-pages/-/issues/507
- pagesRootCert = flag.String("root-cert", "", "The default path to file certificate to serve static pages")
- pagesRootKey = flag.String("root-key", "", "The default path to file certificate to serve static pages")
- redirectHTTP = flag.Bool("redirect-http", false, "Redirect pages from HTTP to HTTPS")
- useHTTP2 = flag.Bool("use-http2", true, "Enable HTTP2 support")
- pagesRoot = flag.String("pages-root", "shared/pages", "The directory where pages are stored")
- pagesDomain = flag.String("pages-domain", "gitlab-example.com", "The domain to serve static pages")
- artifactsServer = flag.String("artifacts-server", "", "API URL to proxy artifact requests to, e.g.: 'https://gitlab.com/api/v4'")
- artifactsServerTimeout = flag.Int("artifacts-server-timeout", 10, "Timeout (in seconds) for a proxied request to the artifacts server")
- pagesStatus = flag.String("pages-status", "", "The url path for a status page, e.g., /@status")
- metricsAddress = flag.String("metrics-address", "", "The address to listen on for metrics requests")
- sentryDSN = flag.String("sentry-dsn", "", "The address for sending sentry crash reporting to")
- sentryEnvironment = flag.String("sentry-environment", "", "The environment for sentry crash reporting")
- daemonUID = flag.Uint("daemon-uid", 0, "Drop privileges to this user")
- daemonGID = flag.Uint("daemon-gid", 0, "Drop privileges to this group")
- daemonInplaceChroot = flag.Bool("daemon-inplace-chroot", false, "Fall back to a non-bind-mount chroot of -pages-root when daemonizing")
- propagateCorrelationID = flag.Bool("propagate-correlation-id", false, "Reuse existing Correlation-ID from the incoming request header `X-Request-ID` if present")
- logFormat = flag.String("log-format", "text", "The log output format: 'text' or 'json'")
- logVerbose = flag.Bool("log-verbose", false, "Verbose logging")
- _ = flag.String("admin-secret-path", "", "DEPRECATED")
- _ = flag.String("admin-unix-listener", "", "DEPRECATED")
- _ = flag.String("admin-https-listener", "", "DEPRECATED")
- _ = flag.String("admin-https-cert", "", "DEPRECATED")
- _ = flag.String("admin-https-key", "", "DEPRECATED")
- secret = flag.String("auth-secret", "", "Cookie store hash key, should be at least 32 bytes long")
- gitLabAuthServer = flag.String("auth-server", "", "DEPRECATED, use gitlab-server instead. GitLab server, for example https://www.gitlab.com")
- gitLabServer = flag.String("gitlab-server", "", "GitLab server, for example https://www.gitlab.com")
- internalGitLabServer = flag.String("internal-gitlab-server", "", "Internal GitLab server used for API requests, useful if you want to send that traffic over an internal load balancer, example value https://www.gitlab.com (defaults to value of gitlab-server)")
- gitLabAPISecretKey = flag.String("api-secret-key", "", "File with secret key used to authenticate with the GitLab API")
- gitlabClientHTTPTimeout = flag.Duration("gitlab-client-http-timeout", 10*time.Second, "GitLab API HTTP client connection timeout in seconds (default: 10s)")
- gitlabClientJWTExpiry = flag.Duration("gitlab-client-jwt-expiry", 30*time.Second, "JWT Token expiry time in seconds (default: 30s)")
- // TODO: implement functionality for disk, auto and gitlab https://gitlab.com/gitlab-org/gitlab/-/issues/217912
- domainConfigSource = flag.String("domain-config-source", "auto", "Domain configuration source 'disk', 'auto' or 'gitlab' (default: 'auto'). DEPRECATED: gitlab-pages will use the API-based configuration starting from 14.0 see https://gitlab.com/gitlab-org/gitlab-pages/-/issues/382")
- clientID = flag.String("auth-client-id", "", "GitLab application Client ID")
- clientSecret = flag.String("auth-client-secret", "", "GitLab application Client Secret")
- redirectURI = flag.String("auth-redirect-uri", "", "GitLab application redirect URI")
- authScope = flag.String("auth-scope", "api", "Scope to be used for authentication (must match GitLab Pages OAuth application settings)")
- maxConns = flag.Uint("max-conns", 5000, "Limit on the number of concurrent connections to the HTTP, HTTPS or proxy listeners")
- insecureCiphers = flag.Bool("insecure-ciphers", false, "Use default list of cipher suites, may contain insecure ones like 3DES and RC4")
- tlsMinVersion = flag.String("tls-min-version", "tls1.2", tlsconfig.FlagUsage("min"))
- tlsMaxVersion = flag.String("tls-max-version", "", tlsconfig.FlagUsage("max"))
- // TODO: move all flags to config pkg https://gitlab.com/gitlab-org/gitlab-pages/-/issues/507
- zipCacheExpiration = flag.Duration("zip-cache-expiration", 60*time.Second, "Zip serving archive cache expiration interval")
- zipCacheCleanup = flag.Duration("zip-cache-cleanup", 30*time.Second, "Zip serving archive cache cleanup interval")
- zipCacheRefresh = flag.Duration("zip-cache-refresh", 30*time.Second, "Zip serving archive cache refresh interval")
- zipOpenTimeout = flag.Duration("zip-open-timeout", 30*time.Second, "Zip archive open timeout")
-
- disableCrossOriginRequests = flag.Bool("disable-cross-origin-requests", false, "Disable cross-origin requests")
-
- // See init()
- listenHTTP = MultiStringFlag{separator: ","}
- listenHTTPS = MultiStringFlag{separator: ","}
- listenProxy = MultiStringFlag{separator: ","}
- listenHTTPSProxyv2 = MultiStringFlag{separator: ","}
-
- header = MultiStringFlag{separator: ";;"}
-)
-
-func gitlabServerFromFlags() string {
- if *gitLabServer != "" {
- return *gitLabServer
- }
-
- if *gitLabAuthServer != "" {
- log.Warn("auth-server parameter is deprecated, use gitlab-server instead")
- return *gitLabAuthServer
- }
-
- u, err := url.Parse(*artifactsServer)
- if err != nil {
- return ""
- }
-
- u.Path = ""
- return u.String()
-}
-
-func internalGitLabServerFromFlags() string {
- if *internalGitLabServer != "" {
- return *internalGitLabServer
- }
-
- return gitlabServerFromFlags()
-}
-
-func setArtifactsServer(artifactsServer string, artifactsServerTimeout int, config *appConfig) {
- u, err := url.Parse(artifactsServer)
- if err != nil {
- log.Fatal(err)
- }
- // url.Parse ensures that the Scheme attribute is always lower case.
- if u.Scheme != request.SchemeHTTP && u.Scheme != request.SchemeHTTPS {
- errortracking.Capture(err)
- log.Fatal("artifacts-server scheme must be either http:// or https://")
- }
-
- if artifactsServerTimeout < 1 {
- errortracking.Capture(err)
- log.Fatal("artifacts-server-timeout must be greater than or equal to 1")
- }
-
- config.ArtifactsServerTimeout = artifactsServerTimeout
- config.ArtifactsServer = artifactsServer
-}
-
-func setGitLabAPISecretKey(secretFile string, config *appConfig) {
- encoded := readFile(secretFile)
-
- decoded := make([]byte, base64.StdEncoding.DecodedLen(len(encoded)))
- secretLength, err := base64.StdEncoding.Decode(decoded, encoded)
- if err != nil {
- log.WithError(err).Fatal("Failed to decode GitLab API secret")
- }
-
- if secretLength != 32 {
- log.WithError(fmt.Errorf("expected 32 bytes GitLab API secret but got %d bytes", secretLength)).Fatal("Failed to decode GitLab API secret")
- }
-
- config.GitLabAPISecretKey = decoded
-}
-
-func configFromFlags() appConfig {
- var config appConfig
-
- config.PagesRoot = *pagesRoot
-
- config.Domain = strings.ToLower(*pagesDomain)
- config.RedirectHTTP = *redirectHTTP
- config.HTTP2 = *useHTTP2
- config.DisableCrossOriginRequests = *disableCrossOriginRequests
- config.PropagateCorrelationID = *propagateCorrelationID
- config.StatusPath = *pagesStatus
- config.LogFormat = *logFormat
- config.LogVerbose = *logVerbose
- config.MaxConns = int(*maxConns)
- config.InsecureCiphers = *insecureCiphers
- // tlsMinVersion and tlsMaxVersion are validated in appMain
- config.TLSMinVersion = tlsconfig.AllTLSVersions[*tlsMinVersion]
- config.TLSMaxVersion = tlsconfig.AllTLSVersions[*tlsMaxVersion]
- config.CustomHeaders = header.Split()
-
- for _, file := range []struct {
- contents *[]byte
- path string
- }{
- {&config.RootCertificate, *pagesRootCert},
- {&config.RootKey, *pagesRootKey},
- } {
- if file.path != "" {
- *file.contents = readFile(file.path)
- }
- }
-
- if *gitLabAPISecretKey != "" {
- setGitLabAPISecretKey(*gitLabAPISecretKey, &config)
- }
-
- if *artifactsServer != "" {
- setArtifactsServer(*artifactsServer, *artifactsServerTimeout, &config)
- }
-
- config.GitLabServer = gitlabServerFromFlags()
- config.InternalGitLabServer = internalGitLabServerFromFlags()
- config.GitlabClientHTTPTimeout = *gitlabClientHTTPTimeout
- config.GitlabJWTTokenExpiration = *gitlabClientJWTExpiry
- config.DomainConfigurationSource = *domainConfigSource
- config.StoreSecret = *secret
- config.ClientID = *clientID
- config.ClientSecret = *clientSecret
- config.RedirectURI = *redirectURI
- config.AuthScope = *authScope
- config.SentryDSN = *sentryDSN
- config.SentryEnvironment = *sentryEnvironment
-
- config.ZipCacheExpiry = *zipCacheExpiration
- config.ZipCacheCleanup = *zipCacheCleanup
- config.ZipCacheRefresh = *zipCacheRefresh
- config.ZipeOpenTimeout = *zipOpenTimeout
-
- checkAuthenticationConfig(config)
-
- return config
-}
-
-func checkAuthenticationConfig(config appConfig) {
- if config.StoreSecret == "" && config.ClientID == "" &&
- config.ClientSecret == "" && config.RedirectURI == "" {
- return
- }
- assertAuthConfig(config)
-}
-
-func assertAuthConfig(config appConfig) {
- if config.StoreSecret == "" {
- log.Fatal("auth-secret must be defined if authentication is supported")
- }
- if config.ClientID == "" {
- log.Fatal("auth-client-id must be defined if authentication is supported")
- }
- if config.ClientSecret == "" {
- log.Fatal("auth-client-secret must be defined if authentication is supported")
- }
- if config.GitLabServer == "" {
- log.Fatal("gitlab-server must be defined if authentication is supported")
- }
- if config.RedirectURI == "" {
- log.Fatal("auth-redirect-uri must be defined if authentication is supported")
- }
- if config.AuthScope == "" {
- log.Fatal("auth-scope must be defined if authentication is supported")
- }
-}
-
func initErrorReporting(sentryDSN, sentryEnvironment string) {
errortracking.Initialize(
errortracking.WithSentryDSN(sentryDSN),
@@ -259,7 +31,7 @@ func initErrorReporting(sentryDSN, sentryEnvironment string) {
errortracking.WithSentryEnvironment(sentryEnvironment))
}
-func loadConfig() appConfig {
+func appMain() {
if err := validateargs.NotAllowed(os.Args[1:]); err != nil {
log.WithError(err).Fatal("Using invalid arguments, use -config=gitlab-pages-config file instead")
}
@@ -268,93 +40,40 @@ func loadConfig() appConfig {
log.WithError(err).Warn("Using deprecated arguments")
}
- config := configFromFlags()
- if config.SentryDSN != "" {
- initErrorReporting(config.SentryDSN, config.SentryEnvironment)
- }
+ config := cfg.LoadConfig()
- log.WithFields(log.Fields{
- "artifacts-server": *artifactsServer,
- "artifacts-server-timeout": *artifactsServerTimeout,
- "daemon-gid": *daemonGID,
- "daemon-uid": *daemonUID,
- "daemon-inplace-chroot": *daemonInplaceChroot,
- "default-config-filename": flag.DefaultConfigFlagname,
- "disable-cross-origin-requests": *disableCrossOriginRequests,
- "domain": config.Domain,
- "insecure-ciphers": config.InsecureCiphers,
- "listen-http": listenHTTP,
- "listen-https": listenHTTPS,
- "listen-proxy": listenProxy,
- "listen-https-proxyv2": listenHTTPSProxyv2,
- "log-format": *logFormat,
- "metrics-address": *metricsAddress,
- "pages-domain": *pagesDomain,
- "pages-root": *pagesRoot,
- "pages-status": *pagesStatus,
- "propagate-correlation-id": *propagateCorrelationID,
- "redirect-http": config.RedirectHTTP,
- "root-cert": *pagesRootKey,
- "root-key": *pagesRootCert,
- "status_path": config.StatusPath,
- "tls-min-version": *tlsMinVersion,
- "tls-max-version": *tlsMaxVersion,
- "use-http-2": config.HTTP2,
- "gitlab-server": config.GitLabServer,
- "internal-gitlab-server": config.InternalGitLabServer,
- "api-secret-key": *gitLabAPISecretKey,
- "domain-config-source": config.DomainConfigurationSource,
- "auth-redirect-uri": config.RedirectURI,
- "auth-scope": config.AuthScope,
- "zip-cache-expiration": config.ZipCacheExpiry,
- "zip-cache-cleanup": config.ZipCacheCleanup,
- "zip-cache-refresh": config.ZipCacheRefresh,
- "zip-open-timeout": config.ZipeOpenTimeout,
- }).Debug("Start daemon with configuration")
-
- return config
-}
+ printVersion(config.General.ShowVersion, VERSION)
-func appMain() {
- var showVersion = flag.Bool("version", false, "Show version")
-
- // read from -config=/path/to/gitlab-pages-config
- flag.String(flag.DefaultConfigFlagname, "", "path to config file")
-
- flag.Parse()
-
- if err := tlsconfig.ValidateTLSVersions(*tlsMinVersion, *tlsMaxVersion); err != nil {
- fatal(err, "invalid TLS version")
+ if config.Sentry.DSN != "" {
+ initErrorReporting(config.Sentry.DSN, config.Sentry.Environment)
}
- printVersion(*showVersion, VERSION)
-
- err := logging.ConfigureLogging(*logFormat, *logVerbose)
+ err := logging.ConfigureLogging(config.Log.Format, config.Log.Verbose)
if err != nil {
log.WithError(err).Fatal("Failed to initialize logging")
}
+ cfg.LogConfig(config)
+
log.WithFields(log.Fields{
"version": VERSION,
"revision": REVISION,
}).Print("GitLab Pages Daemon")
log.Printf("URL: https://gitlab.com/gitlab-org/gitlab-pages")
- if err := os.Chdir(*pagesRoot); err != nil {
+ if err := os.Chdir(config.General.RootDir); err != nil {
fatal(err, "could not change directory into pagesRoot")
}
- config := loadConfig()
-
for _, cs := range [][]io.Closer{
- createAppListeners(&config),
- createMetricsListener(&config),
+ createAppListeners(config),
+ createMetricsListener(config),
} {
defer closeAll(cs)
}
- if *daemonUID != 0 || *daemonGID != 0 {
- if err := daemonize(config, *daemonUID, *daemonGID, *daemonInplaceChroot, config.PagesRoot); err != nil {
+ if config.Daemon.UID != 0 || config.Daemon.GID != 0 {
+ if err := daemonize(config); err != nil {
errortracking.Capture(err)
fatal(err, "could not create pages daemon")
}
@@ -374,10 +93,14 @@ func closeAll(cs []io.Closer) {
// createAppListeners returns net.Listener and *os.File instances. The
// caller must ensure they don't get closed or garbage-collected (which
// implies closing) too soon.
-func createAppListeners(config *appConfig) []io.Closer {
+func createAppListeners(config *cfg.Config) []io.Closer {
var closers []io.Closer
+ var httpListeners []uintptr
+ var httpsListeners []uintptr
+ var proxyListeners []uintptr
+ var httpsProxyv2Listeners []uintptr
- for _, addr := range listenHTTP.Split() {
+ for _, addr := range config.ListenHTTPStrings.Split() {
l, f := createSocket(addr)
closers = append(closers, l, f)
@@ -385,10 +108,10 @@ func createAppListeners(config *appConfig) []io.Closer {
"listener": addr,
}).Debug("Set up HTTP listener")
- config.ListenHTTP = append(config.ListenHTTP, f.Fd())
+ httpListeners = append(httpListeners, f.Fd())
}
- for _, addr := range listenHTTPS.Split() {
+ for _, addr := range config.ListenHTTPSStrings.Split() {
l, f := createSocket(addr)
closers = append(closers, l, f)
@@ -396,10 +119,10 @@ func createAppListeners(config *appConfig) []io.Closer {
"listener": addr,
}).Debug("Set up HTTPS listener")
- config.ListenHTTPS = append(config.ListenHTTPS, f.Fd())
+ httpsListeners = append(httpsListeners, f.Fd())
}
- for _, addr := range listenProxy.Split() {
+ for _, addr := range config.ListenProxyStrings.Split() {
l, f := createSocket(addr)
closers = append(closers, l, f)
@@ -407,10 +130,10 @@ func createAppListeners(config *appConfig) []io.Closer {
"listener": addr,
}).Debug("Set up proxy listener")
- config.ListenProxy = append(config.ListenProxy, f.Fd())
+ proxyListeners = append(proxyListeners, f.Fd())
}
- for _, addr := range listenHTTPSProxyv2.Split() {
+ for _, addr := range config.ListenHTTPSProxyv2Strings.Split() {
l, f := createSocket(addr)
closers = append(closers, l, f)
@@ -418,7 +141,14 @@ func createAppListeners(config *appConfig) []io.Closer {
"listener": addr,
}).Debug("Set up https proxyv2 listener")
- config.ListenHTTPSProxyv2 = append(config.ListenHTTPSProxyv2, f.Fd())
+ httpsProxyv2Listeners = append(httpsProxyv2Listeners, f.Fd())
+ }
+
+ config.Listeners = cfg.Listeners{
+ HTTP: httpListeners,
+ HTTPS: httpsListeners,
+ Proxy: proxyListeners,
+ HTTPSProxyv2: httpsProxyv2Listeners,
}
return closers
@@ -427,8 +157,8 @@ func createAppListeners(config *appConfig) []io.Closer {
// createMetricsListener returns net.Listener and *os.File instances. The
// caller must ensure they don't get closed or garbage-collected (which
// implies closing) too soon.
-func createMetricsListener(config *appConfig) []io.Closer {
- addr := *metricsAddress
+func createMetricsListener(config *cfg.Config) []io.Closer {
+ addr := config.General.MetricsAddress
if addr == "" {
return nil
}