-
-
Save greatghoul/4e49d778bec4e8efa55f5849a9e11999 to your computer and use it in GitHub Desktop.
example nginx config with SSL from LetsEncrypt for a rails app (see steps.txt)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# rails config needs `force_ssl` | |
# example: | |
# Force all access to the app over SSL, use Strict-Transport-Security, | |
# and use secure cookies. | |
# config.force_ssl = true | |
# and rails needs some headers to be set, otherwise will be trapped in a redirect loop | |
# nginx config for headers: | |
# 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 https; | |
# proxy_set_header Host $http_host; | |
# help at | |
# https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-14-04 | |
upstream example { | |
server unix:///<path>/shared/tmp/sockets/puma.sock fail_timeout=1; | |
} | |
server { | |
listen 80 default deferred; | |
server_name example.com; | |
return 307 https://example.com$request_uri; | |
} | |
server { | |
listen 443 ssl; | |
server_name example.com; | |
root <path>/current/public; | |
access_log <path>/current/log/nginx.access.log; | |
error_log <path>/current/log/nginx.error.log info; | |
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; | |
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; | |
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; | |
ssl_prefer_server_ciphers on; | |
ssl_dhparam /etc/ssl/certs/dhparam.pem; | |
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; | |
ssl_session_timeout 1d; | |
ssl_session_cache shared:SSL:50m; | |
ssl_stapling on; | |
ssl_stapling_verify on; | |
# is set by rails to "max-age=31536000" | |
# so not valid here | |
# add_header Strict-Transport-Security max-age=15768000; | |
location ^~ /assets/ { | |
gzip_static on; | |
expires max; | |
add_header Cache-Control public; | |
} | |
try_files $uri/index.html $uri @example; | |
location @example { | |
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 https; | |
proxy_set_header Host $http_host; | |
proxy_redirect off; | |
proxy_pass http://example; | |
} | |
error_page 500 502 503 504 /500.html; | |
client_max_body_size 10M; | |
keepalive_timeout 10; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Followed the digital ocean guide on [Nginx + Let's Encrypt](https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-14-04) | |
For additional security the guide suggests to generate a `dhparam` file | |
it is [recommended](http://security.stackexchange.com/questions/38206/can-someone-explain-a-little-better-what-exactly-is-accomplished-by-generation-o) for a new random seed value | |
command: `sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048` | |
After the `Let's Encrypt` setup is done, we will use the following certificate files: | |
- fullchain.pem | |
- privkey.pem | |
They are stored under the `/etc/letsencrypt/live/some.example.com/` folder | |
replace `some.example.com` with your website | |
Now that the certificates are already generated, the steps to steps to configure `Nginx` + `Puma (the rails server` are: | |
- edit the site config. will be under `/etc/nginx/sites-enabled/site-name` to add `ssl` | |
am guessing ssl is not configuered. | |
there might be a `listen 80` or something like it | |
or there will be no `listen` directive at all, which as per the [docs](http://nginx.org/en/docs/http/ngx_http_core_module.html#listen) means that: | |
``` | |
If the directive is not present then either *:80 is used | |
if nginx runs with the superuser privileges, or *:8000 otherwise. | |
``` | |
`ssl` is not on by default so we need to add it | |
change it to something like: | |
``` | |
server { | |
listen 443 ssl; | |
server_name some.example.com; | |
ssl_certificate /etc/letsencrypt/live/some.example.com/fullchain.pem; | |
ssl_certificate_key /etc/letsencrypt/live/some.example.com/privkey.pem; | |
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; | |
ssl_prefer_server_ciphers on; | |
ssl_dhparam /etc/ssl/certs/dhparam.pem; | |
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; | |
ssl_session_timeout 1d; | |
ssl_session_cache shared:SSL:50m; | |
ssl_stapling on; | |
ssl_stapling_verify on; | |
# is set by rails to "max-age=31536000" | |
# so not valid here | |
# add_header Strict-Transport-Security max-age=15768000; | |
... | |
} | |
``` | |
The Digital Ocean [guide](https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-14-04) asks us to add a `add_header Strict-Transport-Security max-age=15768000;` header. but [Rails](http://rubyonrails.org) already sets it for us. Adding it here duplicates the same header in the response, but with different values. | |
so the response looks like: | |
``` | |
add_header Strict-Transport-Security max-age=15768000; | |
add_header Strict-Transport-Security max-age=31536000; | |
``` | |
This is not what we want. No error is shown on the browser. But am not sure which value is used and [ssllabs](www.ssllabs.com/ssltest/analyze.html) throws a warning. So let [Rails](http://rubyonrails.org) manage it for us. | |
- make sure `http` redirects to `https` | |
``` | |
server { | |
listen 80 deferred; | |
server_name some.example.com; | |
return 307 https://example.com$request_uri; | |
} | |
``` | |
Again some changes here from the Digital Ocean [guide](https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-14-04). | |
- they use [301 Moved Permanently](http://httpstatus.es/301). But we use a [307 Temporary Redirect](https://httpstatuses.com/307). Just in case we change our mind. Although I cannot think of a good reason why we would want to do that :-) | |
- `deferred` is just a linux only performance micro-tweak. see [h5bp](https://github.com/h5bp/server-configs-nginx/issues/100) | |
- reload nginx with a `sudo service nginx reload` | |
- test that the website works by going to the `https` url eg. `https://some.example.com` | |
and that the `http` url redirects to the https version | |
- lint that the `ssl` setup is correct by visiting `ssllabs`. eg. url is `https://www.ssllabs.com/ssltest/analyze.html?d=some.example.com`. replace the param at the end with your own website url | |
A potential cause for error might be that the ruby http/s parser cannot recognize the https request. Rails and most of the http servers depend on the `host` header being set for https | |
``` | |
location @example { | |
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 https; | |
proxy_set_header Host $http_host; | |
proxy_redirect off; | |
proxy_pass http://example; | |
} | |
``` | |
`proxy_set_header Host $http_host;` is https specific. Others are for the reverse-proxy. | |
Also notice that, in the above code-snippet, we proxy to `http` not `https`. Otherwise we will get error like | |
``` | |
if proxying to https, logs will look like | |
==> /shared/log/nginx.error.log <== | |
2016/04/12 09:05:34 [info] 8373#0: *347 peer closed connection in SSL handshake while SSL handshaking, client: <some-ip>, server: 0.0.0.0:443 | |
2016/04/12 09:05:35 [info] 8373#0: *348 peer closed connection in SSL handshake while SSL handshaking, client: <some-ip>, server: 0.0.0.0:443 | |
2016/04/12 09:05:35 [info] 8373#0: *349 peer closed connection in SSL handshake while SSL handshaking, client: <some-ip>, server: 0.0.0.0:443 | |
2016/04/12 09:05:35 [info] 8373#0: *350 peer closed connection in SSL handshake while SSL handshaking, client: <some-ip>, server: 0.0.0.0:443 | |
2016/04/12 09:05:36 [info] 8373#0: *351 peer closed connection in SSL handshake while SSL handshaking, client: <some-ip>, server: 0.0.0.0:443 | |
2016/04/12 09:05:36 [info] 8373#0: *352 peer closed connection in SSL handshake while SSL handshaking, client: <some-ip>, server: 0.0.0.0:443 | |
2016/04/12 09:05:37 [info] 8373#0: *353 peer closed connection in SSL handshake while SSL handshaking, client: <some-ip>, server: 0.0.0.0:443 | |
2016/04/12 09:05:37 [info] 8373#0: *354 peer closed connection in SSL handshake while SSL handshaking, client: <some-ip>, server: 0.0.0.0:443 | |
2016/04/12 09:19:03 [info] 8373#0: *359 recv() failed (104: Connection reset by peer) while SSL handshaking, client: 5.45.64.228, server: 0.0.0.0:443 | |
2016/04/12 09:23:20 [error] 8373#0: *361 SSL_do_handshake() failed (SSL: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol) while SSL handshaking to upstream, client: 202.83.38.120, server: example.com, request: "GET / HTTP/1.1", upstream: "https://unix:///shared/tmp/sockets/puma.sock:/", host: "example.com" | |
==> /shared/log/puma.access.log <== | |
--- | |
2016-04-12 09:03:30 +0200: HTTP parse error, malformed request (127.0.0.1): #<Puma::HttpParserError: Invalid HTTP format, parsing fails.> | |
2016-04-12 09:03:30 +0200: ENV: {"rack.version"=>[1, 3], "rack.errors"=>#<File:/current/log/puma.access.log>, "rack.multithread"=>true, "rack.multiprocess"=>false, "rack.run_once"=>false, "SCRIPT_NAME"=>"", "QUERY_STRING"=>"", "SERVER_PROTOCOL"=>"HTTP/1.1", "SERVER_SOFTWARE"=>"2.16.0", "GATEWAY_INTERFACE"=>"CGI/1.2", "REMOTE_ADDR"=>"127.0.0.1"} | |
``` | |
in the rails server logs. [puma](http://puma.io) in this case |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment