setting.go 25 KB

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