Amazon ELB not working and throwing 500 Server Error - django

Created Amazon Load Balancer with 2 EC2 micro instance behind.
2 EC2 micro instances are having python services.
Services are running fine and responding while directly calling them
Services NOT running when we call them via public DNS of Load Balancer. ELB throws 500 error.
Example of Direct calling EC2 instances services:
ec2-54-200-1-2.us-west-2.compute.amazonaws.com/myservice ==> returns data
Example of calling Load Balancer:
test-12345678.us-west-2.elb.amazonaws.com/myservice ==> returns 500 error
Further points:
DJANGO property ALLOWED_HOSTS is set to ['*'] but did not work.
Using HTTP protocol i.e. Mapping Load Balancer Protocol = HTTP with port 80 to Instance Protocol = HTTP with port 80

I acknowledge this is a very old question but I used a solution that I believe is better and doesn't jeopardize the security of the system by setting the ALLOWED_HOSTS = ['*'].
Here's a piece of Middleware class I wrote that I feel can be reused at will.
It inherits from CommonMiddleware and it is the one who should be used instead of CommonMiddleware in the MIDDLEWARE_CLASSES setting in settings.py.
from django.middleware.common import CommonMiddleware
from django.conf import settings
from django.http.response import HttpResponse
from django.utils import importlib
class CommonWithAllowedHeartbeatMiddleWare(CommonMiddleware):
"""
A middleware class to take care of LoadBalancers whose HOST headers might change dynamically
but are supposed to only access a specific path that returns a 200-OK status.
This middleware allows these requests to the "heartbeat" path to bypass the ALLOWED_HOSTS mechanism check
without jeopardizing the rest of the framework.
(for more details on ALLOWED_HOSTS setting, see: https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts )
settings.HEARTBEAT_PATH (default: "/heartbeat/"):
This will be the path that is cleared and bypasses the common middleware's ALLOWED_HOSTS check.
settings.HEARTBEAT_METHOD (default: None):
The full path to the method that should be called that checks the status.
This setting allows you to hook a method that will be called that can perform any kind of health checks and return the result.
If no method is specified a simple HttpResponse("OK", status_code=200) will be returned.
The method should expect the HttpRequest object of this request and return either True, False or a HttpResponse object.
- True: middleware will return HttpResponse("OK")
- False: middleware will return HttpResponse("Unhealthy", status_code=503) [status 503 - "Service Unavailable"]
- HttpResponse: middleware will just return the HttpResponse object it got back.
IMPORTANT NOTE:
The method being called and any method it calls, cannot call HttpRequest.get_host().
If they do, the ALLOWED_HOSTS mechanism will be called and SuspeciousOperation exception will be thrown.
"""
def process_request(self, request):
# Checking if the request is for the heartbeat path
if request.path == getattr(settings, "HEARTBEAT_PATH", "/heartbeat/"):
method_path = getattr(settings, "HEARTBEAT_METHOD", None)
# Checking if there is a method that should be called instead of the default HttpResponse
if method_path:
# Getting the module to import and method to call
last_sep = method_path.rfind(".")
module_name = method_path[:last_sep]
method = method_path[(last_sep+1):]
module = importlib.import_module(module_name)
# Calling the desired method
result = getattr(module, method)(request)
# If the result is alreay a HttpResponse object just return it
if isinstance(result, HttpResponse):
return result
# Otherwise, if the result is False, return a 503-response
elif not result:
return HttpResponse("Unhealthy", status_code=503)
# Just return OK
return HttpResponse("OK")
# If this is not a request to the specific heartbeat path, just let the common middleware do its magic
return CommonMiddleware.process_request(self, request)
Of course I realize that this mechanism bypasses the CommonMiddleware altogether, but it does that only when the heartbeat path is requested, so I feel this is a little price to pay.
Hope somebody else finds it useful.

Related

Is there a way to restrict apps based on IP?

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.

How sites framework works?

