Nginx and rails headers don't work - ruby-on-rails

I'm trying to add some headers to nginx config, but now only the one header is working(Strict-Transport-Security).
upstream puma_muninn {
server app:3000;
}
server {
listen 80;
return 301 https://$host$request_uri;
}
server {
listen 443 default ssl;
server_name production.test.com;
root /var/www/muninn/public;
ssl on;
ssl_certificate /var/www/muninn/test.crt;
ssl_certificate_key /var/www/muninn/test.key;
ssl_session_timeout 5m;
ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
client_max_body_size 4G;
keepalive_timeout 10;
error_page 500 502 504 /500.html;
error_page 503 #503;
try_files $uri/index.html $uri #puma_muninn;
location #puma_muninn {
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options nosniff;
add_header Content-Security-Policy "default-src 'self';";
add_header 'Referrer-Policy' 'origin';
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
access_log /var/www/muninn/log/nginx.access.log;
error_log /var/www/muninn/log/nginx.error.log;
}
If I add some headers on my rails side:
config.action_dispatch.default_headers = {
'X-Frame-Options' => 'SAMEORIGIN'
}
It turns off any headers from nginx.
Ideas?

To ensure all traffic your application has is treated equally as far as security goes, move those add_header declarations outside the location block and avoid setting headers in your application that are being set on NGINX. Your file should look something like this:
upstream puma_muninn {
server app:3000;
}
server {
listen 80;
return 301 https://$host$request_uri;
}
server {
listen 443 default ssl;
server_name production.test.com;
root /var/www/muninn/public;
ssl on;
ssl_certificate /var/www/muninn/test.crt;
ssl_certificate_key /var/www/muninn/test.key;
ssl_session_timeout 5m;
ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
client_max_body_size 4G;
keepalive_timeout 10;
error_page 500 502 504 /500.html;
error_page 503 #503;
try_files $uri/index.html $uri #puma_muninn;
# Equal security to all requests
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options nosniff;
add_header Content-Security-Policy "default-src 'self';";
add_header 'Referrer-Policy' 'origin';
location #puma_muninn {
# No need to especify security headers here, since global config will take care of the rest.
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
access_log /var/www/muninn/log/nginx.access.log;
error_log /var/www/muninn/log/nginx.error.log;
}
If you are enforcing all SSL/TSL, cookies and HSTS security in NGINX you should remove security headers from your Rails application, as you may have followed similar steps as this answer. Duplicate headers can trigger a false negative or even a real one in those automatic site analysis tools.

Related

Rails 5, Nginx, Puma, Rails admin - ERR_TOO_MANY_REDIRECTS

