2014-04-10 23:50:58 +05:30
// Copyright 2014 The Gogs Authors. All rights reserved.
2017-05-29 12:47:15 +05:30
// Copyright 2017 The Gitea Authors. All rights reserved.
2022-11-27 23:50:29 +05:30
// SPDX-License-Identifier: MIT
2014-04-10 23:50:58 +05:30
2014-05-26 05:41:25 +05:30
package setting
2014-04-10 23:50:58 +05:30
import (
2023-02-03 22:52:11 +05:30
"errors"
2019-04-02 13:18:31 +05:30
"fmt"
2014-04-10 23:50:58 +05:30
"os"
"os/exec"
"path"
"path/filepath"
2019-04-29 01:18:46 +05:30
"runtime"
2014-04-10 23:50:58 +05:30
"strings"
2014-07-25 02:01:59 +05:30
"time"
2014-04-10 23:50:58 +05:30
2023-02-20 10:50:30 +05:30
"code.gitea.io/gitea/modules/auth/password/hash"
2016-12-22 23:42:23 +05:30
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/user"
2020-11-28 08:12:08 +05:30
"code.gitea.io/gitea/modules/util"
2017-01-24 04:14:23 +05:30
2019-01-09 22:52:57 +05:30
ini "gopkg.in/ini.v1"
2014-04-10 23:50:58 +05:30
)
2016-11-27 15:44:25 +05:30
// settings
2014-04-10 23:50:58 +05:30
var (
2021-02-20 03:06:43 +05:30
// AppVer is the version of the current build of Gitea. It is set in main.go from main.Version.
AppVer string
// AppBuiltWith represents a human readable version go runtime build version and build tags. (See main.go formatBuiltWith().)
AppBuiltWith string
// AppStartTime store time gitea has started
AppStartTime time . Time
2023-02-19 21:42:01 +05:30
2021-02-20 03:06:43 +05:30
// AppPath represents the path to the gitea binary
AppPath string
// AppWorkPath is the "working directory" of Gitea. It maps to the environment variable GITEA_WORK_DIR.
// If that is not set it is the default set here by the linker or failing that the directory of AppPath.
//
// AppWorkPath is used as the base path for several other paths.
AppWorkPath string
2015-03-23 19:49:19 +05:30
2015-12-18 09:01:34 +05:30
// Global setting objects
2023-03-16 12:52:54 +05:30
CfgProvider ConfigProvider
CustomPath string // Custom directory path
CustomConf string
RunMode string
RunUser string
IsProd bool
IsWindows bool
2014-04-10 23:50:58 +05:30
)
2017-11-03 14:26:20 +05:30
func getAppPath ( ) ( string , error ) {
var appPath string
var err error
if IsWindows && filepath . IsAbs ( os . Args [ 0 ] ) {
appPath = filepath . Clean ( os . Args [ 0 ] )
} else {
appPath , err = exec . LookPath ( os . Args [ 0 ] )
2017-09-12 17:57:44 +05:30
}
2017-11-03 14:26:20 +05:30
2022-12-14 11:45:11 +05:30
if err != nil {
2023-02-03 22:52:11 +05:30
if ! errors . Is ( err , exec . ErrDot ) {
2022-12-14 11:45:11 +05:30
return "" , err
}
appPath , err = filepath . Abs ( os . Args [ 0 ] )
}
2014-05-26 05:41:25 +05:30
if err != nil {
return "" , err
}
2017-11-03 14:26:20 +05:30
appPath , err = filepath . Abs ( appPath )
if err != nil {
return "" , err
}
// Note: we don't use path.Dir here because it does not handle case
// which path starts with two "/" in Windows: "//psf/Home/..."
2020-10-12 01:57:20 +05:30
return strings . ReplaceAll ( appPath , "\\" , "/" ) , err
2017-11-03 14:26:20 +05:30
}
func getWorkPath ( appPath string ) string {
2019-04-29 23:38:21 +05:30
workPath := AppWorkPath
2017-11-03 14:26:20 +05:30
2019-04-29 23:38:21 +05:30
if giteaWorkPath , ok := os . LookupEnv ( "GITEA_WORK_DIR" ) ; ok {
2017-11-03 14:26:20 +05:30
workPath = giteaWorkPath
2019-04-29 23:38:21 +05:30
}
if len ( workPath ) == 0 {
2017-11-03 14:26:20 +05:30
i := strings . LastIndex ( appPath , "/" )
if i == - 1 {
workPath = appPath
} else {
workPath = appPath [ : i ]
}
}
2022-06-06 20:13:17 +05:30
workPath = strings . ReplaceAll ( workPath , "\\" , "/" )
if ! filepath . IsAbs ( workPath ) {
log . Info ( "Provided work path %s is not absolute - will be made absolute against the current working directory" , workPath )
absPath , err := filepath . Abs ( workPath )
if err != nil {
log . Error ( "Unable to absolute %s against the current working directory %v. Will absolute against the AppPath %s" , workPath , err , appPath )
workPath = filepath . Join ( appPath , workPath )
} else {
workPath = absPath
}
}
2020-10-12 01:57:20 +05:30
return strings . ReplaceAll ( workPath , "\\" , "/" )
2015-11-09 03:29:56 +05:30
}
func init ( ) {
2019-04-29 01:18:46 +05:30
IsWindows = runtime . GOOS == "windows"
2019-04-02 13:18:31 +05:30
// We can rely on log.CanColorStdout being set properly because modules/log/console_windows.go comes before modules/setting/setting.go lexicographically
2021-06-27 06:26:58 +05:30
// By default set this logger at Info - we'll change it later but we need to start with something.
log . NewLogger ( 0 , "console" , "console" , fmt . Sprintf ( ` { "level": "info", "colorize": %t, "stacktraceLevel": "none"} ` , log . CanColorStdout ) )
2015-11-09 03:29:56 +05:30
var err error
2017-11-03 14:26:20 +05:30
if AppPath , err = getAppPath ( ) ; err != nil {
2019-04-02 13:18:31 +05:30
log . Fatal ( "Failed to get app path: %v" , err )
2014-05-26 05:41:25 +05:30
}
2017-11-03 14:26:20 +05:30
AppWorkPath = getWorkPath ( AppPath )
2014-05-26 05:41:25 +05:30
}
2015-03-18 13:55:55 +05:30
func forcePathSeparator ( path string ) {
if strings . Contains ( path , "\\" ) {
2019-04-02 13:18:31 +05:30
log . Fatal ( "Do not use '\\' or '\\\\' in paths, instead, please use '/' in all places" )
2015-03-18 13:55:55 +05:30
}
}
2016-08-10 06:11:18 +05:30
// IsRunUserMatchCurrentUser returns false if configured run user does not match
// actual user that runs the app. The first return value is the actual user name.
// This check is ignored under Windows since SSH remote login is not the main
// method to login on Windows.
func IsRunUserMatchCurrentUser ( runUser string ) ( string , bool ) {
2019-06-16 08:19:07 +05:30
if IsWindows || SSH . StartBuiltinServer {
2016-08-10 06:11:18 +05:30
return "" , true
}
currentUser := user . CurrentUsername ( )
return currentUser , runUser == currentUser
}
2019-04-29 23:38:21 +05:30
// SetCustomPathAndConf will set CustomPath and CustomConf with reference to the
// GITEA_CUSTOM environment variable and with provided overrides before stepping
// back to the default
2019-05-14 20:50:35 +05:30
func SetCustomPathAndConf ( providedCustom , providedConf , providedWorkPath string ) {
if len ( providedWorkPath ) != 0 {
AppWorkPath = filepath . ToSlash ( providedWorkPath )
}
2019-04-29 23:38:21 +05:30
if giteaCustom , ok := os . LookupEnv ( "GITEA_CUSTOM" ) ; ok {
CustomPath = giteaCustom
}
if len ( providedCustom ) != 0 {
CustomPath = providedCustom
}
2014-05-26 05:41:25 +05:30
if len ( CustomPath ) == 0 {
2017-11-03 14:26:20 +05:30
CustomPath = path . Join ( AppWorkPath , "custom" )
} else if ! filepath . IsAbs ( CustomPath ) {
CustomPath = path . Join ( AppWorkPath , CustomPath )
2014-05-26 05:41:25 +05:30
}
2019-04-29 23:38:21 +05:30
if len ( providedConf ) != 0 {
CustomConf = providedConf
2017-01-09 17:24:57 +05:30
}
2015-02-05 15:42:37 +05:30
if len ( CustomConf ) == 0 {
2017-11-03 14:26:20 +05:30
CustomConf = path . Join ( CustomPath , "conf/app.ini" )
2017-07-01 08:40:04 +05:30
} else if ! filepath . IsAbs ( CustomConf ) {
2017-11-03 14:26:20 +05:30
CustomConf = path . Join ( CustomPath , CustomConf )
2020-02-02 21:50:20 +05:30
log . Warn ( "Using 'custom' directory as relative origin for configuration file: '%s'" , CustomConf )
2015-02-05 15:42:37 +05:30
}
2019-04-29 23:38:21 +05:30
}
2023-02-19 21:42:01 +05:30
// PrepareAppDataPath creates app data directory if necessary
func PrepareAppDataPath ( ) error {
// FIXME: There are too many calls to MkdirAll in old code. It is incorrect.
// For example, if someDir=/mnt/vol1/gitea-home/data, if the mount point /mnt/vol1 is not mounted when Gitea runs,
// then gitea will make new empty directories in /mnt/vol1, all are stored in the root filesystem.
// The correct behavior should be: creating parent directories is end users' duty. We only create sub-directories in existing parent directories.
// For quickstart, the parent directories should be created automatically for first startup (eg: a flag or a check of INSTALL_LOCK).
// 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.
st , err := os . Stat ( AppDataPath )
if os . IsNotExist ( err ) {
err = os . MkdirAll ( AppDataPath , os . ModePerm )
if err != nil {
return fmt . Errorf ( "unable to create the APP_DATA_PATH directory: %q, Error: %w" , AppDataPath , err )
}
return nil
}
2021-12-01 13:20:01 +05:30
2023-02-19 21:42:01 +05:30
if err != nil {
return fmt . Errorf ( "unable to use APP_DATA_PATH %q. Error: %w" , AppDataPath , err )
}
2021-12-01 13:20:01 +05:30
2023-02-19 21:42:01 +05:30
if ! st . IsDir ( ) /* also works for symlink */ {
return fmt . Errorf ( "the APP_DATA_PATH %q is not a directory (or symlink to a directory) and can't be used" , AppDataPath )
2021-12-01 13:20:01 +05:30
}
2023-02-19 21:42:01 +05:30
return nil
2021-12-01 13:20:01 +05:30
}
2023-02-19 21:42:01 +05:30
// InitProviderFromExistingFile initializes config provider from an existing config file (app.ini)
func InitProviderFromExistingFile ( ) {
2023-03-16 12:52:54 +05:30
CfgProvider = newFileProviderFromConf ( CustomConf , false , "" )
2022-01-20 22:30:38 +05:30
}
2023-02-19 21:42:01 +05:30
// InitProviderAllowEmpty initializes config provider from file, it's also fine that if the config file (app.ini) doesn't exist
func InitProviderAllowEmpty ( ) {
2023-03-16 12:52:54 +05:30
CfgProvider = newFileProviderFromConf ( CustomConf , true , "" )
2023-02-19 21:42:01 +05:30
}
// InitProviderAndLoadCommonSettingsForTest initializes config provider and load common setttings for tests
func InitProviderAndLoadCommonSettingsForTest ( extraConfigs ... string ) {
2023-03-16 12:52:54 +05:30
CfgProvider = newFileProviderFromConf ( CustomConf , true , strings . Join ( extraConfigs , "\n" ) )
2023-02-19 21:42:01 +05:30
loadCommonSettingsFrom ( CfgProvider )
if err := PrepareAppDataPath ( ) ; err != nil {
log . Fatal ( "Can not prepare APP_DATA_PATH: %v" , err )
2022-10-17 04:59:26 +05:30
}
2023-02-20 10:50:30 +05:30
// register the dummy hash algorithm function used in the test fixtures
_ = hash . Register ( "dummy" , hash . NewDummyHasher )
PasswordHashAlgo , _ = hash . SetDefaultPasswordHashAlgorithm ( "dummy" )
2022-10-17 04:59:26 +05:30
}
2023-02-19 21:42:01 +05:30
// newFileProviderFromConf initializes configuration context.
2019-04-29 23:38:21 +05:30
// NOTE: do not print any log except error.
2023-03-16 12:52:54 +05:30
func newFileProviderFromConf ( customConf string , allowEmpty bool , extraConfig string ) * ini . File {
2023-02-19 21:42:01 +05:30
cfg := ini . Empty ( )
2019-04-29 23:38:21 +05:30
2023-02-19 21:42:01 +05:30
isFile , err := util . IsFile ( customConf )
2020-11-28 08:12:08 +05:30
if err != nil {
2023-02-19 21:42:01 +05:30
log . Error ( "Unable to check if %s is a file. Error: %v" , customConf , err )
2020-11-28 08:12:08 +05:30
}
if isFile {
2023-02-19 21:42:01 +05:30
if err := cfg . Append ( customConf ) ; err != nil {
log . Fatal ( "Failed to load custom conf '%s': %v" , customConf , err )
2014-05-26 05:41:25 +05:30
}
2021-12-01 13:20:01 +05:30
} else if ! allowEmpty {
log . Fatal ( "Unable to find configuration file: %q.\nEnsure you are running in the correct environment or set the correct configuration file with -c." , CustomConf )
} // else: no config file, a config file might be created at CustomConf later (might not)
2021-12-08 13:04:23 +05:30
if extraConfig != "" {
2023-02-19 21:42:01 +05:30
if err = cfg . Append ( [ ] byte ( extraConfig ) ) ; err != nil {
2021-12-08 13:04:23 +05:30
log . Fatal ( "Unable to append more config: %v" , err )
}
}
2023-02-19 21:42:01 +05:30
cfg . NameMapper = ini . SnackCase
return cfg
}
2014-07-24 00:45:47 +05:30
2023-02-19 21:42:01 +05:30
// LoadCommonSettings loads common configurations from a configuration provider.
func LoadCommonSettings ( ) {
loadCommonSettingsFrom ( CfgProvider )
}
2019-08-15 20:16:21 +05:30
2023-02-19 21:42:01 +05:30
// loadCommonSettingsFrom loads common configurations from a configuration provider.
func loadCommonSettingsFrom ( cfg ConfigProvider ) {
// WARNNING: don't change the sequence except you know what you are doing.
loadRunModeFrom ( cfg )
loadLogFrom ( cfg )
loadServerFrom ( cfg )
loadSSHFrom ( cfg )
loadOAuth2From ( cfg )
loadSecurityFrom ( cfg )
loadAttachmentFrom ( cfg )
loadLFSFrom ( cfg )
loadTimeFrom ( cfg )
loadRepositoryFrom ( cfg )
loadPictureFrom ( cfg )
loadPackagesFrom ( cfg )
loadActionsFrom ( cfg )
loadUIFrom ( cfg )
loadAdminFrom ( cfg )
loadAPIFrom ( cfg )
loadMetricsFrom ( cfg )
loadCamoFrom ( cfg )
loadI18nFrom ( cfg )
loadGitFrom ( cfg )
loadMirrorFrom ( cfg )
loadMarkupFrom ( cfg )
loadOtherFrom ( cfg )
}
2014-07-25 02:01:59 +05:30
2023-02-19 21:42:01 +05:30
func loadRunModeFrom ( rootCfg ConfigProvider ) {
rootSec := rootCfg . Section ( "" )
RunUser = rootSec . Key ( "RUN_USER" ) . MustString ( user . CurrentUsername ( ) )
2021-10-07 14:22:08 +05:30
// The following is a purposefully undocumented option. Please do not run Gitea as root. It will only cause future headaches.
// Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly.
2023-02-19 21:42:01 +05:30
unsafeAllowRunAsRoot := rootSec . Key ( "I_AM_BEING_UNSAFE_RUNNING_AS_ROOT" ) . MustBool ( false )
2022-12-27 11:30:34 +05:30
RunMode = os . Getenv ( "GITEA_RUN_MODE" )
if RunMode == "" {
2023-02-19 21:42:01 +05:30
RunMode = rootSec . Key ( "RUN_MODE" ) . MustString ( "prod" )
2022-12-27 11:30:34 +05:30
}
2021-10-20 20:07:19 +05:30
IsProd = strings . EqualFold ( RunMode , "prod" )
2014-05-26 05:41:25 +05:30
// Does not check run user when the install lock is off.
2023-02-19 21:42:01 +05:30
installLock := rootCfg . Section ( "security" ) . Key ( "INSTALL_LOCK" ) . MustBool ( false )
if installLock {
2016-08-10 06:11:18 +05:30
currentUser , match := IsRunUserMatchCurrentUser ( RunUser )
if ! match {
2019-04-02 13:18:31 +05:30
log . Fatal ( "Expect user '%s' but current user is: %s" , RunUser , currentUser )
2016-08-10 06:11:18 +05:30
}
2014-05-26 05:41:25 +05:30
}
2021-10-07 14:22:08 +05:30
// check if we run as root
if os . Getuid ( ) == 0 {
if ! unsafeAllowRunAsRoot {
// Special thanks to VLC which inspired the wording of this messaging.
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" )
}
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." )
}
2020-12-22 16:43:50 +05:30
}
2021-05-30 00:14:14 +05:30
// CreateOrAppendToCustomConf creates or updates the custom config.
// Use the callback to set individual values.
2022-10-01 22:56:33 +05:30
func CreateOrAppendToCustomConf ( purpose string , callback func ( cfg * ini . File ) ) {
if CustomConf == "" {
log . Error ( "Custom config path must not be empty" )
return
}
2021-05-30 00:14:14 +05:30
cfg := ini . Empty ( )
isFile , err := util . IsFile ( CustomConf )
if err != nil {
log . Error ( "Unable to check if %s is a file. Error: %v" , CustomConf , err )
}
if isFile {
if err := cfg . Append ( CustomConf ) ; err != nil {
log . Error ( "failed to load custom conf %s: %v" , CustomConf , err )
return
}
}
callback ( cfg )
if err := os . MkdirAll ( filepath . Dir ( CustomConf ) , os . ModePerm ) ; err != nil {
log . Fatal ( "failed to create '%s': %v" , CustomConf , err )
return
}
if err := cfg . SaveTo ( CustomConf ) ; err != nil {
log . Fatal ( "error saving to custom config: %v" , err )
}
2022-10-01 22:56:33 +05:30
log . Info ( "Settings for %s saved to: %q" , purpose , CustomConf )
2021-06-27 15:37:36 +05:30
// Change permissions to be more restrictive
fi , err := os . Stat ( CustomConf )
if err != nil {
log . Error ( "Failed to determine current conf file permissions: %v" , err )
return
}
if fi . Mode ( ) . Perm ( ) > 0 o600 {
if err = os . Chmod ( CustomConf , 0 o600 ) ; err != nil {
log . Warn ( "Failed changing conf file permissions to -rw-------. Consider changing them manually." )
}
}
2021-05-30 00:14:14 +05:30
}
2023-02-19 21:42:01 +05:30
// LoadSettings initializes the settings for normal start up
func LoadSettings ( ) {
2023-03-16 12:52:54 +05:30
loadDBSetting ( CfgProvider )
2023-02-19 21:42:01 +05:30
loadServiceFrom ( CfgProvider )
loadOAuth2ClientFrom ( CfgProvider )
InitLogs ( false )
loadCacheFrom ( CfgProvider )
loadSessionFrom ( CfgProvider )
loadCorsFrom ( CfgProvider )
loadMailsFrom ( CfgProvider )
loadProxyFrom ( CfgProvider )
loadWebhookFrom ( CfgProvider )
loadMigrationsFrom ( CfgProvider )
loadIndexerFrom ( CfgProvider )
loadTaskFrom ( CfgProvider )
LoadQueueSettings ( )
loadProjectFrom ( CfgProvider )
loadMimeTypeMapFrom ( CfgProvider )
loadFederationFrom ( CfgProvider )
2014-04-10 23:50:58 +05:30
}
2021-06-17 05:02:57 +05:30
2023-02-19 21:42:01 +05:30
// LoadSettingsForInstall initializes the settings for install
func LoadSettingsForInstall ( ) {
2023-03-16 12:52:54 +05:30
loadDBSetting ( CfgProvider )
2023-02-19 21:42:01 +05:30
loadServiceFrom ( CfgProvider )
loadMailerFrom ( CfgProvider )
2021-06-17 05:02:57 +05:30
}