setting.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  1. // Copyright 2014 Unknwon
  2. // Copyright 2014 Torkel Ödegaard
  3. package setting
  4. import (
  5. "bytes"
  6. "fmt"
  7. "net/http"
  8. "net/url"
  9. "os"
  10. "path"
  11. "path/filepath"
  12. "regexp"
  13. "runtime"
  14. "strings"
  15. "time"
  16. "github.com/go-macaron/session"
  17. "github.com/grafana/grafana/pkg/log"
  18. "github.com/grafana/grafana/pkg/util"
  19. ini "gopkg.in/ini.v1"
  20. )
  21. type Scheme string
  22. const (
  23. HTTP Scheme = "http"
  24. HTTPS Scheme = "https"
  25. SOCKET Scheme = "socket"
  26. DEFAULT_HTTP_ADDR string = "0.0.0.0"
  27. )
  28. const (
  29. DEV = "development"
  30. PROD = "production"
  31. TEST = "test"
  32. APP_NAME = "Grafana"
  33. APP_NAME_ENTERPRISE = "Grafana Enterprise"
  34. )
  35. var (
  36. ERR_TEMPLATE_NAME = "error"
  37. )
  38. var (
  39. // App settings.
  40. Env = DEV
  41. AppUrl string
  42. AppSubUrl string
  43. InstanceName string
  44. // build
  45. BuildVersion string
  46. BuildCommit string
  47. BuildBranch string
  48. BuildStamp int64
  49. IsEnterprise bool
  50. ApplicationName string
  51. // packaging
  52. Packaging = "unknown"
  53. // Paths
  54. HomePath string
  55. PluginsPath string
  56. CustomInitPath = "conf/custom.ini"
  57. // Log settings.
  58. LogConfigs []util.DynMap
  59. // Http server options
  60. Protocol Scheme
  61. Domain string
  62. HttpAddr, HttpPort string
  63. SshPort int
  64. CertFile, KeyFile string
  65. SocketPath string
  66. RouterLogging bool
  67. DataProxyLogging bool
  68. DataProxyTimeout int
  69. StaticRootPath string
  70. EnableGzip bool
  71. EnforceDomain bool
  72. // Security settings.
  73. SecretKey string
  74. DisableGravatar bool
  75. EmailCodeValidMinutes int
  76. DataProxyWhiteList map[string]bool
  77. DisableBruteForceLoginProtection bool
  78. CookieSecure bool
  79. CookieSameSite http.SameSite
  80. // Snapshots
  81. ExternalSnapshotUrl string
  82. ExternalSnapshotName string
  83. ExternalEnabled bool
  84. SnapShotRemoveExpired bool
  85. // Dashboard history
  86. DashboardVersionsToKeep int
  87. // User settings
  88. AllowUserSignUp bool
  89. AllowUserOrgCreate bool
  90. AutoAssignOrg bool
  91. AutoAssignOrgId int
  92. AutoAssignOrgRole string
  93. VerifyEmailEnabled bool
  94. LoginHint string
  95. PasswordHint string
  96. DefaultTheme string
  97. DisableLoginForm bool
  98. DisableSignoutMenu bool
  99. SignoutRedirectUrl string
  100. ExternalUserMngLinkUrl string
  101. ExternalUserMngLinkName string
  102. ExternalUserMngInfo string
  103. OAuthAutoLogin bool
  104. ViewersCanEdit bool
  105. // Http auth
  106. AdminUser string
  107. AdminPassword string
  108. LoginCookieName string
  109. LoginMaxLifetimeDays int
  110. AnonymousEnabled bool
  111. AnonymousOrgName string
  112. AnonymousOrgRole string
  113. // Auth proxy settings
  114. AuthProxyEnabled bool
  115. AuthProxyHeaderName string
  116. AuthProxyHeaderProperty string
  117. AuthProxyAutoSignUp bool
  118. AuthProxyLdapSyncTtl int
  119. AuthProxyWhitelist string
  120. AuthProxyHeaders map[string]string
  121. // Basic Auth
  122. BasicAuthEnabled bool
  123. // Session settings.
  124. SessionOptions session.Options
  125. SessionConnMaxLifetime int64
  126. // Global setting objects.
  127. Raw *ini.File
  128. ConfRootPath string
  129. IsWindows bool
  130. // for logging purposes
  131. configFiles []string
  132. appliedCommandLineProperties []string
  133. appliedEnvOverrides []string
  134. ReportingEnabled bool
  135. CheckForUpdates bool
  136. GoogleAnalyticsId string
  137. GoogleTagManagerId string
  138. // LDAP
  139. LdapEnabled bool
  140. LdapConfigFile string
  141. LdapAllowSignup = true
  142. // QUOTA
  143. Quota QuotaSettings
  144. // Alerting
  145. AlertingEnabled bool
  146. ExecuteAlerts bool
  147. AlertingRenderLimit int
  148. AlertingErrorOrTimeout string
  149. AlertingNoDataOrNullValues string
  150. AlertingEvaluationTimeout time.Duration
  151. AlertingNotificationTimeout time.Duration
  152. AlertingMaxAttempts int
  153. // Explore UI
  154. ExploreEnabled bool
  155. // logger
  156. logger log.Logger
  157. // Grafana.NET URL
  158. GrafanaComUrl string
  159. // S3 temp image store
  160. S3TempImageStoreBucketUrl string
  161. S3TempImageStoreAccessKey string
  162. S3TempImageStoreSecretKey string
  163. ImageUploadProvider string
  164. )
  165. // TODO move all global vars to this struct
  166. type Cfg struct {
  167. Raw *ini.File
  168. // HTTP Server Settings
  169. AppUrl string
  170. AppSubUrl string
  171. // Paths
  172. ProvisioningPath string
  173. DataPath string
  174. LogsPath string
  175. // SMTP email settings
  176. Smtp SmtpSettings
  177. // Rendering
  178. ImagesDir string
  179. PhantomDir string
  180. RendererUrl string
  181. RendererCallbackUrl string
  182. RendererLimit int
  183. RendererLimitAlerting int
  184. // Security
  185. DisableBruteForceLoginProtection bool
  186. CookieSecure bool
  187. CookieSameSite http.SameSite
  188. TempDataLifetime time.Duration
  189. MetricsEndpointEnabled bool
  190. MetricsEndpointBasicAuthUsername string
  191. MetricsEndpointBasicAuthPassword string
  192. PluginsEnableAlpha bool
  193. PluginsAppsSkipVerifyTLS bool
  194. DisableSanitizeHtml bool
  195. EnterpriseLicensePath string
  196. // Auth
  197. LoginCookieName string
  198. LoginMaxInactiveLifetimeDays int
  199. LoginMaxLifetimeDays int
  200. TokenRotationIntervalMinutes int
  201. // Dataproxy
  202. SendUserHeader bool
  203. // DistributedCache
  204. RemoteCacheOptions *RemoteCacheOptions
  205. EditorsCanAdmin bool
  206. }
  207. type CommandLineArgs struct {
  208. Config string
  209. HomePath string
  210. Args []string
  211. }
  212. func init() {
  213. IsWindows = runtime.GOOS == "windows"
  214. logger = log.New("settings")
  215. }
  216. func parseAppUrlAndSubUrl(section *ini.Section) (string, string) {
  217. appUrl := section.Key("root_url").MustString("http://localhost:3000/")
  218. if appUrl[len(appUrl)-1] != '/' {
  219. appUrl += "/"
  220. }
  221. // Check if has app suburl.
  222. url, err := url.Parse(appUrl)
  223. if err != nil {
  224. log.Fatal(4, "Invalid root_url(%s): %s", appUrl, err)
  225. }
  226. appSubUrl := strings.TrimSuffix(url.Path, "/")
  227. return appUrl, appSubUrl
  228. }
  229. func ToAbsUrl(relativeUrl string) string {
  230. return AppUrl + relativeUrl
  231. }
  232. func shouldRedactKey(s string) bool {
  233. uppercased := strings.ToUpper(s)
  234. return strings.Contains(uppercased, "PASSWORD") || strings.Contains(uppercased, "SECRET") || strings.Contains(uppercased, "PROVIDER_CONFIG")
  235. }
  236. func shouldRedactURLKey(s string) bool {
  237. uppercased := strings.ToUpper(s)
  238. return strings.Contains(uppercased, "DATABASE_URL")
  239. }
  240. func applyEnvVariableOverrides(file *ini.File) error {
  241. appliedEnvOverrides = make([]string, 0)
  242. for _, section := range file.Sections() {
  243. for _, key := range section.Keys() {
  244. sectionName := strings.ToUpper(strings.Replace(section.Name(), ".", "_", -1))
  245. keyName := strings.ToUpper(strings.Replace(key.Name(), ".", "_", -1))
  246. envKey := fmt.Sprintf("GF_%s_%s", sectionName, keyName)
  247. envValue := os.Getenv(envKey)
  248. if len(envValue) > 0 {
  249. key.SetValue(envValue)
  250. if shouldRedactKey(envKey) {
  251. envValue = "*********"
  252. }
  253. if shouldRedactURLKey(envKey) {
  254. u, err := url.Parse(envValue)
  255. if err != nil {
  256. return fmt.Errorf("could not parse environment variable. key: %s, value: %s. error: %v", envKey, envValue, err)
  257. }
  258. ui := u.User
  259. if ui != nil {
  260. _, exists := ui.Password()
  261. if exists {
  262. u.User = url.UserPassword(ui.Username(), "-redacted-")
  263. envValue = u.String()
  264. }
  265. }
  266. }
  267. appliedEnvOverrides = append(appliedEnvOverrides, fmt.Sprintf("%s=%s", envKey, envValue))
  268. }
  269. }
  270. }
  271. return nil
  272. }
  273. func applyCommandLineDefaultProperties(props map[string]string, file *ini.File) {
  274. appliedCommandLineProperties = make([]string, 0)
  275. for _, section := range file.Sections() {
  276. for _, key := range section.Keys() {
  277. keyString := fmt.Sprintf("default.%s.%s", section.Name(), key.Name())
  278. value, exists := props[keyString]
  279. if exists {
  280. key.SetValue(value)
  281. if shouldRedactKey(keyString) {
  282. value = "*********"
  283. }
  284. appliedCommandLineProperties = append(appliedCommandLineProperties, fmt.Sprintf("%s=%s", keyString, value))
  285. }
  286. }
  287. }
  288. }
  289. func applyCommandLineProperties(props map[string]string, file *ini.File) {
  290. for _, section := range file.Sections() {
  291. sectionName := section.Name() + "."
  292. if section.Name() == ini.DEFAULT_SECTION {
  293. sectionName = ""
  294. }
  295. for _, key := range section.Keys() {
  296. keyString := sectionName + key.Name()
  297. value, exists := props[keyString]
  298. if exists {
  299. appliedCommandLineProperties = append(appliedCommandLineProperties, fmt.Sprintf("%s=%s", keyString, value))
  300. key.SetValue(value)
  301. }
  302. }
  303. }
  304. }
  305. func getCommandLineProperties(args []string) map[string]string {
  306. props := make(map[string]string)
  307. for _, arg := range args {
  308. if !strings.HasPrefix(arg, "cfg:") {
  309. continue
  310. }
  311. trimmed := strings.TrimPrefix(arg, "cfg:")
  312. parts := strings.Split(trimmed, "=")
  313. if len(parts) != 2 {
  314. log.Fatal(3, "Invalid command line argument. argument: %v", arg)
  315. return nil
  316. }
  317. props[parts[0]] = parts[1]
  318. }
  319. return props
  320. }
  321. func makeAbsolute(path string, root string) string {
  322. if filepath.IsAbs(path) {
  323. return path
  324. }
  325. return filepath.Join(root, path)
  326. }
  327. func evalEnvVarExpression(value string) string {
  328. regex := regexp.MustCompile(`\${(\w+)}`)
  329. return regex.ReplaceAllStringFunc(value, func(envVar string) string {
  330. envVar = strings.TrimPrefix(envVar, "${")
  331. envVar = strings.TrimSuffix(envVar, "}")
  332. envValue := os.Getenv(envVar)
  333. // if env variable is hostname and it is empty use os.Hostname as default
  334. if envVar == "HOSTNAME" && envValue == "" {
  335. envValue, _ = os.Hostname()
  336. }
  337. return envValue
  338. })
  339. }
  340. func evalConfigValues(file *ini.File) {
  341. for _, section := range file.Sections() {
  342. for _, key := range section.Keys() {
  343. key.SetValue(evalEnvVarExpression(key.Value()))
  344. }
  345. }
  346. }
  347. func loadSpecifedConfigFile(configFile string, masterFile *ini.File) error {
  348. if configFile == "" {
  349. configFile = filepath.Join(HomePath, CustomInitPath)
  350. // return without error if custom file does not exist
  351. if !pathExists(configFile) {
  352. return nil
  353. }
  354. }
  355. userConfig, err := ini.Load(configFile)
  356. if err != nil {
  357. return fmt.Errorf("Failed to parse %v, %v", configFile, err)
  358. }
  359. userConfig.BlockMode = false
  360. for _, section := range userConfig.Sections() {
  361. for _, key := range section.Keys() {
  362. if key.Value() == "" {
  363. continue
  364. }
  365. defaultSec, err := masterFile.GetSection(section.Name())
  366. if err != nil {
  367. defaultSec, _ = masterFile.NewSection(section.Name())
  368. }
  369. defaultKey, err := defaultSec.GetKey(key.Name())
  370. if err != nil {
  371. defaultKey, _ = defaultSec.NewKey(key.Name(), key.Value())
  372. }
  373. defaultKey.SetValue(key.Value())
  374. }
  375. }
  376. configFiles = append(configFiles, configFile)
  377. return nil
  378. }
  379. func (cfg *Cfg) loadConfiguration(args *CommandLineArgs) (*ini.File, error) {
  380. var err error
  381. // load config defaults
  382. defaultConfigFile := path.Join(HomePath, "conf/defaults.ini")
  383. configFiles = append(configFiles, defaultConfigFile)
  384. // check if config file exists
  385. if _, err := os.Stat(defaultConfigFile); os.IsNotExist(err) {
  386. fmt.Println("Grafana-server Init Failed: Could not find config defaults, make sure homepath command line parameter is set or working directory is homepath")
  387. os.Exit(1)
  388. }
  389. // load defaults
  390. parsedFile, err := ini.Load(defaultConfigFile)
  391. if err != nil {
  392. fmt.Println(fmt.Sprintf("Failed to parse defaults.ini, %v", err))
  393. os.Exit(1)
  394. return nil, err
  395. }
  396. parsedFile.BlockMode = false
  397. // command line props
  398. commandLineProps := getCommandLineProperties(args.Args)
  399. // load default overrides
  400. applyCommandLineDefaultProperties(commandLineProps, parsedFile)
  401. // load specified config file
  402. err = loadSpecifedConfigFile(args.Config, parsedFile)
  403. if err != nil {
  404. cfg.initLogging(parsedFile)
  405. log.Fatal(3, err.Error())
  406. }
  407. // apply environment overrides
  408. err = applyEnvVariableOverrides(parsedFile)
  409. if err != nil {
  410. return nil, err
  411. }
  412. // apply command line overrides
  413. applyCommandLineProperties(commandLineProps, parsedFile)
  414. // evaluate config values containing environment variables
  415. evalConfigValues(parsedFile)
  416. // update data path and logging config
  417. cfg.DataPath = makeAbsolute(parsedFile.Section("paths").Key("data").String(), HomePath)
  418. cfg.initLogging(parsedFile)
  419. return parsedFile, err
  420. }
  421. func pathExists(path string) bool {
  422. _, err := os.Stat(path)
  423. if err == nil {
  424. return true
  425. }
  426. if os.IsNotExist(err) {
  427. return false
  428. }
  429. return false
  430. }
  431. func setHomePath(args *CommandLineArgs) {
  432. if args.HomePath != "" {
  433. HomePath = args.HomePath
  434. return
  435. }
  436. HomePath, _ = filepath.Abs(".")
  437. // check if homepath is correct
  438. if pathExists(filepath.Join(HomePath, "conf/defaults.ini")) {
  439. return
  440. }
  441. // try down one path
  442. if pathExists(filepath.Join(HomePath, "../conf/defaults.ini")) {
  443. HomePath = filepath.Join(HomePath, "../")
  444. }
  445. }
  446. var skipStaticRootValidation = false
  447. func validateStaticRootPath() error {
  448. if skipStaticRootValidation {
  449. return nil
  450. }
  451. if _, err := os.Stat(path.Join(StaticRootPath, "build")); err != nil {
  452. logger.Error("Failed to detect generated javascript files in public/build")
  453. }
  454. return nil
  455. }
  456. func NewCfg() *Cfg {
  457. return &Cfg{
  458. Raw: ini.Empty(),
  459. }
  460. }
  461. func (cfg *Cfg) Load(args *CommandLineArgs) error {
  462. setHomePath(args)
  463. iniFile, err := cfg.loadConfiguration(args)
  464. if err != nil {
  465. return err
  466. }
  467. cfg.Raw = iniFile
  468. // Temporary keep global, to make refactor in steps
  469. Raw = cfg.Raw
  470. ApplicationName = APP_NAME
  471. if IsEnterprise {
  472. ApplicationName = APP_NAME_ENTERPRISE
  473. }
  474. Env = iniFile.Section("").Key("app_mode").MustString("development")
  475. InstanceName = iniFile.Section("").Key("instance_name").MustString("unknown_instance_name")
  476. PluginsPath = makeAbsolute(iniFile.Section("paths").Key("plugins").String(), HomePath)
  477. cfg.ProvisioningPath = makeAbsolute(iniFile.Section("paths").Key("provisioning").String(), HomePath)
  478. server := iniFile.Section("server")
  479. AppUrl, AppSubUrl = parseAppUrlAndSubUrl(server)
  480. cfg.AppUrl = AppUrl
  481. cfg.AppSubUrl = AppSubUrl
  482. Protocol = HTTP
  483. if server.Key("protocol").MustString("http") == "https" {
  484. Protocol = HTTPS
  485. CertFile = server.Key("cert_file").String()
  486. KeyFile = server.Key("cert_key").String()
  487. }
  488. if server.Key("protocol").MustString("http") == "socket" {
  489. Protocol = SOCKET
  490. SocketPath = server.Key("socket").String()
  491. }
  492. Domain = server.Key("domain").MustString("localhost")
  493. HttpAddr = server.Key("http_addr").MustString(DEFAULT_HTTP_ADDR)
  494. HttpPort = server.Key("http_port").MustString("3000")
  495. RouterLogging = server.Key("router_logging").MustBool(false)
  496. EnableGzip = server.Key("enable_gzip").MustBool(false)
  497. EnforceDomain = server.Key("enforce_domain").MustBool(false)
  498. StaticRootPath = makeAbsolute(server.Key("static_root_path").String(), HomePath)
  499. if err := validateStaticRootPath(); err != nil {
  500. return err
  501. }
  502. // read data proxy settings
  503. dataproxy := iniFile.Section("dataproxy")
  504. DataProxyLogging = dataproxy.Key("logging").MustBool(false)
  505. DataProxyTimeout = dataproxy.Key("timeout").MustInt(30)
  506. cfg.SendUserHeader = dataproxy.Key("send_user_header").MustBool(false)
  507. // read security settings
  508. security := iniFile.Section("security")
  509. SecretKey = security.Key("secret_key").String()
  510. DisableGravatar = security.Key("disable_gravatar").MustBool(true)
  511. cfg.DisableBruteForceLoginProtection = security.Key("disable_brute_force_login_protection").MustBool(false)
  512. DisableBruteForceLoginProtection = cfg.DisableBruteForceLoginProtection
  513. CookieSecure = security.Key("cookie_secure").MustBool(false)
  514. cfg.CookieSecure = CookieSecure
  515. samesiteString := security.Key("cookie_samesite").MustString("lax")
  516. validSameSiteValues := map[string]http.SameSite{
  517. "lax": http.SameSiteLaxMode,
  518. "strict": http.SameSiteStrictMode,
  519. "none": http.SameSiteDefaultMode,
  520. }
  521. if samesite, ok := validSameSiteValues[samesiteString]; ok {
  522. CookieSameSite = samesite
  523. cfg.CookieSameSite = CookieSameSite
  524. } else {
  525. CookieSameSite = http.SameSiteLaxMode
  526. cfg.CookieSameSite = CookieSameSite
  527. }
  528. // read snapshots settings
  529. snapshots := iniFile.Section("snapshots")
  530. ExternalSnapshotUrl = snapshots.Key("external_snapshot_url").String()
  531. ExternalSnapshotName = snapshots.Key("external_snapshot_name").String()
  532. ExternalEnabled = snapshots.Key("external_enabled").MustBool(true)
  533. SnapShotRemoveExpired = snapshots.Key("snapshot_remove_expired").MustBool(true)
  534. // read dashboard settings
  535. dashboards := iniFile.Section("dashboards")
  536. DashboardVersionsToKeep = dashboards.Key("versions_to_keep").MustInt(20)
  537. // read data source proxy white list
  538. DataProxyWhiteList = make(map[string]bool)
  539. for _, hostAndIp := range util.SplitString(security.Key("data_source_proxy_whitelist").String()) {
  540. DataProxyWhiteList[hostAndIp] = true
  541. }
  542. // admin
  543. AdminUser = security.Key("admin_user").String()
  544. AdminPassword = security.Key("admin_password").String()
  545. // users
  546. users := iniFile.Section("users")
  547. AllowUserSignUp = users.Key("allow_sign_up").MustBool(true)
  548. AllowUserOrgCreate = users.Key("allow_org_create").MustBool(true)
  549. AutoAssignOrg = users.Key("auto_assign_org").MustBool(true)
  550. AutoAssignOrgId = users.Key("auto_assign_org_id").MustInt(1)
  551. AutoAssignOrgRole = users.Key("auto_assign_org_role").In("Editor", []string{"Editor", "Admin", "Viewer"})
  552. VerifyEmailEnabled = users.Key("verify_email_enabled").MustBool(false)
  553. LoginHint = users.Key("login_hint").String()
  554. PasswordHint = users.Key("password_hint").String()
  555. DefaultTheme = users.Key("default_theme").String()
  556. ExternalUserMngLinkUrl = users.Key("external_manage_link_url").String()
  557. ExternalUserMngLinkName = users.Key("external_manage_link_name").String()
  558. ExternalUserMngInfo = users.Key("external_manage_info").String()
  559. ViewersCanEdit = users.Key("viewers_can_edit").MustBool(false)
  560. cfg.EditorsCanAdmin = users.Key("editors_can_admin").MustBool(false)
  561. // auth
  562. auth := iniFile.Section("auth")
  563. LoginCookieName = auth.Key("login_cookie_name").MustString("grafana_session")
  564. cfg.LoginCookieName = LoginCookieName
  565. cfg.LoginMaxInactiveLifetimeDays = auth.Key("login_maximum_inactive_lifetime_days").MustInt(7)
  566. LoginMaxLifetimeDays = auth.Key("login_maximum_lifetime_days").MustInt(30)
  567. cfg.LoginMaxLifetimeDays = LoginMaxLifetimeDays
  568. cfg.TokenRotationIntervalMinutes = auth.Key("token_rotation_interval_minutes").MustInt(10)
  569. if cfg.TokenRotationIntervalMinutes < 2 {
  570. cfg.TokenRotationIntervalMinutes = 2
  571. }
  572. DisableLoginForm = auth.Key("disable_login_form").MustBool(false)
  573. DisableSignoutMenu = auth.Key("disable_signout_menu").MustBool(false)
  574. OAuthAutoLogin = auth.Key("oauth_auto_login").MustBool(false)
  575. SignoutRedirectUrl = auth.Key("signout_redirect_url").String()
  576. // anonymous access
  577. AnonymousEnabled = iniFile.Section("auth.anonymous").Key("enabled").MustBool(false)
  578. AnonymousOrgName = iniFile.Section("auth.anonymous").Key("org_name").String()
  579. AnonymousOrgRole = iniFile.Section("auth.anonymous").Key("org_role").String()
  580. // auth proxy
  581. authProxy := iniFile.Section("auth.proxy")
  582. AuthProxyEnabled = authProxy.Key("enabled").MustBool(false)
  583. AuthProxyHeaderName = authProxy.Key("header_name").String()
  584. AuthProxyHeaderProperty = authProxy.Key("header_property").String()
  585. AuthProxyAutoSignUp = authProxy.Key("auto_sign_up").MustBool(true)
  586. AuthProxyLdapSyncTtl = authProxy.Key("ldap_sync_ttl").MustInt()
  587. AuthProxyWhitelist = authProxy.Key("whitelist").String()
  588. AuthProxyHeaders = make(map[string]string)
  589. for _, propertyAndHeader := range util.SplitString(authProxy.Key("headers").String()) {
  590. split := strings.SplitN(propertyAndHeader, ":", 2)
  591. if len(split) == 2 {
  592. AuthProxyHeaders[split[0]] = split[1]
  593. }
  594. }
  595. // basic auth
  596. authBasic := iniFile.Section("auth.basic")
  597. BasicAuthEnabled = authBasic.Key("enabled").MustBool(true)
  598. // Rendering
  599. renderSec := iniFile.Section("rendering")
  600. cfg.RendererUrl = renderSec.Key("server_url").String()
  601. cfg.RendererCallbackUrl = renderSec.Key("callback_url").String()
  602. if cfg.RendererCallbackUrl == "" {
  603. cfg.RendererCallbackUrl = AppUrl
  604. } else {
  605. if cfg.RendererCallbackUrl[len(cfg.RendererCallbackUrl)-1] != '/' {
  606. cfg.RendererCallbackUrl += "/"
  607. }
  608. _, err := url.Parse(cfg.RendererCallbackUrl)
  609. if err != nil {
  610. log.Fatal(4, "Invalid callback_url(%s): %s", cfg.RendererCallbackUrl, err)
  611. }
  612. }
  613. cfg.ImagesDir = filepath.Join(cfg.DataPath, "png")
  614. cfg.PhantomDir = filepath.Join(HomePath, "tools/phantomjs")
  615. cfg.TempDataLifetime = iniFile.Section("paths").Key("temp_data_lifetime").MustDuration(time.Second * 3600 * 24)
  616. cfg.MetricsEndpointEnabled = iniFile.Section("metrics").Key("enabled").MustBool(true)
  617. cfg.MetricsEndpointBasicAuthUsername = iniFile.Section("metrics").Key("basic_auth_username").String()
  618. cfg.MetricsEndpointBasicAuthPassword = iniFile.Section("metrics").Key("basic_auth_password").String()
  619. analytics := iniFile.Section("analytics")
  620. ReportingEnabled = analytics.Key("reporting_enabled").MustBool(true)
  621. CheckForUpdates = analytics.Key("check_for_updates").MustBool(true)
  622. GoogleAnalyticsId = analytics.Key("google_analytics_ua_id").String()
  623. GoogleTagManagerId = analytics.Key("google_tag_manager_id").String()
  624. ldapSec := iniFile.Section("auth.ldap")
  625. LdapEnabled = ldapSec.Key("enabled").MustBool(false)
  626. LdapConfigFile = ldapSec.Key("config_file").String()
  627. LdapAllowSignup = ldapSec.Key("allow_sign_up").MustBool(true)
  628. alerting := iniFile.Section("alerting")
  629. AlertingEnabled = alerting.Key("enabled").MustBool(true)
  630. ExecuteAlerts = alerting.Key("execute_alerts").MustBool(true)
  631. AlertingRenderLimit = alerting.Key("concurrent_render_limit").MustInt(5)
  632. AlertingErrorOrTimeout = alerting.Key("error_or_timeout").MustString("alerting")
  633. AlertingNoDataOrNullValues = alerting.Key("nodata_or_nullvalues").MustString("no_data")
  634. AlertingEvaluationTimeout = alerting.Key("evaluation_timeout_seconds").MustDuration(time.Second * 30)
  635. AlertingNotificationTimeout = alerting.Key("notification_timeout_seconds").MustDuration(time.Second * 30)
  636. AlertingMaxAttempts = alerting.Key("max_attempts").MustInt(3)
  637. explore := iniFile.Section("explore")
  638. ExploreEnabled = explore.Key("enabled").MustBool(true)
  639. panelsSection := iniFile.Section("panels")
  640. cfg.DisableSanitizeHtml = panelsSection.Key("disable_sanitize_html").MustBool(false)
  641. pluginsSection := iniFile.Section("plugins")
  642. cfg.PluginsEnableAlpha = pluginsSection.Key("enable_alpha").MustBool(false)
  643. cfg.PluginsAppsSkipVerifyTLS = iniFile.Section("plugins").Key("app_tls_skip_verify_insecure").MustBool(false)
  644. // check old location for this option
  645. if panelsSection.Key("enable_alpha").MustBool(false) {
  646. cfg.PluginsEnableAlpha = true
  647. }
  648. cfg.readSessionConfig()
  649. cfg.readSmtpSettings()
  650. cfg.readQuotaSettings()
  651. if VerifyEmailEnabled && !cfg.Smtp.Enabled {
  652. log.Warn("require_email_validation is enabled but smtp is disabled")
  653. }
  654. // check old key name
  655. GrafanaComUrl = iniFile.Section("grafana_net").Key("url").MustString("")
  656. if GrafanaComUrl == "" {
  657. GrafanaComUrl = iniFile.Section("grafana_com").Key("url").MustString("https://grafana.com")
  658. }
  659. imageUploadingSection := iniFile.Section("external_image_storage")
  660. ImageUploadProvider = imageUploadingSection.Key("provider").MustString("")
  661. enterprise := iniFile.Section("enterprise")
  662. cfg.EnterpriseLicensePath = enterprise.Key("license_path").MustString(filepath.Join(cfg.DataPath, "license.jwt"))
  663. cacheServer := iniFile.Section("remote_cache")
  664. cfg.RemoteCacheOptions = &RemoteCacheOptions{
  665. Name: cacheServer.Key("type").MustString("database"),
  666. ConnStr: cacheServer.Key("connstr").MustString(""),
  667. }
  668. return nil
  669. }
  670. type RemoteCacheOptions struct {
  671. Name string
  672. ConnStr string
  673. }
  674. func (cfg *Cfg) readSessionConfig() {
  675. sec := cfg.Raw.Section("session")
  676. SessionOptions = session.Options{}
  677. SessionOptions.Provider = sec.Key("provider").In("memory", []string{"memory", "file", "redis", "mysql", "postgres", "memcache"})
  678. SessionOptions.ProviderConfig = strings.Trim(sec.Key("provider_config").String(), "\" ")
  679. SessionOptions.CookieName = sec.Key("cookie_name").MustString("grafana_sess")
  680. SessionOptions.CookiePath = AppSubUrl
  681. SessionOptions.Secure = sec.Key("cookie_secure").MustBool()
  682. SessionOptions.Gclifetime = cfg.Raw.Section("session").Key("gc_interval_time").MustInt64(86400)
  683. SessionOptions.Maxlifetime = cfg.Raw.Section("session").Key("session_life_time").MustInt64(86400)
  684. SessionOptions.IDLength = 16
  685. if SessionOptions.Provider == "file" {
  686. SessionOptions.ProviderConfig = makeAbsolute(SessionOptions.ProviderConfig, cfg.DataPath)
  687. os.MkdirAll(path.Dir(SessionOptions.ProviderConfig), os.ModePerm)
  688. }
  689. if SessionOptions.CookiePath == "" {
  690. SessionOptions.CookiePath = "/"
  691. }
  692. SessionConnMaxLifetime = cfg.Raw.Section("session").Key("conn_max_lifetime").MustInt64(14400)
  693. }
  694. func (cfg *Cfg) initLogging(file *ini.File) {
  695. // split on comma
  696. logModes := strings.Split(file.Section("log").Key("mode").MustString("console"), ",")
  697. // also try space
  698. if len(logModes) == 1 {
  699. logModes = strings.Split(file.Section("log").Key("mode").MustString("console"), " ")
  700. }
  701. cfg.LogsPath = makeAbsolute(file.Section("paths").Key("logs").String(), HomePath)
  702. log.ReadLoggingConfig(logModes, cfg.LogsPath, file)
  703. }
  704. func (cfg *Cfg) LogConfigSources() {
  705. var text bytes.Buffer
  706. for _, file := range configFiles {
  707. logger.Info("Config loaded from", "file", file)
  708. }
  709. if len(appliedCommandLineProperties) > 0 {
  710. for _, prop := range appliedCommandLineProperties {
  711. logger.Info("Config overridden from command line", "arg", prop)
  712. }
  713. }
  714. if len(appliedEnvOverrides) > 0 {
  715. text.WriteString("\tEnvironment variables used:\n")
  716. for _, prop := range appliedEnvOverrides {
  717. logger.Info("Config overridden from Environment variable", "var", prop)
  718. }
  719. }
  720. logger.Info("Path Home", "path", HomePath)
  721. logger.Info("Path Data", "path", cfg.DataPath)
  722. logger.Info("Path Logs", "path", cfg.LogsPath)
  723. logger.Info("Path Plugins", "path", PluginsPath)
  724. logger.Info("Path Provisioning", "path", cfg.ProvisioningPath)
  725. logger.Info("App mode " + Env)
  726. }