setting.go 29 KB

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