Is there a way to restrict apps based on IP? - django

I'd like to be able to only allow certain apps to be used from the corporate office which has a static IP. Is there a django package that I can use to do this? If so do you know if it can be used as an additional security measure? Certain apps and the data in those apps I wouldnt want to be accessible by anyone outside the office. I already have pretty good app security but as an additional security measure is this a good option? Thanks

Just include this middleware class under the 'MIDDLEWARE_CLASSES' variable in your settings.py file.
Also include the variable BLOCKED_IPS = ('123.123.123.123',) variable, where the value is a tuple of IP addresses you want blocked from your site.
"""
simple middlware to block IP addresses via settings
variable BLOCKED_IPS
"""
from django.conf import settings
from django import http
class BlockedIpMiddleware(object):
def process_request(self, request):
if request.META['REMOTE_ADDR'] in settings.BLOCKED_IPS:
return http.HttpResponseForbidden('<h1>Forbidden</h1>')
return None

It looks like this is what I'm looking for...
django-iprestrict.readthedocs.io/en/latest/configuration.html
That package will let you either allow or deny by default and set IP whitelists or blacklists per app –

This is partly based on answer by Ramesh K, with changes for Django 2.2.19. On the server that I use: the load-balancer puts the IP address received into the "X-Real-IP" header (and also passes the "X-Forwarded-For" header as a comma-separated list of IP addresses). Then the "REMOTE_ADDR" contains load-balancer address, rather than the actual remote address.
from django.conf import settings
from django import http
class BlockedIpMiddleware:
def __init__(self, get_response):
# One-time configuration and initialization, when the webserver starts.
self.get_response = get_response
def __call__(self, request):
# Code to be executed for each request before the view (and later
# middleware) are called.
# if request.META['REMOTE_ADDR'] in settings.BLOCKED_IPS:
if request.META['HTTP_X_REAL_IP'] in settings.BLOCKED_IPS:
return http.HttpResponseForbidden('<h1>Forbidden</h1>')
return self.get_response(request)

There is a library I have been having my eye on, Django IP Restrict, I suggest you give a try and tell us your experiance too with it.

Related

how can I configure my app to only accept connections from three explicitly-specified clients?

