diff --git a/cmd/imageproxy/main.go b/cmd/imageproxy/main.go index b2e622f..bbdad53 100644 --- a/cmd/imageproxy/main.go +++ b/cmd/imageproxy/main.go @@ -48,7 +48,7 @@ var denyHosts = flag.String("denyHosts", "", "comma separated list of denied rem var referrers = flag.String("referrers", "", "comma separated list of allowed referring hosts") var baseURL = flag.String("baseURL", "", "default base URL for relative remote URLs") var cache tieredCache -var signatureKey = flag.String("signatureKey", "", "HMAC key used in calculating request signatures") +var signatureKeyList SignatureKeyList var scaleUp = flag.Bool("scaleUp", false, "allow images to scale beyond their original dimensions") var timeout = flag.Duration("timeout", 0, "time limit for requests served by this proxy") var verbose = flag.Bool("verbose", false, "print verbose logging messages") @@ -58,6 +58,7 @@ var userAgent = flag.String("userAgent", "willnorris/imageproxy", "specify the u func init() { flag.Var(&cache, "cache", "location to cache images (see https://github.com/willnorris/imageproxy#cache)") + flag.Var(&signatureKeyList, "signatureKey", "HMAC key used in calculating request signatures") } func main() { @@ -77,18 +78,7 @@ func main() { if *contentTypes != "" { p.ContentTypes = strings.Split(*contentTypes, ",") } - 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 - } + p.SignatureKeys = signatureKeyList if *baseURL != "" { var err error p.DefaultBaseURL, err = url.Parse(*baseURL) @@ -112,6 +102,27 @@ func main() { log.Fatal(http.ListenAndServe(*addr, nil)) } +type SignatureKeyList [][]byte + +func (skl *SignatureKeyList) String() string { + return fmt.Sprint(*skl) +} + +func (skl *SignatureKeyList) Set(value string) error { + key := []byte(value) + if strings.HasPrefix(value, "@") { + file := strings.TrimPrefix(value, "@") + var err error + key, err = ioutil.ReadFile(file) + if err != nil { + log.Fatalf("error reading signature file: %v", err) + } + } + + *skl = append(*skl, key) + return nil +} + // tieredCache allows specifying multiple caches via flags, which will create // tiered caches using the twotier package. type tieredCache struct { diff --git a/imageproxy.go b/imageproxy.go index 4bf4ca5..e8bb9c8 100644 --- a/imageproxy.go +++ b/imageproxy.go @@ -64,8 +64,9 @@ type Proxy struct { // The Logger used by the image proxy Logger *log.Logger - // SignatureKey is the HMAC key used to verify signed requests. - SignatureKey []byte + // SignatureKeys is a list of HMAC keys used to verify signed requests. + // Any of them can be used to verify signed requests. + SignatureKeys [][]byte // Allow images to scale beyond their original dimensions. ScaleUp bool @@ -258,7 +259,7 @@ func (p *Proxy) allowed(r *Request) error { return errDeniedHost } - if len(p.AllowHosts) == 0 && len(p.SignatureKey) == 0 { + if len(p.AllowHosts) == 0 && len(p.SignatureKeys) == 0 { return nil // no allowed hosts or signature key, all requests accepted } @@ -266,8 +267,10 @@ func (p *Proxy) allowed(r *Request) error { return nil } - if len(p.SignatureKey) > 0 && validSignature(p.SignatureKey, r) { - return nil + for _, signatureKey := range p.SignatureKeys { + if len(signatureKey) > 0 && validSignature(signatureKey, r) { + return nil + } } return errNotAllowed diff --git a/imageproxy_test.go b/imageproxy_test.go index 2779573..33267f8 100644 --- a/imageproxy_test.go +++ b/imageproxy_test.go @@ -116,7 +116,13 @@ func TestCopyHeader(t *testing.T) { func TestAllowed(t *testing.T) { allowHosts := []string{"good"} - key := []byte("c0ffee") + key := [][]byte{ + []byte("c0ffee"), + } + multipleKey := [][]byte{ + []byte("c0ffee"), + []byte("beer"), + } genRequest := func(headers map[string]string) *http.Request { req := &http.Request{Header: make(http.Header)} @@ -132,7 +138,7 @@ func TestAllowed(t *testing.T) { allowHosts []string denyHosts []string referrers []string - key []byte + keys [][]byte request *http.Request allowed bool }{ @@ -151,7 +157,10 @@ func TestAllowed(t *testing.T) { // signature key {"http://test/image", Options{Signature: "NDx5zZHx7QfE8E-ijowRreq6CJJBZjwiRfOVk_mkfQQ="}, nil, nil, nil, key, nil, true}, + {"http://test/image", Options{Signature: "NDx5zZHx7QfE8E-ijowRreq6CJJBZjwiRfOVk_mkfQQ="}, nil, nil, nil, multipleKey, nil, true}, // signed with key "c0ffee" + {"http://test/image", Options{Signature: "FWIawYV4SEyI4zKJMeGugM-eJM1eI_jXPEQ20ZgRe4A="}, nil, nil, nil, multipleKey, nil, true}, // signed with key "beer" {"http://test/image", Options{Signature: "deadbeef"}, nil, nil, nil, key, nil, false}, + {"http://test/image", Options{Signature: "deadbeef"}, nil, nil, nil, multipleKey, nil, false}, {"http://test/image", emptyOptions, nil, nil, nil, key, nil, false}, // allowHosts and signature @@ -169,7 +178,7 @@ func TestAllowed(t *testing.T) { p := NewProxy(nil, nil) p.AllowHosts = tt.allowHosts p.DenyHosts = tt.denyHosts - p.SignatureKey = tt.key + p.SignatureKeys = tt.keys p.Referrers = tt.referrers u, err := url.Parse(tt.url)