setting.go 28 KB

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