Getting nginx to work remotely and securely via HTTPS/SSL

Hi there,

I’ve done a ridiculous amount of searching and IRC bothering on getting a lot of the Rock-On containers to work with nginx via reverse proxy all passed through SSL/HTTPS. I finally got a great set up working so I wanted to share with the rest of the world considering I found absolutely no configuration online as to what I wanted. This how-to is a work in progress, so please feel free to send me a message or comment on this page if there are any suggestions or if I’m blatantly providing misinformation!


Just fyi, if you don’t feel comfortable modifying your nginx.conf, just stick with the default config. Before starting, I would do a config backup via the GUI to save any changes you make, just to be safe.


So, the main problem I ran into was trying to get RocketChat to proxy externally. Since it’s built with Meteor which accesses a different Docker container to reference its assets, it made it almost impossible to use URI locations: ie;

    location /chat {
        proxy_pass http://192.168.1.35:3000/;

So, I used subdomains and everything seems to work pretty well.

Here’s my configuration, I’m using letsencrypt for my SSL certificate, and just modifying the local instance of the nginx conf located here:

/opt/rockstor/etc/nginx/nginx.conf

So you can generate your certificate any way you want, I used certbot which comes with the package letsencrypt and it makes it pretty easy:

letsencrypt certonly -d your-domain.com

Obviously replacing your-domain.com with your own domain suffix. I created a wildcard cert, indicating that any domain prefix ie; chat, cloud, portal etc. .your-domain.com would work. This is necessary for getting the subdomains to work in nginx.

Enter in your necessary details and it should dump your chain in:

/etc/letsencrypt/live/your-domain.com/

I also recommend using a hosting company to register for dynamic DNS. Hurricane Electric, dyndns, freedns, etc all off free solutions. Once I created an account, I had chat.my-domain.com, cloud.my-domain.com, and a few others point to my public IP address as A records.

So now that you have your free SSL certificate generated, you can upload it to your Rockstor instance under System > SSL Certificate. Just cat your full-chain.pem and your private key:

/etc/letsencrypt/live/your-domain.com/fullchain.pem

Take this and paste it into the input box on the GUI labeled Certifcate and take your private key in:

/etc/letsencrypt/live/your-domain.com/privkey.key

and paste it into the input box on the GUI labeled Private Key


A quick word about security; you absolutely never want to share your private key with anyone. I’d even go as far as to say that you should clear your clipboard after you’ve done this step just to make sure it’s not accidentally pasted somewhere public


You then update your certificate, and now Rockstor should be using your letsencrypt certificate to encrypt your server. It places these under:

/opt/rockstor/certs/

For extra security, we’re going to generate a Diffie-Hellman key exchange to make it a lot harder for people to get in if for some terrible reason they have your server keys:

openssl dhparam -out /opt/rockstor/certs/dhparam.pem 2048

So now lets modify your nginx.conf. First, back up your existing config, the following command moves your existing config to .backup so it essentially removes your active nginx.conf file, but there is a lot to paste into the new config which I’ll have below:

mv /opt/rockstor/etc/nginx/nginx.conf /opt/rockstor/etc/nginx/nginx.conf.backup

Now use your favorite editor to modify your existing nginx.conf:

vim /opt/rockstor/etc/nginx/nginx.conf

If you’re using vim, do a :set paste to go to paste mode so you don’t get a bunch of line indentation problems. Now hit ‘i’ to insert and paste this in:

daemon off;
worker_processes  2;

events {
    worker_connections  1024;
    use epoll;
}

http {
    include             /opt/rockstor/etc/nginx/mime.types;
    default_type        application/octet-stream;

    log_format main
        '$remote_addr - $remote_user [$time_local] '
            '"$request" $status $bytes_sent '
        '"$http_referer" "$http_user_agent" '
        '"$gzip_ratio"';

    client_header_timeout       10m;
    client_body_timeout 10m;
    send_timeout                10m;

    connection_pool_size                256;
    client_header_buffer_size   1k;
    large_client_header_buffers 4 8k;
    request_pool_size           4k;

    gzip on;
    gzip_min_length     1100;
    gzip_buffers        4 8k;
    gzip_types  text/plain;

    output_buffers      1 32k;
    postpone_output     1460;

    sendfile    on;
    tcp_nopush  on;
    tcp_nodelay on;

    keepalive_timeout   75 20;
    ignore_invalid_headers      on;
    index index.html index.php;

    # Servers
    server {
        listen 80;
        server_name .your-domain.com;
        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl;
        server_name main.your-domain.com;
        ssl_certificate /opt/rockstor/certs/rockstor.cert;
        ssl_trusted_certificate /opt/rockstor/certs/rockstor.cert;
        ssl_certificate_key /opt/rockstor/certs/rockstor.key;
        ssl_dhparam /opt/rockstor/certs/dhparam.pem;
        ssl_buffer_size 8k;
        ssl_session_timeout 1d;
        ssl_session_cache builtin:1000 shared:SSL:50m;
        ssl_session_tickets off;
        ssl_stapling on;
        ssl_stapling_verify on;
        resolver 8.8.8.8 8.8.4.4 valid=300s;
        resolver_timeout 10s;

        add_header Strict-Transport-Security "max-age=15768000; includeSubdomains; preload";

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
        ssl_prefer_server_ciphers on;

        location /site_media  {
            root /media/; # Notice this is the /media folder that we create above
            }
        location ~* ^.+\.(zip|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|mov) {
            access_log   off;
            expires      30d;
            }
        location /static  {
            root /opt/rockstor/;
            }
        location /logs {
            root /opt/rockstor/src/rockstor/;
            }
        location / {
            proxy_pass_header Server;
            proxy_set_header Host $http_host;
            proxy_set_header X-Forwarded-Proto https;
            proxy_redirect off;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Scheme $scheme;
            proxy_connect_timeout 75;
            proxy_read_timeout 120;
            proxy_pass http://127.0.0.1:8000/;
            }
        location /socket.io {
            proxy_pass http://127.0.0.1:8001/socket.io;
            proxy_redirect off;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            }
        }

    server {
        listen 443 ssl;
        server_name chat.your-domain.com;

        error_log /var/log/nginx/rocketchat.access.log;

        ssl_certificate /opt/rockstor/certs/rockstor.cert;
        ssl_certificate_key /opt/rockstor/certs/rockstor.key;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # dont use SSLv3 ref: POODLE
        ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
        ssl_prefer_server_ciphers on;

        location / {
            proxy_pass http://127.0.0.1:3000/;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $http_host;

            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forward-Proto http;
            proxy_set_header X-Nginx-Proxy true;

            proxy_redirect off;
        }
     }
     server {
         listen 443 ssl;
         server_name cloud.your-domain.com;

         error_log /var/log/nginx/owncloud.access.log;

         ssl_certificate /opt/rockstor/certs/rockstor.cert;
         ssl_trusted_certificate /opt/rockstor/certs/rockstor.cert;
         ssl_certificate_key /opt/rockstor/certs/rockstor.key;
         ssl_dhparam /opt/rockstor/certs/dhparam.pem;
         ssl_buffer_size 8k;
         ssl_session_timeout 1d;
         ssl_session_cache builtin:1000 shared:SSL:50m;
         ssl_session_tickets off;
         ssl_stapling on;
         ssl_stapling_verify on;
         resolver 8.8.8.8 8.8.4.4 valid=300s;
         resolver_timeout 10s;

         add_header Strict-Transport-Security "max-age=15768000; includeSubdomains; preload";

         ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
         ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
         ssl_prefer_server_ciphers on;

          location / {
              proxy_pass http://127.0.0.1:8085;
              proxy_set_header X-Real-IP $remote_addr;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
              proxy_set_header X-Forwarded-Proto $scheme;
              proxy_set_header X-Forwarded-Port 443;
              proxy_set_header Host $host;
              }
    }
}

The last step is to obviously modify your front end gateway/router to only have SSL/HTTPS open for incoming connections. Just go to your WAN settings and add SSL/443 to your port forwarding rules and have it point to your internal Rockstor server. Make sure you have a secure username/password configuration for your Rockstor front end GUI.


If anyone has a suggestion on how to bypass exposing the Rockstor GUI frontend, or think that this is a bad idea (I only have my username given GUI access and the password is intense) please let me know. I’m definitely not an nginx expert nor a Rockstor one, so any help is appreciated to make this instance more secure!


Then just restart your nginx service with:

service rockstor restart

Then test out your connections!

Regards,

exelan

4 Likes