setting.go 23 KB

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