I would like to know how to enforce SSL in a rails 5 app. The app works fine in development. In production some POST requests over SSL via rails admin do not work.
If SSL is enforced via production.rb, the browser returns:
"ERR_TOO_MANY_REDIRECTS"
And if "force_ssl" is set to false, some "POST" requests via rails admin return:
HTTP Origin header (https://www.example.com) didn't match request.base_url (http://www.example.com)
Thanks in advance.
These are the app settings:
production.rb
# ...
config.force_ssl = true
# ...
nginx.conf
upstream puma {
server unix:///home/bgc/apps/domain/shared/tmp/sockets/domain-puma.sock;
}
server {
listen 80;
listen 443 ssl;
server_name domain.com;
ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/domain.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
ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers AES256+EECDH:AES256+EDH:!aNULL;
root /home/bgc/apps/domain/current/public;
access_log /home/bgc/apps/domain/current/log/nginx.access.log;
error_log /home/bgc/apps/domain/current/log/nginx.error.log info;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri #puma;
location #puma {
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://puma;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 10M;
keepalive_timeout 10;
}
The reason for this is pretty simple. When you are using the Nginx server with ssl, it will already sort out the ssl for you. If you remove config.force_ssl = true from production.rb and it will be sorted.

HTTPS redirect Elastic Beanstalk Environment Puma Rails 5

I am unable to redirect http://example.com to https://example.com. I tried various configurations but nothin works.
Based on the research, I have realized that I need to add this to nginx config.
if ($http_x_forwarded_proto != 'https') {
rewrite ^ https://$host$request_uri? permanent;
}
I create a new config file in the ..ebextensions directory with the following content,
upstream my_app {
server unix:///var/run/puma/my_app.sock;
}
log_format healthd '$msec"$uri"'
'$status"$request_time"$upstream_response_time"'
'$http_x_forwarded_for';
server {
listen 80;
server_name _ localhost; # need to listen to localhost for worker tier
if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2})") {
set $year $1;
set $month $2;
set $day $3;
set $hour $4;
}
access_log /var/log/nginx/access.log main;
access_log /var/log/nginx/healthd/application.log.$year-$month-$day-$hour healthd;
location / {
proxy_pass http://my_app; # match the name of upstream directive which is defined above
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /assets {
alias /var/app/current/public/assets;
gzip_static on;
gzip on;
expires max;
add_header Cache-Control public;
}
location /public {
alias /var/app/current/public;
gzip_static on;
gzip on;
expires max;
add_header Cache-Control public;
}
}
save it and do
eb deploy
Goto http://example.com
I still get the "unsure" message.
I also used the content as is from this. But that does not work either.
What am i missing?
Sunil
Nowhere in your code do I see a listen to 443 which is the HTTPS.
This is my SSL script.
upstream puma_production {
server unix:/home/deploy/games.directory/shared/tmp/sockets/puma.sock fail_timeout=0;
}
server {
listen 80;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443;
server_name games.directory;
root /home/deploy/games.directory/current/public;
try_files $uri/index.html $uri #puma_production;
ssl on;
ssl_certificate '';
ssl_certificate_key '';
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers ''
ssl_prefer_server_ciphers on;
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;
# OCSP Stapling ---
# fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/games.directory/chain.pem;
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
location #puma_production {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Forwarded-Proto https;
proxy_pass http://puma_production;
access_log /home/deploy/games.directory/shared/log/nginx.access.log;
error_log /home/deploy/games.directory/shared/log/nginx.error.log;
}
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
if ($request_method !~ ^(GET|HEAD|PUT|PATCH|POST|DELETE|OPTIONS)$ ){
return 405;
}
}
Use EB file keys to customise for nginx conf generation template.
http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html
Create a file .ebextensions/01_puma_nginx.conf (Configuration files are executed in alphabetical order, so tweak name prefix based on additional requirements) with following content.
files:
"/opt/elasticbeanstalk/support/conf/nginx_config.erb":
mode: "000644"
owner: root
group: root
content: |
Paste your custom nginx configuration template content here....
Instead of building your own template and break things, check the existing template in the current instance and tweak only required things (here just your http redirection part).
Well, if this seems a little complicated, you can use rails force_ssl option by checking 'X-Forwarded-Proto' header
force_ssl if: :ssl_required?
def ssl_required?
if request.headers["X-Forwarded-Proto"]!="https"
true
else
false
end
end
Redirecting HTTP traffic to HTTPS is a very common problem needing to be solved. So AWS have documented the solution here, which covers all their different Elastic Beanstalk platforms:
https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/configuring-https-httpredirect.html

SSL on Nginx causes redirect loop

