setting.go 29 KB

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