okfn-brasil / jarbas

🎩 API for information and suspicions about reimbursements by Brazilian congresspeople
https://jarbas.serenata.ai/
296 stars 61 forks source link

Use HTTPS in production with Docker #291

Closed cuducos closed 6 years ago

cuducos commented 6 years ago

What is the problem?

The way to run Jarbas in production is almost Dockerized the only missing piece is HTTPS. The command docker-compose -f docker-compose.yml -f docker-compose.prod.yml works but doesn't serve through HTTPS.

Our manually provisioned and maintained production server which does not use Docker yet has HTTPS setup, that's why jarbas.serenatadeamor.org is secure; however the idea is to replace this manually provisioned and maintained architecture with Docker.

How can this be addressed?

Probably using jwilder/nginx-proxy this task is supposed to be pretty simple. However I couldn't properly set nginx to serve static files following it's instructions and adapting where I though it was relevant. This is my WIP branch at the point I got stuck almost 1 month ago.

Who could help with this issue?

Anyone more experienced than I am with Docker and DevOps I guess. From the history of this repo I would list @caduvieira @gomex @lipemorais @pedrommone

fredgcosta commented 6 years ago

Cuducos,

Here is what I've made: First, I created a self-signed certificate using openssl and copied them to a new folder certs

sudo openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout jarbas.serenatadeamor.org.key -out jarbas.serenatadeamor.org.crt -subj "/CN=jarbas.serenatadeamor.org" -days 3650

Then I changed the Dockerfile-nginx to copy the certificates to the docker image:

COPY certs/jarbas.serenatadeamor.org.crt /etc/nginx/certs/jarbas.serenatadeamor.org.crt
COPY certs/jarbas.serenatadeamor.org.key /etc/nginx/certs/jarbas.serenatadeamor.org.key

After that I recreated the image: sudo docker build -f Dockerfile-nginx -t datasciencebr/jarbas-server:latest . On docker-compose.yml I changed the ALLOWD_HOSTS like this: - ALLOWED_HOSTS=jarbas.serenatadeamor.org,localhost,127.0.0.1 On docker-compose.prod.yml I added the https port 443:

  nginx:
    image: datasciencebr/jarbas-server
    depends_on:
      - django
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - assets:/code/staticfiles

And finally the nginx.docker file:

resolver 127.0.0.11 valid=1s;

server {
    listen              443 ssl;
    server_name jarbas.serenatadeamor.org;
    set $alias "django";

    #keepalive_timeout   70;    
    #ssl_session_cache   shared:SSL:10m;
    #ssl_session_timeout 10m;
    ssl_certificate     /etc/nginx/certs/jarbas.serenatadeamor.org.crt;
    ssl_certificate_key /etc/nginx/certs/jarbas.serenatadeamor.org.key;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    access_log /dev/stdout;
    access_log on;

    location /.well-known/ {
        alias /code/staticfiles/.well-known/;
    }

    location /static/ {
        alias /code/staticfiles/;
    }

    location /favicon.ico {
        alias /code/staticfiles/favicon/favicon.ico;
    }

    location /browserconfig.xml {
        alias /code/staticfiles/favicon/browserconfig.xml;
    } 

    location / {
        proxy_pass http://$alias:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Host $server_name;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        add_header P3P 'CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"';
    }
}

server {
  listen 80 default_server;
  listen [::]:80 default_server ipv6only=on;

  server_name jarbas.serenatadeamor.org;

  return 301 https://$host$request_uri;
}
cuducos commented 6 years ago

Many thanks, @fredgcosta! Reading your code it seams simple and straightforward. Have you opened a PR with this code? I think this is the best way to allow other people to test your implementation.

One doubts about this strategy (sorry if I'm a noob in openssl, I've just used letsencrypt/certbot): how to renew the certificates using this strategy?

fredgcosta commented 6 years ago

@cuducos I will open a PR. I was just trying to figure out how. I am not allowed to create branches, so I should create it in my fork of jarbas? Actually, using openssl to create a self-signed certificate works but is not a solution for a production environment. Browsers would still complain that the certificate is not trusted. So you definitely need a real certificate signed by a trusted CA.

thiagoarrais commented 6 years ago

@cuducos: I am not familiar with your setup and may be completely off base here. But have you taken a look into serving HTTPS through CloudFlare? You would keep your server serving HTTP and they would proxy HTTPS traffic to it. They have a free offering. But I am almost sure they will want you to use their DNS servers. Don't know if that is practical for Jarbas.

cuducos commented 6 years ago

@cuducos I will open a PR. I was just trying to figure out how.

Great, thanks @fredgcosta – as far as I can see @anaschwendler already helped you with that in the Telegram group, right? Don't hesitate to drop a line if you need further help with that.

Actually, using openssl to create a self-signed certificate works but is not a solution for a production environment. Browsers would still complain that the certificate is not trusted. So you definitely need a real certificate signed by a trusted CA.

Hum… got it. Sorry about my noob-ness on that point. However the idea is to have HTTPS on production, so I think something along the lines of the branch linked in the opening post would work better. Many thanks anyway ; )

@cuducos: I am not familiar with your setup and may be completely off base here. But have you taken a look into serving HTTPS through CloudFlare?

Yep, @thiagoarrais — that was discussed in the Telegram group one of these days, thank you very much either way : ) I'll take a look later, but I service independent of CloudFlare might be useful too, so I wouldn't drop this issue even if we start serving HTTPS through CloudFlare. Does that make sense?

Many thanks you both!

ltouro commented 6 years ago

Using jwilder/nginx-proxy is pretty straightforward. I'm currently using it in production along with several other containers. Will give a stab and send a PR soon.

cuducos commented 6 years ago

Using jwilder/nginx-proxy is pretty straightforward. I'm currently using it in production along with several other containers. Will give a stab and send a PR soon.

Many thanks, @ltouro! I failed to serve statics using jwilder/nginx-proxy as I mentioned. But I'm sure it's my noob-ness, not a problem with that image! Feel free to jump in in my branch and fix whatever I've got wrong, or start a fresh one to avoid my chicanes – we'd be so glad to get it fixed ; )

ltouro commented 6 years ago

@cuducos is there any significant difference between serving static files on the edge proxy instead on the inner nginx container? Is the concern here with the traffic overhead in the internal network?

cuducos commented 6 years ago

is there any significant difference between serving static files on the edge proxy instead on the inner nginx container?

Not at all. The only detail is that the staticfiles/ dir is a volume that (as far as I can remember) three containers should have access to:

  1. django and node should be able to write
  2. nginx should be able to read it (serving it to the world)
ltouro commented 6 years ago

@cuducos Great. I think the edge proxy can skip the "serve static content" role then and do only the SSL offload and certificate renewal. The advantage of this aproach is we dont have to manage a custom nginx template file. The default one from nginx-proxy should be just fine.

The CloudFlare approach can help to mitigate internal network traffic overhead, if that turns to be an issue, by caching the content in their free CDN (really good service, btw).

cuducos commented 6 years ago

Closed by #293