Files
postgres-web/pgweb/util/misc.py
Magnus Hagander 7de55adb97 Fix long standing bug in determining remote IP
The check was for is_behind_cache without the (), meaning it always
returned true, which in turn meant we trusted all x-forwarded-for
headers. It was pretty hard to get them into the system, and
we didn't actually use it for anything other than locking survey
submissions, so it's not a big problem.

However, the basic logic was also wrong, as it assumes that all
SSL connections terminate directly at the backend server, which is
not necessarily true anymore.

The new version of the function will trust an X-Forwarded-For as
long as it's set on one of our frontend servers, regardless of if
it's an encrypted connection or not.
2015-12-17 17:36:00 +01:00

85 lines
2.7 KiB
Python

from django.db import connection
from django.conf import settings
from Crypto.Hash import SHA256
from Crypto import Random
from pgweb.mailqueue.util import send_simple_mail
from pgweb.util.helpers import template_to_string
import re
def send_template_mail(sender, receiver, subject, templatename, templateattr={}, usergenerated=False):
send_simple_mail(sender, receiver, subject,
template_to_string(templatename, templateattr),
usergenerated=usergenerated)
def is_behind_cache(request):
"""
Determine if the client is behind a cache. In this, we are only interested in our own
frontend caches, we don't care about any client side caches or such things.
"""
if request.is_secure():
# We never proxy https requests, so shortcut the check if it's there
return False
if request.META.has_key('HTTP_X_VARNISH'):
# So, it's a varnish cache. Check that it's one of our frontends
if request.META['REMOTE_ADDR'] in settings.FRONTEND_SERVERS:
# Yup, it's one of our varnish servers, so we're behind a cache
return True
else:
# It's someone elses varnish? Or misconfigured? Either way, don't claim
# it's behind a cache.
return False
# X-Varnish not set, clearly we're not behind a cache
return False
def get_client_ip(request):
"""
Get the IP of the client. If the client is served through our Varnish caches,
or behind one of our SSL proxies, make sure to get the *actual* client IP,
and not the IP of the cache/proxy.
"""
if request.META.has_key('HTTP_X_FORWARDED_FOR'):
# There is a x-forwarded-for header, so trust it but only if the actual connection
# is coming in from one of our frontends.
if request.META['REMOTE_ADDR'] in settings.FRONTEND_SERVERS:
return request.META['HTTP_X_FORWARDED_FOR']
# Else fall back and return the actual IP of the connection
return request.META['REMOTE_ADDR']
def varnish_purge(url):
"""
Purge the specified URL from Varnish. Will add initial anchor to the URL,
but no trailing one, so by default a wildcard match is done.
"""
url = '^%s' % url
connection.cursor().execute("SELECT varnish_purge(%s)", (url, ))
def version_sort(l):
"""
map a directory name to a format that will show up sensibly in an ascii sort
"""
mkey = l['url']
m = re.match('v([0-9]+)\.([0-9]+)\.([0-9]+)$',l['url'])
if m:
mkey = m.group(1) + '%02d' % int(m.group(2)) + '%02d' % int(m.group(3));
m = re.match('v([0-9]+)\.([0-9]+)$',l['url'])
if m:
mkey = m.group(1) + '%02d' % int(m.group(2));
return mkey
def generate_random_token():
"""
Generate a random token of 64 characters. This token will be
generated using a strong random number, and then hex encoded to make
sure all characters are safe to put in emails and URLs.
"""
s = SHA256.new()
r = Random.new()
s.update(r.read(250))
return s.hexdigest()