setting.go 29 KB

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