Situation:
we have one server and on it 2 flask websites, in our case just flask backends.
We want to protect both apps with ssl (https), but to use reverse proxy redirection we need to setup the nginx with the certificates as well.
It is reccommended to first read this article on the same topic:
https://blog.miguelgrinberg.com/post/running-your-flask-application-over-https
Our web apps:
app1 localhost:5001, DNS example1.com
app2 localhost:5002, DNS example2.com
nginx will listen on default https port 443, will redirect requests for http port 80 to port 443 bydefault, so communication between app and client is always ssl secured.
so nginx will recieve something like this:
example1.com:80 -> nginx -> example1.com:443 -> nginx -> example1.com:5001
example1.com:443 -> nginx -> example1.com:5001
and the same for second app:
example2.com:80 -> nginx -> example2.com:443 -> nginx -> example2.com:5002
example2.com:443 -> nginx -> example.com:5002
We will use self signed certificates for this excercise, we will also have both web apps using the same certificate pair for simplicity. We will have the certs in the same folder where we keep our flask apps.
openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout priv_key.pem -days 3650
Set your local DNS mapping in etc/hosts
$ cat /etc/hosts
127.0.0.1 localhost
127.0.0.1 example1.com
127.0.0.1 www.example1.com
127.0.0.1 example2.com
127.0.0.1 www.example2.com
Flask apps:
coil@coil$ cat app_example1.py
#from crypt import methods
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/")
def main_page():
return jsonify({"secured": "Hello example1.com"})
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True, port=5001, threaded=True, ssl_context=('cert.pem', 'priv_key.pem'))
coil@coil$ cat app_example2.py
#from crypt import methods
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/")
def main_page():
return jsonify({"secured": "Hello example2.com"})
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True, port=5002, threaded=True, ssl_context=('cert.pem', 'priv_key.pem'))
start them as:
python3 app_example1.py
python3 app_example2.py
Now we need to setup nginx as reverse proxy that will use ssl:
create these 2 files in /etc/nginx/sites-enabled
directory. Of course your path to certs will be different.
coil@coil:/etc/nginx/sites-enabled$ cat example1.com
server {
# SSL configuration
listen 443 ssl;
listen [::]:443 ssl;
server_name example1.com;
ssl_certificate /home/coil/Desktop/flask_ssl/cert.pem;
ssl_certificate_key /home/coil/Desktop/flask_ssl/priv_key.pem;
location / {
return 301 https://example1.com:5001$request_uri;
}
port_in_redirect off;
}
server {
listen 80;
listen [::]:80;
server_name example1.com;
location / {
return 301 https://$host$request_uri;
}
}
coil@coil:/etc/nginx/sites-enabled$ cat example2.com
server {
# SSL configuration
listen 443 ssl;
listen [::]:443 ssl;
server_name example2.com;
ssl_certificate /home/coil/Desktop/flask_ssl/cert.pem;
ssl_certificate_key /home/coil/Desktop/flask_ssl/priv_key.pem;
location / {
return 301 https://example2.com:5002$request_uri;
}
port_in_redirect off;
}
server {
listen 80;
listen [::]:80;
server_name example2.com;
location / {
return 301 https://$host$request_uri;
}
}
Reload nginx for changes to take effect
sudo systemctl reload nginx
check your access via web browser and curl -k
ignores the fact that the certificates are self signed and -L
follows our 301 redirect from nginx
coil@coil$ curl -k -L https://example1.com
{
"secured": "Hello example1.com"
}
coil@coil$ curl -k -L http://example1.com
{
"secured": "Hello example1.com"
}
The same should work for example2.com. Notice that we have ssl/https answer even for http request (this will be visible in web browser).