I am trying to develop a ModelManager with an operation similar to the Sites Framework. Depending on a user's field, the ModelManager returns a queryset. I tried to imitate the operation of the Sites Framework but I do not understand how the SITE_ID is obtained dynamically with this function:
def get_queryset(self):
return super(CurrentSiteManager, self).get_queryset().filter(
**{self._get_field_name() + '__id': settings.SITE_ID})
It seems to be static :/.
I capture the user's field through a Middleware and assign it to request.field. How can I retrieve that field in the ModelManager and perform the query?
I think you are missing the dynamic way to get the current site instance.
From the documentation example:
from django.contrib.sites.shortcuts import get_current_site
def article_detail(request, article_id):
try:
a = Article.objects.get(id=article_id,
sites__id=get_current_site(request).id)
except Article.DoesNotExist:
raise Http404("Article does not exist on this site")
# ...
You should use the get_current_site method to get the current site.
It should be noted that it's not working if you actually define the current site in the settings like SITE_ID=1.
It looks up the current site based on request.get_host() if the SITE_ID setting is not defined.
You should read this part of the documentation actually explaining how django can get the current site in a dynamic fashion:
shortcuts.get_current_site(request)
A function that checks if django.contrib.sites is installed and returns either the current Site object or a RequestSite object based on the request. It looks up the current site based on request.get_host() if the SITE_ID setting is not defined.
Both a domain and a port may be returned by request.get_host() when the Host header has a port explicitly specified, e.g. example.com:80. In such cases, if the lookup fails because the host does not match a record in the database, the port is stripped and the lookup is retried with the domain part only. This does not apply to RequestSite which will always use the unmodified host.
And here is the code of the get_current actually called by get_current_site:
def get_current(self, request=None):
"""
Return the current Site based on the SITE_ID in the project's settings.
If SITE_ID isn't defined, return the site with domain matching
request.get_host(). The ``Site`` object is cached the first time it's
retrieved from the database.
"""
from django.conf import settings
if getattr(settings, 'SITE_ID', ''):
site_id = settings.SITE_ID
return self._get_site_by_id(site_id)
elif request:
return self._get_site_by_request(request)
raise ImproperlyConfigured(
"You're using the Django \"sites framework\" without having "
"set the SITE_ID setting. Create a site in your database and "
"set the SITE_ID setting or pass a request to "
"Site.objects.get_current() to fix this error."
)

Calling api without knowing ip address in django

I have a post request
http://localhost:8000/api/orders/shipment
what i want to do is i dont want to pass domain/ip address and access the api something like Django admin console give "172.18.0.1 - - [08/Sep/2017 14:30:29] "POST /adminorder/order/import/ HTTP/1.1" 200 "
I searched in the django documentation get_absolute_url() method is there to define custom urls, I am not getting how to use this in this scenario.
I believe it's impossible to use a relative url in requests, but you can get around this by creating a function which prepends the relevant domain to your url. (Make sure to import your settings file!)
from django.conf import settings
development_url = "http://localhost:8000/"
production_url = "http://myapisite.com/"
def create_url(relative_url):
# If you are on your development server. This is assuming your development always has DEBUG = True, and your production is always DEBUG = False
if settings.DEBUG:
return development_url + relative_url
# Production
return production_url + relative_url
Therefore print(create_url("api/test/anothertest")) will return http://localhost:8000/api/test/anothertest.
And this is how you would use it:
url = create_url("api/orders/shipment/")
data = requests.post(url=url, data=content, headers = headers)

Django Login/Session Not Sticking Over HTTPS