I'm trying to setup SSL on a Digital Ocean VPS using Nginx. My server conf is like so:
upstream unicorn {
server unix:/home/ubuntu/apps/example/shared/sock/unicorn.example.sock fail_timeout=0;
}
server {
listen 80;
server_name www.example.com example.com;
rewrite ^/(.*) https://example.com/$1 permanent;
}
server {
listen 443;
include example_ssl;
server_name www.example.com;
rewrite ^/(.*) https://example.com/$1 permanent;
}
server {
listen 443;
include example_ssl;
server_name example.com;
root /home/ubuntu/apps/example/current/public;
try_files $uri/index.html $uri #unicorn;
location #unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_pass http://unicorn;
}
location ~ ^/(assets)/ {
gzip_static on;
expires max;
add_header Cache-Control public;
#add_header Last-Modified "";
#add_header ETag "";
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 60;
}
The ssl info is in example_ssl:
ssl on;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_session_timeout 10m;
ssl_ciphers "AES256+EECDH:AES256+EDH";
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
This is causing an endless redirect loop. Http requests are redirected to HTTPS as expected but all tries to https://example.com are being redirected back to HTTP.
I can't seem to figure out why the HTTPS requests are being redirected. I checked my SSL cert by going to www.digicert.com and everything came back saying it was successfully installed.
When I try from my terminal:
openssl s_client -connect example.com:443
I get the following error:
verify error:num=20:unable to get local issuer certificate
The cert I got from my client did not contain the intermediate certs, but from what I understand this should still work when trying to access the site from the browser.
If I change the server block to only listen for 80 and not use SSL the site is able to load successfully, but I need to use SSL only.
Also, this is hosting a Rails app with Unicorn web server. My unicorn.log is empty other than starting the webserver so am I correct that this is not touching my Rails configuration at all, just an issue with nginx/ssl cert configuration?
Thanks!
Try this:
upstream unicorn {
server unix:/home/ubuntu/apps/example/shared/sock/unicorn.example.sock fail_timeout=0;
}
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
include example_ssl;
server_name example.com;
root /home/ubuntu/apps/example/current/public;
try_files $uri/index.html $uri #unicorn;
location #unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_pass http://unicorn;
}
location ~ ^/(assets)/ {
gzip_static on;
expires max;
add_header Cache-Control public;
#add_header Last-Modified "";
#add_header ETag "";
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 60;
}
You will need to remove "ssl on;" from your ssl include file, that is for older versions of nginx. You might have to modify this if you are using multiple ssl on a single IP.

Redirection loop with NGinx on a rails application with ssl

I know there is a lot of question on a similar problem but I can find the solution for my case. I have this nginx configuration :
upstream unicorn {
server unix:/tmp/unicorn.lescollectionneurs.sock fail_timeout=0;
}
server {
listen 80 default deferred;
# server_name example.com;
root /home/deployer/apps/lescollectionneurs/current/public;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri #unicorn;
location #unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://unicorn;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
server {
listen 443 default ssl;
# server_name example.com;
root /home/deployer/apps/lescollectionneurs/current/public;
ssl on;
ssl_certificate /etc/ssl/certs/myssl.crt;
ssl_certificate_key /etc/ssl/private/myssl.key;
ssl_session_timeout 5m;
ssl_protocols sslv3 tlsv1 tlsv1.1 tlsv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri #unicorn;
location #unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://unicorn;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
In my rails application, I have config.force_ssl = true. When I log at the nginx's logs, I have a redirection loop and it fail in the browser. There is nothing in my production logs.
What can I do?
The solution was only to add proxy_set_header X-Forwarded-Proto $scheme; in the location block in the ssl part.

Why is SSL redirect not working with force_ssl and Nginx?

I have a Rails 3.2.13 app that I am trying to configure SSL for with Nginx and Unicorn. I want to be able to tell some controllers and some controller actions to 'force_ssl' and to properly redirect. I have been able to get this working so that I can manually hit the app with 'https://foo.com' and things work. When I put 'force_ssl' into a controller action, let's say users#index:
class UsersController < ApplicationController
force_ssl
def index
# do some stuff
end
end
I would expect that if I navigate to 'http://foo.com/users' that it would redirect to 'https://foo.com/users'.
It does not.
Instead, it redirects to: 'https://unicorn_foo/users'. What am I missing?
nginx.conf:
upstream unicorn_foo {
server unix:/tmp/unicorn.foo.sock fail_timeout=0;
}
server {
listen 80 default;
server_name foo.com;
root /home/webuser/apps/foo/current/public;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri #unicorn_foo;
location #unicorn_foo {
proxy_set_header X-Forwarded-Proto http;
proxy_pass http://unicorn_foo;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 5G;
keepalive_timeout 10;
send_timeout 240;
sendfile_max_chunk 5m;
}
server {
listen 443;
server_name foo.com;
root /home/webuser/apps/foo/current/public;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri #unicorn_foo;
location #unicorn_foo {
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://unicorn_foo;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 5G;
keepalive_timeout 10;
ssl on;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ALL:-ADH:+HIGH:+MEDIUM:-LOW:-SSLv2:-EXP;
ssl_session_cache shared:SSL:10m;
send_timeout 240;
sendfile_max_chunk 5m;
}
First guess... the port 80 server block does not pass the host through, maybe that's it?
proxy_set_header Host $http_host;
The SSL block does, but if you start at the non-SSL side and Rails picks it up, it might not have the full header there?

Resources