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:
parent
0370572130
commit
70276f36bc
7 changed files with 61 additions and 49 deletions
30
README.md
30
README.md
|
@ -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 ###
|
||||
|
||||
|
|
|
@ -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, ",")
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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/*"},
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue