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