You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

server.go 12KB

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