diff --git a/README.md b/README.md index 96fbe5eb8..d9bf2d6ee 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,12 @@ image horizontally. Images are flipped **after** being resized and rotated. The `q{percentage}` option can be used to specify the output quality (JPEG only). If not specified, the default value of `95` is used. +#### Signature #### + +The `s{signature}` option specifies an optional base64 encoded HMAC used to +sign the remote URL in the request. The HMAC key used to verify signatures is +provided to the imageproxy server on startup. + ### Remote URL ### The URL of the original image to load is specified as the remainder of the @@ -129,10 +135,11 @@ unbounded. To cache images on disk instead, include the `cacheDir` flag: imageproxy -cacheDir /tmp/imageproxy -Reload the [codercat URL](http://localhost:8080/500/https://octodex.github.com/images/codercat.jpg), -and then inspect the contents of `/tmp/imageproxy`. There should be two files -there, one for the original full-size codercat image, and one for the resized -500px version. +Reload the [codercat URL][], and then inspect the contents of +`/tmp/imageproxy`. There should be two files there, one for the original +full-size codercat image, and one for the resized 500px version. + +[codercat URL]: http://localhost:8080/500/https://octodex.github.com/images/codercat.jpg ### Host whitelist ### @@ -144,10 +151,39 @@ running: imageproxy -whitelist example.com -Reload the [codercat URL](http://localhost:8080/500/https://octodex.github.com/images/codercat.jpg), -and you should now get an error message. You can specify multiple hosts as a -comma separated list, or prefix a host value with `*.` to allow all sub-domains -as well. +Reload the [codercat URL][], and you should now get an error message. You can +specify multiple hosts as a comma separated list, or prefix a host value with +`*.` to allow all sub-domains as well. + +### Signed Requests ### + +Instead of a host whitelist, you can require that requests be signed. This is +useful in preventing abuse when you don't have just a static list of hosts you +want to allow. Signatures are generated using HMAC-SHA256 against the remote +URL, and url-safe base64 encoding the result: + + base64urlencode(hmac.New(sha256, ).digest()) + +The HMAC key is specified using the `signatureKey` flag. If this flag +begins with an "@", the remainder of the value is interpreted as a file on disk +which contains the HMAC key. + +Try it out by running: + + imageproxy -signatureKey "secret key" + +Reload the [codercat URL][], and you should see an error message. Now load a +[signed codercat URL][] and verify that it loads properly. + +[signed codercat URL]: http://localhost:8080/500,sXyMwWKIC5JPCtlYOQ2f4yMBTqpjtUsfI67Sp7huXIYY=/https://octodex.github.com/images/codercat.jpg + +Some simple code samples for generating signatures in various languages can be +found starting in [this comment](https://github.com/willnorris/imageproxy/issues/11#issuecomment-101428470). + +If both a whiltelist and signatureKey are specified, requests can match either. +In other words, requests that match one of the whitelisted hosts don't +necessarily need to be signed, though they can be. + Run `imageproxy -help` for a complete list of flags the command accepts. If you want to use a different caching implementation, it's probably easiest to diff --git a/cmd/imageproxy/main.go b/cmd/imageproxy/main.go index 20840b6c6..77ae03555 100644 --- a/cmd/imageproxy/main.go +++ b/cmd/imageproxy/main.go @@ -18,6 +18,7 @@ package main import ( "flag" "fmt" + "io/ioutil" "log" "net/http" "net/url" @@ -43,6 +44,7 @@ var whitelist = flag.String("whitelist", "", "comma separated list of allowed re var baseURL = flag.String("baseURL", "", "default base URL for relative remote URLs") var cacheDir = flag.String("cacheDir", "", "directory to use for file cache") var cacheSize = flag.Uint64("cacheSize", 100, "maximum size of file cache (in MB)") +var signatureKey = flag.String("signatureKey", "", "HMAC key used in calculating request signatures") var version = flag.Bool("version", false, "print version information") func main() { @@ -68,6 +70,18 @@ func main() { if *whitelist != "" { p.Whitelist = strings.Split(*whitelist, ",") } + if *signatureKey != "" { + key := []byte(*signatureKey) + if strings.HasPrefix(*signatureKey, "@") { + file := strings.TrimPrefix(*signatureKey, "@") + var err error + key, err = ioutil.ReadFile(file) + if err != nil { + log.Fatalf("error reading signature file: %v", err) + } + } + p.SignatureKey = key + } if *baseURL != "" { var err error p.DefaultBaseURL, err = url.Parse(*baseURL)