I'm working on a Django site hosted on an Apache server with mod_wsgi.
The site is only on https as we have Apache redirect any http requests to https.
The project I'm working on is called Skittle.
I have a custom user model called SkittleUser which inherits from AbstractBaseUser and is set as the AUTH_USER_MODEL in our settings.py file.
os.environ['HTTPS'] = "on" is set in the wsgi.py file.
SESSION_COOKIE_SECURE = True and CSRF_COOKIE_SECURE = True are both set in settings.py
The issue that we are having right now is that logging in as a user is unreliable.
When you go to the login page, some times it works while other times it doesn't.
Then while browsing the site, you will suddenly lose your session and be kicked down to an anonymous user.
We are currently running our test site here if anybody wants to take a look:
https://skittle.newlinetechnicalinnovations.com/discover/
Our production site is at www.dnaskittle.com but does not yet incorporate user logins as the feature doesn't work.
A test user:
email: test#dnaskittle.com
password: asdf
If the login does not work, you will see in the top right "Welcome, Login" in which case, just try clicking on Login again and use the same credentials.
It may take 5-6 times of doing that process before you will actually get logged in.
You will know it works when you see "Welcome Tester, Logout, My Genomes"
After you are logged in, it may stick for a while, but browsing around to other pages will eventually kick you back off.
There is no consistent amount of pages that you can go through before this happens, and it doesn't happen on any specific page.
Any insights on this would be greatly appreciated.
Also of note, going to the Django admin page (which is not our code, but base django code) has the same issue.
I've gotten this issue sorted out now.
Users can not login while on HTTPS using while using the listed setup.
What I did:
In settings.py add:
SESSION_SAVE_EVERY_REQUEST = True
SESSION_COOKIE_NAME = 'DNASkittle'
I also wiped the current django_sessions database in case that was causing issues with old lingering data.
I did not setup extra middleware or SSLRedirect, and everything is working all ship shape.
It's little longer and complex the SSL system. to handle the session/login properly with https you can set a configuration with the session_cookies.
settings.py:
ENABLE_SSL=False #in the debug mode in the production passed it to True
MIDDLEWARE_CLASSES = (
'commerce.SSLMiddleware.SSLRedirect', #)
# the session here is to use in your views when a user is connected
SESSION_COKIE_NAME='sessionid'
#the module to store sessions data
SESSION_ENGINE='django.contrib.sessions.backends.db'
#age of cookie in seconds (default: 2 weeks)
SESSION_COOKIE_AGE=7776000 # the number of seconds in 90 days
#whether a user's session cookie expires when the web browser is closed
SESSION_EXPIRE_AT_BROWSER_CLOSE=False
#whether the session cookie should be secure (https:// only)
SESSION_COOKIE_SECURE=False
SSLMiddleware is a file you going to define in your project like.
SSLMiddleware.py:
from django.conf import settings
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
SSL='SSL'
class SSLRedirect:
def process_view(self,request,view_func,view_args,view_kwargs):
if SSL in view_kwargs:
secure =view_kwargs[SSL]
del view_kwargs[SSL]
else:
secure=False
if not secure == self._is_secure(request):
return self._redirect(request,secure)
def _is_secure(self,request):
if request.is_secure():
return True
if 'HTTP_X_FORWARD_SSL' in request.META:
return request.META['HTTP_X_FORWARD_SSL'] == 'on'
return False
def _redirect(self,request,secure):
protocol = secure and "https" or "http"
newurl ="%s://%s%s" % (protocol, request, request.get_full_path())
if settings.DEBUG and request.method=="POST":
raise RuntimeError, \
return HttpResponsePermanentRedirect(newurl)
Now in your urls which should handle your logins or connection add the line
urls.py:
from project import settings
urlpatterns += patterns('django.contrib.auth.views',
(r'^login/$','login',{'template_name':'registration/login.html','SSL':settings.ENABLE_SSL},'login'),
Try to adapt this to your code. and don't forget to turn ENABLE_SSL to True
For users login with HTTPS, you have to enable SSL and use code that matches your case to use it. For the user session you can use this:
First check if you have in settings.py:
INSTALLED_APPS= ('django.contrib.sessions',)
and use the request.sessions in your file.py:
request.session['id']='the_id_to_store_in_browser' or 'other_thing'
Here you use request.session like a special SessionStore class which is similar to python dictionary.
In your views.py for example before rendering a template or redirecting test the cookie with the code:
if :
# whatever you want
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
return HttpResponseRedirect(up-to-you)
else:
# whatever you want
request.session.set_test_cookie()
return what-you-want
with this code, the user session will stick a wide, depending on your SESSION_COOKIE_AGE period.

Django URL Detail

I have to assign to work on one Django project. I need to know about the URL say, http://....
Since with ‘urls.py’ we indeed have ‘raw’ information. How I come to know about the complete URL name; mean with
http+domain+parameters
Amit.
Look at this snippet :
http://djangosnippets.org/snippets/1197/
I modified it like this :
from django.contrib.sites.models import RequestSite
from django.contrib.sites.models import Site
def site_info(request):
site_info = {'protocol': request.is_secure() and 'https' or 'http'}
if Site._meta.installed:
site_info['domain'] = Site.objects.get_current().domain
site_info['name'] = Site.objects.get_current().name
else:
site_info['domain'] = RequestSite(request).domain
site_info['name'] = RequestSite(request).name
site_info['root'] = site_info['protocol'] + '://' + site_info['domain']
return {'site_info':site_info}
The if/else is because of different versions of Django Site API
This snippet is actually a context processor, so you have to paste it in a file called context_processors.py in your application, then add to your settings :
TEMPLATE_CONTEXT_PROCESSORS = DEFAULT_SETTINGS.TEMPLATE_CONTEXT_PROCESSORS + (
'name-of-your-app.context_processors.site_info',
)
The + is here to take care that we d'ont override the possible default context processor set up by django, now or in the future, we just add this one to the tuple.
Finally, make sure that you use RequestContext in your views when returning the response, and not just Context. This explained here in the docs.
It's just a matter of using :
def some_view(request):
# ...
return render_to_response('my_template.html',
my_data_dictionary,
context_instance=RequestContext(request))
HTTPS status would be handled differently by different web servers.
For my Nginx reverse proxy to Apache+WSGI setup, I explicitly set a header that apache (django) can check to see if the connection is secure.
This info would not be available in the URL but in your view request object.
django uses request.is_secure() to determine if the connection is secure. How it does so depends on the backend.
http://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.is_secure
For example, for mod_python, it's the following code:
def is_secure(self):
try:
return self._req.is_https()
except AttributeError:
# mod_python < 3.2.10 doesn't have req.is_https().
return self._req.subprocess_env.get('HTTPS', '').lower() in ('on', '1')
If you are using a proxy, you will probably find it useful that HTTP Headers are available in HttpRequest.META
http://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.META
Update: if you want to log every secure request, use the above example with a middleware
class LogHttpsMiddleware(object):
def process_request(self, request):
if request.is_secure():
protocol = 'https'
else:
protocol = 'http'
print "%s://www.mydomain.com%s" % (protocol, request.path)
Add LogHttpsMiddleware to your settings.py MIDDLEWARE_CLASSES