package main
import (
"encoding/json"
"io/ioutil"
"net"
"net/http"
"strings"
"time"
"github.com/kelseyhightower/envconfig"
uuid "github.com/nu7hatch/gouuid"
"github.com/TykTechnologies/tyk/apidef"
)
type PoliciesConfig struct {
PolicySource string `json:"policy_source"`
PolicyConnectionString string `json:"policy_connection_string"`
PolicyRecordName string `json:"policy_record_name"`
AllowExplicitPolicyID bool `json:"allow_explicit_policy_id"`
}
type DBAppConfOptionsConfig struct {
ConnectionString string `json:"connection_string"`
NodeIsSegmented bool `json:"node_is_segmented"`
Tags []string `json:"tags"`
}
type EnvMapString map[string]string
func (e *EnvMapString) Decode(value string) error {
units := strings.Split(value, ",")
m := make(map[string]string)
for _, unit := range units {
kvArr := strings.Split(unit, ":")
if len(kvArr) > 1 {
m[kvArr[0]] = kvArr[1]
}
}
*e = m
return nil
}
type StorageOptionsConf struct {
Type string `json:"type"`
Host string `json:"host"`
Port int `json:"port"`
Hosts EnvMapString `json:"hosts"`
Username string `json:"username"`
Password string `json:"password"`
Database int `json:"database"`
MaxIdle int `json:"optimisation_max_idle"`
MaxActive int `json:"optimisation_max_active"`
EnableCluster bool `json:"enable_cluster"`
}
type NormalisedURLConfig struct {
Enabled bool `json:"enabled"`
NormaliseUUIDs bool `json:"normalise_uuids"`
NormaliseNumbers bool `json:"normalise_numbers"`
Custom []string `json:"custom_patterns"`
compiledPatternSet NormaliseURLPatterns // see analytics.go
}
type AnalyticsConfigConfig struct {
Type string `json:"type"`
IgnoredIPs []string `json:"ignored_ips"`
EnableDetailedRecording bool `json:"enable_detailed_recording"`
EnableGeoIP bool `json:"enable_geo_ip"`
GeoIPDBLocation string `json:"geo_ip_db_path"`
NormaliseUrls NormalisedURLConfig `json:"normalise_urls"`
PoolSize int `json:"pool_size"`
ignoredIPsCompiled map[string]bool
}
type HealthCheckConfig struct {
EnableHealthChecks bool `json:"enable_health_checks"`
HealthCheckValueTimeout int64 `json:"health_check_value_timeouts"`
}
type MonitorConfig struct {
EnableTriggerMonitors bool `json:"enable_trigger_monitors"`
Config WebHookHandlerConf `json:"configuration"`
GlobalTriggerLimit float64 `json:"global_trigger_limit"`
MonitorUserKeys bool `json:"monitor_user_keys"`
MonitorOrgKeys bool `json:"monitor_org_keys"`
}
type SlaveOptionsConfig struct {
UseRPC bool `json:"use_rpc"`
ConnectionString string `json:"connection_string"`
RPCKey string `json:"rpc_key"`
APIKey string `json:"api_key"`
EnableRPCCache bool `json:"enable_rpc_cache"`
BindToSlugsInsteadOfListenPaths bool `json:"bind_to_slugs"`
DisableKeySpaceSync bool `json:"disable_keyspace_sync"`
GroupID string `json:"group_id"`
CallTimeout int `json:"call_timeout"`
PingTimeout int `json:"ping_timeout"`
}
type LocalSessionCacheConf struct {
DisableCacheSessionState bool `json:"disable_cached_session_state"`
CachedSessionTimeout int `json:"cached_session_timeout"`
CacheSessionEviction int `json:"cached_session_eviction"`
}
type HttpServerOptionsConfig struct {
OverrideDefaults bool `json:"override_defaults"`
ReadTimeout int `json:"read_timeout"`
WriteTimeout int `json:"write_timeout"`
UseSSL bool `json:"use_ssl"`
UseLE_SSL bool `json:"use_ssl_le"`
EnableWebSockets bool `json:"enable_websockets"`
Certificates []CertData `json:"certificates"`
ServerName string `json:"server_name"`
MinVersion uint16 `json:"min_version"`
FlushInterval int `json:"flush_interval"`
SkipURLCleaning bool `json:"skip_url_cleaning"`
}
type AuthOverrideConf struct {
ForceAuthProvider bool `json:"force_auth_provider"`
AuthProvider apidef.AuthProviderMeta `json:"auth_provider"`
ForceSessionProvider bool `json:"force_session_provider"`
SessionProvider apidef.SessionProviderMeta `json:"session_provider"`
}
type UptimeTestsConfigDetail struct {
FailureTriggerSampleSize int `json:"failure_trigger_sample_size"`
TimeWait int `json:"time_wait"`
CheckerPoolSize int `json:"checker_pool_size"`
EnableUptimeAnalytics bool `json:"enable_uptime_analytics"`
}
type UptimeTestsConfig struct {
Disable bool `json:"disable"`
Config UptimeTestsConfigDetail `json:"config"`
}
type ServiceDiscoveryConf struct {
DefaultCacheTimeout int `json:"default_cache_timeout"`
}
type CoProcessConfig struct {
EnableCoProcess bool `json:"enable_coprocess"`
CoProcessGRPCServer string `json:"coprocess_grpc_server"`
}
// Config is the configuration object used by tyk to set up various parameters.
type Config struct {
ListenAddress string `json:"listen_address"`
ListenPort int `json:"listen_port"`
Secret string `json:"secret"`
NodeSecret string `json:"node_secret"`
TemplatePath string `json:"template_path"`
TykJSPath string `json:"tyk_js_path"`
MiddlewarePath string `json:"middleware_path"`
Policies PoliciesConfig `json:"policies"`
UseDBAppConfigs bool `json:"use_db_app_configs"`
DBAppConfOptions DBAppConfOptionsConfig `json:"db_app_conf_options"`
DisableDashboardZeroConf bool `json:"disable_dashboard_zeroconf"`
AppPath string `json:"app_path"`
Storage StorageOptionsConf `json:"storage"`
EnableSeperateCacheStore bool `json:"enable_separate_cache_store"`
CacheStorage StorageOptionsConf `json:"cache_storage"`
EnableAnalytics bool `json:"enable_analytics"`
AnalyticsConfig AnalyticsConfigConfig `json:"analytics_config"`
HealthCheck HealthCheckConfig `json:"health_check"`
UseAsyncSessionWrite bool `json:"optimisations_use_async_session_write"`
AllowMasterKeys bool `json:"allow_master_keys"`
HashKeys bool `json:"hash_keys"`
SuppressRedisSignalReload bool `json:"suppress_redis_signal_reload"`
SupressDefaultOrgStore bool `json:"suppress_default_org_store"`
UseRedisLog bool `json:"use_redis_log"`
SentryCode string `json:"sentry_code"`
UseSentry bool `json:"use_sentry"`
UseSyslog bool `json:"use_syslog"`
UseGraylog bool `json:"use_graylog"`
UseLogstash bool `json:"use_logstash"`
GraylogNetworkAddr string `json:"graylog_network_addr"`
LogstashNetworkAddr string `json:"logstash_network_addr"`
SyslogTransport string `json:"syslog_transport"`
LogstashTransport string `json:"logstash_transport"`
SyslogNetworkAddr string `json:"syslog_network_addr"`
StatsdConnectionString string `json:"statsd_connection_string"`
StatsdPrefix string `json:"statsd_prefix"`
EnforceOrgDataAge bool `json:"enforce_org_data_age"`
EnforceOrgDataDeailLogging bool `json:"enforce_org_data_detail_logging"`
EnforceOrgQuotas bool `json:"enforce_org_quotas"`
ExperimentalProcessOrgOffThread bool `json:"experimental_process_org_off_thread"`
EnableNonTransactionalRateLimiter bool `json:"enable_non_transactional_rate_limiter"`
EnableSentinelRateLImiter bool `json:"enable_sentinel_rate_limiter"`
EnableRedisRollingLimiter bool `json:"enable_redis_rolling_limiter"`
Monitor MonitorConfig
OauthRefreshExpire int64 `json:"oauth_refresh_token_expire"`
OauthTokenExpire int32 `json:"oauth_token_expire"`
OauthRedirectUriSeparator string `json:"oauth_redirect_uri_separator"`
SlaveOptions SlaveOptionsConfig `json:"slave_options"`
DisableVirtualPathBlobs bool `json:"disable_virtual_path_blobs"`
LocalSessionCache LocalSessionCacheConf `json:"local_session_cache"`
HttpServerOptions HttpServerOptionsConfig `json:"http_server_options"`
ServiceDiscovery ServiceDiscoveryConf `json:"service_discovery"`
CloseConnections bool `json:"close_connections"`
AuthOverride AuthOverrideConf `json:"auth_override"`
UptimeTests UptimeTestsConfig `json:"uptime_tests"`
HostName string `json:"hostname"`
EnableAPISegregation bool `json:"enable_api_segregation"`
ControlAPIHostname string `json:"control_api_hostname"`
ControlAPIPort int `json:"control_api_port"`
EnableCustomDomains bool `json:"enable_custom_domains"`
EnableJSVM bool `json:"enable_jsvm"`
CoProcessOptions CoProcessConfig `json:"coprocess_options"`
HideGeneratorHeader bool `json:"hide_generator_header"`
EventHandlers apidef.EventHandlerMetaConfig `json:"event_handlers"`
EventTriggers map[apidef.TykEvent][]TykEventHandler `json:"event_trigers_defunct"`
PIDFileLocation string `json:"pid_file_location"`
AllowInsecureConfigs bool `json:"allow_insecure_configs"`
PublicKeyPath string `json:"public_key_path"`
CloseIdleConnections bool `json:"close_idle_connections"`
DRLNotificationFrequency int `json:"drl_notification_frequency"`
GlobalSessionLifetime int64 `bson:"global_session_lifetime" json:"global_session_lifetime"`
ForceGlobalSessionLifetime bool `bson:"force_global_session_lifetime" json:"force_global_session_lifetime"`
BundleBaseURL string `bson:"bundle_base_url" json:"bundle_base_url"`
EnableBundleDownloader bool `bson:"enable_bundle_downloader" json:"enable_bundle_downloader"`
AllowRemoteConfig bool `bson:"allow_remote_config" json:"allow_remote_config"`
LegacyEnableAllowanceCountdown bool `bson:"legacy_enable_allowance_countdown" json:"legacy_enable_allowance_countdown"`
MaxIdleConnsPerHost int `bson:"max_idle_connections_per_host" json:"max_idle_connections_per_host"`
ReloadWaitTime int `bson:"reload_wait_time" json:"reload_wait_time"`
}
type CertData struct {
Name string `json:"domain_name"`
CertFile string `json:"cert_file"`
KeyFile string `json:"key_file"`
}
const envPrefix = "TYK_GW"
// writeDefaultConf will create a default configuration file and set the storage type to "memory"
func writeDefaultConf(conf *Config) {
conf.ListenAddress = ""
conf.ListenPort = 8080
conf.Secret = "352d20ee67be67f6340b4c0605b044b7"
conf.TemplatePath = "./templates"
conf.TykJSPath = "./js/tyk.js"
conf.MiddlewarePath = "./middleware"
conf.Storage.Type = "redis"
conf.AppPath = "./apps/"
conf.Storage.Host = "localhost"
conf.Storage.Username = ""
conf.Storage.Password = ""
conf.Storage.Database = 0
conf.Storage.MaxIdle = 100
conf.Storage.Port = 6379
conf.EnableAnalytics = false
conf.HealthCheck.EnableHealthChecks = true
conf.HealthCheck.HealthCheckValueTimeout = 60
conf.AnalyticsConfig.IgnoredIPs = make([]string, 0)
conf.UseAsyncSessionWrite = false
conf.HideGeneratorHeader = false
conf.OauthRedirectUriSeparator = ""
if err := envconfig.Process(envPrefix, conf); err != nil {
log.Error("Failed to process environment variables: ", err)
}
newConfig, err := json.MarshalIndent(conf, "", " ")
if err != nil {
log.Error("Problem marshalling default configuration: ", err)
} else if !runningTests {
ioutil.WriteFile("tyk.conf", newConfig, 0644)
}
}
// LoadConfig will load the configuration file from filePath, if it can't open
// the file for reading, it assumes there is no configuration file and will try to create
// one on the default path (tyk.conf in the local directory)
func loadConfig(filePath string, conf *Config) {
configuration, err := ioutil.ReadFile(filePath)
if err != nil {
if !runningTests {
log.Error("Couldn't load configuration file: ", err)
log.Info("Writing a default file to ./tyk.conf")
writeDefaultConf(conf)
log.Info("Loading default configuration...")
loadConfig("tyk.conf", conf)
}
} else {
if err := json.Unmarshal(configuration, &conf); err != nil {
log.Error("Couldn't unmarshal configuration: ", err)
}
if err := envconfig.Process(envPrefix, conf); err != nil {
log.Error("Failed to process environment variables after file load: ", err)
}
}
if conf.SlaveOptions.CallTimeout == 0 {
conf.SlaveOptions.CallTimeout = 30
}
if conf.SlaveOptions.PingTimeout == 0 {
conf.SlaveOptions.PingTimeout = 60
}
GlobalRPCPingTimeout = time.Second * time.Duration(conf.SlaveOptions.PingTimeout)
GlobalRPCCallTimeout = time.Second * time.Duration(conf.SlaveOptions.CallTimeout)
conf.EventTriggers = InitGenericEventHandlers(conf.EventHandlers)
}
func (c *Config) loadIgnoredIPs() {
c.AnalyticsConfig.ignoredIPsCompiled = make(map[string]bool, len(c.AnalyticsConfig.IgnoredIPs))
for _, ip := range c.AnalyticsConfig.IgnoredIPs {
c.AnalyticsConfig.ignoredIPsCompiled[ip] = true
}
}
func (c *Config) StoreAnalytics(r *http.Request) bool {
if !c.EnableAnalytics {
return false
}
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
forwarded := r.Header.Get("X-FORWARDED-FOR")
if forwarded != "" {
ips := strings.Split(forwarded, ", ")
ip = ips[0]
}
return !c.AnalyticsConfig.ignoredIPsCompiled[ip]
}
func generateRandomNodeID() string {
u, _ := uuid.NewV4()
return "solo-" + u.String()
}