From cb3370e5ebf537e6156dd8776a3348301a8ebcf8 Mon Sep 17 00:00:00 2001 From: Mike Olund Date: Mon, 27 Feb 2017 18:34:57 -0800 Subject: [PATCH] Filter traffic by IP address in Django --- .../apps/core/middleware/bceid_middleware.py | 37 +++++++++-- edivorce/settings/base.py | 1 + edivorce/settings/openshift.py | 3 +- nginx-proxy/conf.d/server.conf | 64 +++++++++++++++++++ .../edivorce-environment-template.yaml | 6 ++ 5 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 nginx-proxy/conf.d/server.conf diff --git a/edivorce/apps/core/middleware/bceid_middleware.py b/edivorce/apps/core/middleware/bceid_middleware.py index a45b7f5a..8daae060 100644 --- a/edivorce/apps/core/middleware/bceid_middleware.py +++ b/edivorce/apps/core/middleware/bceid_middleware.py @@ -1,14 +1,15 @@ import uuid - +from ipaddress import ip_address, ip_network from django.conf import settings +from django.shortcuts import redirect class BceidUser(object): - def __init__(self, guid, first_name, last_name, type, is_authenticated): + def __init__(self, guid, first_name, last_name, user_type, is_authenticated): self.guid = guid self.first_name = first_name self.last_name = last_name - self.type = type + self.type = user_type self.is_authenticated = is_authenticated @@ -20,14 +21,17 @@ class BceidMiddleware(object): localdev = settings.DEPLOYMENT_TYPE == 'localdev' + # make sure the request didn't bypass the proxy + if not localdev and not self.__request_came_from_proxy(request): + return redirect(settings.PROXY_BASE_URL + settings.FORCE_SCRIPT_NAME) + if not localdev and request.META.get('HTTP_SM_USERDN', '') != '': # 1. Real BCeID user / logged in - request.bceid_user = BceidUser( guid=request.META.get('HTTP_SM_USERDN', ''), is_authenticated=True, - type='BCEID', + user_type='BCEID', first_name='Bud', last_name='Bundy' ) @@ -38,10 +42,11 @@ class BceidMiddleware(object): request.bceid_user = BceidUser( guid=request.session.get('fake-bceid-guid', ''), is_authenticated=True, - type='FAKE', + user_type='FAKE', first_name='Kelly', last_name='Bundy' ) + else: # 3. Anonymous User / not logged in @@ -51,10 +56,28 @@ class BceidMiddleware(object): request.bceid_user = BceidUser( guid=request.session.get('anon-guid'), is_authenticated=False, - type='ANONYMOUS', + user_type='ANONYMOUS', first_name='', last_name='' ) def process_response(self, request, response): return response + + + def __request_came_from_proxy(self, request): + """ + Validate that the request is coming from inside the BC Government data centre + """ + # allow all OpenShift health checks + if request.path == settings.FORCE_SCRIPT_NAME + 'health': + return True + + bcgov_network = ip_network(settings.BCGOV_NETWORK) + x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '') + forwarded_for = x_forwarded_for.split(',') + + for ip in forwarded_for: + if ip_address(ip) in bcgov_network: + return True + return False \ No newline at end of file diff --git a/edivorce/settings/base.py b/edivorce/settings/base.py index 79c86adb..41104cbc 100644 --- a/edivorce/settings/base.py +++ b/edivorce/settings/base.py @@ -109,5 +109,6 @@ STATICFILES_FINDERS = ( 'compressor.finders.CompressorFinder', ) +BCGOV_NETWORK = os.environ.get('PROXY_NETWORK', '0.0.0.0/0') FORCE_SCRIPT_NAME = '/' diff --git a/edivorce/settings/openshift.py b/edivorce/settings/openshift.py index 75afe74c..9ab49029 100644 --- a/edivorce/settings/openshift.py +++ b/edivorce/settings/openshift.py @@ -68,6 +68,7 @@ FORCE_SCRIPT_NAME = PROXY_URL_PREFIX + '/' STATIC_URL = PROXY_URL_PREFIX + '/static/' WEASYPRINT_CSS_LOOPBACK += PROXY_URL_PREFIX +# Integration URLs LOGOUT_URL = 'https://logon.gov.bc.ca/clp-cgi/logoff.cgi?returl=https://justice.gov.bc.ca%s&retnow=1' % PROXY_URL_PREFIX - +PROXY_BASE_URL = 'https://justice.gov.bc.ca' diff --git a/nginx-proxy/conf.d/server.conf b/nginx-proxy/conf.d/server.conf new file mode 100644 index 00000000..a71e8cea --- /dev/null +++ b/nginx-proxy/conf.d/server.conf @@ -0,0 +1,64 @@ +# This configuration works with the S2I image defined in https://github.com/BCDevOps/s2i-nginx + +server { + listen 8080; + server_name _; + + # Allows non-standard headers like SMGOV_USERGUID + ignore_invalid_headers off; + + # default path + location / { + proxy_pass http://edivorce-django:8080; + proxy_pass_request_headers on; + + # rewrite 302 redirect responses to absolute URL's so the justice proxy + # doesn't mangle them by adding double slashes to relative URL's + proxy_redirect http://edivorce-django:8080 https://justice.gov.bc.ca; + + # remove directories from incoming requests; + rewrite ^/divorce-dev$ / last; + rewrite ^/divorce-test$ / last; + rewrite ^/divorce$ / last; + + rewrite ^/divorce-dev(.*)$ $1 last; + rewrite ^/divorce-test(.*)$ $1 last; + rewrite ^/divorce(.*)$ $1 last; + } + + merge_slashes off; + # replace merge_slashes' behavior with "rewrite double slashes" + location ~* "//" { + rewrite ^(.*)//(.*)$ $1/$2; + } + + # static no rewrite (dev) + location /divorce-dev/static/ { + #todo: add caching + proxy_pass http://edivorce-django:8080; + proxy_pass_request_headers on; + } + + # static no rewrite (test) + location /divorce-test/static/ { + #todo: add caching + proxy_pass http://edivorce-django:8080; + proxy_pass_request_headers on; + } + + # static no rewrite (prod) + location /divorce/static/ { + #todo: add caching + proxy_pass http://edivorce-django:8080; + proxy_pass_request_headers on; + } + + # For status of ngnix service + location /nginx_status { + # Enable Nginx stats + stub_status on; + + # No need to log this request, its just noise + access_log off; + } +} \ No newline at end of file diff --git a/openshift/templates/edivorce-environment-template.yaml b/openshift/templates/edivorce-environment-template.yaml index d880e94b..92a9edac 100644 --- a/openshift/templates/edivorce-environment-template.yaml +++ b/openshift/templates/edivorce-environment-template.yaml @@ -86,6 +86,8 @@ objects: value: "${DJANGO_SECRET_KEY}" - name: ENVIRONMENT_TYPE value: "${ENVIRONMENT_TYPE}" + - name: PROXY_NETWORK + value: "${PROXY_NETWORK}" resources: limits: memory: "${MEMORY_LIMIT}" @@ -319,3 +321,7 @@ parameters: - name: ENVIRONMENT_TYPE displayName: Type of environnment (dev,test or prod). required: true +- name: PROXY_NETWORK + displayName: Network of upstream proxy + value: 0.0.0.0/0 + required: true