code.gitea.io/gitea@v1.22.3/modules/setting/server.go (about) 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package setting 5 6 import ( 7 "encoding/base64" 8 "net" 9 "net/url" 10 "path/filepath" 11 "strconv" 12 "strings" 13 "time" 14 15 "code.gitea.io/gitea/modules/json" 16 "code.gitea.io/gitea/modules/log" 17 "code.gitea.io/gitea/modules/util" 18 ) 19 20 // Scheme describes protocol types 21 type Scheme string 22 23 // enumerates all the scheme types 24 const ( 25 HTTP Scheme = "http" 26 HTTPS Scheme = "https" 27 FCGI Scheme = "fcgi" 28 FCGIUnix Scheme = "fcgi+unix" 29 HTTPUnix Scheme = "http+unix" 30 ) 31 32 // LandingPage describes the default page 33 type LandingPage string 34 35 // enumerates all the landing page types 36 const ( 37 LandingPageHome LandingPage = "/" 38 LandingPageExplore LandingPage = "/explore" 39 LandingPageOrganizations LandingPage = "/explore/organizations" 40 LandingPageLogin LandingPage = "/user/login" 41 ) 42 43 var ( 44 // AppName is the Application name, used in the page title. 45 // It maps to ini:"APP_NAME" 46 AppName string 47 // AppURL is the Application ROOT_URL. It always has a '/' suffix 48 // It maps to ini:"ROOT_URL" 49 AppURL string 50 // AppSubURL represents the sub-url mounting point for gitea. It is either "" or starts with '/' and ends without '/', such as '/{subpath}'. 51 // This value is empty if site does not have sub-url. 52 AppSubURL string 53 // AppDataPath is the default path for storing data. 54 // It maps to ini:"APP_DATA_PATH" in [server] and defaults to AppWorkPath + "/data" 55 AppDataPath string 56 // LocalURL is the url for locally running applications to contact Gitea. It always has a '/' suffix 57 // It maps to ini:"LOCAL_ROOT_URL" in [server] 58 LocalURL string 59 // AssetVersion holds a opaque value that is used for cache-busting assets 60 AssetVersion string 61 62 // Server settings 63 64 Protocol Scheme 65 UseProxyProtocol bool // `ini:"USE_PROXY_PROTOCOL"` 66 ProxyProtocolTLSBridging bool //`ini:"PROXY_PROTOCOL_TLS_BRIDGING"` 67 ProxyProtocolHeaderTimeout time.Duration 68 ProxyProtocolAcceptUnknown bool 69 Domain string 70 HTTPAddr string 71 HTTPPort string 72 LocalUseProxyProtocol bool 73 RedirectOtherPort bool 74 RedirectorUseProxyProtocol bool 75 PortToRedirect string 76 OfflineMode bool 77 CertFile string 78 KeyFile string 79 StaticRootPath string 80 StaticCacheTime time.Duration 81 EnableGzip bool 82 LandingPageURL LandingPage 83 UnixSocketPermission uint32 84 EnablePprof bool 85 PprofDataPath string 86 EnableAcme bool 87 AcmeTOS bool 88 AcmeLiveDirectory string 89 AcmeEmail string 90 AcmeURL string 91 AcmeCARoot string 92 SSLMinimumVersion string 93 SSLMaximumVersion string 94 SSLCurvePreferences []string 95 SSLCipherSuites []string 96 GracefulRestartable bool 97 GracefulHammerTime time.Duration 98 StartupTimeout time.Duration 99 PerWriteTimeout = 30 * time.Second 100 PerWritePerKbTimeout = 10 * time.Second 101 StaticURLPrefix string 102 AbsoluteAssetURL string 103 104 ManifestData string 105 ) 106 107 // MakeManifestData generates web app manifest JSON 108 func MakeManifestData(appName, appURL, absoluteAssetURL string) []byte { 109 type manifestIcon struct { 110 Src string `json:"src"` 111 Type string `json:"type"` 112 Sizes string `json:"sizes"` 113 } 114 115 type manifestJSON struct { 116 Name string `json:"name"` 117 ShortName string `json:"short_name"` 118 StartURL string `json:"start_url"` 119 Icons []manifestIcon `json:"icons"` 120 } 121 122 bytes, err := json.Marshal(&manifestJSON{ 123 Name: appName, 124 ShortName: appName, 125 StartURL: appURL, 126 Icons: []manifestIcon{ 127 { 128 Src: absoluteAssetURL + "/assets/img/logo.png", 129 Type: "image/png", 130 Sizes: "512x512", 131 }, 132 { 133 Src: absoluteAssetURL + "/assets/img/logo.svg", 134 Type: "image/svg+xml", 135 Sizes: "512x512", 136 }, 137 }, 138 }) 139 if err != nil { 140 log.Error("unable to marshal manifest JSON. Error: %v", err) 141 return make([]byte, 0) 142 } 143 144 return bytes 145 } 146 147 // MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash 148 func MakeAbsoluteAssetURL(appURL, staticURLPrefix string) string { 149 parsedPrefix, err := url.Parse(strings.TrimSuffix(staticURLPrefix, "/")) 150 if err != nil { 151 log.Fatal("Unable to parse STATIC_URL_PREFIX: %v", err) 152 } 153 154 if err == nil && parsedPrefix.Hostname() == "" { 155 if staticURLPrefix == "" { 156 return strings.TrimSuffix(appURL, "/") 157 } 158 159 // StaticURLPrefix is just a path 160 return util.URLJoin(appURL, strings.TrimSuffix(staticURLPrefix, "/")) 161 } 162 163 return strings.TrimSuffix(staticURLPrefix, "/") 164 } 165 166 func loadServerFrom(rootCfg ConfigProvider) { 167 sec := rootCfg.Section("server") 168 AppName = rootCfg.Section("").Key("APP_NAME").MustString("Gitea: Git with a cup of tea") 169 170 Domain = sec.Key("DOMAIN").MustString("localhost") 171 HTTPAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0") 172 HTTPPort = sec.Key("HTTP_PORT").MustString("3000") 173 174 Protocol = HTTP 175 protocolCfg := sec.Key("PROTOCOL").String() 176 switch protocolCfg { 177 case "https": 178 Protocol = HTTPS 179 180 // DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version 181 // if these are removed, the warning will not be shown 182 if sec.HasKey("ENABLE_ACME") { 183 EnableAcme = sec.Key("ENABLE_ACME").MustBool(false) 184 } else { 185 deprecatedSetting(rootCfg, "server", "ENABLE_LETSENCRYPT", "server", "ENABLE_ACME", "v1.19.0") 186 EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false) 187 } 188 if EnableAcme { 189 AcmeURL = sec.Key("ACME_URL").MustString("") 190 AcmeCARoot = sec.Key("ACME_CA_ROOT").MustString("") 191 192 if sec.HasKey("ACME_ACCEPTTOS") { 193 AcmeTOS = sec.Key("ACME_ACCEPTTOS").MustBool(false) 194 } else { 195 deprecatedSetting(rootCfg, "server", "LETSENCRYPT_ACCEPTTOS", "server", "ACME_ACCEPTTOS", "v1.19.0") 196 AcmeTOS = sec.Key("LETSENCRYPT_ACCEPTTOS").MustBool(false) 197 } 198 if !AcmeTOS { 199 log.Fatal("ACME TOS is not accepted (ACME_ACCEPTTOS).") 200 } 201 202 if sec.HasKey("ACME_DIRECTORY") { 203 AcmeLiveDirectory = sec.Key("ACME_DIRECTORY").MustString("https") 204 } else { 205 deprecatedSetting(rootCfg, "server", "LETSENCRYPT_DIRECTORY", "server", "ACME_DIRECTORY", "v1.19.0") 206 AcmeLiveDirectory = sec.Key("LETSENCRYPT_DIRECTORY").MustString("https") 207 } 208 209 if sec.HasKey("ACME_EMAIL") { 210 AcmeEmail = sec.Key("ACME_EMAIL").MustString("") 211 } else { 212 deprecatedSetting(rootCfg, "server", "LETSENCRYPT_EMAIL", "server", "ACME_EMAIL", "v1.19.0") 213 AcmeEmail = sec.Key("LETSENCRYPT_EMAIL").MustString("") 214 } 215 } else { 216 CertFile = sec.Key("CERT_FILE").String() 217 KeyFile = sec.Key("KEY_FILE").String() 218 if len(CertFile) > 0 && !filepath.IsAbs(CertFile) { 219 CertFile = filepath.Join(CustomPath, CertFile) 220 } 221 if len(KeyFile) > 0 && !filepath.IsAbs(KeyFile) { 222 KeyFile = filepath.Join(CustomPath, KeyFile) 223 } 224 } 225 SSLMinimumVersion = sec.Key("SSL_MIN_VERSION").MustString("") 226 SSLMaximumVersion = sec.Key("SSL_MAX_VERSION").MustString("") 227 SSLCurvePreferences = sec.Key("SSL_CURVE_PREFERENCES").Strings(",") 228 SSLCipherSuites = sec.Key("SSL_CIPHER_SUITES").Strings(",") 229 case "fcgi": 230 Protocol = FCGI 231 case "fcgi+unix", "unix", "http+unix": 232 switch protocolCfg { 233 case "fcgi+unix": 234 Protocol = FCGIUnix 235 case "unix": 236 log.Warn("unix PROTOCOL value is deprecated, please use http+unix") 237 fallthrough 238 case "http+unix": 239 Protocol = HTTPUnix 240 } 241 UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666") 242 UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32) 243 if err != nil || UnixSocketPermissionParsed > 0o777 { 244 log.Fatal("Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw) 245 } 246 247 UnixSocketPermission = uint32(UnixSocketPermissionParsed) 248 if !filepath.IsAbs(HTTPAddr) { 249 HTTPAddr = filepath.Join(AppWorkPath, HTTPAddr) 250 } 251 } 252 UseProxyProtocol = sec.Key("USE_PROXY_PROTOCOL").MustBool(false) 253 ProxyProtocolTLSBridging = sec.Key("PROXY_PROTOCOL_TLS_BRIDGING").MustBool(false) 254 ProxyProtocolHeaderTimeout = sec.Key("PROXY_PROTOCOL_HEADER_TIMEOUT").MustDuration(5 * time.Second) 255 ProxyProtocolAcceptUnknown = sec.Key("PROXY_PROTOCOL_ACCEPT_UNKNOWN").MustBool(false) 256 GracefulRestartable = sec.Key("ALLOW_GRACEFUL_RESTARTS").MustBool(true) 257 GracefulHammerTime = sec.Key("GRACEFUL_HAMMER_TIME").MustDuration(60 * time.Second) 258 StartupTimeout = sec.Key("STARTUP_TIMEOUT").MustDuration(0 * time.Second) 259 PerWriteTimeout = sec.Key("PER_WRITE_TIMEOUT").MustDuration(PerWriteTimeout) 260 PerWritePerKbTimeout = sec.Key("PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout) 261 262 defaultAppURL := string(Protocol) + "://" + Domain + ":" + HTTPPort 263 AppURL = sec.Key("ROOT_URL").MustString(defaultAppURL) 264 265 // Check validity of AppURL 266 appURL, err := url.Parse(AppURL) 267 if err != nil { 268 log.Fatal("Invalid ROOT_URL '%s': %s", AppURL, err) 269 } 270 // Remove default ports from AppURL. 271 // (scheme-based URL normalization, RFC 3986 section 6.2.3) 272 if (appURL.Scheme == string(HTTP) && appURL.Port() == "80") || (appURL.Scheme == string(HTTPS) && appURL.Port() == "443") { 273 appURL.Host = appURL.Hostname() 274 } 275 // This should be TrimRight to ensure that there is only a single '/' at the end of AppURL. 276 AppURL = strings.TrimRight(appURL.String(), "/") + "/" 277 278 // Suburl should start with '/' and end without '/', such as '/{subpath}'. 279 // This value is empty if site does not have sub-url. 280 AppSubURL = strings.TrimSuffix(appURL.Path, "/") 281 StaticURLPrefix = strings.TrimSuffix(sec.Key("STATIC_URL_PREFIX").MustString(AppSubURL), "/") 282 283 // Check if Domain differs from AppURL domain than update it to AppURL's domain 284 urlHostname := appURL.Hostname() 285 if urlHostname != Domain && net.ParseIP(urlHostname) == nil && urlHostname != "" { 286 Domain = urlHostname 287 } 288 289 AbsoluteAssetURL = MakeAbsoluteAssetURL(AppURL, StaticURLPrefix) 290 AssetVersion = strings.ReplaceAll(AppVer, "+", "~") // make sure the version string is clear (no real escaping is needed) 291 292 manifestBytes := MakeManifestData(AppName, AppURL, AbsoluteAssetURL) 293 ManifestData = `application/json;base64,` + base64.StdEncoding.EncodeToString(manifestBytes) 294 295 var defaultLocalURL string 296 switch Protocol { 297 case HTTPUnix: 298 defaultLocalURL = "http://unix/" 299 case FCGI: 300 defaultLocalURL = AppURL 301 case FCGIUnix: 302 defaultLocalURL = AppURL 303 default: 304 defaultLocalURL = string(Protocol) + "://" 305 if HTTPAddr == "0.0.0.0" { 306 defaultLocalURL += net.JoinHostPort("localhost", HTTPPort) + "/" 307 } else { 308 defaultLocalURL += net.JoinHostPort(HTTPAddr, HTTPPort) + "/" 309 } 310 } 311 LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL) 312 LocalURL = strings.TrimRight(LocalURL, "/") + "/" 313 LocalUseProxyProtocol = sec.Key("LOCAL_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol) 314 RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false) 315 PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80") 316 RedirectorUseProxyProtocol = sec.Key("REDIRECTOR_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol) 317 OfflineMode = sec.Key("OFFLINE_MODE").MustBool(true) 318 if len(StaticRootPath) == 0 { 319 StaticRootPath = AppWorkPath 320 } 321 StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(StaticRootPath) 322 StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour) 323 AppDataPath = sec.Key("APP_DATA_PATH").MustString(filepath.Join(AppWorkPath, "data")) 324 if !filepath.IsAbs(AppDataPath) { 325 AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath)) 326 } 327 328 EnableGzip = sec.Key("ENABLE_GZIP").MustBool() 329 EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false) 330 PprofDataPath = sec.Key("PPROF_DATA_PATH").MustString(filepath.Join(AppWorkPath, "data/tmp/pprof")) 331 if !filepath.IsAbs(PprofDataPath) { 332 PprofDataPath = filepath.Join(AppWorkPath, PprofDataPath) 333 } 334 checkOverlappedPath("[server].PPROF_DATA_PATH", PprofDataPath) 335 336 landingPage := sec.Key("LANDING_PAGE").MustString("home") 337 switch landingPage { 338 case "explore": 339 LandingPageURL = LandingPageExplore 340 case "organizations": 341 LandingPageURL = LandingPageOrganizations 342 case "login": 343 LandingPageURL = LandingPageLogin 344 case "", "home": 345 LandingPageURL = LandingPageHome 346 default: 347 LandingPageURL = LandingPage(landingPage) 348 } 349 }