1
0

config.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. package main
  2. import (
  3. "bytes"
  4. "crypto/rand"
  5. "encoding/hex"
  6. "flag"
  7. "fmt"
  8. "io/ioutil"
  9. "log"
  10. "os"
  11. "runtime"
  12. "strconv"
  13. )
  14. func intEnvConfig(i *int, name string) {
  15. if env, err := strconv.Atoi(os.Getenv(name)); err == nil {
  16. *i = env
  17. }
  18. }
  19. func megaIntEnvConfig(f *int, name string) {
  20. if env, err := strconv.ParseFloat(os.Getenv(name), 64); err == nil {
  21. *f = int(env * 1000000)
  22. }
  23. }
  24. func strEnvConfig(s *string, name string) {
  25. if env := os.Getenv(name); len(env) > 0 {
  26. *s = env
  27. }
  28. }
  29. func boolEnvConfig(b *bool, name string) {
  30. *b = false
  31. if env, err := strconv.ParseBool(os.Getenv(name)); err == nil {
  32. *b = env
  33. }
  34. }
  35. func hexEnvConfig(b *[]byte, name string) {
  36. var err error
  37. if env := os.Getenv(name); len(env) > 0 {
  38. if *b, err = hex.DecodeString(env); err != nil {
  39. log.Fatalf("%s expected to be hex-encoded string\n", name)
  40. }
  41. }
  42. }
  43. func hexFileConfig(b *[]byte, filepath string) {
  44. if len(filepath) == 0 {
  45. return
  46. }
  47. f, err := os.Open(filepath)
  48. if err != nil {
  49. log.Fatalf("Can't open file %s\n", filepath)
  50. }
  51. src, err := ioutil.ReadAll(f)
  52. if err != nil {
  53. log.Fatalln(err)
  54. }
  55. src = bytes.TrimSpace(src)
  56. dst := make([]byte, hex.DecodedLen(len(src)))
  57. n, err := hex.Decode(dst, src)
  58. if err != nil {
  59. log.Fatalf("%s expected to contain hex-encoded string\n", filepath)
  60. }
  61. *b = dst[:n]
  62. }
  63. type config struct {
  64. Bind string
  65. ReadTimeout int
  66. WaitTimeout int
  67. WriteTimeout int
  68. DownloadTimeout int
  69. Concurrency int
  70. MaxClients int
  71. TTL int
  72. MaxSrcDimension int
  73. MaxSrcResolution int
  74. Quality int
  75. GZipCompression int
  76. Key []byte
  77. Salt []byte
  78. Secret string
  79. AllowOrigin string
  80. LocalFileSystemRoot string
  81. ETagEnabled bool
  82. ETagSignature []byte
  83. BaseURL string
  84. }
  85. var conf = config{
  86. Bind: ":8080",
  87. ReadTimeout: 10,
  88. WriteTimeout: 10,
  89. DownloadTimeout: 5,
  90. Concurrency: runtime.NumCPU() * 2,
  91. TTL: 3600,
  92. MaxSrcDimension: 8192,
  93. MaxSrcResolution: 16800000,
  94. Quality: 80,
  95. GZipCompression: 5,
  96. ETagEnabled: false,
  97. }
  98. func init() {
  99. keypath := flag.String("keypath", "", "path of the file with hex-encoded key")
  100. saltpath := flag.String("saltpath", "", "path of the file with hex-encoded salt")
  101. flag.Parse()
  102. if port := os.Getenv("PORT"); len(port) > 0 {
  103. conf.Bind = fmt.Sprintf(":%s", port)
  104. }
  105. strEnvConfig(&conf.Bind, "IMGPROXY_BIND")
  106. intEnvConfig(&conf.ReadTimeout, "IMGPROXY_READ_TIMEOUT")
  107. intEnvConfig(&conf.WriteTimeout, "IMGPROXY_WRITE_TIMEOUT")
  108. intEnvConfig(&conf.DownloadTimeout, "IMGPROXY_DOWNLOAD_TIMEOUT")
  109. intEnvConfig(&conf.Concurrency, "IMGPROXY_CONCURRENCY")
  110. intEnvConfig(&conf.MaxClients, "IMGPROXY_MAX_CLIENTS")
  111. intEnvConfig(&conf.TTL, "IMGPROXY_TTL")
  112. intEnvConfig(&conf.MaxSrcDimension, "IMGPROXY_MAX_SRC_DIMENSION")
  113. megaIntEnvConfig(&conf.MaxSrcResolution, "IMGPROXY_MAX_SRC_RESOLUTION")
  114. intEnvConfig(&conf.Quality, "IMGPROXY_QUALITY")
  115. intEnvConfig(&conf.GZipCompression, "IMGPROXY_GZIP_COMPRESSION")
  116. hexEnvConfig(&conf.Key, "IMGPROXY_KEY")
  117. hexEnvConfig(&conf.Salt, "IMGPROXY_SALT")
  118. hexFileConfig(&conf.Key, *keypath)
  119. hexFileConfig(&conf.Salt, *saltpath)
  120. strEnvConfig(&conf.Secret, "IMGPROXY_SECRET")
  121. strEnvConfig(&conf.AllowOrigin, "IMGPROXY_ALLOW_ORIGIN")
  122. strEnvConfig(&conf.LocalFileSystemRoot, "IMGPROXY_LOCAL_FILESYSTEM_ROOT")
  123. boolEnvConfig(&conf.ETagEnabled, "IMGPROXY_USE_ETAG")
  124. strEnvConfig(&conf.BaseURL, "IMGPROXY_BASE_URL")
  125. if len(conf.Key) == 0 {
  126. log.Fatalln("Key is not defined")
  127. }
  128. if len(conf.Salt) == 0 {
  129. log.Fatalln("Salt is not defined")
  130. }
  131. if len(conf.Bind) == 0 {
  132. log.Fatalln("Bind address is not defined")
  133. }
  134. if conf.ReadTimeout <= 0 {
  135. log.Fatalf("Read timeout should be greater than 0, now - %d\n", conf.ReadTimeout)
  136. }
  137. if conf.WriteTimeout <= 0 {
  138. log.Fatalf("Write timeout should be greater than 0, now - %d\n", conf.WriteTimeout)
  139. }
  140. if conf.DownloadTimeout <= 0 {
  141. log.Fatalf("Download timeout should be greater than 0, now - %d\n", conf.DownloadTimeout)
  142. }
  143. if conf.Concurrency <= 0 {
  144. log.Fatalf("Concurrency should be greater than 0, now - %d\n", conf.Concurrency)
  145. }
  146. if conf.MaxClients <= 0 {
  147. conf.MaxClients = conf.Concurrency * 10
  148. }
  149. if conf.TTL <= 0 {
  150. log.Fatalf("TTL should be greater than 0, now - %d\n", conf.TTL)
  151. }
  152. if conf.MaxSrcDimension <= 0 {
  153. log.Fatalf("Max src dimension should be greater than 0, now - %d\n", conf.MaxSrcDimension)
  154. }
  155. if conf.MaxSrcResolution <= 0 {
  156. log.Fatalf("Max src resolution should be greater than 0, now - %d\n", conf.MaxSrcResolution)
  157. }
  158. if conf.Quality <= 0 {
  159. log.Fatalf("Quality should be greater than 0, now - %d\n", conf.Quality)
  160. } else if conf.Quality > 100 {
  161. log.Fatalf("Quality can't be greater than 100, now - %d\n", conf.Quality)
  162. }
  163. if conf.GZipCompression < 0 {
  164. log.Fatalf("GZip compression should be greater than or quual to 0, now - %d\n", conf.GZipCompression)
  165. } else if conf.GZipCompression > 9 {
  166. log.Fatalf("GZip compression can't be greater than 9, now - %d\n", conf.GZipCompression)
  167. }
  168. if conf.LocalFileSystemRoot != "" {
  169. stat, err := os.Stat(conf.LocalFileSystemRoot)
  170. if err != nil {
  171. log.Fatalf("Cannot use local directory: %s", err)
  172. } else {
  173. if !stat.IsDir() {
  174. log.Fatalf("Cannot use local directory: not a directory")
  175. }
  176. }
  177. if conf.LocalFileSystemRoot == "/" {
  178. log.Print("Exposing root via IMGPROXY_LOCAL_FILESYSTEM_ROOT is unsafe")
  179. }
  180. }
  181. if conf.ETagEnabled {
  182. conf.ETagSignature = make([]byte, 16)
  183. rand.Read(conf.ETagSignature)
  184. log.Printf("ETag support is activated. The random value was generated to be used for ETag calculation: %s\n",
  185. fmt.Sprintf("%x", conf.ETagSignature))
  186. }
  187. initVips()
  188. initDownloading()
  189. }