Django Rest Framework Reverse Relationship Breaks When Behind Proxy - django

I am trying to serve a django webapp which uses gunicorn with httpd acting as a proxy which also serves static content. Everything works fine, except that the django rest framework searchable API root (the entry point for the app's API) does not provide the correct url. The url is for localhost and the port served by gunicorn rather than the ipaddress of the machine or or its host name. Here's httpd conf file for the app:
<VirtualHost *:80>
DocumentRoot /opt/example
ProxyPass /static/ !
Alias /static/ /var/www/html/static/
ProxyPass / http://localhost:8000/
ProxyPassReverse / http://localhost:8000/
</VirtualHost>
Gunicorn serves on localhost on port 8000.
Here's the view for the api:
#api_view(('GET',))
def api_root(request, format=None):
return Response({
'activity': reverse('activity-list', request=request, format=format)
'test' : reverse('test-list', request=request, format=format)
})
When hitting the api root page this is the response I get:
HTTP 200 OK
Content-Type: application/json
Vary: Accept
Allow: GET, HEAD, OPTIONS
{
"activity": "http://localhost:8000/activity/",
"tests": "http://localhost:8000/test/",
}

I have a similar setup and it seems to work just fine.
Make sure you have USE_X_FORWARDED_HOST = True in your settings.py as it defaults to False
More info here

In your httpd.conf there is a typo httpd.
The line should be:
ProxyPassReverse / http://localhost:8000/

Related

Python Flask not forwarding files to httpd without ProxyPass /

I have apache httpd configured as a proxy for flask. I have gotten flask to pass the static files through to httpd but only when proxying the root dir "/" like below
Flask:
def web():
return render_template('requestor/landingReq.html')
httpd.conf:
ProxyPass / balancer://LBFDMCLUS/ stickysession=JSESSIONID
ProxyPassReverse / balancer://LBFDMCLUS/ stickysession=JSESSIONID
ProxyPass /Web balancer://LBFDMCLUS/Web stickysession=JSESSIONID
ProxyPassReverse /Web balancer://LBFDMCLUS/Web stickysession=JSESSIONID
RequestHeader set X-Forwarded-Proto http
RequestHeader set X-Forwarded-Prefix /
While this works it does not work in my instance as I need to proxy / to a different server. However if I try to delete the proxy to / and just leave the /Web proxy I no longer get my static files forwarded.

Github oauth apache2 django daphne python-social-auth | Authentication failed ... redirect_uri

I can't get the oauth running on the productive server while there is no problem when testing with localhost:8000 during development.
This is the setup:
Apache as reverse proxy -> Daphne -> Django
This is the exception as answer when clicking on the oauth button to login:
AuthFailed at /oauth/complete/github/
Authentication failed: The redirect_uri MUST match the registered callback URL for this application.
Request Method: GET
Request URL: http://nomoney.shop/oauth/complete/github/?error=redirect_uri_mismatch&error_description=The+redirect_uri+MUST+match+the+registered+callback+URL+for+this+application.&error_uri=https%3A%2F%2Fdeveloper.github.com%2Fapps%2Fmanaging-oauth-apps%2Ftroubleshooting-authorization-request-errors%2F%23redirect-uri-mismatch&state=bHGqHRlbQjIYNKJWrb8t46ia1XV5OuxM
Django Version: 3.0.5
Exception Type: AuthFailed
Exception Value:
Authentication failed: The redirect_uri MUST match the registered callback URL for this application.
Apache Proxy settings:
<VirtualHost *:443>
...
ProxyPass / http://127.0.0.1:8002/
ProxyPassReverse / https://127.0.0.1:8002/
ProxyPreserveHost On
ProxyRequests Off
...
</VirtualHost>
I think, the proxy setting causes that error but after hours of searching i cant find the solution.
With a little luck i found the solution short after creating this question (it's the same problem, when using Gunicorn instead of Daphne:
I added this to the apache vhost
RequestHeader set X-Forwarded-Proto 'https' env=HTTPS
and added this to django settings.py
USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
More informations:
https://ubuntu.com/blog/django-behind-a-proxy-fixing-absolute-urls

OAuth Callback URL incompatible with nginx proxy server behavior

I have spent a good part of the last 3 days trying every solution that is on the internet and feeling desperate. Here's the problem statement:
I have a Dockerized app with three services:
A django application with gunicorn (web)
A Nginx server (nginx)
PostgreSQL (db)
My web application requires user to log in with their GitHub account through a fairly standard OAuth process. This has always worked without nginx. User clicks on the "log in with github" button, sent them to GitHub to authorize the application, and redirects it back to a completion page.
I have "Authorization callback URL" filled in as http://localhost:8000. Without Nginx I can navigate to the app on localhost, click on the button, and upon authorization, get redirected back to my app on localhost.
With Nginx, I would always fail with the error (nginx in console):
GET /auth/complete/github/?error=redirect_uri_mismatch&error_description=The+redirect_uri+MUST+match+the+registered+callback+URL+for+this+application.&error_uri=https%3A%2F%2Fdeveloper.github.com%2Fapps%2Fmanaging-oauth-apps%2Ftroubleshooting-authorization-request-errors%2F%23redirect-uri-mismatch&state=nmegLb41b959su31nRU4ugFOzAqE8Cbl HTTP/1.1
This is my Nginx configuration:
upstream webserver {
# docker will automatically resolve this to the correct address
# because we use the same name as the service: "web"
server web:8000;
}
# now we declare our main server
server {
listen 80;
server_name localhost;
location / {
# everything is passed to Gunicorn
proxy_pass http://webserver;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
}
location /static {
autoindex off;
alias /static_files/;
}
location /media {
alias /opt/services/starport/media;
}
}
This is my Dockerfile:
version: '3.7'
services:
web:
build: .
command: sh -c "cd starport && gunicorn starport.wsgi:application --bind 0.0.0.0:8000"
volumes:
- static_files:/static_files # <-- bind the static volume
networks:
- nginx_network
nginx:
image: nginx
ports:
- 8000:80
volumes:
- ./config/nginx/conf.d:/etc/nginx/conf.d
- static_files:/static_files # <-- bind the static volume
depends_on:
- web
networks:
- nginx_network
networks:
nginx_network:
driver: bridge
volumes:
static_files:
My hunch was that the reason it worked without Nginx but doesn't with the Nginx http server has got to do with the redirection since Nginx is listening to a port and then forwarding it to a different port. GitHub's doc specifically said that the redirect URI needs to be exactly the same as the registered callback url. I've also used the inspector tools and these are my Request headers:
GET /accounts/login/ HTTP/1.1
Cookie: ...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Upgrade-Insecure-Requests: 1
Host: localhost:8000
User-Agent: Mozilla/5.0
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: keep-alive
The error message I get with Nginx (again, stressing that it works like a charm without error without nginx 10 out of 10 times) is:
http://localhost:8000/auth/complete/github/?error=redirect_uri_mismatch&error_description=The+redirect_uri+MUST+match+the+registered+callback+URL+for+this+application.&error_uri=https%3A%2F%2Fdeveloper.github.com%2Fapps%2Fmanaging-oauth-apps%2Ftroubleshooting-authorization-request-errors%2F%23redirect-uri-mismatch
As an additional detail, I'm using the social-auth-app-django package but this should not matter.
Further troubleshooting
After countless hours of probing, I run this in local on debug mode and this time closely monitored my Request information. When use hits the link to authorize with GitHub via OAuth, this is the Request along with all the header information:
CSRF_COOKIE 'abc'
HTTP_ACCEPT 'text/html,application/xhtml+xml'
HTTP_ACCEPT_ENCODING 'gzip, deflate'
HTTP_ACCEPT_LANGUAGE 'en-us'
HTTP_CONNECTION 'close'
HTTP_COOKIE ('csrftoken=...; ')
HTTP_HOST 'localhost'
HTTP_REFERER 'http://localhost:8000/accounts/login/?next=/'
HTTP_USER_AGENT ('Mozilla/5.0')
HTTP_X_FORWARDED_FOR '172.26.0.1'
HTTP_X_FORWARDED_PROTO 'http'
PATH_INFO '/auth/complete/github/'
SERVER_NAME '0.0.0.0'
SERVER_PORT '8000'
QUERY_STRING
'error=redirect_uri_mismatch&error_description=The+redirect_uri+MUST+match+the+registered+callback+URL+for+this+application.&error_uri=https%3A%2F%2Fdeveloper.github.com%2Fapps%2Fmanaging-oauth-apps%2Ftroubleshooting-authorization-request-errors%2F%23redirect-uri-mismatch&state=NwUhVqfOCNb51zpvoFbXVvm1Cr7k3Fda'
RAW_URI
'/auth/complete/github/?error=redirect_uri_mismatch&error_description=The+redirect_uri+MUST+match+the+registered+callback+URL+for+this+application.&error_uri=https%3A%2F%2Fdeveloper.github.com%2Fapps%2Fmanaging-oauth-apps%2Ftroubleshooting-authorization-request-errors%2F%23redirect-uri-mismatch&state=NwUhVqfOCNb51zpvoFbXVvm1Cr7k3Fda'
What immediately stood out to me was the value of HTTP_HOST, HTTP_REFERRER and SERVER_NAME. What's also interesting to me was the error message says:
http://localhost/auth/complete/github/?error=redirect_uri_mismatch&error_description=The+redirect_uri+MUST+match+the+registered+callback+URL+for+this+application.&error_uri=https%3A%2F%2Fdeveloper.github.com%2Fapps%2Fmanaging-oauth-apps%2Ftroubleshooting-authorization-request-errors%2F%23redirect-uri-mismatch&state=NwUhVqfOCNb51zpvoFbXVvm1Cr7k3Fda
Where instead of http://localhost:8000 it only has http://localhost, which looks like a big hint that I am not configuring things correctly. Any leads or assistance would help!
Resources I've tried
StackOverflow threads like this seems promising but similar questions like this receive no meaningful response except to explain the error.

how to make django render the default home page instead of apache

As is known, after issuing a request with a sole IP or server name through web explorer, apache will return its default static home page to the explorer. I'm using django and apache with mod_wsgi to deploy my site(i.e. django app on apache web server). How to route the processing to django mapping rules defined in views.py to dynamically produce a home page(i.e. index page) for that site when I issue a request to that server running apache? Thanks in advance!
I added the following configuration in the main Apache 'httpd.conf':
<VirtualHost 127.0.0.1:80>
ServerName 127.0.0.1
ServerAlias example.com
ServerAdmin webmaster#example.com
DocumentRoot /usr/local/www/documents
Alias /robots.txt /usr/local/www/documents/robots.txt
Alias /favicon.ico /usr/local/www/documents/favicon.ico
Alias /media/ /usr/local/www/documents/media/
<Directory /usr/local/www/documents>
Require all granted
</Directory>
WSGIDaemonProcess 127.0.0.1 processes=2 threads=15 display-name=%{GROUP}
WSGIProcessGroup 127.0.0.1
WSGIScriptAlias / /usr/local/www/wsgi-scripts/myapp.wsgi
<Directory /usr/local/www/wsgi-scripts>
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
Code in myapp.wsgi:
def application(environ, start_response):
status = '200 OK'
output = b'Hello World!'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
start_response(status, response_headers)
return [output]
Then I issued 127.0.0.1, the explorer displayed 'It works!', which is the default page produced by apache, instead of which I wanted 'Hello world!' returned in this case. I just want that when just issued an IP or site address, the work of generating the default home page is directly delegated to django to dynamically handle rather than to return a static index.html by Apache. Although I configured WSGIScriptAlias / /usr/local/www/wsgi-scripts/myapp.wsgi , this seems still not work.
Virtual hosts don't work in that way for 127.0.0.1 as it is treated a bit special. What is going to happen, unless that is the only VirtualHost in your configuration, is that Apache will fall back to using the first VirtualHost definition it found, usually the default site.
So normally you would have:
<VirtualHost *:80>
ServerName my.fqdn.host.name
...
</VirtualHost>
That is, no IP in VirtualHost directive and ServerName is the actual hostname that appears in the URL, not an IP address.
Was there a prior VirtualHost in httpd.conf or was it including including extra/httpd-vhosts.conf, that would result in a VirtualHost superseding this one?

Django nginx returning 502 Bad Gateway status code

Edit: I re-followed the instructions and now this is the issue I am having: Django Nginx not serving media files
I'm following this tutorial:
http://uwsgi-docs.readthedocs.org/en/latest/tutorials/Django_and_nginx.html
Everything was going well until I reached the "Basic nginx test" section. When I stopped and started nginx and then added "media.png" to my media folder and went to 192.168.** *.** *:8000/media/media.png it gave me a 403 Forbidden status code.
When I visit 192.168.** *.** *:8000 it gives a 502 Bad Gateway status code.
this is mysite_nginx.conf:
# mysite_nginx.conf
# the upstream component nginx needs to connect to
upstream django {
# server unix:///path/to/your/mysite/mysite.sock; # for a file socket
server 127.0.0.1:8001; # for a web port socket (we'll use this first)
}
# configuration of the server
server {
# the port your site will be served on
listen 8000;
# the domain name it will serve for
server_name 192.168.***.***; # substitute your machine's IP address or FQDN
charset utf-8;
# max upload size
client_max_body_size 75M; # adjust to taste
# Django media
location /media {
alias /home/a/Documents/media; # your Django project's media files - amend as required
}
location /static {
alias /home/a/Documents/CMS/CMSApp/static; # your Django project's static files - amend as required
}
# Finally, send all non-media requests to the Django server.
location / {
uwsgi_pass django;
include /home/a/Documents/CMS/CMS/uwsgi_params; # the uwsgi_params file you installed
}
}
Any idea why nginx is returning those status code's and how I can fix the issue (how I can get it to show me the "media.png" image)?