diff options
author | Balasankar "Balu" C <balasankarc@autistici.org> | 2021-02-17 10:56:11 +0300 |
---|---|---|
committer | Balasankar "Balu" C <balasankarc@autistici.org> | 2021-03-01 08:35:57 +0300 |
commit | b7e2085b76c11212ac41f80672d5c5f9b0287fee (patch) | |
tree | 92bb6e221257aeea9da8986c6f1e2a297b9c089c | |
parent | 01da18ea5717658eb98f539f921ed02fd35bd3d1 (diff) |
Move configuration parsing to Config package
Changelog: changed
Signed-off-by: Balasankar "Balu" C <balasankarc@autistici.org>
-rw-r--r-- | app.go | 76 | ||||
-rw-r--r-- | app_config.go | 74 | ||||
-rw-r--r-- | app_test.go | 12 | ||||
-rw-r--r-- | daemon.go | 20 | ||||
-rw-r--r-- | helpers.go | 9 | ||||
-rw-r--r-- | internal/config/config.go | 394 | ||||
-rw-r--r-- | internal/config/config_test.go (renamed from config_test.go) | 2 | ||||
-rw-r--r-- | internal/config/flags.go | 81 | ||||
-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-- | main.go | 342 |
11 files changed, 564 insertions, 450 deletions
@@ -47,7 +47,7 @@ var ( ) type theApp struct { - appConfig + 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.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.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.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.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.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.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.General.MaxConns) // Use a common pipeline to use a single instance of each handler, // instead of making two nearly identical pipelines @@ -373,22 +373,22 @@ func (a *theApp) Run() { httpHandler := a.httpInitialMiddleware(commonHandlerPipeline) // Listen for HTTP - for _, fd := range a.ListenHTTP { + for _, fd := range a.Listeners.HTTP { a.listenHTTPFD(&wg, fd, httpHandler, limiter) } // Listen for HTTPS - for _, fd := range a.ListenHTTPS { + for _, fd := range a.Listeners.HTTPS { a.listenHTTPSFD(&wg, fd, httpHandler, limiter) } // Listen for HTTP proxy requests - for _, fd := range a.ListenProxy { + for _, fd := range a.Listeners.Proxy { a.listenProxyFD(&wg, fd, proxyHandler, limiter) } // Listen for HTTPS PROXYv2 requests - for _, fd := range a.ListenHTTPSProxyv2 { + for _, fd := range a.Listeners.HTTPSProxyv2 { a.ListenHTTPSProxyv2FD(&wg, fd, httpHandler, limiter) } @@ -397,7 +397,7 @@ func (a *theApp) Run() { a.listenMetricsFD(&wg, a.ListenMetrics) } - a.domains.Read(a.Domain) + a.domains.Read(a.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.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.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.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.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.Log.Format, a.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.General.RootCertificate, a.General.RootKey, a.ServeTLS, + a.General.InsecureCiphers, a.TLS.MinVersion, a.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..1414623a 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 @@ -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,7 +44,7 @@ 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") } @@ -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 := uint(config.Daemon.UID) + gid := uint(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 @@ -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) } @@ -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..34d3c9a6 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 int + GID int + 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: int(*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: int(*daemonUID), + GID: int(*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..11bd335a --- /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.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")) + 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" @@ -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 } |