0
Fork 0
mirror of https://github.com/willnorris/imageproxy.git synced 2024-12-16 21:56:43 -05:00

rename 'Whitelist' to 'RemoteHosts"

This better describes what exactly is being allowed.
This commit is contained in:
Will Norris 2018-09-15 05:49:33 +00:00
parent 0370572130
commit 70276f36bc
7 changed files with 61 additions and 49 deletions

View file

@ -7,7 +7,7 @@
imageproxy is a caching image proxy server written in go. It features:
- basic image adjustments like resizing, cropping, and rotation
- access control using host whitelists or request signing (HMAC-SHA256)
- access control using allowed hosts list or request signing (HMAC-SHA256)
- support for jpeg, png, webp (decode only), tiff, and gif image formats
(including animated gifs)
- caching in-memory, on disk, or with Amazon S3, Google Cloud Storage, Azure
@ -91,8 +91,8 @@ using:
imageproxy
This will start the proxy on port 8080, without any caching and with no host
whitelist (meaning any remote URL can be proxied). Test this by navigating to
This will start the proxy on port 8080, without any caching and with no allowed
host list (meaning any remote URL can be proxied). Test this by navigating to
<http://localhost:8080/500/https://octodex.github.com/images/codercat.jpg> and
you should see a 500px square coder octocat.
@ -148,7 +148,7 @@ followed by a gcs bucket:
[tiered fashion]: https://godoc.org/github.com/die-net/lrucache/twotier
### Referrer Whitelist ###
### Allowed Referrer List ###
You can limit images to only be accessible for certain hosts in the HTTP
referrer header, which can help prevent others from hotlinking to images. It can
@ -161,21 +161,21 @@ 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.
### Host whitelist ###
### Allowed Hosts List ###
You can limit the remote hosts that the proxy will fetch images from using the
`whitelist` flag. This is useful, for example, for locking the proxy down to
`remoteHosts` flag. This is useful, for example, for locking the proxy down to
your own hosts to prevent others from abusing it. Of course if you want to
support fetching from any host, leave off the whitelist flag. Try it out by
support fetching from any host, leave off the remoteHosts flag. Try it out by
running:
imageproxy -whitelist example.com
imageproxy -remoteHosts example.com
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.
### Content-Type whitelist ###
### Allowed Content-Type List ###
You can limit what content types can be proxied by using the `contentTypes`
flag. By default, this is set to `image/*`, meaning that imageproxy will
@ -185,10 +185,10 @@ flag to an empty string to proxy all requests, regardless of content type.
### 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:
Instead of an allowed host list, 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, <key>).digest(<remote_url>))
@ -209,8 +209,8 @@ Some simple code samples for generating signatures in various languages can be
found in [URL Signing](https://github.com/willnorris/imageproxy/wiki/URL-signing).
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.
In other words, requests that match one of the allowed hosts don't necessarily
need to be signed, though they can be.
### Default Base URL ###

View file

@ -42,7 +42,8 @@ import (
const defaultMemorySize = 100
var addr = flag.String("addr", "localhost:8080", "TCP address to listen on")
var whitelist = flag.String("whitelist", "", "comma separated list of allowed remote hosts")
var remoteHosts = flag.String("remoteHosts", "", "comma separated list of allowed remote hosts")
var whitelist = flag.String("whitelist", "", "deprecated. use 'remoteHosts' instead")
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
@ -59,10 +60,14 @@ func init() {
func main() {
flag.Parse()
if *remoteHosts == "" {
// backwards compatible with old naming of the flag
*remoteHosts = *whitelist
}
p := imageproxy.NewProxy(nil, cache.Cache)
if *whitelist != "" {
p.Whitelist = strings.Split(*whitelist, ",")
if *remoteHosts != "" {
p.RemoteHosts = strings.Split(*remoteHosts, ",")
}
if *referrers != "" {
p.Referrers = strings.Split(*referrers, ",")

View file

@ -29,7 +29,7 @@ case "$1" in
test -n "$ADDR" && DAEMON_OPTS+=" -addr=$ADDR"
test -n "$CACHE" && DAEMON_OPTS+=" -cache=$CACHE"
test -n "$LOG_DIR" && DAEMON_OPTS+=" -log_dir=$LOG_DIR"
test -n "$ALLOWED_REMOTE_HOSTS" && DAEMON_OPTS+=" -whitelist=$ALLOWED_REMOTE_HOSTS"
test -n "$ALLOWED_REMOTE_HOSTS" && DAEMON_OPTS+=" -remoteHosts=$ALLOWED_REMOTE_HOSTS"
echo -n "Starting $NAME: "
start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \

View file

@ -11,4 +11,4 @@ exec start-stop-daemon --start -c www-data --exec /usr/bin/imageproxy -- \
-cache /var/cache/imageproxy \
-signatureKey @/etc/imageproxy.key \
-baseURL https://willnorris.com/ \
-whitelist willnorris.com,notsoserendipitous.com,gabenorris.com
-remoteHosts willnorris.com,notsoserendipitous.com,gabenorris.com

View file

@ -9,7 +9,7 @@ ExecStart=/usr/local/bin/imageproxy \
-cache /var/cache/imageproxy \
-signatureKey @/etc/imageproxy.key \
-baseURL https://willnorris.com/ \
-whitelist willnorris.com,notsoserendipitous.com,gabenorris.com
-remoteHosts willnorris.com,notsoserendipitous.com,gabenorris.com
Restart=on-abort
[Install]

View file

@ -42,8 +42,11 @@ type Proxy struct {
Client *http.Client // client used to fetch remote URLs
Cache Cache // cache used to cache responses
// Whitelist specifies a list of remote hosts that images can be
// RemoteHosts specifies a list of remote hosts that images can be
// proxied from. An empty list means all hosts are allowed.
RemoteHosts []string
// Whitelist should no longer be used. Use "RemoteHosts" instead.
Whitelist []string
// Referrers, when given, requires that requests to the image
@ -207,15 +210,19 @@ func copyHeader(dst, src http.Header, keys ...string) {
// referrer, host, and signature. It returns an error if the request is not
// allowed.
func (p *Proxy) allowed(r *Request) error {
if p.RemoteHosts == nil {
// backwards compatible with old naming of the field
p.RemoteHosts = p.Whitelist
}
if len(p.Referrers) > 0 && !validReferrer(p.Referrers, r.Original) {
return fmt.Errorf("request does not contain an allowed referrer: %v", r)
}
if len(p.Whitelist) == 0 && len(p.SignatureKey) == 0 {
return nil // no whitelist or signature key, all requests accepted
if len(p.RemoteHosts) == 0 && len(p.SignatureKey) == 0 {
return nil // no allowed hosts or signature key, all requests accepted
}
if len(p.Whitelist) > 0 && validHost(p.Whitelist, r.URL) {
if len(p.RemoteHosts) > 0 && validHost(p.RemoteHosts, r.URL) {
return nil
}

View file

@ -97,7 +97,7 @@ func TestCopyHeader(t *testing.T) {
}
func TestAllowed(t *testing.T) {
whitelist := []string{"good"}
remoteHosts := []string{"good"}
key := []byte("c0ffee")
genRequest := func(headers map[string]string) *http.Request {
@ -109,41 +109,41 @@ func TestAllowed(t *testing.T) {
}
tests := []struct {
url string
options Options
whitelist []string
referrers []string
key []byte
request *http.Request
allowed bool
url string
options Options
remoteHosts []string
referrers []string
key []byte
request *http.Request
allowed bool
}{
// no whitelist or signature key
// no remoteHosts or signature key
{"http://test/image", emptyOptions, nil, nil, nil, nil, true},
// whitelist
{"http://good/image", emptyOptions, whitelist, nil, nil, nil, true},
{"http://bad/image", emptyOptions, whitelist, nil, nil, nil, false},
// remoteHosts
{"http://good/image", emptyOptions, remoteHosts, nil, nil, nil, true},
{"http://bad/image", emptyOptions, remoteHosts, nil, nil, nil, false},
// referrer
{"http://test/image", emptyOptions, nil, whitelist, nil, genRequest(map[string]string{"Referer": "http://good/foo"}), true},
{"http://test/image", emptyOptions, nil, whitelist, nil, genRequest(map[string]string{"Referer": "http://bad/foo"}), false},
{"http://test/image", emptyOptions, nil, whitelist, nil, genRequest(map[string]string{"Referer": "MALFORMED!!"}), false},
{"http://test/image", emptyOptions, nil, whitelist, nil, genRequest(map[string]string{}), false},
{"http://test/image", emptyOptions, nil, remoteHosts, nil, genRequest(map[string]string{"Referer": "http://good/foo"}), true},
{"http://test/image", emptyOptions, nil, remoteHosts, nil, genRequest(map[string]string{"Referer": "http://bad/foo"}), false},
{"http://test/image", emptyOptions, nil, remoteHosts, nil, genRequest(map[string]string{"Referer": "MALFORMED!!"}), false},
{"http://test/image", emptyOptions, nil, remoteHosts, nil, genRequest(map[string]string{}), false},
// signature key
{"http://test/image", Options{Signature: "NDx5zZHx7QfE8E-ijowRreq6CJJBZjwiRfOVk_mkfQQ="}, nil, nil, key, nil, true},
{"http://test/image", Options{Signature: "deadbeef"}, nil, nil, key, nil, false},
{"http://test/image", emptyOptions, nil, nil, key, nil, false},
// whitelist and signature
{"http://good/image", emptyOptions, whitelist, nil, key, nil, true},
// remoteHosts and signature
{"http://good/image", emptyOptions, remoteHosts, nil, key, nil, true},
{"http://bad/image", Options{Signature: "gWivrPhXBbsYEwpmWAKjbJEiAEgZwbXbltg95O2tgNI="}, nil, nil, key, nil, true},
{"http://bad/image", emptyOptions, whitelist, nil, key, nil, false},
{"http://bad/image", emptyOptions, remoteHosts, nil, key, nil, false},
}
for _, tt := range tests {
p := NewProxy(nil, nil)
p.Whitelist = tt.whitelist
p.RemoteHosts = tt.remoteHosts
p.SignatureKey = tt.key
p.Referrers = tt.referrers
@ -159,7 +159,7 @@ func TestAllowed(t *testing.T) {
}
func TestValidHost(t *testing.T) {
whitelist := []string{"a.test", "*.b.test", "*c.test"}
remoteHosts := []string{"a.test", "*.b.test", "*c.test"}
tests := []struct {
url string
@ -182,8 +182,8 @@ func TestValidHost(t *testing.T) {
if err != nil {
t.Errorf("error parsing url %q: %v", tt.url, err)
}
if got, want := validHost(whitelist, u), tt.valid; got != want {
t.Errorf("validHost(%v, %q) returned %v, want %v", whitelist, u, got, want)
if got, want := validHost(remoteHosts, u), tt.valid; got != want {
t.Errorf("validHost(%v, %q) returned %v, want %v", remoteHosts, u, got, want)
}
}
}
@ -326,7 +326,7 @@ func TestProxy_ServeHTTP(t *testing.T) {
Client: &http.Client{
Transport: testTransport{},
},
Whitelist: []string{"good.test"},
RemoteHosts: []string{"good.test"},
ContentTypes: []string{"image/*"},
}