I'm currently developing an application which consists of a React frontend, which makes frequent requests to a Django backend. Both the React and Django applications are running on the same server.
My problem is I wish to hide my Django backend from the world, so it only accepts requests from my React application. To do so, I've been trying several configurations of ALLOWED_HOSTS in my Django settings.py, but so far none of them seem to be successful. An example route that I wish to hide is the following:
https://api.jobot.es/auth/user/1
At first I tried the following configuration:
ALLOWED_HOSTS=['jobot.es']
but while this hid the Django backend from the world, it also blocked the petitions coming from the React app (at jobot.es). Changing the configuration to:
ALLOWED_HOSTS=['127.0.0.1']
enabled my React app to access the backend but so could do the rest of the world. When the Django backend is inaccessible from the outside world, a get request from https://api.jobot.es/auth/user/1 should return a 400 "Bad Request" status.
The error I get when the React app fails to request data from the Django backend is the following:
Access to XMLHttpRequest at 'https://api.jobot.es/auth/login' from origin 'https://jobot.es' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource., but in settings.py I have allowed all Cors origins with CORS_ORIGIN_ALLOW_ALL = True.
The url of my React application is https://jobot.es, while the url for the Django backend is https://api.jobot.es, but as both apps are hosted on the same server both urls resolve to the same ip address. On the server I'm using Nginx to redirect traffic accordingly to either the React app or the Django backend.
In case it is of any help, here are the Nginx configurations for the React app (first) and the Django backend (second):
React app Nginx configuration
server {
server_name jobot.es www.jobot.es;
access_log off;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
add_header P3P 'CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"';
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/jobot.es/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/jobot.es/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = jobot.es) {
return 301 https://$host$request_uri;
} # managed by Certbot
server_name jobot.es;
listen 80;
return 404; # managed by Certbot
}
Django backend Nginx configuration:
server {
server_name api.jobot.es;
access_log off;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
add_header P3P 'CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"';
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/jobot.es/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/jobot.es/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = api.jobot.es) {
return 301 https://$host$request_uri;
} # managed by Certbot
server_name api.jobot.es;
listen 80;
return 404; # managed by Certbot
}
I also attach the GitHub repositories for both the React App and the Django backend in hopes that they may be of any help.
React App:
https://github.com/PaburoTC/jobot
DJango Backend:
https://github.com/PaburoTC/JoboBackend
Thank you in advance <3
You can't "hide" the Django application, since the React app, which would be contacting the Django backend, is running in users' browsers (i.e. in the outside world).
In other words, there is no separate "React application" connecting to your Django API backend, it's just the user's browser first requesting jobot.es, then api.jobot.es.
You could check for the referer header, but it has no real security benefit at all.
Related
Hive -
I have a Flask + React app running on Debian using Nginx and Gunicorn (which is maintained via supervisor). When I set up my Nginx CONF file to just serve up the site over port 80, everything seemed to work fine except for a CORS error. This only happened in Chrome, but the entire site worked fine in Safari (a known Chrome issue). After tracking down the issue and determining that the cause was the lack of an SSL certificate, I set up my Nginx CONF file to support SSL. Now two things happen that frustrate me to no end:
When I go to the site, the Developer Console shows that the site is getting a Connection Refused on https://localhost:5000/.
When I use CURL to test the API, it works.
Both the React and Flask applications are hosted on the same server, and I even have port 5000 open to be safe (as well as SSL and standard 80).
My conf file is below, but some info that might be useful:
All URLs are served up at the root "domain.com/" of the website.
The Flask app has the API in a nested folder and the exposed API is of the format "domain.com/api/v1/{calls}".
I have UI Swagger installed, but can not access it from the browser.
The development environment works fine, but that is because I'm using the built-in Python/Flask server and running the frontend React app with NPM Start.
My code is below and I've left in place, as commented lines, other things I have tried to make this work to show the various efforts I've exerted. In the location /api section, I previously just had the include proxy_params and the proxy_pass...all other lines were added after they didn't work in the location/section. server_name has _ as the server_name. I also had this as the subdomain of my site and mixed & matched, but no dice.
server {
root /var/www/project-app/frontend/build;
index index.html;
server_name _;
location / {
# proxy_pass http://localhost:5000;
# proxy_redirect off;
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
try_files $uri $uri/ =404;
add_header Cache-Control "no-cache";
}
location /static {
alias /var/www/project-app/frontend/build/static;
expires 1y;
add_header Cache-Control "public";
}
location /api {
include proxy_params;
proxy_pass http://localhost:5000;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
listen 443 ssl; # managed by Certbot
listen [::]:443 ssl;
ssl_certificate /etc/letsencrypt/live/project/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/project/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
access_log /var/log/project_access.log;
error_log /var/log/project_access.log;
proxy_headers_hash_max_size 512;
proxy_headers_hash_bucket_size 128;
}
server {
if ($host = app.project.tld) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
# server_name app.project.tld;
server_name _;
return 404; # managed by Certbot
}
Debian 11
Latest Nginx Version from APT
All packages are updated and installed via both PIP and NPM
Python 3.9.2
There are fillers in the code...such as app.project.tld...in my actual code, I have the subdomain of the actual app. Just trying to avoid someone telling me I made a copy/paste snafu :)
Thanks!
I'm trying to serve a static build of a ReactJS app using Nginx, but something really strange is happening: the stylesheet isn't getting applied and the image isn't loading. I can see in the developer tools that the resources are there (see the image below), they just aren't getting applied. However, the javascript file is running--otherwise there wouldn't be any content on the screen.
What makes this even weirder is that I tried serving the files in the same directory using a python http server (command: python3 -m http.server 80), and it was fine; all of the assets loaded correctly.
Since it seems to be an nginx issue, here's my nginx config:
nginx.conf
events {
worker_connections 1024;
}
http {
resolver 127.0.0.11;
# Http redirect to https (unless it's a challenge)
server {
listen 80;
listen [::]:80;
server_name ambitx.io www.ambitx.io wc.ambitx.io rk.ambitx.io;
server_tokens off;
include letsencrypt.conf;
location / {
return 301 https://$server_name$request_uri;
}
}
# React frontend
server {
listen 443 default_server ssl http2;
listen [::]:443 ssl http2;
server_name ambitx.io www.ambitx.io;
include ssl.conf;
include letsencrypt.conf;
location / {
root /var/www/staticfiles;
index index.html index.htm;
try_files $uri /index.html =404;
}
}
# Websocket backend
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name wc.ambitx.io;
include ssl.conf;
include letsencrypt.conf;
location / {
proxy_pass "http://wsserver:8080";
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
}
# Rocket backend
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name rk.ambitx.io;
include ssl.conf;
include letsencrypt.conf;
location / {
proxy_pass "http://rocketserver:80";
}
}
}
letsencrypt.conf
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
ssl.conf
ssl_certificate /etc/letsencrypt/live/ambitx.io/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ambitx.io/privkey.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # don't use SSLv3. Ref: POODLE
ssl_prefer_server_ciphers on;
Thanks in advance for the help.
I figured it out: it turns out the Nginx server was missing it's MIME types (the browser thought that the css file was text/plain instead of text/css).
Usually the best practice is to add files to /etc/nginx/conf.d/ (and mount your docker volume there) instead of editting nginx.conf directly, but I wanted to be able to place other files in the /etc/nginx/ directory so I decided to mount my docker volume there.
As it turns out, that's a bad idea. I overwrote a lot of other important config files inside the docker container. Now, I could just copy all of those files into my docker volume and call it good, but I decided it would be worth doing it the "right" way so I don't mess up stuff in the future.
So, now I have a docker volume mounted at /etc/nginx/cond.f/ and another volume mounted at /etc/nginx/lib/ so that I can import files without the main nginx.conf reading is as a server config.
I'm running NGINX on Ubuntu 18.04 LTS Bionix in a Digital Ocean Droplet. I have a dockerized React App which I can successfully reach and use with a dockerized Flask/Quart App when using http. The React App can be reached at example:8000 and the Quart at example:9000. The 2 communicate fine.
Now when setting up https using NGINX and the commonly used certbot method (thank you for the tutorial: Digital Ocean tutorial) I get errors that don't completely tell me what is going on.
After completing the tutorial and successfully displaying the html file in /var/www/example/index.html at https://www.example.com, I try https://www.example.com/app for the React app that I can reach on http 8000 and the /api site that I can reach on http 9000 with a standard doc page.
The React App shows the title as expected in the browser tab but there is nothing displayed. Only 404 errors saying 'failed to load resource' from css chunks.
The API fails also with failed to load resource.
The only NGINX configuration changed based on research found so far is the following:
# /etc/nginx/sites-enabled/example
server {
root /var/www/example.com/html;
index index.html index.htm index.nginx-debian.html;
server_name example.com www.example.com;
location / {
try_files $uri $uri/ =404;
}
location /app {
proxy_pass http://localhost:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /api {
proxy_pass http://localhost:9000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = www.example.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = example.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 404; # managed by Certbot
}
Also have tried to add / to both api and app (location /app/, /api/) and the result changes but still errors. In this case it is still the 404 for api but it says URI doesn't exist. In the case of the react app it gives syntax errors based on the css it is reading.
Any advice is greatly appreciated.
We are making a V2 Docusaurus website.
After building the website in the server, we could well use it with https. Here is a part of my_server_block.conf:
server {
listen 3001 ssl;
ssl_certificate /certs/server.crt;
ssl_certificate_key /certs/server.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://localhost:3002;
proxy_redirect off;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Ssl on;
}
}
In localhost, http works. However, we need to test https in localhost now. But https returns an error, though I started it by HTTPS=true yarn start: This site can’t provide a secure connection localhost sent an invalid response. ERR_SSL_PROTOCOL_ERROR:
Does anyone know what I should do to make https work in localhost?
Edit 1: I tried HTTPs=true SSL_CRT_FILE=certs/server.crt SSL_KEY_FILE=certs/server.key yarn start, https://localhost:3001 still returned the same error. Note that certs/server.crt and certs/server.crt are the files that make https work in our production server via ngnix:
server {
listen 3001 ssl;
ssl_certificate /certs/server.crt;
ssl_certificate_key /certs/server.key;
You are using Nginx, so use it for SSL offloading (your current config) and don't start https on the Docusaurus site. So user in the browser will use https, but Docusaurus will be using http.
If you start https on the Docusaurus site and you will be proxypassing with http proxy_pass http://localhost:3002;, then it is obvious problem - connection with http protocol to https endpoint. You may proxypass with https protocol proxy_pass https://localhost:3002; of course, but that may need more advance configuration. Just keep it simple and use SSL offloading in the Nginx.
There is an issue with https support on localhost in react-dev-utils#^v9.0.3, which is a dependency of docusaurus.
https://github.com/facebook/create-react-app/issues/8075
https://github.com/facebook/create-react-app/pull/8079
It is fixed in react-dev-utils#10.1.0
Docusaurus 2 uses Create React App's utils internally and you might need to specify the path to your cert and key as per the instructions here. I'm not familiar with the server config so I can't help you there.
Maybe this answer will be helpful - How can I provide a SSL certificate with create-react-app?
I bought a VPS on OVH, which currently runs on Debian 9.
I installed successfully SSL over defaults ports (80 and 443) and it's working great when displaying basic html.
However, I'm totally lost concerning the run of my react app (basic app to try configuration).
It works in http in Safari but doesn't work at all in Chrome : "This site can’t provide a secure connection wecode-it.fr sent an invalid response.
ERR_SSL_PROTOCOL_ERROR"
I already checked the date of my server which is correct.
I'm running my app locally with npm start and want to use development mode for now. If you have any advice tho on building the app for production, I'll take it too. I think I'll use docker but I don't how to use it yet.
Here is my nginx configuration.
listen 80 default_server;
listen [::]:80 default_server;
server_name wecode-it.fr www.wecode-it.fr;
root /usr/share/nginx/html;
index index.html;
location ~ /.well-known {
allow all;
}
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
ssl on;
ssl_certificate /etc/letsencrypt/live/wecode-it.fr/fullchain.pem
ssl_certificate_key /etc/letsencrypt/live/wecode-it.fr/privkey.pem
ss_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_servers_ciphers on;
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off; # Requires nginx >= 1.5.9
ssl_stapling on; # Requires nginx >= 1.3.7
ssl_stapling_verify on; # Requires nginx => 1.3.7
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
location / {
proxy_pass https://MYIP:3030;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
I'm available to answer any of you questions.
Thank you.
You have to decide if you want to use the proxy or direct.
Some ssl settings are missing (if certificate is local).
But for now, you can try this setting:
server {
listen 443 ssl;
server_name backend1.example.com;
ssl_certificate /etc/ssl/certs/server.crt;
ssl_certificate_key /etc/ssl/certs/server.key;
#...
location /yourapp {
proxy_pass http://url_to_app.com;
#...
}
}
See more in docs
https://docs.nginx.com/nginx/admin-guide/security-controls/securing-http-traffic-upstream/#configuring-upstream-servers
I completely reinstalled my vps, did every steps from scratch and it's now working. I think I lost myself trying too much stuff, and starting all over again made it simple.