So I have a Django app that serves as the backend for another app that I've written. I only want my Django app to be accessed from this other app, which will probably have a handful of versions (production, staging, dev1, dev2). How can I configure my Django app to only accept connections from these handful of clients?
If I understand you well you want to control the access to your Django App, one option is to add a custom Middleware to check the IP of the user and if it's not allowed you can show an error, or redirect to other site or whatever you want.
class CheckIPMiddleware(object):
# Check if client IP is allowed
def process_request(self, request):
allowed_ips = ['192.168.1.1', '123.123.123.123', etc...]
ip = request.META.get('REMOTE_ADDR') # Get client IP
if ip not in allowed_ips:
# Here you can raise a 403 Forbidden
# or redirect to any other site/page
# If user is allowed nothing happens
return None
If this is useful to you you have to remember 2 things:
Add code to a file following this path: your_project/middleware/checkipmiddleware.py
Edit your settings and add your_project_name.middleware.checkipmiddleware.CheckIPMiddleware into your middleware ( look for MIDDLEWARE_CLASSES = (... in your settings.py )

Django XFrameOptionsMiddleware (X-Frame-Options) - allow iframe by client IP

I'm using Django XFrameOptionsMiddleware to control clickjacking, but I have a customer that needs to be able to browse the app in an iframe from within their network. I want to be able to apply (or remove) the xframe_options_exempt decorator from within the view method.
Best approach is to override get_xframe_options_value. XFRAME_EXEMPT_IPS is a glob_list in my case to detect allowable networks using fnmatch (192.168.*).
class TFXFrameOptionsMiddleware(XFrameOptionsMiddleware):
def get_xframe_options_value(self, request, response):
if request.META['REMOTE_ADDR'] in settings.XFRAME_EXEMPT_IPS:
return 'ALLOWALL' # non standard, equivalent to omitting
return getattr(settings, 'X_FRAME_OPTIONS', 'SAMEORIGIN').upper()

django multiple domains, single app

I am trying to set my Django app work with multiple domains (while serving slightly different content)
I wrote this middleware:
class MultiSiteMiddleware(object):
def process_request(self, request):
host = request.get_host()
host_part = host.split(':')[0].split('.com')[0].split('.')
host = host_part[len(host_part)-1] + '.com'
site = Site.objects.get(domain=host)
settings.SITE_ID = site.id
settings.CURRENT_HOST = host
Site.objects.clear_cache()
return
In views I use this:
def get_site(request):
current_site = get_current_site(request)
return current_site.name
def view(request, pk):
site = get_site(request)
if site == 'site1':
# serve content1
...
elif site == 'site2'
# serve content2
...
But now there are 404 errors (I sometimes find them in logs, don't see them while browsing my site manually) where they aren't supposed to be, like my site sometimes is serving content for wrong domains, can they happen because of some flaw in the above middleware and view code or I should look somewhere else?
I had a similar requirement and decided not to use the django sites framework. My middleware looks like
class MultiSiteMiddleware(object):
def process_request(self, request):
try:
domain = request.get_host().split(":")[0]
request.site = Site.objects.get(domain=domain)
except Site.DoesNotExist:
return http.HttpResponseNotFound()
then my views have access to request.site
If you're seeing 404's for sites that aren't yours in your logs it would seem like somebody has pointed their domain at your servers IP address, you could use apache/nginx to filter these out before they hit your app, but your middleware should catch them (though possibly by raising an uncaught 500 error instead of a 404)
Serve multiple domain from one website (django1.8 python3+)
The goal is, obviously, to serve multiple domains, from one Django instance. That means, we use the same models, the same database, the same logic, the same views, the same templates, but to serve different things.
Searching the interwebs, I came to the idea of using the sites framework. The sites framework was designed to do exactly that thing. In fact, the sites framework has been used to do exactly that. But I haven't been able to know on which version of Django it was, and actually, I came to the idea the sites framework was just a vestigial module. Basically, it is just a table, with SITE_ID and SITE_URL, that you can easily access with a function. But I've been unable to find how, from that, you can do a multi-domain website.
So my idea has been, how to modify the url resolver. The idea behind all these is easy : www.domain1.com/bar is resolved to /domain1/bar, and www.domain2.foo is resolved to /domain2/foo. This solves the problem because, if you want to serve multiple domains, you just have to serve multiple folders.
In Django, to achieve this, you have to modify two things :
* the way Django routes requests
* the way django write urls
The way Django routes requests
Django routes requests with middlewares. That's it. So we just have to write a middleware that re-route requests.
To make it easier, middlewares can have a process_request method that process requests (WOW), before requests are handled. So let's write a DomainNameMiddleware
#!python
#app/middleware.py
class DomaineNameMiddleware:
"""
change the request path to add the domain_name at the first
"""
def process_request(self, request):
#first, we split the domain name, and take the part before the extension
request_domain = request.META['HTTP_HOST'].split('.')[-2]
request.path_info = "/%s/%s" % (request_domain, request.path.split('/')[1:])
The way Django writes URL
When I'm talking about django writing url, i'm principally thinking of the {% url %} template tag, the get_absolute_url methods, the resolve and resolve_lazy functions, and those basic Django thing. If we rewrite the way Django handle url, we have to tell Django to write url that way.
But basically it's pretty easy, thanks to Django.
You can easily rewrite basic Django function by just rewriting them, typically in init.py files of modules you added as apps. So :
#!python
#anyapp/__init__.py
from django.core import urlresolvers
old_reverse = urlresolvers.reverse
def new_reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None):
"""
return an url with the first folder as a domain name in .com
"""
TLD = 'com'
old_reverse_url = old_reverse(viewname, urlconf, args, kwargs, current_app)
# admin will add itself everytime you reload an admin page, so we have to delete it
if current_app == 'admin':
return '/%s' % old_reverse_url[len('admin'):].replace('adminadmin', 'admin')
return '//%s.%s/%s' % (app, TLD, path)
How to use it ?
I use it with base urls.py as dispatchedr.
#!python
#urls.py
from django.conf.urls import include, url
from domain1 import urls as domain1_urls
from domain2 import urls as domain2_urls
urlpatterns = [
url(r'^domain1/', include(domain1_urls, namespace='domain1')),
url(r'^domain2/', include(domain2_urls, namespace='domain2)),
]
Django's Sites framework has built-in middleware to accomplish this.
Simply enable the Sites framework and add this to your MIDDLEWARE:
'django.contrib.sites.middleware.CurrentSiteMiddleware'
This automatically passes a request object to Site.objects.get_current() on every request. It solves your problem by giving you access to request.site on every request.
For reference, the code as of 1.11 is:
from django.utils.deprecation import MiddlewareMixin
from .shortcuts import get_current_site
class CurrentSiteMiddleware(MiddlewareMixin):
"""
Middleware that sets `site` attribute to request object.
"""
def process_request(self, request):
request.site = get_current_site(request)
I will suggest you to use django-multisite .It will fulfill your requirement.
Try using the "sites" framework in django to get the domain name. You already know this I guess.
Take a look here: https://docs.djangoproject.com/en/1.7/ref/contrib/sites/#getting-the-current-domain-for-full-urls
See this:
>>> Site.objects.get_current().domain
'example.com'
Without the https://www or http://www.. Probably your domains will end in .org or some country .pe .ru etc not just .com.
There might be a case when people don't point to your domain but to your IP address for some reason, maybe development of testing so you should always raise an exception with Site.DoesNotExist

Different type of anonymous users with Django

The Django project I'm currently working on is a website that should be accessed from a local network and the Internet. And part of the content should be available to anonymous users only if the visit the site from the local network (basically it's a test on the IP address), while authenticated users have access to the whole content.
I though about checking the IP as described here, but it seems to me quite bad to check the ip each time the user loads a page.
Is there a way to cleanly store user data even on anonymous user ? It would be nice to just be able to use a decorator like #login_required, but which would redirect only if the anonymous user has an external IP.
Actually, checking the ip on every requests seems to me to be one of the fastest methods available. Consider that the ip is already loaded in memory on every request, all you have to do is a simple dictionary lookup and a comparison, with a conditional string split/additional dict lookup. Compared to what happens on even the simplest page views, the performance impact is fully neglectable and comparable with the impact of using sessions or any other mechanism to save the ip.
You can write your own decorator function using user_passes_test:
from django.contrib.auth.decorators import user_passes_test
from django.utils.decorators import available_attrs
from functools import wraps
LOCAL_IPS = (
'127.0.0.1',
'other_ip',
'etc.',
)
def is_local_request(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip in LOCAL_IPS
def local_ip_required(view_func):
def wrapped_view(request, *args, **kwargs):
if not is_local_request(request):
raise Http404 # or PermissionDenied or redirect
return view_func(request, *args, **kwargs)
return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)
Then simply use #local_ip_required. Note that this implementation will prevent both anonymous and logged-in users from accessing the view from an external location.

How can I detect multiple logins into a Django web application from different locations?

I want to only allow one authenticated session at a time for an individual login in my Django application. So if a user is logged into the webpage on a given IP address, and those same user credentials are used to login from a different IP address I want to do something (either logout the first user or deny access to the second user.)
Not sure if this is still needed but thought I would share my solution:
1) Install django-tracking (thankyou for that tip Van Gale Google Maps + GeoIP is amazing!)
2) Add this middleware:
from django.contrib.sessions.models import Session
from tracking.models import Visitor
from datetime import datetime
class UserRestrictMiddleware(object):
"""
Prevents more than one user logging in at once from two different IPs
"""
def process_request(self, request):
ip_address = request.META.get('REMOTE_ADDR','')
try:
last_login = request.user.last_login
except:
last_login = 0
if unicode(last_login)==unicode(datetime.now())[:19]:
previous_visitors = Visitor.objects.filter(user=request.user).exclude(ip_address=ip_address)
for visitor in previous_visitors:
Session.objects.filter(session_key=visitor.session_key).delete()
visitor.user = None
visitor.save()
3) Make sure it goes after the VisitorTrackingMiddleware and you should find previous logins are automatically bumped when someone new logs in :)
If you're already using django-tracking as suggested here, there's a much easier way to implement this:
Define a signal handler:
# myapp/signals.py
def kick_my_other_sessions(sender, request=None, user=None, **kwargs):
from tracking.models import Visitor
from django.contrib.sessions.models import Session
keys = [v.session_key for v in Visitor.objects.filter(user=request.user).exclude(session_key=request.session.session_key)]
Session.objects.filter(session_key__in=keys).delete()
Create a listener for the user_logged_in signal:
# myapp/__init__.py
from myapp.signals import kick_my_other_sessions
from django.contrib.auth.signals import user_logged_in
user_logged_in.connect(kick_my_other_sessions, sender=User)
This will institute a sort of "last user to login wins" system. If you want to allow multiple logins by the same user from the same ip, you can add an .exclude() to the Visitors lookup.
Django's middleware will probably help you achieve this. The issue is that you will probably want to allow multiple anonymous sessions from the same IP address, even authenticated sessions for different users, but not authenticated sessions for the same user.
You'll want to:
Create a user profile model to store the IP address of a user's last login. See Django's Storing additional information about users documentation.
Implement a custom authentication backend. This backend, when triggered and successfully authenticating a user (just call super) would wipe out the user's last login IP in the profile model.
Implement a subclass of Django's django.contrib.sessions.SessionMiddleware class. Implement process_request. If the request.user object's profile model has no IP address, set it and allow the request. If it has an IP, and the IP is different from the current request's IP (request.META.REMOTE_ADDR), then do whatever you like to either log out the other user, or return an error to the requestor.
Update your settings.py file so that your custom auth backend is processed first, and so that your custom session middleware is also processed first. This involves updating settings.AUTHENTICATION_BACKENDS and settings.MIDDLEWARE_CLASSES.
You'll need to do this with custom middleware.
In your middleware process_request() method you will have access to the request object so you can do something like the following:
session_key = request.session.session_key
ip_address = request.META.get('REMOTE_ADDR', '')
Now you know the IP address, so check a model you create that (roughly) would look like this:
class SessionIPS(models.Model):
session = models.ForeignKey(Session)
IP = models.CharField(max_length=20)
So when a session is created or deleted you will modifiy your session ip's table accordingly, and when a request comes in make sure the IP address isn't being used for another session. If if is, then return a Http404 (or something like it) from the middleware.
A pluggable app that can show you a lot more detail (and even includes IP address in its own model) is django-tracking.