config.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. package main
  2. import (
  3. "bufio"
  4. "bytes"
  5. "encoding/hex"
  6. "flag"
  7. "fmt"
  8. "io/ioutil"
  9. "log"
  10. "os"
  11. "runtime"
  12. "strconv"
  13. "strings"
  14. )
  15. func intEnvConfig(i *int, name string) {
  16. if env, err := strconv.Atoi(os.Getenv(name)); err == nil {
  17. *i = env
  18. }
  19. }
  20. func floatEnvConfig(i *float64, name string) {
  21. if env, err := strconv.ParseFloat(os.Getenv(name), 64); err == nil {
  22. *i = env
  23. }
  24. }
  25. func megaIntEnvConfig(f *int, name string) {
  26. if env, err := strconv.ParseFloat(os.Getenv(name), 64); err == nil {
  27. *f = int(env * 1000000)
  28. }
  29. }
  30. func strEnvConfig(s *string, name string) {
  31. if env := os.Getenv(name); len(env) > 0 {
  32. *s = env
  33. }
  34. }
  35. func boolEnvConfig(b *bool, name string) {
  36. *b = false
  37. if env, err := strconv.ParseBool(os.Getenv(name)); err == nil {
  38. *b = env
  39. }
  40. }
  41. func hexEnvConfig(b *[]byte, name string) {
  42. var err error
  43. if env := os.Getenv(name); len(env) > 0 {
  44. if *b, err = hex.DecodeString(env); err != nil {
  45. log.Fatalf("%s expected to be hex-encoded string\n", name)
  46. }
  47. }
  48. }
  49. func hexFileConfig(b *[]byte, filepath string) {
  50. if len(filepath) == 0 {
  51. return
  52. }
  53. f, err := os.Open(filepath)
  54. if err != nil {
  55. log.Fatalf("Can't open file %s\n", filepath)
  56. }
  57. src, err := ioutil.ReadAll(f)
  58. if err != nil {
  59. log.Fatalln(err)
  60. }
  61. src = bytes.TrimSpace(src)
  62. dst := make([]byte, hex.DecodedLen(len(src)))
  63. n, err := hex.Decode(dst, src)
  64. if err != nil {
  65. log.Fatalf("%s expected to contain hex-encoded string\n", filepath)
  66. }
  67. *b = dst[:n]
  68. }
  69. func presetEnvConfig(p presets, name string) {
  70. if env := os.Getenv(name); len(env) > 0 {
  71. presetStrings := strings.Split(env, ",")
  72. for _, presetStr := range presetStrings {
  73. if err := parsePreset(p, presetStr); err != nil {
  74. log.Fatalln(err)
  75. }
  76. }
  77. }
  78. }
  79. func presetFileConfig(p presets, filepath string) {
  80. if len(filepath) == 0 {
  81. return
  82. }
  83. f, err := os.Open(filepath)
  84. if err != nil {
  85. log.Fatalf("Can't open file %s\n", filepath)
  86. }
  87. scanner := bufio.NewScanner(f)
  88. for scanner.Scan() {
  89. if err := parsePreset(p, scanner.Text()); err != nil {
  90. log.Fatalln(err)
  91. }
  92. }
  93. if err := scanner.Err(); err != nil {
  94. log.Fatalf("Failed to read presets file: %s", err)
  95. }
  96. }
  97. type config struct {
  98. Bind string
  99. ReadTimeout int
  100. WaitTimeout int
  101. WriteTimeout int
  102. DownloadTimeout int
  103. Concurrency int
  104. MaxClients int
  105. TTL int
  106. MaxSrcDimension int
  107. MaxSrcResolution int
  108. JpegProgressive bool
  109. PngInterlaced bool
  110. Quality int
  111. GZipCompression int
  112. EnableWebpDetection bool
  113. EnforceWebp bool
  114. EnableClientHints bool
  115. Key []byte
  116. Salt []byte
  117. AllowInsecure bool
  118. SignatureSize int
  119. Secret string
  120. AllowOrigin string
  121. UserAgent string
  122. IgnoreSslVerification bool
  123. LocalFileSystemRoot string
  124. S3Enabled bool
  125. S3Region string
  126. S3Endpoint string
  127. GCSKey string
  128. ETagEnabled bool
  129. BaseURL string
  130. Presets presets
  131. WatermarkData string
  132. WatermarkPath string
  133. WatermarkURL string
  134. WatermarkOpacity float64
  135. NewRelicAppName string
  136. NewRelicKey string
  137. PrometheusBind string
  138. BugsnagKey string
  139. BugsnagStage string
  140. HoneybadgerKey string
  141. HoneybadgerEnv string
  142. }
  143. var conf = config{
  144. Bind: ":8080",
  145. ReadTimeout: 10,
  146. WriteTimeout: 10,
  147. DownloadTimeout: 5,
  148. Concurrency: runtime.NumCPU() * 2,
  149. TTL: 3600,
  150. IgnoreSslVerification: false,
  151. MaxSrcDimension: 8192,
  152. MaxSrcResolution: 16800000,
  153. AllowInsecure: false,
  154. SignatureSize: 32,
  155. Quality: 80,
  156. GZipCompression: 5,
  157. UserAgent: fmt.Sprintf("imgproxy/%s", version),
  158. ETagEnabled: false,
  159. S3Enabled: false,
  160. WatermarkOpacity: 1,
  161. BugsnagStage: "production",
  162. HoneybadgerEnv: "production",
  163. }
  164. func init() {
  165. keyPath := flag.String("keypath", "", "path of the file with hex-encoded key")
  166. saltPath := flag.String("saltpath", "", "path of the file with hex-encoded salt")
  167. presetsPath := flag.String("presets", "", "path of the file with presets")
  168. showVersion := flag.Bool("v", false, "show version")
  169. flag.Parse()
  170. if *showVersion {
  171. fmt.Println(version)
  172. os.Exit(0)
  173. }
  174. if port := os.Getenv("PORT"); len(port) > 0 {
  175. conf.Bind = fmt.Sprintf(":%s", port)
  176. }
  177. strEnvConfig(&conf.Bind, "IMGPROXY_BIND")
  178. intEnvConfig(&conf.ReadTimeout, "IMGPROXY_READ_TIMEOUT")
  179. intEnvConfig(&conf.WriteTimeout, "IMGPROXY_WRITE_TIMEOUT")
  180. intEnvConfig(&conf.DownloadTimeout, "IMGPROXY_DOWNLOAD_TIMEOUT")
  181. intEnvConfig(&conf.Concurrency, "IMGPROXY_CONCURRENCY")
  182. intEnvConfig(&conf.MaxClients, "IMGPROXY_MAX_CLIENTS")
  183. intEnvConfig(&conf.TTL, "IMGPROXY_TTL")
  184. intEnvConfig(&conf.MaxSrcDimension, "IMGPROXY_MAX_SRC_DIMENSION")
  185. megaIntEnvConfig(&conf.MaxSrcResolution, "IMGPROXY_MAX_SRC_RESOLUTION")
  186. boolEnvConfig(&conf.JpegProgressive, "IMGPROXY_JPEG_PROGRESSIVE")
  187. boolEnvConfig(&conf.PngInterlaced, "IMGPROXY_PNG_INTERLACED")
  188. intEnvConfig(&conf.Quality, "IMGPROXY_QUALITY")
  189. intEnvConfig(&conf.GZipCompression, "IMGPROXY_GZIP_COMPRESSION")
  190. boolEnvConfig(&conf.EnableWebpDetection, "IMGPROXY_ENABLE_WEBP_DETECTION")
  191. boolEnvConfig(&conf.EnforceWebp, "IMGPROXY_ENFORCE_WEBP")
  192. boolEnvConfig(&conf.EnableClientHints, "IMGPROXY_ENABLE_CLIENT_HINTS")
  193. hexEnvConfig(&conf.Key, "IMGPROXY_KEY")
  194. hexEnvConfig(&conf.Salt, "IMGPROXY_SALT")
  195. intEnvConfig(&conf.SignatureSize, "IMGPROXY_SIGNATURE_SIZE")
  196. hexFileConfig(&conf.Key, *keyPath)
  197. hexFileConfig(&conf.Salt, *saltPath)
  198. strEnvConfig(&conf.Secret, "IMGPROXY_SECRET")
  199. strEnvConfig(&conf.AllowOrigin, "IMGPROXY_ALLOW_ORIGIN")
  200. strEnvConfig(&conf.UserAgent, "IMGPROXY_USER_AGENT")
  201. boolEnvConfig(&conf.IgnoreSslVerification, "IMGPROXY_IGNORE_SSL_VERIFICATION")
  202. strEnvConfig(&conf.LocalFileSystemRoot, "IMGPROXY_LOCAL_FILESYSTEM_ROOT")
  203. boolEnvConfig(&conf.S3Enabled, "IMGPROXY_USE_S3")
  204. strEnvConfig(&conf.S3Region, "IMGPROXY_S3_REGION")
  205. strEnvConfig(&conf.S3Endpoint, "IMGPROXY_S3_ENDPOINT")
  206. strEnvConfig(&conf.GCSKey, "IMGPROXY_GCS_KEY")
  207. boolEnvConfig(&conf.ETagEnabled, "IMGPROXY_USE_ETAG")
  208. strEnvConfig(&conf.BaseURL, "IMGPROXY_BASE_URL")
  209. conf.Presets = make(presets)
  210. presetEnvConfig(conf.Presets, "IMGPROXY_PRESETS")
  211. presetFileConfig(conf.Presets, *presetsPath)
  212. strEnvConfig(&conf.WatermarkData, "IMGPROXY_WATERMARK_DATA")
  213. strEnvConfig(&conf.WatermarkPath, "IMGPROXY_WATERMARK_PATH")
  214. strEnvConfig(&conf.WatermarkURL, "IMGPROXY_WATERMARK_URL")
  215. floatEnvConfig(&conf.WatermarkOpacity, "IMGPROXY_WATERMARK_OPACITY")
  216. strEnvConfig(&conf.NewRelicAppName, "IMGPROXY_NEW_RELIC_APP_NAME")
  217. strEnvConfig(&conf.NewRelicKey, "IMGPROXY_NEW_RELIC_KEY")
  218. strEnvConfig(&conf.PrometheusBind, "IMGPROXY_PROMETHEUS_BIND")
  219. strEnvConfig(&conf.BugsnagKey, "IMGPROXY_BUGSNAG_KEY")
  220. strEnvConfig(&conf.BugsnagStage, "IMGPROXY_BUGSNAG_STAGE")
  221. strEnvConfig(&conf.HoneybadgerKey, "IMGPROXY_HONEYBADGER_KEY")
  222. strEnvConfig(&conf.HoneybadgerEnv, "IMGPROXY_HONEYBADGER_ENV")
  223. if len(conf.Key) == 0 {
  224. warning("Key is not defined, so signature checking is disabled")
  225. conf.AllowInsecure = true
  226. }
  227. if len(conf.Salt) == 0 {
  228. warning("Salt is not defined, so signature checking is disabled")
  229. conf.AllowInsecure = true
  230. }
  231. if conf.SignatureSize < 1 || conf.SignatureSize > 32 {
  232. log.Fatalf("Signature size should be within 1 and 32, now - %d\n", conf.SignatureSize)
  233. }
  234. if len(conf.Bind) == 0 {
  235. log.Fatalln("Bind address is not defined")
  236. }
  237. if conf.ReadTimeout <= 0 {
  238. log.Fatalf("Read timeout should be greater than 0, now - %d\n", conf.ReadTimeout)
  239. }
  240. if conf.WriteTimeout <= 0 {
  241. log.Fatalf("Write timeout should be greater than 0, now - %d\n", conf.WriteTimeout)
  242. }
  243. if conf.DownloadTimeout <= 0 {
  244. log.Fatalf("Download timeout should be greater than 0, now - %d\n", conf.DownloadTimeout)
  245. }
  246. if conf.Concurrency <= 0 {
  247. log.Fatalf("Concurrency should be greater than 0, now - %d\n", conf.Concurrency)
  248. }
  249. if conf.MaxClients <= 0 {
  250. conf.MaxClients = conf.Concurrency * 10
  251. }
  252. if conf.TTL <= 0 {
  253. log.Fatalf("TTL should be greater than 0, now - %d\n", conf.TTL)
  254. }
  255. if conf.MaxSrcDimension <= 0 {
  256. log.Fatalf("Max src dimension should be greater than 0, now - %d\n", conf.MaxSrcDimension)
  257. }
  258. if conf.MaxSrcResolution <= 0 {
  259. log.Fatalf("Max src resolution should be greater than 0, now - %d\n", conf.MaxSrcResolution)
  260. }
  261. if conf.Quality <= 0 {
  262. log.Fatalf("Quality should be greater than 0, now - %d\n", conf.Quality)
  263. } else if conf.Quality > 100 {
  264. log.Fatalf("Quality can't be greater than 100, now - %d\n", conf.Quality)
  265. }
  266. if conf.GZipCompression < 0 {
  267. log.Fatalf("GZip compression should be greater than or quual to 0, now - %d\n", conf.GZipCompression)
  268. } else if conf.GZipCompression > 9 {
  269. log.Fatalf("GZip compression can't be greater than 9, now - %d\n", conf.GZipCompression)
  270. }
  271. if conf.IgnoreSslVerification {
  272. warning("Ignoring SSL verification is very unsafe")
  273. }
  274. if conf.LocalFileSystemRoot != "" {
  275. stat, err := os.Stat(conf.LocalFileSystemRoot)
  276. if err != nil {
  277. log.Fatalf("Cannot use local directory: %s", err)
  278. } else {
  279. if !stat.IsDir() {
  280. log.Fatalf("Cannot use local directory: not a directory")
  281. }
  282. }
  283. if conf.LocalFileSystemRoot == "/" {
  284. log.Print("Exposing root via IMGPROXY_LOCAL_FILESYSTEM_ROOT is unsafe")
  285. }
  286. }
  287. if err := checkPresets(conf.Presets); err != nil {
  288. log.Fatalln(err)
  289. }
  290. if conf.WatermarkOpacity <= 0 {
  291. log.Fatalln("Watermark opacity should be greater than 0")
  292. } else if conf.WatermarkOpacity > 1 {
  293. log.Fatalln("Watermark opacity should be less than or equal to 1")
  294. }
  295. if len(conf.PrometheusBind) > 0 && conf.PrometheusBind == conf.Bind {
  296. log.Fatalln("Can't use the same binding for the main server and Prometheus")
  297. }
  298. initDownloading()
  299. initNewrelic()
  300. initPrometheus()
  301. initErrorsReporting()
  302. initVips()
  303. }