code.gitea.io/gitea@v1.22.3/modules/setting/setting.go (about)

     1  // Copyright 2014 The Gogs Authors. All rights reserved.
     2  // Copyright 2017 The Gitea Authors. All rights reserved.
     3  // SPDX-License-Identifier: MIT
     4  
     5  package setting
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"runtime"
    11  	"strings"
    12  	"time"
    13  
    14  	"code.gitea.io/gitea/modules/log"
    15  	"code.gitea.io/gitea/modules/user"
    16  	"code.gitea.io/gitea/modules/util"
    17  )
    18  
    19  // settings
    20  var (
    21  	// AppVer is the version of the current build of Gitea. It is set in main.go from main.Version.
    22  	AppVer string
    23  	// AppBuiltWith represents a human-readable version go runtime build version and build tags. (See main.go formatBuiltWith().)
    24  	AppBuiltWith string
    25  	// AppStartTime store time gitea has started
    26  	AppStartTime time.Time
    27  
    28  	// Other global setting objects
    29  
    30  	CfgProvider ConfigProvider
    31  	RunMode     string
    32  	RunUser     string
    33  	IsProd      bool
    34  	IsWindows   bool
    35  
    36  	// IsInTesting indicates whether the testing is running. A lot of unreliable code causes a lot of nonsense error logs during testing
    37  	// TODO: this is only a temporary solution, we should make the test code more reliable
    38  	IsInTesting = false
    39  )
    40  
    41  func init() {
    42  	IsWindows = runtime.GOOS == "windows"
    43  	if AppVer == "" {
    44  		AppVer = "dev"
    45  	}
    46  
    47  	// We can rely on log.CanColorStdout being set properly because modules/log/console_windows.go comes before modules/setting/setting.go lexicographically
    48  	// By default set this logger at Info - we'll change it later, but we need to start with something.
    49  	log.SetConsoleLogger(log.DEFAULT, "console", log.INFO)
    50  }
    51  
    52  // IsRunUserMatchCurrentUser returns false if configured run user does not match
    53  // actual user that runs the app. The first return value is the actual user name.
    54  // This check is ignored under Windows since SSH remote login is not the main
    55  // method to login on Windows.
    56  func IsRunUserMatchCurrentUser(runUser string) (string, bool) {
    57  	if IsWindows || SSH.StartBuiltinServer {
    58  		return "", true
    59  	}
    60  
    61  	currentUser := user.CurrentUsername()
    62  	return currentUser, runUser == currentUser
    63  }
    64  
    65  // PrepareAppDataPath creates app data directory if necessary
    66  func PrepareAppDataPath() error {
    67  	// FIXME: There are too many calls to MkdirAll in old code. It is incorrect.
    68  	// For example, if someDir=/mnt/vol1/gitea-home/data, if the mount point /mnt/vol1 is not mounted when Gitea runs,
    69  	// then gitea will make new empty directories in /mnt/vol1, all are stored in the root filesystem.
    70  	// The correct behavior should be: creating parent directories is end users' duty. We only create sub-directories in existing parent directories.
    71  	// For quickstart, the parent directories should be created automatically for first startup (eg: a flag or a check of INSTALL_LOCK).
    72  	// Now we can take the first step to do correctly (using Mkdir) in other packages, and prepare the AppDataPath here, then make a refactor in future.
    73  
    74  	st, err := os.Stat(AppDataPath)
    75  	if os.IsNotExist(err) {
    76  		err = os.MkdirAll(AppDataPath, os.ModePerm)
    77  		if err != nil {
    78  			return fmt.Errorf("unable to create the APP_DATA_PATH directory: %q, Error: %w", AppDataPath, err)
    79  		}
    80  		return nil
    81  	}
    82  
    83  	if err != nil {
    84  		return fmt.Errorf("unable to use APP_DATA_PATH %q. Error: %w", AppDataPath, err)
    85  	}
    86  
    87  	if !st.IsDir() /* also works for symlink */ {
    88  		return fmt.Errorf("the APP_DATA_PATH %q is not a directory (or symlink to a directory) and can't be used", AppDataPath)
    89  	}
    90  
    91  	return nil
    92  }
    93  
    94  func InitCfgProvider(file string) {
    95  	var err error
    96  	if CfgProvider, err = NewConfigProviderFromFile(file); err != nil {
    97  		log.Fatal("Unable to init config provider from %q: %v", file, err)
    98  	}
    99  	CfgProvider.DisableSaving() // do not allow saving the CfgProvider into file, it will be polluted by the "MustXxx" calls
   100  }
   101  
   102  func MustInstalled() {
   103  	if !InstallLock {
   104  		log.Fatal(`Unable to load config file for a installed Gitea instance, you should either use "--config" to set your config file (app.ini), or run "gitea web" command to install Gitea.`)
   105  	}
   106  }
   107  
   108  func LoadCommonSettings() {
   109  	if err := loadCommonSettingsFrom(CfgProvider); err != nil {
   110  		log.Fatal("Unable to load settings from config: %v", err)
   111  	}
   112  }
   113  
   114  // loadCommonSettingsFrom loads common configurations from a configuration provider.
   115  func loadCommonSettingsFrom(cfg ConfigProvider) error {
   116  	// WARNING: don't change the sequence except you know what you are doing.
   117  	loadRunModeFrom(cfg)
   118  	loadLogGlobalFrom(cfg)
   119  	loadServerFrom(cfg)
   120  	loadSSHFrom(cfg)
   121  
   122  	mustCurrentRunUserMatch(cfg) // it depends on the SSH config, only non-builtin SSH server requires this check
   123  
   124  	loadOAuth2From(cfg)
   125  	loadSecurityFrom(cfg)
   126  	if err := loadAttachmentFrom(cfg); err != nil {
   127  		return err
   128  	}
   129  	if err := loadLFSFrom(cfg); err != nil {
   130  		return err
   131  	}
   132  	loadTimeFrom(cfg)
   133  	loadRepositoryFrom(cfg)
   134  	if err := loadAvatarsFrom(cfg); err != nil {
   135  		return err
   136  	}
   137  	if err := loadRepoAvatarFrom(cfg); err != nil {
   138  		return err
   139  	}
   140  	if err := loadPackagesFrom(cfg); err != nil {
   141  		return err
   142  	}
   143  	if err := loadActionsFrom(cfg); err != nil {
   144  		return err
   145  	}
   146  	loadUIFrom(cfg)
   147  	loadAdminFrom(cfg)
   148  	loadAPIFrom(cfg)
   149  	loadMetricsFrom(cfg)
   150  	loadCamoFrom(cfg)
   151  	loadI18nFrom(cfg)
   152  	loadGitFrom(cfg)
   153  	loadMirrorFrom(cfg)
   154  	loadMarkupFrom(cfg)
   155  	loadOtherFrom(cfg)
   156  	return nil
   157  }
   158  
   159  func loadRunModeFrom(rootCfg ConfigProvider) {
   160  	rootSec := rootCfg.Section("")
   161  	RunUser = rootSec.Key("RUN_USER").MustString(user.CurrentUsername())
   162  
   163  	// The following is a purposefully undocumented option. Please do not run Gitea as root. It will only cause future headaches.
   164  	// Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly.
   165  	unsafeAllowRunAsRoot := ConfigSectionKeyBool(rootSec, "I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")
   166  	unsafeAllowRunAsRoot = unsafeAllowRunAsRoot || util.OptionalBoolParse(os.Getenv("GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")).Value()
   167  	RunMode = os.Getenv("GITEA_RUN_MODE")
   168  	if RunMode == "" {
   169  		RunMode = rootSec.Key("RUN_MODE").MustString("prod")
   170  	}
   171  
   172  	// non-dev mode is treated as prod mode, to protect users from accidentally running in dev mode if there is a typo in this value.
   173  	RunMode = strings.ToLower(RunMode)
   174  	if RunMode != "dev" {
   175  		RunMode = "prod"
   176  	}
   177  	IsProd = RunMode != "dev"
   178  
   179  	// check if we run as root
   180  	if os.Getuid() == 0 {
   181  		if !unsafeAllowRunAsRoot {
   182  			// Special thanks to VLC which inspired the wording of this messaging.
   183  			log.Fatal("Gitea is not supposed to be run as root. Sorry. If you need to use privileged TCP ports please instead use setcap and the `cap_net_bind_service` permission")
   184  		}
   185  		log.Critical("You are running Gitea using the root user, and have purposely chosen to skip built-in protections around this. You have been warned against this.")
   186  	}
   187  }
   188  
   189  // HasInstallLock checks the install-lock in ConfigProvider directly, because sometimes the config file is not loaded into setting variables yet.
   190  func HasInstallLock(rootCfg ConfigProvider) bool {
   191  	return rootCfg.Section("security").Key("INSTALL_LOCK").MustBool(false)
   192  }
   193  
   194  func mustCurrentRunUserMatch(rootCfg ConfigProvider) {
   195  	// Does not check run user when the "InstallLock" is off.
   196  	if HasInstallLock(rootCfg) {
   197  		currentUser, match := IsRunUserMatchCurrentUser(RunUser)
   198  		if !match {
   199  			log.Fatal("Expect user '%s' but current user is: %s", RunUser, currentUser)
   200  		}
   201  	}
   202  }
   203  
   204  // LoadSettings initializes the settings for normal start up
   205  func LoadSettings() {
   206  	initAllLoggers()
   207  
   208  	loadDBSetting(CfgProvider)
   209  	loadServiceFrom(CfgProvider)
   210  	loadOAuth2ClientFrom(CfgProvider)
   211  	loadCacheFrom(CfgProvider)
   212  	loadSessionFrom(CfgProvider)
   213  	loadCorsFrom(CfgProvider)
   214  	loadMailsFrom(CfgProvider)
   215  	loadProxyFrom(CfgProvider)
   216  	loadWebhookFrom(CfgProvider)
   217  	loadMigrationsFrom(CfgProvider)
   218  	loadIndexerFrom(CfgProvider)
   219  	loadTaskFrom(CfgProvider)
   220  	LoadQueueSettings()
   221  	loadProjectFrom(CfgProvider)
   222  	loadMimeTypeMapFrom(CfgProvider)
   223  	loadFederationFrom(CfgProvider)
   224  }
   225  
   226  // LoadSettingsForInstall initializes the settings for install
   227  func LoadSettingsForInstall() {
   228  	loadDBSetting(CfgProvider)
   229  	loadServiceFrom(CfgProvider)
   230  	loadMailerFrom(CfgProvider)
   231  }
   232  
   233  var configuredPaths = make(map[string]string)
   234  
   235  func checkOverlappedPath(name, path string) {
   236  	// TODO: some paths shouldn't overlap (storage.xxx.path), while some could (data path is the base path for storage path)
   237  	if targetName, ok := configuredPaths[path]; ok && targetName != name {
   238  		LogStartupProblem(1, log.ERROR, "Configured path %q is used by %q and %q at the same time. The paths must be unique to prevent data loss.", path, targetName, name)
   239  	}
   240  	configuredPaths[path] = name
   241  }