config.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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 megaIntEnvConfig(f *int, name string) {
  21. if env, err := strconv.ParseFloat(os.Getenv(name), 64); err == nil {
  22. *f = int(env * 1000000)
  23. }
  24. }
  25. func strEnvConfig(s *string, name string) {
  26. if env := os.Getenv(name); len(env) > 0 {
  27. *s = env
  28. }
  29. }
  30. func boolEnvConfig(b *bool, name string) {
  31. *b = false
  32. if env, err := strconv.ParseBool(os.Getenv(name)); err == nil {
  33. *b = env
  34. }
  35. }
  36. func hexEnvConfig(b *[]byte, name string) {
  37. var err error
  38. if env := os.Getenv(name); len(env) > 0 {
  39. if *b, err = hex.DecodeString(env); err != nil {
  40. log.Fatalf("%s expected to be hex-encoded string\n", name)
  41. }
  42. }
  43. }
  44. func hexFileConfig(b *[]byte, filepath string) {
  45. if len(filepath) == 0 {
  46. return
  47. }
  48. f, err := os.Open(filepath)
  49. if err != nil {
  50. log.Fatalf("Can't open file %s\n", filepath)
  51. }
  52. src, err := ioutil.ReadAll(f)
  53. if err != nil {
  54. log.Fatalln(err)
  55. }
  56. src = bytes.TrimSpace(src)
  57. dst := make([]byte, hex.DecodedLen(len(src)))
  58. n, err := hex.Decode(dst, src)
  59. if err != nil {
  60. log.Fatalf("%s expected to contain hex-encoded string\n", filepath)
  61. }
  62. *b = dst[:n]
  63. }
  64. func presetEnvConfig(p presets, name string) {
  65. if env := os.Getenv(name); len(env) > 0 {
  66. presetStrings := strings.Split(env, ",")
  67. for _, presetStr := range presetStrings {
  68. parsePreset(p, presetStr)
  69. }
  70. }
  71. }
  72. func presetFileConfig(p presets, filepath string) {
  73. if len(filepath) == 0 {
  74. return
  75. }
  76. f, err := os.Open(filepath)
  77. if err != nil {
  78. log.Fatalf("Can't open file %s\n", filepath)
  79. }
  80. scanner := bufio.NewScanner(f)
  81. for scanner.Scan() {
  82. parsePreset(p, scanner.Text())
  83. }
  84. if err := scanner.Err(); err != nil {
  85. log.Fatalf("Failed to read presets file: %s", err)
  86. }
  87. }
  88. type config struct {
  89. Bind string
  90. ReadTimeout int
  91. WaitTimeout int
  92. WriteTimeout int
  93. DownloadTimeout int
  94. Concurrency int
  95. MaxClients int
  96. TTL int
  97. MaxSrcDimension int
  98. MaxSrcResolution int
  99. JpegProgressive bool
  100. PngInterlaced bool
  101. Quality int
  102. GZipCompression int
  103. EnableWebpDetection bool
  104. EnforceWebp bool
  105. Key []byte
  106. Salt []byte
  107. AllowInsecure bool
  108. Secret string
  109. AllowOrigin string
  110. IgnoreSslVerification bool
  111. LocalFileSystemRoot string
  112. ETagEnabled bool
  113. BaseURL string
  114. Presets presets
  115. }
  116. var conf = config{
  117. Bind: ":8080",
  118. ReadTimeout: 10,
  119. WriteTimeout: 10,
  120. DownloadTimeout: 5,
  121. Concurrency: runtime.NumCPU() * 2,
  122. TTL: 3600,
  123. IgnoreSslVerification: false,
  124. MaxSrcDimension: 8192,
  125. MaxSrcResolution: 16800000,
  126. AllowInsecure: false,
  127. Quality: 80,
  128. GZipCompression: 5,
  129. ETagEnabled: false,
  130. }
  131. func init() {
  132. keyPath := flag.String("keypath", "", "path of the file with hex-encoded key")
  133. saltPath := flag.String("saltpath", "", "path of the file with hex-encoded salt")
  134. presetsPath := flag.String("presets", "", "path of the file with presets")
  135. showVersion := flag.Bool("v", false, "show version")
  136. flag.Parse()
  137. if *showVersion {
  138. fmt.Println(version)
  139. os.Exit(0)
  140. }
  141. if port := os.Getenv("PORT"); len(port) > 0 {
  142. conf.Bind = fmt.Sprintf(":%s", port)
  143. }
  144. strEnvConfig(&conf.Bind, "IMGPROXY_BIND")
  145. intEnvConfig(&conf.ReadTimeout, "IMGPROXY_READ_TIMEOUT")
  146. intEnvConfig(&conf.WriteTimeout, "IMGPROXY_WRITE_TIMEOUT")
  147. intEnvConfig(&conf.DownloadTimeout, "IMGPROXY_DOWNLOAD_TIMEOUT")
  148. intEnvConfig(&conf.Concurrency, "IMGPROXY_CONCURRENCY")
  149. intEnvConfig(&conf.MaxClients, "IMGPROXY_MAX_CLIENTS")
  150. intEnvConfig(&conf.TTL, "IMGPROXY_TTL")
  151. intEnvConfig(&conf.MaxSrcDimension, "IMGPROXY_MAX_SRC_DIMENSION")
  152. megaIntEnvConfig(&conf.MaxSrcResolution, "IMGPROXY_MAX_SRC_RESOLUTION")
  153. boolEnvConfig(&conf.JpegProgressive, "IMGPROXY_JPEG_PROGRESSIVE")
  154. boolEnvConfig(&conf.PngInterlaced, "IMGPROXY_PNG_INTERLACED")
  155. intEnvConfig(&conf.Quality, "IMGPROXY_QUALITY")
  156. intEnvConfig(&conf.GZipCompression, "IMGPROXY_GZIP_COMPRESSION")
  157. boolEnvConfig(&conf.EnableWebpDetection, "IMGPROXY_ENABLE_WEBP_DETECTION")
  158. boolEnvConfig(&conf.EnforceWebp, "IMGPROXY_ENFORCE_WEBP")
  159. hexEnvConfig(&conf.Key, "IMGPROXY_KEY")
  160. hexEnvConfig(&conf.Salt, "IMGPROXY_SALT")
  161. hexFileConfig(&conf.Key, *keyPath)
  162. hexFileConfig(&conf.Salt, *saltPath)
  163. strEnvConfig(&conf.Secret, "IMGPROXY_SECRET")
  164. strEnvConfig(&conf.AllowOrigin, "IMGPROXY_ALLOW_ORIGIN")
  165. boolEnvConfig(&conf.IgnoreSslVerification, "IMGPROXY_IGNORE_SSL_VERIFICATION")
  166. strEnvConfig(&conf.LocalFileSystemRoot, "IMGPROXY_LOCAL_FILESYSTEM_ROOT")
  167. boolEnvConfig(&conf.ETagEnabled, "IMGPROXY_USE_ETAG")
  168. strEnvConfig(&conf.BaseURL, "IMGPROXY_BASE_URL")
  169. conf.Presets = make(presets)
  170. presetEnvConfig(conf.Presets, "IMGPROXY_PRESETS")
  171. presetFileConfig(conf.Presets, *presetsPath)
  172. if len(conf.Key) == 0 {
  173. warning("Key is not defined, so signature checking is disabled")
  174. conf.AllowInsecure = true
  175. }
  176. if len(conf.Salt) == 0 {
  177. warning("Salt is not defined, so signature checking is disabled")
  178. conf.AllowInsecure = true
  179. }
  180. if len(conf.Bind) == 0 {
  181. log.Fatalln("Bind address is not defined")
  182. }
  183. if conf.ReadTimeout <= 0 {
  184. log.Fatalf("Read timeout should be greater than 0, now - %d\n", conf.ReadTimeout)
  185. }
  186. if conf.WriteTimeout <= 0 {
  187. log.Fatalf("Write timeout should be greater than 0, now - %d\n", conf.WriteTimeout)
  188. }
  189. if conf.DownloadTimeout <= 0 {
  190. log.Fatalf("Download timeout should be greater than 0, now - %d\n", conf.DownloadTimeout)
  191. }
  192. if conf.Concurrency <= 0 {
  193. log.Fatalf("Concurrency should be greater than 0, now - %d\n", conf.Concurrency)
  194. }
  195. if conf.MaxClients <= 0 {
  196. conf.MaxClients = conf.Concurrency * 10
  197. }
  198. if conf.TTL <= 0 {
  199. log.Fatalf("TTL should be greater than 0, now - %d\n", conf.TTL)
  200. }
  201. if conf.MaxSrcDimension <= 0 {
  202. log.Fatalf("Max src dimension should be greater than 0, now - %d\n", conf.MaxSrcDimension)
  203. }
  204. if conf.MaxSrcResolution <= 0 {
  205. log.Fatalf("Max src resolution should be greater than 0, now - %d\n", conf.MaxSrcResolution)
  206. }
  207. if conf.Quality <= 0 {
  208. log.Fatalf("Quality should be greater than 0, now - %d\n", conf.Quality)
  209. } else if conf.Quality > 100 {
  210. log.Fatalf("Quality can't be greater than 100, now - %d\n", conf.Quality)
  211. }
  212. if conf.GZipCompression < 0 {
  213. log.Fatalf("GZip compression should be greater than or quual to 0, now - %d\n", conf.GZipCompression)
  214. } else if conf.GZipCompression > 9 {
  215. log.Fatalf("GZip compression can't be greater than 9, now - %d\n", conf.GZipCompression)
  216. }
  217. if conf.IgnoreSslVerification {
  218. log.Println("Ignoring SSL verification is very unsafe. Hope you know what you're doing")
  219. }
  220. if conf.LocalFileSystemRoot != "" {
  221. stat, err := os.Stat(conf.LocalFileSystemRoot)
  222. if err != nil {
  223. log.Fatalf("Cannot use local directory: %s", err)
  224. } else {
  225. if !stat.IsDir() {
  226. log.Fatalf("Cannot use local directory: not a directory")
  227. }
  228. }
  229. if conf.LocalFileSystemRoot == "/" {
  230. log.Print("Exposing root via IMGPROXY_LOCAL_FILESYSTEM_ROOT is unsafe")
  231. }
  232. }
  233. checkPresets(conf.Presets)
  234. initVips()
  235. initDownloading()
  236. }