setting.go 28 KB

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