mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-05-20 10:34:30 +00:00
axes: Minor fixes to configuration for IP blocking
- Use the X-Forwarded-For header only if specified in the configuration. This makes FreedomBox safe to use when not behind a reverse proxy server (although we are unlikely to do this). - When fetching the IP address to reset after successful login, use the X-Forwarded-For header only if specified in the configuration. - Minor flake8 refactorings. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
22dd2d26f3
commit
dc9ab52edc
@ -17,6 +17,7 @@ port = 8000
|
|||||||
|
|
||||||
# Enable the following only if Plinth is behind a proxy server. The
|
# Enable the following only if Plinth is behind a proxy server. The
|
||||||
# proxy server should properly clean and the following HTTP headers:
|
# proxy server should properly clean and the following HTTP headers:
|
||||||
|
# X-Forwarded-For
|
||||||
# X-Forwarded-Host
|
# X-Forwarded-Host
|
||||||
# X-Forwarded-Proto
|
# X-Forwarded-Proto
|
||||||
# If you enable these unnecessarily, this will lead to serious security
|
# If you enable these unnecessarily, this will lead to serious security
|
||||||
@ -27,6 +28,7 @@ port = 8000
|
|||||||
# configuration allows only connections from localhost
|
# configuration allows only connections from localhost
|
||||||
#
|
#
|
||||||
# Leave the values blank to disable
|
# Leave the values blank to disable
|
||||||
|
use_x_forwarded_for = True
|
||||||
use_x_forwarded_host = True
|
use_x_forwarded_host = True
|
||||||
secure_proxy_ssl_header = HTTP_X_FORWARDED_PROTO
|
secure_proxy_ssl_header = HTTP_X_FORWARDED_PROTO
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,7 @@ port = 8000
|
|||||||
|
|
||||||
# Enable the following only if Plinth is behind a proxy server. The
|
# Enable the following only if Plinth is behind a proxy server. The
|
||||||
# proxy server should properly clean and the following HTTP headers:
|
# proxy server should properly clean and the following HTTP headers:
|
||||||
|
# X-Forwarded-For
|
||||||
# X-Forwarded-Host
|
# X-Forwarded-Host
|
||||||
# X-Forwarded-Proto
|
# X-Forwarded-Proto
|
||||||
# If you enable these unnecessarily, this will lead to serious security
|
# If you enable these unnecessarily, this will lead to serious security
|
||||||
@ -27,6 +28,7 @@ port = 8000
|
|||||||
# configuration allows only connections from localhost
|
# configuration allows only connections from localhost
|
||||||
#
|
#
|
||||||
# Leave the values blank to disable
|
# Leave the values blank to disable
|
||||||
|
use_x_forwarded_for = True
|
||||||
use_x_forwarded_host = True
|
use_x_forwarded_host = True
|
||||||
secure_proxy_ssl_header = HTTP_X_FORWARDED_PROTO
|
secure_proxy_ssl_header = HTTP_X_FORWARDED_PROTO
|
||||||
|
|
||||||
|
|||||||
@ -32,6 +32,7 @@ actions_dir = None
|
|||||||
doc_dir = None
|
doc_dir = None
|
||||||
host = None
|
host = None
|
||||||
port = None
|
port = None
|
||||||
|
use_x_forwarded_for = False
|
||||||
use_x_forwarded_host = False
|
use_x_forwarded_host = False
|
||||||
secure_proxy_ssl_header = None
|
secure_proxy_ssl_header = None
|
||||||
develop = False
|
develop = False
|
||||||
@ -98,6 +99,7 @@ def read(config_path=None, root_directory=None):
|
|||||||
('Network', 'host', 'string'),
|
('Network', 'host', 'string'),
|
||||||
('Network', 'port', 'int'),
|
('Network', 'port', 'int'),
|
||||||
('Network', 'secure_proxy_ssl_header', 'string'),
|
('Network', 'secure_proxy_ssl_header', 'string'),
|
||||||
|
('Network', 'use_x_forwarded_for', 'bool'),
|
||||||
('Network', 'use_x_forwarded_host', 'bool'),
|
('Network', 'use_x_forwarded_host', 'bool'),
|
||||||
('Misc', 'box_name', 'string'),
|
('Misc', 'box_name', 'string'),
|
||||||
('Misc', 'danube_edition', 'bool'),
|
('Misc', 'danube_edition', 'bool'),
|
||||||
|
|||||||
@ -22,13 +22,13 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
|
import axes.utils
|
||||||
|
from axes.decorators import axes_form_invalid
|
||||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||||
from django.contrib.auth.views import LoginView, LogoutView
|
from django.contrib.auth.views import LoginView, LogoutView
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
|
|
||||||
from axes.decorators import axes_form_invalid
|
from plinth import actions, web_framework
|
||||||
from axes.utils import reset
|
|
||||||
from plinth import actions
|
|
||||||
|
|
||||||
from .forms import AuthenticationForm
|
from .forms import AuthenticationForm
|
||||||
|
|
||||||
@ -66,8 +66,8 @@ class SSOLoginView(LoginView):
|
|||||||
response = super(SSOLoginView, self).dispatch(request, *args, **kwargs)
|
response = super(SSOLoginView, self).dispatch(request, *args, **kwargs)
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
return set_ticket_cookie(request.user, response)
|
return set_ticket_cookie(request.user, response)
|
||||||
else:
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@axes_form_invalid
|
@axes_form_invalid
|
||||||
def form_invalid(self, *args, **kwargs):
|
def form_invalid(self, *args, **kwargs):
|
||||||
@ -82,24 +82,18 @@ class CaptchaLoginView(LoginView):
|
|||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
response = super(CaptchaLoginView, self).dispatch(
|
response = super(CaptchaLoginView, self).dispatch(
|
||||||
request, *args, **kwargs)
|
request, *args, **kwargs)
|
||||||
if request.POST:
|
if not request.POST:
|
||||||
if request.user.is_authenticated:
|
return response
|
||||||
ip = get_ip_address_from_request(request)
|
|
||||||
reset(ip=ip)
|
|
||||||
return set_ticket_cookie(request.user, response)
|
|
||||||
else:
|
|
||||||
return response
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
if not request.user.is_authenticated:
|
||||||
|
return response
|
||||||
|
|
||||||
def get_ip_address_from_request(request):
|
# Successful authentication
|
||||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
ip_address = web_framework.get_ip_address_from_request(request)
|
||||||
if x_forwarded_for:
|
axes.utils.reset(ip=ip_address)
|
||||||
ip = x_forwarded_for.split(',')[0]
|
logger.info('Login attempts reset for IP after successful login: %s',
|
||||||
else:
|
ip_address)
|
||||||
ip = request.META.get('REMOTE_ADDR')
|
return set_ticket_cookie(request.user, response)
|
||||||
logger.warning("IP address is " + ip)
|
|
||||||
return ip
|
|
||||||
|
|
||||||
|
|
||||||
class SSOLogoutView(LogoutView):
|
class SSOLogoutView(LogoutView):
|
||||||
|
|||||||
@ -17,6 +17,7 @@ port = 8000
|
|||||||
|
|
||||||
# Enable the following only if Plinth is behind a proxy server. The
|
# Enable the following only if Plinth is behind a proxy server. The
|
||||||
# proxy server should properly clean and the following HTTP headers:
|
# proxy server should properly clean and the following HTTP headers:
|
||||||
|
# X-Forwarded-For
|
||||||
# X-Forwarded-Host
|
# X-Forwarded-Host
|
||||||
# X-Forwarded-Proto
|
# X-Forwarded-Proto
|
||||||
# If you enable these unnecessarily, this will lead to serious security
|
# If you enable these unnecessarily, this will lead to serious security
|
||||||
@ -27,6 +28,7 @@ port = 8000
|
|||||||
# configuration allows only connections from localhost
|
# configuration allows only connections from localhost
|
||||||
#
|
#
|
||||||
# Leave the values blank to disable
|
# Leave the values blank to disable
|
||||||
|
use_x_forwarded_for = True
|
||||||
use_x_forwarded_host = True
|
use_x_forwarded_host = True
|
||||||
secure_proxy_ssl_header = HTTP_X_FORWARDED_PROTO
|
secure_proxy_ssl_header = HTTP_X_FORWARDED_PROTO
|
||||||
|
|
||||||
|
|||||||
@ -126,12 +126,16 @@ class TestCfg(unittest.TestCase):
|
|||||||
self.assertEqual(parser.get('Path', 'actions_dir'), cfg.actions_dir)
|
self.assertEqual(parser.get('Path', 'actions_dir'), cfg.actions_dir)
|
||||||
self.assertEqual(parser.get('Path', 'doc_dir'), cfg.doc_dir)
|
self.assertEqual(parser.get('Path', 'doc_dir'), cfg.doc_dir)
|
||||||
|
|
||||||
self.assertEqual(5, len(parser.items('Network')))
|
self.assertEqual(6, len(parser.items('Network')))
|
||||||
self.assertEqual(parser.get('Network', 'host'), cfg.host)
|
self.assertEqual(parser.get('Network', 'host'), cfg.host)
|
||||||
self.assertEqual(int(parser.get('Network', 'port')), cfg.port)
|
self.assertEqual(int(parser.get('Network', 'port')), cfg.port)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
parser.get('Network', 'secure_proxy_ssl_header'),
|
parser.get('Network', 'secure_proxy_ssl_header'),
|
||||||
cfg.secure_proxy_ssl_header)
|
cfg.secure_proxy_ssl_header)
|
||||||
|
self.assertIsInstance(cfg.use_x_forwarded_for, bool)
|
||||||
|
self.assertEqual(
|
||||||
|
parser.get('Network', 'use_x_forwarded_for'),
|
||||||
|
str(cfg.use_x_forwarded_for))
|
||||||
self.assertIsInstance(cfg.use_x_forwarded_host, bool)
|
self.assertIsInstance(cfg.use_x_forwarded_host, bool)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
parser.get('Network', 'use_x_forwarded_host'),
|
parser.get('Network', 'use_x_forwarded_host'),
|
||||||
|
|||||||
@ -72,6 +72,10 @@ def init():
|
|||||||
if cfg.secure_proxy_ssl_header:
|
if cfg.secure_proxy_ssl_header:
|
||||||
secure_proxy_ssl_header = (cfg.secure_proxy_ssl_header, 'https')
|
secure_proxy_ssl_header = (cfg.secure_proxy_ssl_header, 'https')
|
||||||
|
|
||||||
|
ipware_meta_precedence_order = ('REMOTE_ADDR', )
|
||||||
|
if cfg.use_x_forwarded_for:
|
||||||
|
ipware_meta_precedence_order = ('HTTP_X_FORWARDED_FOR', )
|
||||||
|
|
||||||
pwd = 'django.contrib.auth.password_validation'
|
pwd = 'django.contrib.auth.password_validation'
|
||||||
|
|
||||||
django.conf.settings.configure(
|
django.conf.settings.configure(
|
||||||
@ -113,7 +117,7 @@ def init():
|
|||||||
DEBUG=cfg.develop,
|
DEBUG=cfg.develop,
|
||||||
FORCE_SCRIPT_NAME=cfg.server_dir,
|
FORCE_SCRIPT_NAME=cfg.server_dir,
|
||||||
INSTALLED_APPS=applications,
|
INSTALLED_APPS=applications,
|
||||||
IPWARE_META_PRECEDENCE_ORDER=('HTTP_X_FORWARDED_FOR', ),
|
IPWARE_META_PRECEDENCE_ORDER=ipware_meta_precedence_order,
|
||||||
LANGUAGES=get_languages(),
|
LANGUAGES=get_languages(),
|
||||||
LOGGING=log.get_configuration(),
|
LOGGING=log.get_configuration(),
|
||||||
LOGIN_URL='users:login',
|
LOGIN_URL='users:login',
|
||||||
@ -191,3 +195,14 @@ def get_wsgi_application():
|
|||||||
def get_static_url():
|
def get_static_url():
|
||||||
"""Return Django static URL."""
|
"""Return Django static URL."""
|
||||||
return django.conf.settings.STATIC_URL
|
return django.conf.settings.STATIC_URL
|
||||||
|
|
||||||
|
|
||||||
|
def get_ip_address_from_request(request):
|
||||||
|
"""Return the IP address of the original client."""
|
||||||
|
if cfg.use_x_forwarded_for:
|
||||||
|
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||||
|
ip_address = x_forwarded_for.split(',')[0]
|
||||||
|
else:
|
||||||
|
ip_address = request.META.get('REMOTE_ADDR')
|
||||||
|
|
||||||
|
return ip_address
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user