config.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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. }
  85. type config struct {
  86. Bind string
  87. ReadTimeout int
  88. WaitTimeout int
  89. WriteTimeout int
  90. DownloadTimeout int
  91. Concurrency int
  92. MaxClients int
  93. TTL int
  94. MaxSrcDimension int
  95. MaxSrcResolution int
  96. AllowInsecure bool
  97. JpegProgressive bool
  98. PngInterlaced bool
  99. Quality int
  100. GZipCompression int
  101. Key []byte
  102. Salt []byte
  103. Secret string
  104. AllowOrigin string
  105. IgnoreSslVerification bool
  106. LocalFileSystemRoot string
  107. ETagEnabled bool
  108. BaseURL string
  109. Presets presets
  110. }
  111. var conf = config{
  112. Bind: ":8080",
  113. ReadTimeout: 10,
  114. WriteTimeout: 10,
  115. DownloadTimeout: 5,
  116. Concurrency: runtime.NumCPU() * 2,
  117. TTL: 3600,
  118. IgnoreSslVerification: false,
  119. MaxSrcDimension: 8192,
  120. MaxSrcResolution: 16800000,
  121. AllowInsecure: false,
  122. Quality: 80,
  123. GZipCompression: 5,
  124. ETagEnabled: false,
  125. }
  126. func init() {
  127. keyPath := flag.String("keypath", "", "path of the file with hex-encoded key")
  128. saltPath := flag.String("saltpath", "", "path of the file with hex-encoded salt")
  129. presetsPath := flag.String("presets", "", "path of the file with presets")
  130. showVersion := flag.Bool("v", false, "show version")
  131. flag.Parse()
  132. if *showVersion {
  133. fmt.Println(version)
  134. os.Exit(0)
  135. }
  136. if port := os.Getenv("PORT"); len(port) > 0 {
  137. conf.Bind = fmt.Sprintf(":%s", port)
  138. }
  139. strEnvConfig(&conf.Bind, "IMGPROXY_BIND")
  140. intEnvConfig(&conf.ReadTimeout, "IMGPROXY_READ_TIMEOUT")
  141. intEnvConfig(&conf.WriteTimeout, "IMGPROXY_WRITE_TIMEOUT")
  142. intEnvConfig(&conf.DownloadTimeout, "IMGPROXY_DOWNLOAD_TIMEOUT")
  143. intEnvConfig(&conf.Concurrency, "IMGPROXY_CONCURRENCY")
  144. intEnvConfig(&conf.MaxClients, "IMGPROXY_MAX_CLIENTS")
  145. intEnvConfig(&conf.TTL, "IMGPROXY_TTL")
  146. intEnvConfig(&conf.MaxSrcDimension, "IMGPROXY_MAX_SRC_DIMENSION")
  147. megaIntEnvConfig(&conf.MaxSrcResolution, "IMGPROXY_MAX_SRC_RESOLUTION")
  148. boolEnvConfig(&conf.AllowInsecure, "IMGPROXY_ALLOW_INSECURE")
  149. boolEnvConfig(&conf.JpegProgressive, "IMGPROXY_JPEG_PROGRESSIVE")
  150. boolEnvConfig(&conf.PngInterlaced, "IMGPROXY_PNG_INTERLACED")
  151. intEnvConfig(&conf.Quality, "IMGPROXY_QUALITY")
  152. intEnvConfig(&conf.GZipCompression, "IMGPROXY_GZIP_COMPRESSION")
  153. hexEnvConfig(&conf.Key, "IMGPROXY_KEY")
  154. hexEnvConfig(&conf.Salt, "IMGPROXY_SALT")
  155. hexFileConfig(&conf.Key, *keyPath)
  156. hexFileConfig(&conf.Salt, *saltPath)
  157. strEnvConfig(&conf.Secret, "IMGPROXY_SECRET")
  158. strEnvConfig(&conf.AllowOrigin, "IMGPROXY_ALLOW_ORIGIN")
  159. boolEnvConfig(&conf.IgnoreSslVerification, "IMGPROXY_IGNORE_SSL_VERIFICATION")
  160. strEnvConfig(&conf.LocalFileSystemRoot, "IMGPROXY_LOCAL_FILESYSTEM_ROOT")
  161. boolEnvConfig(&conf.ETagEnabled, "IMGPROXY_USE_ETAG")
  162. strEnvConfig(&conf.BaseURL, "IMGPROXY_BASE_URL")
  163. conf.Presets = make(presets)
  164. presetEnvConfig(&conf.Presets, "IMGPROXY_PRESETS")
  165. presetFileConfig(&conf.Presets, *presetsPath)
  166. if len(conf.Key) == 0 {
  167. log.Fatalln("Key is not defined")
  168. }
  169. if len(conf.Salt) == 0 {
  170. log.Fatalln("Salt is not defined")
  171. }
  172. if len(conf.Bind) == 0 {
  173. log.Fatalln("Bind address is not defined")
  174. }
  175. if conf.ReadTimeout <= 0 {
  176. log.Fatalf("Read timeout should be greater than 0, now - %d\n", conf.ReadTimeout)
  177. }
  178. if conf.WriteTimeout <= 0 {
  179. log.Fatalf("Write timeout should be greater than 0, now - %d\n", conf.WriteTimeout)
  180. }
  181. if conf.DownloadTimeout <= 0 {
  182. log.Fatalf("Download timeout should be greater than 0, now - %d\n", conf.DownloadTimeout)
  183. }
  184. if conf.Concurrency <= 0 {
  185. log.Fatalf("Concurrency should be greater than 0, now - %d\n", conf.Concurrency)
  186. }
  187. if conf.MaxClients <= 0 {
  188. conf.MaxClients = conf.Concurrency * 10
  189. }
  190. if conf.TTL <= 0 {
  191. log.Fatalf("TTL should be greater than 0, now - %d\n", conf.TTL)
  192. }
  193. if conf.MaxSrcDimension <= 0 {
  194. log.Fatalf("Max src dimension should be greater than 0, now - %d\n", conf.MaxSrcDimension)
  195. }
  196. if conf.MaxSrcResolution <= 0 {
  197. log.Fatalf("Max src resolution should be greater than 0, now - %d\n", conf.MaxSrcResolution)
  198. }
  199. if conf.AllowInsecure {
  200. warning("Token validation is disabled. Hope you know what you're doing")
  201. }
  202. if conf.Quality <= 0 {
  203. log.Fatalf("Quality should be greater than 0, now - %d\n", conf.Quality)
  204. } else if conf.Quality > 100 {
  205. log.Fatalf("Quality can't be greater than 100, now - %d\n", conf.Quality)
  206. }
  207. if conf.GZipCompression < 0 {
  208. log.Fatalf("GZip compression should be greater than or quual to 0, now - %d\n", conf.GZipCompression)
  209. } else if conf.GZipCompression > 9 {
  210. log.Fatalf("GZip compression can't be greater than 9, now - %d\n", conf.GZipCompression)
  211. }
  212. if conf.IgnoreSslVerification {
  213. log.Println("Ignoring SSL verification is very unsafe. Hope you know what you're doing")
  214. }
  215. if conf.LocalFileSystemRoot != "" {
  216. stat, err := os.Stat(conf.LocalFileSystemRoot)
  217. if err != nil {
  218. log.Fatalf("Cannot use local directory: %s", err)
  219. } else {
  220. if !stat.IsDir() {
  221. log.Fatalf("Cannot use local directory: not a directory")
  222. }
  223. }
  224. if conf.LocalFileSystemRoot == "/" {
  225. log.Print("Exposing root via IMGPROXY_LOCAL_FILESYSTEM_ROOT is unsafe")
  226. }
  227. }
  228. initVips()
  229. initDownloading()
  230. }