1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2024-11-24 08:12:38 +02:00
imgproxy/config.go

449 lines
12 KiB
Go
Raw Normal View History

2017-06-20 15:58:55 +02:00
package main
import (
2018-09-07 19:41:06 +02:00
"bufio"
2017-06-20 15:58:55 +02:00
"encoding/hex"
"flag"
"fmt"
2017-06-20 15:58:55 +02:00
"os"
2017-09-28 19:17:23 +02:00
"runtime"
2017-06-26 00:20:48 +02:00
"strconv"
2018-09-07 19:41:06 +02:00
"strings"
2017-06-20 15:58:55 +02:00
)
2017-06-26 00:20:48 +02:00
func intEnvConfig(i *int, name string) {
if env, err := strconv.Atoi(os.Getenv(name)); err == nil {
*i = env
}
2017-06-20 15:58:55 +02:00
}
2018-10-19 11:47:11 +02:00
func floatEnvConfig(i *float64, name string) {
if env, err := strconv.ParseFloat(os.Getenv(name), 64); err == nil {
*i = env
}
}
func megaIntEnvConfig(f *int, name string) {
if env, err := strconv.ParseFloat(os.Getenv(name), 64); err == nil {
*f = int(env * 1000000)
}
}
2017-06-26 00:20:48 +02:00
func strEnvConfig(s *string, name string) {
if env := os.Getenv(name); len(env) > 0 {
*s = env
2017-06-20 15:58:55 +02:00
}
2017-06-26 00:20:48 +02:00
}
2017-06-20 15:58:55 +02:00
func boolEnvConfig(b *bool, name string) {
*b = false
if env, err := strconv.ParseBool(os.Getenv(name)); err == nil {
*b = env
}
}
2018-11-15 14:35:06 +02:00
func hexEnvConfig(b *[]securityKey, name string) {
2017-06-26 00:20:48 +02:00
var err error
if env := os.Getenv(name); len(env) > 0 {
2018-11-15 14:35:06 +02:00
parts := strings.Split(env, ",")
keys := make([]securityKey, len(parts))
for i, part := range parts {
if keys[i], err = hex.DecodeString(part); err != nil {
2019-01-11 17:01:48 +02:00
logFatal("%s expected to be hex-encoded strings. Invalid: %s\n", name, part)
2018-11-15 14:35:06 +02:00
}
2017-06-26 00:20:48 +02:00
}
2018-11-15 14:35:06 +02:00
*b = keys
2017-06-26 00:20:48 +02:00
}
2017-06-20 15:58:55 +02:00
}
2018-11-15 14:35:06 +02:00
func hexFileConfig(b *[]securityKey, filepath string) {
2017-06-26 00:20:48 +02:00
if len(filepath) == 0 {
return
}
2017-06-20 15:58:55 +02:00
2017-06-27 10:29:57 +02:00
f, err := os.Open(filepath)
2017-06-20 15:58:55 +02:00
if err != nil {
2019-01-11 17:01:48 +02:00
logFatal("Can't open file %s\n", filepath)
2017-06-20 15:58:55 +02:00
}
2018-11-15 14:35:06 +02:00
keys := []securityKey{}
2017-06-20 15:58:55 +02:00
2018-11-15 14:35:06 +02:00
scanner := bufio.NewScanner(f)
for scanner.Scan() {
part := scanner.Text()
2017-06-26 00:20:48 +02:00
2018-11-15 14:35:06 +02:00
if len(part) == 0 {
continue
}
if key, err := hex.DecodeString(part); err == nil {
keys = append(keys, key)
} else {
2019-01-11 17:01:48 +02:00
logFatal("%s expected to contain hex-encoded strings. Invalid: %s\n", filepath, part)
2018-11-15 14:35:06 +02:00
}
}
if err := scanner.Err(); err != nil {
2019-01-11 17:01:48 +02:00
logFatal("Failed to read file %s: %s", filepath, err)
2017-06-20 15:58:55 +02:00
}
2018-11-15 14:35:06 +02:00
*b = keys
2017-06-26 00:20:48 +02:00
}
2017-06-20 15:58:55 +02:00
func presetEnvConfig(p presets, name string) {
2018-09-07 19:41:06 +02:00
if env := os.Getenv(name); len(env) > 0 {
presetStrings := strings.Split(env, ",")
for _, presetStr := range presetStrings {
2018-11-06 13:19:34 +02:00
if err := parsePreset(p, presetStr); err != nil {
2019-01-11 17:01:48 +02:00
logFatal(err.Error())
2018-11-06 13:19:34 +02:00
}
2018-09-07 19:41:06 +02:00
}
}
}
func presetFileConfig(p presets, filepath string) {
2018-09-07 19:41:06 +02:00
if len(filepath) == 0 {
return
}
f, err := os.Open(filepath)
if err != nil {
2019-01-11 17:01:48 +02:00
logFatal("Can't open file %s\n", filepath)
2018-09-07 19:41:06 +02:00
}
scanner := bufio.NewScanner(f)
for scanner.Scan() {
2018-11-06 13:19:34 +02:00
if err := parsePreset(p, scanner.Text()); err != nil {
2019-01-11 17:01:48 +02:00
logFatal(err.Error())
2018-11-06 13:19:34 +02:00
}
2018-09-07 19:41:06 +02:00
}
if err := scanner.Err(); err != nil {
2019-01-11 17:01:48 +02:00
logFatal("Failed to read presets file: %s", err)
}
2018-09-07 19:41:06 +02:00
}
2017-06-26 00:20:48 +02:00
type config struct {
2017-07-05 14:28:22 +02:00
Bind string
ReadTimeout int
2018-03-15 16:53:39 +02:00
WaitTimeout int
2017-07-05 14:28:22 +02:00
WriteTimeout int
DownloadTimeout int
Concurrency int
2017-09-15 12:29:51 +02:00
MaxClients int
2017-07-05 14:28:22 +02:00
TTL int
2017-07-03 12:10:44 +02:00
MaxSrcDimension int
MaxSrcResolution int
2019-01-21 12:36:31 +02:00
MaxSrcFileSize int
2018-11-15 16:04:12 +02:00
MaxGifFrames int
2017-06-20 15:58:55 +02:00
JpegProgressive bool
PngInterlaced bool
2017-06-27 02:41:37 +02:00
Quality int
GZipCompression int
2017-06-20 15:58:55 +02:00
2018-09-11 14:26:27 +02:00
EnableWebpDetection bool
EnforceWebp bool
EnableClientHints bool
2018-09-11 14:26:27 +02:00
2018-11-15 14:35:06 +02:00
Keys []securityKey
Salts []securityKey
AllowInsecure bool
SignatureSize int
Secret string
2018-04-26 13:22:31 +02:00
AllowOrigin string
UserAgent string
IgnoreSslVerification bool
LocalFileSystemRoot string
S3Enabled bool
2018-11-13 15:23:59 +02:00
S3Region string
S3Endpoint string
2018-10-30 14:12:56 +02:00
GCSKey string
2018-02-26 11:41:37 +02:00
ETagEnabled bool
2018-04-26 13:38:40 +02:00
BaseURL string
2018-09-07 19:41:06 +02:00
Presets presets
2018-10-19 11:47:11 +02:00
WatermarkData string
WatermarkPath string
WatermarkURL string
WatermarkOpacity float64
2018-10-25 15:24:34 +02:00
NewRelicAppName string
NewRelicKey string
2018-10-29 14:04:47 +02:00
PrometheusBind string
2018-11-14 15:41:16 +02:00
BugsnagKey string
BugsnagStage string
HoneybadgerKey string
HoneybadgerEnv string
SentryDSN string
SentryEnvironment string
SentryRelease string
FreeMemoryInterval int
DownloadBufferSize int
BufferPoolCalibrationThreshold int
2017-06-26 00:20:48 +02:00
}
2017-06-20 15:58:55 +02:00
2017-06-26 00:20:48 +02:00
var conf = config{
Bind: ":8080",
ReadTimeout: 10,
WriteTimeout: 10,
DownloadTimeout: 5,
Concurrency: runtime.NumCPU() * 2,
TTL: 3600,
IgnoreSslVerification: false,
MaxSrcResolution: 16800000,
MaxGifFrames: 1,
AllowInsecure: false,
SignatureSize: 32,
Quality: 80,
GZipCompression: 5,
UserAgent: fmt.Sprintf("imgproxy/%s", version),
ETagEnabled: false,
S3Enabled: false,
WatermarkOpacity: 1,
BugsnagStage: "production",
HoneybadgerEnv: "production",
SentryEnvironment: "production",
SentryRelease: fmt.Sprintf("imgproxy/%s", version),
FreeMemoryInterval: 10,
BufferPoolCalibrationThreshold: 1024,
2017-06-26 00:20:48 +02:00
}
func init() {
2019-01-11 17:01:48 +02:00
initSyslog()
2018-09-07 19:41:06 +02:00
keyPath := flag.String("keypath", "", "path of the file with hex-encoded key")
saltPath := flag.String("saltpath", "", "path of the file with hex-encoded salt")
presetsPath := flag.String("presets", "", "path of the file with presets")
showVersion := flag.Bool("v", false, "show version")
2017-06-26 00:20:48 +02:00
flag.Parse()
if *showVersion {
fmt.Println(version)
os.Exit(0)
}
if port := os.Getenv("PORT"); len(port) > 0 {
conf.Bind = fmt.Sprintf(":%s", port)
}
2017-06-26 00:20:48 +02:00
strEnvConfig(&conf.Bind, "IMGPROXY_BIND")
intEnvConfig(&conf.ReadTimeout, "IMGPROXY_READ_TIMEOUT")
intEnvConfig(&conf.WriteTimeout, "IMGPROXY_WRITE_TIMEOUT")
2017-07-05 14:28:22 +02:00
intEnvConfig(&conf.DownloadTimeout, "IMGPROXY_DOWNLOAD_TIMEOUT")
2017-07-04 16:05:53 +02:00
intEnvConfig(&conf.Concurrency, "IMGPROXY_CONCURRENCY")
2017-09-15 12:29:51 +02:00
intEnvConfig(&conf.MaxClients, "IMGPROXY_MAX_CLIENTS")
2017-06-26 00:20:48 +02:00
2017-07-03 12:10:44 +02:00
intEnvConfig(&conf.TTL, "IMGPROXY_TTL")
2017-06-26 00:20:48 +02:00
intEnvConfig(&conf.MaxSrcDimension, "IMGPROXY_MAX_SRC_DIMENSION")
megaIntEnvConfig(&conf.MaxSrcResolution, "IMGPROXY_MAX_SRC_RESOLUTION")
2019-01-21 12:36:31 +02:00
intEnvConfig(&conf.MaxSrcFileSize, "IMGPROXY_MAX_SRC_FILE_SIZE")
2018-11-15 16:04:12 +02:00
intEnvConfig(&conf.MaxGifFrames, "IMGPROXY_MAX_GIF_FRAMES")
2017-06-26 00:20:48 +02:00
boolEnvConfig(&conf.JpegProgressive, "IMGPROXY_JPEG_PROGRESSIVE")
boolEnvConfig(&conf.PngInterlaced, "IMGPROXY_PNG_INTERLACED")
2017-06-26 00:20:48 +02:00
intEnvConfig(&conf.Quality, "IMGPROXY_QUALITY")
2017-06-27 02:41:37 +02:00
intEnvConfig(&conf.GZipCompression, "IMGPROXY_GZIP_COMPRESSION")
2017-06-20 15:58:55 +02:00
2018-09-11 14:26:27 +02:00
boolEnvConfig(&conf.EnableWebpDetection, "IMGPROXY_ENABLE_WEBP_DETECTION")
boolEnvConfig(&conf.EnforceWebp, "IMGPROXY_ENFORCE_WEBP")
boolEnvConfig(&conf.EnableClientHints, "IMGPROXY_ENABLE_CLIENT_HINTS")
2018-09-11 14:26:27 +02:00
2018-11-15 14:35:06 +02:00
hexEnvConfig(&conf.Keys, "IMGPROXY_KEY")
hexEnvConfig(&conf.Salts, "IMGPROXY_SALT")
intEnvConfig(&conf.SignatureSize, "IMGPROXY_SIGNATURE_SIZE")
2017-06-26 00:20:48 +02:00
2018-11-15 14:35:06 +02:00
hexFileConfig(&conf.Keys, *keyPath)
hexFileConfig(&conf.Salts, *saltPath)
2017-06-26 00:20:48 +02:00
strEnvConfig(&conf.Secret, "IMGPROXY_SECRET")
2018-04-26 13:22:31 +02:00
strEnvConfig(&conf.AllowOrigin, "IMGPROXY_ALLOW_ORIGIN")
strEnvConfig(&conf.UserAgent, "IMGPROXY_USER_AGENT")
boolEnvConfig(&conf.IgnoreSslVerification, "IMGPROXY_IGNORE_SSL_VERIFICATION")
strEnvConfig(&conf.LocalFileSystemRoot, "IMGPROXY_LOCAL_FILESYSTEM_ROOT")
2018-02-26 11:41:37 +02:00
2018-05-26 16:22:41 +02:00
boolEnvConfig(&conf.S3Enabled, "IMGPROXY_USE_S3")
2018-11-13 15:23:59 +02:00
strEnvConfig(&conf.S3Region, "IMGPROXY_S3_REGION")
strEnvConfig(&conf.S3Endpoint, "IMGPROXY_S3_ENDPOINT")
2018-10-30 14:12:56 +02:00
strEnvConfig(&conf.GCSKey, "IMGPROXY_GCS_KEY")
2018-05-26 16:22:41 +02:00
boolEnvConfig(&conf.ETagEnabled, "IMGPROXY_USE_ETAG")
2018-04-26 13:38:40 +02:00
strEnvConfig(&conf.BaseURL, "IMGPROXY_BASE_URL")
2018-09-07 19:41:06 +02:00
conf.Presets = make(presets)
presetEnvConfig(conf.Presets, "IMGPROXY_PRESETS")
presetFileConfig(conf.Presets, *presetsPath)
2018-09-07 19:41:06 +02:00
2018-10-19 11:47:11 +02:00
strEnvConfig(&conf.WatermarkData, "IMGPROXY_WATERMARK_DATA")
strEnvConfig(&conf.WatermarkPath, "IMGPROXY_WATERMARK_PATH")
strEnvConfig(&conf.WatermarkURL, "IMGPROXY_WATERMARK_URL")
floatEnvConfig(&conf.WatermarkOpacity, "IMGPROXY_WATERMARK_OPACITY")
2018-10-25 15:24:34 +02:00
strEnvConfig(&conf.NewRelicAppName, "IMGPROXY_NEW_RELIC_APP_NAME")
strEnvConfig(&conf.NewRelicKey, "IMGPROXY_NEW_RELIC_KEY")
2018-10-29 14:04:47 +02:00
strEnvConfig(&conf.PrometheusBind, "IMGPROXY_PROMETHEUS_BIND")
2018-11-14 15:41:16 +02:00
strEnvConfig(&conf.BugsnagKey, "IMGPROXY_BUGSNAG_KEY")
strEnvConfig(&conf.BugsnagStage, "IMGPROXY_BUGSNAG_STAGE")
strEnvConfig(&conf.HoneybadgerKey, "IMGPROXY_HONEYBADGER_KEY")
strEnvConfig(&conf.HoneybadgerEnv, "IMGPROXY_HONEYBADGER_ENV")
strEnvConfig(&conf.SentryDSN, "IMGPROXY_SENTRY_DSN")
strEnvConfig(&conf.SentryEnvironment, "IMGPROXY_SENTRY_ENVIRONMENT")
strEnvConfig(&conf.SentryRelease, "IMGPROXY_SENTRY_RELEASE")
2018-11-14 15:41:16 +02:00
intEnvConfig(&conf.FreeMemoryInterval, "IMGPROXY_FREE_MEMORY_INTERVAL")
intEnvConfig(&conf.DownloadBufferSize, "IMGPROXY_DOWNLOAD_BUFFER_SIZE")
intEnvConfig(&conf.BufferPoolCalibrationThreshold, "IMGPROXY_BUFFER_POOL_CALIBRATION_THRESHOLD")
2018-11-15 14:35:06 +02:00
if len(conf.Keys) != len(conf.Salts) {
2019-01-11 17:01:48 +02:00
logFatal("Number of keys and number of salts should be equal. Keys: %d, salts: %d", len(conf.Keys), len(conf.Salts))
2018-11-15 14:35:06 +02:00
}
if len(conf.Keys) == 0 {
2019-01-11 17:01:48 +02:00
logWarning("No keys defined, so signature checking is disabled")
conf.AllowInsecure = true
2017-06-26 00:20:48 +02:00
}
2018-11-15 14:35:06 +02:00
if len(conf.Salts) == 0 {
2019-01-11 17:01:48 +02:00
logWarning("No salts defined, so signature checking is disabled")
conf.AllowInsecure = true
2017-06-20 15:58:55 +02:00
}
2018-11-15 14:35:06 +02:00
if conf.SignatureSize < 1 || conf.SignatureSize > 32 {
2019-01-11 17:01:48 +02:00
logFatal("Signature size should be within 1 and 32, now - %d\n", conf.SignatureSize)
}
if len(conf.Bind) == 0 {
2019-01-11 17:01:48 +02:00
logFatal("Bind address is not defined")
}
if conf.ReadTimeout <= 0 {
2019-01-11 17:01:48 +02:00
logFatal("Read timeout should be greater than 0, now - %d\n", conf.ReadTimeout)
}
if conf.WriteTimeout <= 0 {
2019-01-11 17:01:48 +02:00
logFatal("Write timeout should be greater than 0, now - %d\n", conf.WriteTimeout)
}
2017-07-05 14:28:22 +02:00
if conf.DownloadTimeout <= 0 {
2019-01-11 17:01:48 +02:00
logFatal("Download timeout should be greater than 0, now - %d\n", conf.DownloadTimeout)
2017-07-05 14:28:22 +02:00
}
2017-07-04 16:05:53 +02:00
if conf.Concurrency <= 0 {
2019-01-11 17:01:48 +02:00
logFatal("Concurrency should be greater than 0, now - %d\n", conf.Concurrency)
2017-07-04 16:05:53 +02:00
}
2017-09-15 12:29:51 +02:00
if conf.MaxClients <= 0 {
2018-03-19 11:06:08 +02:00
conf.MaxClients = conf.Concurrency * 10
2017-09-15 12:29:51 +02:00
}
2017-07-03 12:10:44 +02:00
if conf.TTL <= 0 {
2019-01-11 17:01:48 +02:00
logFatal("TTL should be greater than 0, now - %d\n", conf.TTL)
2017-07-03 12:10:44 +02:00
}
2018-11-15 15:25:53 +02:00
if conf.MaxSrcDimension < 0 {
2019-01-11 17:01:48 +02:00
logFatal("Max src dimension should be greater than or equal to 0, now - %d\n", conf.MaxSrcDimension)
2018-11-15 15:25:53 +02:00
} else if conf.MaxSrcDimension > 0 {
2019-01-11 17:01:48 +02:00
logWarning("IMGPROXY_MAX_SRC_DIMENSION is deprecated and can be removed in future versions. Use IMGPROXY_MAX_SRC_RESOLUTION")
}
if conf.MaxSrcResolution <= 0 {
2019-01-11 17:01:48 +02:00
logFatal("Max src resolution should be greater than 0, now - %d\n", conf.MaxSrcResolution)
}
2019-01-21 12:36:31 +02:00
if conf.MaxSrcFileSize < 0 {
logFatal("Max src file size should be greater than or equal to 0, now - %d\n", conf.MaxSrcFileSize)
}
2018-11-15 16:04:12 +02:00
if conf.MaxGifFrames <= 0 {
2019-01-11 17:01:48 +02:00
logFatal("Max GIF frames should be greater than 0, now - %d\n", conf.MaxGifFrames)
2018-11-15 16:04:12 +02:00
}
if conf.Quality <= 0 {
2019-01-11 17:01:48 +02:00
logFatal("Quality should be greater than 0, now - %d\n", conf.Quality)
} else if conf.Quality > 100 {
2019-01-11 17:01:48 +02:00
logFatal("Quality can't be greater than 100, now - %d\n", conf.Quality)
}
if conf.GZipCompression < 0 {
logFatal("GZip compression should be greater than or equal to 0, now - %d\n", conf.GZipCompression)
} else if conf.GZipCompression > 9 {
2019-01-11 17:01:48 +02:00
logFatal("GZip compression can't be greater than 9, now - %d\n", conf.GZipCompression)
}
2017-09-27 10:42:49 +02:00
if conf.IgnoreSslVerification {
2019-01-11 17:01:48 +02:00
logWarning("Ignoring SSL verification is very unsafe")
}
if conf.LocalFileSystemRoot != "" {
stat, err := os.Stat(conf.LocalFileSystemRoot)
if err != nil {
2019-01-11 17:01:48 +02:00
logFatal("Cannot use local directory: %s", err)
} else {
if !stat.IsDir() {
2019-01-11 17:01:48 +02:00
logFatal("Cannot use local directory: not a directory")
}
}
if conf.LocalFileSystemRoot == "/" {
2019-01-11 17:01:48 +02:00
logNotice("Exposing root via IMGPROXY_LOCAL_FILESYSTEM_ROOT is unsafe")
}
}
2018-11-06 13:19:34 +02:00
if err := checkPresets(conf.Presets); err != nil {
2019-01-11 17:01:48 +02:00
logFatal(err.Error())
2018-11-06 13:19:34 +02:00
}
2018-10-19 11:47:11 +02:00
if conf.WatermarkOpacity <= 0 {
2019-01-11 17:01:48 +02:00
logFatal("Watermark opacity should be greater than 0")
2018-10-19 11:47:11 +02:00
} else if conf.WatermarkOpacity > 1 {
2019-01-11 17:01:48 +02:00
logFatal("Watermark opacity should be less than or equal to 1")
2018-10-19 11:47:11 +02:00
}
2018-10-29 14:04:47 +02:00
if len(conf.PrometheusBind) > 0 && conf.PrometheusBind == conf.Bind {
2019-01-11 17:01:48 +02:00
logFatal("Can't use the same binding for the main server and Prometheus")
2018-10-29 14:04:47 +02:00
}
if conf.FreeMemoryInterval <= 0 {
logFatal("Free memory interval should be greater than zero")
}
if conf.DownloadBufferSize < 0 {
logFatal("Download buffer size should be greater than or equal to 0")
} else if conf.DownloadBufferSize > int(^uint32(0)) {
2019-02-01 13:53:46 +02:00
logFatal("Download buffer size can't be greater than %d", ^uint32(0))
}
if conf.BufferPoolCalibrationThreshold < 64 {
2019-02-01 13:53:46 +02:00
logFatal("Buffer pool calibration threshold should be greater than or equal to 64")
}
2018-10-25 15:24:34 +02:00
initNewrelic()
2018-10-29 14:04:47 +02:00
initPrometheus()
2019-01-28 18:18:54 +02:00
initDownloading()
2018-11-14 15:41:16 +02:00
initErrorsReporting()
2018-10-19 11:47:11 +02:00
initVips()
2017-06-20 15:58:55 +02:00
}