Files
grafana/pkg/setting/setting.go
T

1124 lines
30 KiB
Go
Raw Normal View History

2014-10-05 16:50:04 +02:00
// Copyright 2014 Unknwon
// Copyright 2014 Torkel Ödegaard
2014-10-04 13:33:20 +02:00
package setting
import (
"bytes"
"errors"
"fmt"
2019-02-01 11:47:21 +01:00
"net/http"
2014-10-04 13:33:20 +02:00
"net/url"
"os"
"path"
"path/filepath"
"regexp"
2014-10-04 13:33:20 +02:00
"runtime"
"strings"
"time"
2018-10-29 13:27:29 +01:00
"github.com/go-macaron/session"
ini "gopkg.in/ini.v1"
2019-05-13 14:45:54 +08:00
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/util"
2014-10-04 13:33:20 +02:00
)
type Scheme string
const (
HTTP Scheme = "http"
HTTPS Scheme = "https"
2019-08-16 16:06:54 +01:00
HTTP2 Scheme = "h2"
SOCKET Scheme = "socket"
DEFAULT_HTTP_ADDR string = "0.0.0.0"
2014-10-04 13:33:20 +02:00
)
2014-12-16 12:04:08 +01:00
const (
DEV = "development"
PROD = "production"
TEST = "test"
APP_NAME = "Grafana"
APP_NAME_ENTERPRISE = "Grafana Enterprise"
2014-12-16 12:04:08 +01:00
)
2018-11-02 10:49:46 +01:00
var (
ERR_TEMPLATE_NAME = "error"
)
// This constant corresponds to the default value for ldap_sync_ttl in .ini files
// it is used for comparision and has to be kept in sync
const (
AUTH_PROXY_SYNC_TTL = 60
)
2014-10-04 13:33:20 +02:00
var (
// App settings.
Env = DEV
AppUrl string
AppSubUrl string
ServeFromSubPath bool
InstanceName string
2014-10-04 13:33:20 +02:00
// build
BuildVersion string
BuildCommit string
2018-10-29 13:27:29 +01:00
BuildBranch string
BuildStamp int64
IsEnterprise bool
ApplicationName string
2018-11-15 14:42:09 +01:00
// packaging
Packaging = "unknown"
// Paths
2018-05-01 15:51:15 +02:00
HomePath string
PluginsPath string
CustomInitPath = "conf/custom.ini"
2014-10-04 13:33:20 +02:00
// Log settings.
2015-04-19 09:29:08 +02:00
LogConfigs []util.DynMap
2014-10-04 13:33:20 +02:00
// Http server options
Protocol Scheme
Domain string
HttpAddr, HttpPort string
SshPort int
CertFile, KeyFile string
SocketPath string
2014-10-05 16:50:04 +02:00
RouterLogging bool
2017-01-11 07:51:46 -08:00
DataProxyLogging bool
2019-01-24 14:04:21 -05:00
DataProxyTimeout int
2014-10-05 16:50:04 +02:00
StaticRootPath string
EnableGzip bool
EnforceDomain bool
2014-10-05 16:50:04 +02:00
// Security settings.
SecretKey string
DisableGravatar bool
EmailCodeValidMinutes int
DataProxyWhiteList map[string]bool
DisableBruteForceLoginProtection bool
CookieSecure bool
CookieSameSite http.SameSite
AllowEmbedding bool
XSSProtectionHeader bool
ContentTypeProtectionHeader bool
StrictTransportSecurity bool
StrictTransportSecurityMaxAge int
StrictTransportSecurityPreload bool
StrictTransportSecuritySubDomains bool
2015-10-14 17:39:57 +03:00
// Snapshots
2016-09-23 16:56:12 +02:00
ExternalSnapshotUrl string
ExternalSnapshotName string
ExternalEnabled bool
SnapShotRemoveExpired bool
2019-09-02 15:15:46 +02:00
SnapshotPublicMode bool
2015-10-14 17:39:57 +03:00
// Dashboard history
DashboardVersionsToKeep int
// User settings
2017-07-31 14:39:33 +02:00
AllowUserSignUp bool
AllowUserOrgCreate bool
AutoAssignOrg bool
AutoAssignOrgId int
2017-07-31 14:39:33 +02:00
AutoAssignOrgRole string
VerifyEmailEnabled bool
LoginHint string
PasswordHint string
2017-07-31 14:39:33 +02:00
DefaultTheme string
DisableLoginForm bool
DisableSignoutMenu bool
SignoutRedirectUrl string
2017-07-31 14:39:33 +02:00
ExternalUserMngLinkUrl string
ExternalUserMngLinkName string
ExternalUserMngInfo string
2018-05-28 16:16:48 +02:00
OAuthAutoLogin bool
ViewersCanEdit bool
// Http auth
AdminUser string
AdminPassword string
LoginCookieName string
LoginMaxLifetimeDays int
2015-01-27 15:45:27 +01:00
AnonymousEnabled bool
AnonymousOrgName string
AnonymousOrgRole string
// Auth proxy settings
AuthProxyEnabled bool
AuthProxyHeaderName string
AuthProxyHeaderProperty string
AuthProxyAutoSignUp bool
AuthProxyEnableLoginToken bool
AuthProxySyncTtl int
AuthProxyWhitelist string
AuthProxyHeaders map[string]string
// Basic Auth
BasicAuthEnabled bool
2014-10-05 16:50:04 +02:00
// Session settings.
SessionOptions session.Options
SessionConnMaxLifetime int64
2014-10-04 13:33:20 +02:00
// Global setting objects.
Raw *ini.File
2014-10-04 13:33:20 +02:00
ConfRootPath string
IsWindows bool
2014-10-06 15:31:54 -04:00
// for logging purposes
configFiles []string
appliedCommandLineProperties []string
appliedEnvOverrides []string
2015-03-22 15:14:00 -04:00
ReportingEnabled bool
CheckForUpdates bool
GoogleAnalyticsId string
GoogleTagManagerId string
2015-06-04 09:34:42 +02:00
// LDAP
LDAPEnabled bool
LDAPConfigFile string
LDAPSyncCron string
LDAPAllowSignup bool
LDAPActiveSyncEnabled bool
2015-07-10 11:10:48 +02:00
2015-09-11 01:47:33 +08:00
// QUOTA
Quota QuotaSettings
// Alerting
AlertingEnabled bool
ExecuteAlerts bool
2018-09-25 12:17:04 +02:00
AlertingRenderLimit int
AlertingErrorOrTimeout string
AlertingNoDataOrNullValues string
AlertingEvaluationTimeout time.Duration
AlertingNotificationTimeout time.Duration
AlertingMaxAttempts int
2018-04-27 11:39:14 +02:00
// Explore UI
ExploreEnabled bool
// Grafana.NET URL
GrafanaComUrl string
// S3 temp image store
S3TempImageStoreBucketUrl string
S3TempImageStoreAccessKey string
S3TempImageStoreSecretKey string
ImageUploadProvider string
2019-10-24 18:15:27 +03:00
FeatureToggles map[string]bool
2014-10-04 13:33:20 +02:00
)
2018-10-12 07:55:36 +02:00
// TODO move all global vars to this struct
type Cfg struct {
Raw *ini.File
Logger log.Logger
2018-10-12 07:55:36 +02:00
// HTTP Server Settings
AppUrl string
AppSubUrl string
ServeFromSubPath bool
2018-10-12 07:55:36 +02:00
2018-05-01 15:51:15 +02:00
// Paths
ProvisioningPath string
2018-10-12 07:55:36 +02:00
DataPath string
LogsPath string
2018-05-01 15:51:15 +02:00
// SMTP email settings
Smtp SmtpSettings
2018-05-24 15:26:27 +02:00
// Rendering
ImagesDir string
PhantomDir string
RendererUrl string
RendererCallbackUrl string
RendererLimit int
RendererLimitAlerting int
// Security
DisableInitAdminCreation bool
DisableBruteForceLoginProtection bool
CookieSecure bool
CookieSameSite http.SameSite
2018-11-01 12:07:11 +01:00
TempDataLifetime time.Duration
MetricsEndpointEnabled bool
MetricsEndpointBasicAuthUsername string
MetricsEndpointBasicAuthPassword string
MetricsEndpointDisableTotalStats bool
PluginsEnableAlpha bool
PluginsAppsSkipVerifyTLS bool
DisableSanitizeHtml bool
2018-11-01 12:07:11 +01:00
EnterpriseLicensePath string
// Auth
2019-02-07 10:51:35 +01:00
LoginCookieName string
LoginMaxInactiveLifetimeDays int
LoginMaxLifetimeDays int
TokenRotationIntervalMinutes int
2019-07-05 16:39:52 +02:00
// SAML Auth
SAMLEnabled bool
// Dataproxy
SendUserHeader bool
2019-03-03 21:48:00 +01:00
// DistributedCache
2019-03-08 20:49:16 +01:00
RemoteCacheOptions *RemoteCacheOptions
2019-03-12 07:32:47 +01:00
EditorsCanAdmin bool
2019-06-26 09:47:03 +03:00
ApiKeyMaxSecondsToLive int64
FeatureToggles map[string]bool
}
type CommandLineArgs struct {
Config string
HomePath string
Args []string
}
2014-10-04 13:33:20 +02:00
func init() {
IsWindows = runtime.GOOS == "windows"
2015-01-01 15:29:10 +01:00
}
2014-10-04 13:33:20 +02:00
func parseAppUrlAndSubUrl(section *ini.Section) (string, string, error) {
appUrl, err := valueAsString(section, "root_url", "http://localhost:3000/")
if err != nil {
return "", "", err
}
if appUrl[len(appUrl)-1] != '/' {
appUrl += "/"
}
// Check if has app suburl.
2015-01-30 14:21:32 +01:00
url, err := url.Parse(appUrl)
if err != nil {
log.Fatal(4, "Invalid root_url(%s): %s", appUrl, err)
}
appSubUrl := strings.TrimSuffix(url.Path, "/")
return appUrl, appSubUrl, nil
}
func ToAbsUrl(relativeUrl string) string {
return AppUrl + relativeUrl
}
func shouldRedactKey(s string) bool {
uppercased := strings.ToUpper(s)
2016-08-27 00:50:35 -07:00
return strings.Contains(uppercased, "PASSWORD") || strings.Contains(uppercased, "SECRET") || strings.Contains(uppercased, "PROVIDER_CONFIG")
}
func shouldRedactURLKey(s string) bool {
uppercased := strings.ToUpper(s)
return strings.Contains(uppercased, "DATABASE_URL")
}
func applyEnvVariableOverrides(file *ini.File) error {
appliedEnvOverrides = make([]string, 0)
for _, section := range file.Sections() {
for _, key := range section.Keys() {
sectionName := strings.ToUpper(strings.Replace(section.Name(), ".", "_", -1))
keyName := strings.ToUpper(strings.Replace(key.Name(), ".", "_", -1))
envKey := fmt.Sprintf("GF_%s_%s", sectionName, keyName)
envValue := os.Getenv(envKey)
if len(envValue) > 0 {
key.SetValue(envValue)
if shouldRedactKey(envKey) {
envValue = "*********"
}
if shouldRedactURLKey(envKey) {
u, err := url.Parse(envValue)
if err != nil {
return fmt.Errorf("could not parse environment variable. key: %s, value: %s. error: %v", envKey, envValue, err)
}
ui := u.User
if ui != nil {
_, exists := ui.Password()
if exists {
u.User = url.UserPassword(ui.Username(), "-redacted-")
envValue = u.String()
}
}
}
appliedEnvOverrides = append(appliedEnvOverrides, fmt.Sprintf("%s=%s", envKey, envValue))
}
}
}
return nil
}
func applyCommandLineDefaultProperties(props map[string]string, file *ini.File) {
appliedCommandLineProperties = make([]string, 0)
for _, section := range file.Sections() {
for _, key := range section.Keys() {
keyString := fmt.Sprintf("default.%s.%s", section.Name(), key.Name())
value, exists := props[keyString]
if exists {
key.SetValue(value)
if shouldRedactKey(keyString) {
value = "*********"
}
appliedCommandLineProperties = append(appliedCommandLineProperties, fmt.Sprintf("%s=%s", keyString, value))
}
}
}
}
func applyCommandLineProperties(props map[string]string, file *ini.File) {
for _, section := range file.Sections() {
2017-10-01 20:02:25 +02:00
sectionName := section.Name() + "."
2019-09-30 15:16:04 +02:00
if section.Name() == ini.DefaultSection {
2017-10-01 20:02:25 +02:00
sectionName = ""
}
for _, key := range section.Keys() {
2017-10-01 20:02:25 +02:00
keyString := sectionName + key.Name()
value, exists := props[keyString]
if exists {
appliedCommandLineProperties = append(appliedCommandLineProperties, fmt.Sprintf("%s=%s", keyString, value))
2017-10-01 20:02:25 +02:00
key.SetValue(value)
}
}
}
}
2015-02-15 14:57:49 -07:00
func getCommandLineProperties(args []string) map[string]string {
props := make(map[string]string)
2014-10-04 13:33:20 +02:00
for _, arg := range args {
if !strings.HasPrefix(arg, "cfg:") {
continue
}
trimmed := strings.TrimPrefix(arg, "cfg:")
parts := strings.Split(trimmed, "=")
if len(parts) != 2 {
2018-08-28 22:26:47 +02:00
log.Fatal(3, "Invalid command line argument. argument: %v", arg)
return nil
}
props[parts[0]] = parts[1]
}
return props
}
func makeAbsolute(path string, root string) string {
if filepath.IsAbs(path) {
return path
}
return filepath.Join(root, path)
}
func EvalEnvVarExpression(value string) string {
regex := regexp.MustCompile(`\${(\w+)}`)
return regex.ReplaceAllStringFunc(value, func(envVar string) string {
envVar = strings.TrimPrefix(envVar, "${")
envVar = strings.TrimSuffix(envVar, "}")
envValue := os.Getenv(envVar)
2017-06-05 15:20:34 +03:00
// if env variable is hostname and it is empty use os.Hostname as default
if envVar == "HOSTNAME" && envValue == "" {
envValue, _ = os.Hostname()
}
return envValue
})
}
func evalConfigValues(file *ini.File) {
for _, section := range file.Sections() {
for _, key := range section.Keys() {
key.SetValue(EvalEnvVarExpression(key.Value()))
}
}
}
func loadSpecifiedConfigFile(configFile string, masterFile *ini.File) error {
if configFile == "" {
configFile = filepath.Join(HomePath, CustomInitPath)
// return without error if custom file does not exist
if !pathExists(configFile) {
return nil
}
}
userConfig, err := ini.Load(configFile)
if err != nil {
return fmt.Errorf("Failed to parse %v, %v", configFile, err)
}
userConfig.BlockMode = false
for _, section := range userConfig.Sections() {
for _, key := range section.Keys() {
if key.Value() == "" {
continue
}
defaultSec, err := masterFile.GetSection(section.Name())
if err != nil {
defaultSec, _ = masterFile.NewSection(section.Name())
}
defaultKey, err := defaultSec.GetKey(key.Name())
if err != nil {
defaultKey, _ = defaultSec.NewKey(key.Name(), key.Value())
}
defaultKey.SetValue(key.Value())
}
}
configFiles = append(configFiles, configFile)
return nil
}
2018-10-12 07:55:36 +02:00
func (cfg *Cfg) loadConfiguration(args *CommandLineArgs) (*ini.File, error) {
var err error
// load config defaults
defaultConfigFile := path.Join(HomePath, "conf/defaults.ini")
configFiles = append(configFiles, defaultConfigFile)
// check if config file exists
if _, err := os.Stat(defaultConfigFile); os.IsNotExist(err) {
fmt.Println("Grafana-server Init Failed: Could not find config defaults, make sure homepath command line parameter is set or working directory is homepath")
os.Exit(1)
}
// load defaults
parsedFile, err := ini.Load(defaultConfigFile)
if err != nil {
fmt.Println(fmt.Sprintf("Failed to parse defaults.ini, %v", err))
os.Exit(1)
return nil, err
}
parsedFile.BlockMode = false
2016-11-18 16:43:08 +01:00
// command line props
commandLineProps := getCommandLineProperties(args.Args)
// load default overrides
applyCommandLineDefaultProperties(commandLineProps, parsedFile)
// load specified config file
err = loadSpecifiedConfigFile(args.Config, parsedFile)
if err != nil {
2019-06-04 22:00:05 +02:00
err2 := cfg.initLogging(parsedFile)
if err2 != nil {
return nil, err2
}
log.Fatal(3, err.Error())
}
2014-10-04 13:33:20 +02:00
// apply environment overrides
err = applyEnvVariableOverrides(parsedFile)
if err != nil {
return nil, err
}
// apply command line overrides
applyCommandLineProperties(commandLineProps, parsedFile)
// evaluate config values containing environment variables
evalConfigValues(parsedFile)
// update data path and logging config
dataPath, err := valueAsString(parsedFile.Section("paths"), "data", "")
if err != nil {
return nil, err
}
cfg.DataPath = makeAbsolute(dataPath, HomePath)
err = cfg.initLogging(parsedFile)
if err != nil {
return nil, err
}
return parsedFile, err
}
func pathExists(path string) bool {
_, err := os.Stat(path)
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
return false
}
func setHomePath(args *CommandLineArgs) {
if args.HomePath != "" {
HomePath = args.HomePath
return
}
HomePath, _ = filepath.Abs(".")
// check if homepath is correct
if pathExists(filepath.Join(HomePath, "conf/defaults.ini")) {
return
}
// try down one path
if pathExists(filepath.Join(HomePath, "../conf/defaults.ini")) {
HomePath = filepath.Join(HomePath, "../")
}
}
var skipStaticRootValidation = false
2015-09-11 08:58:45 +02:00
func NewCfg() *Cfg {
return &Cfg{
Logger: log.New("settings"),
Raw: ini.Empty(),
}
}
func (cfg *Cfg) validateStaticRootPath() error {
2015-09-11 08:58:45 +02:00
if skipStaticRootValidation {
return nil
}
2017-10-01 20:02:25 +02:00
if _, err := os.Stat(path.Join(StaticRootPath, "build")); err != nil {
cfg.Logger.Error("Failed to detect generated javascript files in public/build")
}
2017-10-01 20:02:25 +02:00
return nil
}
func (cfg *Cfg) Load(args *CommandLineArgs) error {
setHomePath(args)
2018-10-12 07:55:36 +02:00
iniFile, err := cfg.loadConfiguration(args)
if err != nil {
return err
}
cfg.Raw = iniFile
// Temporary keep global, to make refactor in steps
Raw = cfg.Raw
ApplicationName = APP_NAME
if IsEnterprise {
ApplicationName = APP_NAME_ENTERPRISE
}
Env, err = valueAsString(iniFile.Section(""), "app_mode", "development")
if err != nil {
return err
}
InstanceName, err = valueAsString(iniFile.Section(""), "instance_name", "unknown_instance_name")
if err != nil {
return err
}
plugins, err := valueAsString(iniFile.Section("paths"), "plugins", "")
if err != nil {
return err
}
PluginsPath = makeAbsolute(plugins, HomePath)
Provisioning, err := valueAsString(iniFile.Section("paths"), "provisioning", "")
if err != nil {
return err
}
cfg.ProvisioningPath = makeAbsolute(Provisioning, HomePath)
server := iniFile.Section("server")
AppUrl, AppSubUrl, err = parseAppUrlAndSubUrl(server)
if err != nil {
return err
}
ServeFromSubPath = server.Key("serve_from_sub_path").MustBool(false)
2018-10-12 07:55:36 +02:00
cfg.AppUrl = AppUrl
cfg.AppSubUrl = AppSubUrl
cfg.ServeFromSubPath = ServeFromSubPath
2014-10-04 13:33:20 +02:00
Protocol = HTTP
protocolStr, err := valueAsString(server, "protocol", "http")
if err != nil {
return err
}
if protocolStr == "https" {
2014-10-04 13:33:20 +02:00
Protocol = HTTPS
CertFile = server.Key("cert_file").String()
KeyFile = server.Key("cert_key").String()
2014-10-04 13:33:20 +02:00
}
2019-08-16 16:06:54 +01:00
if protocolStr == "h2" {
Protocol = HTTP2
CertFile = server.Key("cert_file").String()
KeyFile = server.Key("cert_key").String()
}
if protocolStr == "socket" {
Protocol = SOCKET
SocketPath = server.Key("socket").String()
}
Domain, err = valueAsString(server, "domain", "localhost")
if err != nil {
return err
}
HttpAddr, err = valueAsString(server, "http_addr", DEFAULT_HTTP_ADDR)
if err != nil {
return err
}
HttpPort, err = valueAsString(server, "http_port", "3000")
if err != nil {
return err
}
RouterLogging = server.Key("router_logging").MustBool(false)
2017-01-16 12:43:59 +01:00
EnableGzip = server.Key("enable_gzip").MustBool(false)
EnforceDomain = server.Key("enforce_domain").MustBool(false)
staticRoot, err := valueAsString(server, "static_root_path", "")
if err != nil {
return err
}
StaticRootPath = makeAbsolute(staticRoot, HomePath)
2015-09-11 08:58:45 +02:00
if err := cfg.validateStaticRootPath(); err != nil {
2015-09-11 08:58:45 +02:00
return err
}
2017-01-16 12:43:59 +01:00
// read data proxy settings
dataproxy := iniFile.Section("dataproxy")
2017-01-16 12:43:59 +01:00
DataProxyLogging = dataproxy.Key("logging").MustBool(false)
2019-01-24 14:04:21 -05:00
DataProxyTimeout = dataproxy.Key("timeout").MustInt(30)
cfg.SendUserHeader = dataproxy.Key("send_user_header").MustBool(false)
2017-01-16 12:43:59 +01:00
// read security settings
security := iniFile.Section("security")
SecretKey, err = valueAsString(security, "secret_key", "")
if err != nil {
return err
}
DisableGravatar = security.Key("disable_gravatar").MustBool(true)
cfg.DisableBruteForceLoginProtection = security.Key("disable_brute_force_login_protection").MustBool(false)
DisableBruteForceLoginProtection = cfg.DisableBruteForceLoginProtection
CookieSecure = security.Key("cookie_secure").MustBool(false)
cfg.CookieSecure = CookieSecure
samesiteString, err := valueAsString(security, "cookie_samesite", "lax")
if err != nil {
return err
}
validSameSiteValues := map[string]http.SameSite{
"lax": http.SameSiteLaxMode,
"strict": http.SameSiteStrictMode,
"none": http.SameSiteDefaultMode,
}
if samesite, ok := validSameSiteValues[samesiteString]; ok {
CookieSameSite = samesite
cfg.CookieSameSite = CookieSameSite
} else {
CookieSameSite = http.SameSiteLaxMode
cfg.CookieSameSite = CookieSameSite
}
AllowEmbedding = security.Key("allow_embedding").MustBool(false)
ContentTypeProtectionHeader = security.Key("x_content_type_options").MustBool(false)
XSSProtectionHeader = security.Key("x_xss_protection").MustBool(false)
StrictTransportSecurity = security.Key("strict_transport_security").MustBool(false)
StrictTransportSecurityMaxAge = security.Key("strict_transport_security_max_age_seconds").MustInt(86400)
StrictTransportSecurityPreload = security.Key("strict_transport_security_preload").MustBool(false)
StrictTransportSecuritySubDomains = security.Key("strict_transport_security_subdomains").MustBool(false)
2015-10-14 17:39:57 +03:00
// read snapshots settings
snapshots := iniFile.Section("snapshots")
ExternalSnapshotUrl, err = valueAsString(snapshots, "external_snapshot_url", "")
if err != nil {
return err
}
ExternalSnapshotName, err = valueAsString(snapshots, "external_snapshot_name", "")
if err != nil {
return err
}
2015-10-14 17:39:57 +03:00
ExternalEnabled = snapshots.Key("external_enabled").MustBool(true)
2016-09-23 16:56:12 +02:00
SnapShotRemoveExpired = snapshots.Key("snapshot_remove_expired").MustBool(true)
2019-09-02 15:15:46 +02:00
SnapshotPublicMode = snapshots.Key("public_mode").MustBool(false)
2015-10-14 17:39:57 +03:00
// read dashboard settings
dashboards := iniFile.Section("dashboards")
2017-11-15 13:36:36 +03:00
DashboardVersionsToKeep = dashboards.Key("versions_to_keep").MustInt(20)
// read data source proxy white list
DataProxyWhiteList = make(map[string]bool)
securityStr, err := valueAsString(security, "data_source_proxy_whitelist", "")
if err != nil {
return err
}
for _, hostAndIp := range util.SplitString(securityStr) {
DataProxyWhiteList[hostAndIp] = true
}
2015-01-27 15:45:27 +01:00
// admin
cfg.DisableInitAdminCreation = security.Key("disable_initial_admin_creation").MustBool(false)
AdminUser, err = valueAsString(security, "admin_user", "")
if err != nil {
return err
}
AdminPassword, err = valueAsString(security, "admin_password", "")
if err != nil {
return err
}
2015-01-27 15:45:27 +01:00
// users
users := iniFile.Section("users")
AllowUserSignUp = users.Key("allow_sign_up").MustBool(true)
AllowUserOrgCreate = users.Key("allow_org_create").MustBool(true)
AutoAssignOrg = users.Key("auto_assign_org").MustBool(true)
AutoAssignOrgId = users.Key("auto_assign_org_id").MustInt(1)
AutoAssignOrgRole = users.Key("auto_assign_org_role").In("Editor", []string{"Editor", "Admin", "Viewer"})
VerifyEmailEnabled = users.Key("verify_email_enabled").MustBool(false)
LoginHint, err = valueAsString(users, "login_hint", "")
if err != nil {
return err
}
PasswordHint, err = valueAsString(users, "password_hint", "")
if err != nil {
return err
}
DefaultTheme, err = valueAsString(users, "default_theme", "")
if err != nil {
return err
}
ExternalUserMngLinkUrl, err = valueAsString(users, "external_manage_link_url", "")
if err != nil {
return err
}
ExternalUserMngLinkName, err = valueAsString(users, "external_manage_link_name", "")
if err != nil {
return err
}
ExternalUserMngInfo, err = valueAsString(users, "external_manage_info", "")
if err != nil {
return err
}
ViewersCanEdit = users.Key("viewers_can_edit").MustBool(false)
2019-03-12 07:32:47 +01:00
cfg.EditorsCanAdmin = users.Key("editors_can_admin").MustBool(false)
// auth
auth := iniFile.Section("auth")
LoginCookieName, err = valueAsString(auth, "login_cookie_name", "grafana_session")
cfg.LoginCookieName = LoginCookieName
if err != nil {
return err
}
cfg.LoginMaxInactiveLifetimeDays = auth.Key("login_maximum_inactive_lifetime_days").MustInt(7)
LoginMaxLifetimeDays = auth.Key("login_maximum_lifetime_days").MustInt(30)
cfg.LoginMaxLifetimeDays = LoginMaxLifetimeDays
2019-06-26 09:47:03 +03:00
cfg.ApiKeyMaxSecondsToLive = auth.Key("api_key_max_seconds_to_live").MustInt64(-1)
cfg.TokenRotationIntervalMinutes = auth.Key("token_rotation_interval_minutes").MustInt(10)
if cfg.TokenRotationIntervalMinutes < 2 {
cfg.TokenRotationIntervalMinutes = 2
}
DisableLoginForm = auth.Key("disable_login_form").MustBool(false)
DisableSignoutMenu = auth.Key("disable_signout_menu").MustBool(false)
2018-05-28 16:16:48 +02:00
OAuthAutoLogin = auth.Key("oauth_auto_login").MustBool(false)
SignoutRedirectUrl, err = valueAsString(auth, "signout_redirect_url", "")
if err != nil {
return err
}
2019-07-05 16:39:52 +02:00
// SAML auth
cfg.SAMLEnabled = iniFile.Section("auth.saml").Key("enabled").MustBool(false)
2015-01-27 15:45:27 +01:00
// anonymous access
AnonymousEnabled = iniFile.Section("auth.anonymous").Key("enabled").MustBool(false)
AnonymousOrgName, err = valueAsString(iniFile.Section("auth.anonymous"), "org_name", "")
if err != nil {
return err
}
AnonymousOrgRole, err = valueAsString(iniFile.Section("auth.anonymous"), "org_role", "")
if err != nil {
return err
}
// auth proxy
authProxy := iniFile.Section("auth.proxy")
AuthProxyEnabled = authProxy.Key("enabled").MustBool(false)
2019-05-17 14:57:26 +03:00
AuthProxyHeaderName, err = valueAsString(authProxy, "header_name", "")
if err != nil {
return err
}
AuthProxyHeaderProperty, err = valueAsString(authProxy, "header_property", "")
if err != nil {
return err
}
AuthProxyAutoSignUp = authProxy.Key("auto_sign_up").MustBool(true)
AuthProxyEnableLoginToken = authProxy.Key("enable_login_token").MustBool(false)
ldapSyncVal := authProxy.Key("ldap_sync_ttl").MustInt()
syncVal := authProxy.Key("sync_ttl").MustInt()
if ldapSyncVal != AUTH_PROXY_SYNC_TTL {
AuthProxySyncTtl = ldapSyncVal
cfg.Logger.Warn("[Deprecated] the configuration setting 'ldap_sync_ttl' is deprecated, please use 'sync_ttl' instead")
} else {
AuthProxySyncTtl = syncVal
}
AuthProxyWhitelist, err = valueAsString(authProxy, "whitelist", "")
if err != nil {
return err
}
AuthProxyHeaders = make(map[string]string)
headers, err := valueAsString(authProxy, "headers", "")
if err != nil {
return err
}
for _, propertyAndHeader := range util.SplitString(headers) {
split := strings.SplitN(propertyAndHeader, ":", 2)
if len(split) == 2 {
AuthProxyHeaders[split[0]] = split[1]
}
}
2016-02-23 14:22:28 +01:00
// basic auth
authBasic := iniFile.Section("auth.basic")
2015-06-30 12:14:13 +02:00
BasicAuthEnabled = authBasic.Key("enabled").MustBool(true)
2018-05-24 15:26:27 +02:00
// Rendering
renderSec := iniFile.Section("rendering")
cfg.RendererUrl, err = valueAsString(renderSec, "server_url", "")
if err != nil {
return err
}
cfg.RendererCallbackUrl, err = valueAsString(renderSec, "callback_url", "")
if err != nil {
return err
}
2018-09-04 19:42:55 +08:00
if cfg.RendererCallbackUrl == "" {
cfg.RendererCallbackUrl = AppUrl
} else {
if cfg.RendererCallbackUrl[len(cfg.RendererCallbackUrl)-1] != '/' {
cfg.RendererCallbackUrl += "/"
}
_, err := url.Parse(cfg.RendererCallbackUrl)
if err != nil {
log.Fatal(4, "Invalid callback_url(%s): %s", cfg.RendererCallbackUrl, err)
}
}
2018-10-12 07:55:36 +02:00
cfg.ImagesDir = filepath.Join(cfg.DataPath, "png")
2018-05-24 15:26:27 +02:00
cfg.PhantomDir = filepath.Join(HomePath, "tools/phantomjs")
cfg.TempDataLifetime = iniFile.Section("paths").Key("temp_data_lifetime").MustDuration(time.Second * 3600 * 24)
cfg.MetricsEndpointEnabled = iniFile.Section("metrics").Key("enabled").MustBool(true)
cfg.MetricsEndpointBasicAuthUsername, err = valueAsString(iniFile.Section("metrics"), "basic_auth_username", "")
if err != nil {
return err
}
cfg.MetricsEndpointBasicAuthPassword, err = valueAsString(iniFile.Section("metrics"), "basic_auth_password", "")
if err != nil {
return err
}
cfg.MetricsEndpointDisableTotalStats = iniFile.Section("metrics").Key("disable_total_stats").MustBool(false)
2014-11-14 17:13:33 +01:00
analytics := iniFile.Section("analytics")
2015-03-27 17:13:44 +01:00
ReportingEnabled = analytics.Key("reporting_enabled").MustBool(true)
CheckForUpdates = analytics.Key("check_for_updates").MustBool(true)
GoogleAnalyticsId = analytics.Key("google_analytics_ua_id").String()
GoogleTagManagerId = analytics.Key("google_tag_manager_id").String()
2015-03-22 15:14:00 -04:00
alerting := iniFile.Section("alerting")
AlertingEnabled = alerting.Key("enabled").MustBool(true)
ExecuteAlerts = alerting.Key("execute_alerts").MustBool(true)
2018-09-25 12:17:04 +02:00
AlertingRenderLimit = alerting.Key("concurrent_render_limit").MustInt(5)
AlertingErrorOrTimeout, err = valueAsString(alerting, "error_or_timeout", "alerting")
if err != nil {
return err
}
AlertingNoDataOrNullValues, err = valueAsString(alerting, "nodata_or_nullvalues", "no_data")
if err != nil {
return err
}
evaluationTimeoutSeconds := alerting.Key("evaluation_timeout_seconds").MustInt64(30)
AlertingEvaluationTimeout = time.Second * time.Duration(evaluationTimeoutSeconds)
notificationTimeoutSeconds := alerting.Key("notification_timeout_seconds").MustInt64(30)
AlertingNotificationTimeout = time.Second * time.Duration(notificationTimeoutSeconds)
AlertingMaxAttempts = alerting.Key("max_attempts").MustInt(3)
explore := iniFile.Section("explore")
2019-01-28 13:02:54 +01:00
ExploreEnabled = explore.Key("enabled").MustBool(true)
2018-04-27 11:39:14 +02:00
panelsSection := iniFile.Section("panels")
cfg.DisableSanitizeHtml = panelsSection.Key("disable_sanitize_html").MustBool(false)
pluginsSection := iniFile.Section("plugins")
cfg.PluginsEnableAlpha = pluginsSection.Key("enable_alpha").MustBool(false)
cfg.PluginsAppsSkipVerifyTLS = pluginsSection.Key("app_tls_skip_verify_insecure").MustBool(false)
// Read and populate feature toggles list
featureTogglesSection := iniFile.Section("feature_toggles")
cfg.FeatureToggles = make(map[string]bool)
featuresTogglesStr, err := valueAsString(featureTogglesSection, "enable", "")
if err != nil {
return err
}
for _, feature := range util.SplitString(featuresTogglesStr) {
cfg.FeatureToggles[feature] = true
}
2019-10-24 18:15:27 +03:00
FeatureToggles = cfg.FeatureToggles
// check old location for this option
if panelsSection.Key("enable_alpha").MustBool(false) {
cfg.PluginsEnableAlpha = true
}
cfg.readLDAPConfig()
cfg.readSessionConfig()
cfg.readSmtpSettings()
cfg.readQuotaSettings()
if VerifyEmailEnabled && !cfg.Smtp.Enabled {
2017-12-28 15:51:15 +01:00
log.Warn("require_email_validation is enabled but smtp is disabled")
}
2015-09-11 08:58:45 +02:00
// check old key name
GrafanaComUrl, err = valueAsString(iniFile.Section("grafana_net"), "url", "")
if err != nil {
return err
}
if GrafanaComUrl == "" {
GrafanaComUrl, err = valueAsString(iniFile.Section("grafana_com"), "url", "https://grafana.com")
if err != nil {
return err
}
}
imageUploadingSection := iniFile.Section("external_image_storage")
ImageUploadProvider, err = valueAsString(imageUploadingSection, "provider", "")
if err != nil {
return err
}
2018-11-01 12:07:11 +01:00
enterprise := iniFile.Section("enterprise")
cfg.EnterpriseLicensePath, err = valueAsString(enterprise, "license_path", filepath.Join(cfg.DataPath, "license.jwt"))
if err != nil {
return err
}
2018-11-01 12:07:11 +01:00
2019-03-08 20:49:16 +01:00
cacheServer := iniFile.Section("remote_cache")
dbName, err := valueAsString(cacheServer, "type", "database")
if err != nil {
return err
}
connStr, err := valueAsString(cacheServer, "connstr", "")
if err != nil {
return err
}
2019-03-08 20:49:16 +01:00
cfg.RemoteCacheOptions = &RemoteCacheOptions{
Name: dbName,
ConnStr: connStr,
2019-03-03 21:48:00 +01:00
}
2015-09-11 08:58:45 +02:00
return nil
}
2014-12-30 10:28:27 +01:00
func valueAsString(section *ini.Section, keyName string, defaultValue string) (value string, err error) {
defer func() {
if err_ := recover(); err_ != nil {
err = errors.New("Invalid value for key '" + keyName + "' in configuration file")
}
}()
return section.Key(keyName).MustString(defaultValue), nil
}
2019-03-08 20:49:16 +01:00
type RemoteCacheOptions struct {
2019-03-03 21:48:00 +01:00
Name string
ConnStr string
}
func (cfg *Cfg) readLDAPConfig() {
ldapSec := cfg.Raw.Section("auth.ldap")
LDAPConfigFile = ldapSec.Key("config_file").String()
LDAPSyncCron = ldapSec.Key("sync_cron").String()
LDAPEnabled = ldapSec.Key("enabled").MustBool(false)
LDAPActiveSyncEnabled = ldapSec.Key("active_sync_enabled").MustBool(false)
LDAPAllowSignup = ldapSec.Key("allow_sign_up").MustBool(true)
}
func (cfg *Cfg) readSessionConfig() {
sec, _ := cfg.Raw.GetSection("session")
if sec != nil {
cfg.Logger.Warn(
"[Removed] Session setting was removed in v6.2, use remote_cache option instead",
)
}
2014-10-05 16:50:04 +02:00
}
func (cfg *Cfg) initLogging(file *ini.File) error {
logModeStr, err := valueAsString(file.Section("log"), "mode", "console")
if err != nil {
return err
}
2016-06-07 12:11:41 +02:00
// split on comma
logModes := strings.Split(logModeStr, ",")
2016-06-07 12:11:41 +02:00
// also try space
2018-10-12 07:55:36 +02:00
if len(logModes) == 1 {
logModes = strings.Split(logModeStr, " ")
}
logsPath, err := valueAsString(file.Section("paths"), "logs", "")
if err != nil {
return err
}
cfg.LogsPath = makeAbsolute(logsPath, HomePath)
return log.ReadLoggingConfig(logModes, cfg.LogsPath, file)
}
func (cfg *Cfg) LogConfigSources() {
var text bytes.Buffer
for _, file := range configFiles {
cfg.Logger.Info("Config loaded from", "file", file)
}
if len(appliedCommandLineProperties) > 0 {
for _, prop := range appliedCommandLineProperties {
cfg.Logger.Info("Config overridden from command line", "arg", prop)
}
}
if len(appliedEnvOverrides) > 0 {
text.WriteString("\tEnvironment variables used:\n")
for _, prop := range appliedEnvOverrides {
cfg.Logger.Info("Config overridden from Environment variable", "var", prop)
}
}
cfg.Logger.Info("Path Home", "path", HomePath)
cfg.Logger.Info("Path Data", "path", cfg.DataPath)
cfg.Logger.Info("Path Logs", "path", cfg.LogsPath)
cfg.Logger.Info("Path Plugins", "path", PluginsPath)
cfg.Logger.Info("Path Provisioning", "path", cfg.ProvisioningPath)
cfg.Logger.Info("App mode " + Env)
}
2019-10-25 15:28:26 +02:00
func IsExpressionsEnabled() bool {
v, ok := FeatureToggles["expressions"]
if !ok {
return false
}
return v
}