NGINX and SSL Enforcement Through Routes in Rails - ruby-on-rails

I have the following snippet that works fine on Heroku (production) for enforcing SSL:
# /config/routes.rb
scope protocol: 'https://', constraints: { protocol: 'https://' } do
resource :user
resource :session
end
I'm trying to setup a development machine using NGINX and passenger with SSL, however I get:
Action Controller: Exception
No Route Matches [GET] "/session/new"
I get a green SSL in Chrome when browsing to other sections of the application with HTTPS, so it appears SSL is working. For some reason enforcing through the routes is not matching correctly though. My nginx.conf is:
worker_processes 4;
events {
worker_connections 1024;
}
http {
gzip on;
sendfile on;
include mime.types;
ssl_certificate cert.crt;
ssl_certificate_key cert.key;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
keepalive_timeout 60;
rack_env development;
passenger_user kevin;
passenger_root /Users/kevin/.rvm/gems/ruby-1.9.2-p290/gems/passenger-3.0.9;
passenger_ruby /Users/kevin/.rvm/wrappers/default/ruby;
server {
listen 80;
listen 443 ssl;
server_name local.demo;
location / {
root /Users/kevin/Sites/demo/public;
passenger_enabled on;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Forwarded-Proto https;
}
}
}
Any ideas how to fix it?

I'm not sure if this is the right approach for your problem but in my nginx.conf I'll tend
to fore-rewrite the URL-space I'd like to default to https:
server {
listen 80;
server_name local.demo;
rewrite ^(.*)$ https://local.demo$1 permanent;
}
server {
listen 443 ssl;
server_name local.demo;
location / {
root /Users/kevin/Sites/demo/public;
passenger_enabled on;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Forwarded-Proto https;
}
}
This setup will force any requests on http://local.demo/ to go through https://local.demo.
Alternatively you can be more specific and filter on a location or pattern match basis:
location ~ ^/sslrequired/(.*)$ {
rewrite ^(.*)$ https://local.demo/$1 permanent;
}
In a generic way it can be considered a workaround but why not let the server enforce
the SSL when you intend the user to actually use it ;)
For more information you may want to look at the HttpRewriteModule at the nginx wiki.
I hope this is helpful for your case.

Related

NGINX Reverse Proxy Configuration Structure

Is there a "proper" structure for the directives of an NGINX Reverse Proxy? I have seen 2 main differences when looking for examples of an NGINX reverse proxy.
http directive is used to house all server directives. Servers with data are listed in a pool within the upstream directive.
server directives are listed directly within the main directive.
Is there any reason for this or is this just a syntactical sugar difference?
Example of #1 within ./nginx.conf file:
upstream docker-registry {
server registry:5000;
}
http {
server {
listen 80;
listen [::]:80;
return 301 https://$host#request_uri;
}
server {
listen 443 default_server;
ssl on;
ssl_certificate external/cert.pem;
ssl_certificate_key external/key.pem;
# set HSTS-Header because we only allow https traffic
add_header Strict-Transport-Security "max-age=31536000;";
proxy_set_header Host $http_host; # required for Docker client sake
proxy_set_header X-Real-IP $remote_addr; # pass on real client IP
location / {
auth_basic "Restricted"
auth_basic_user_file external/docker-registry.htpasswd;
proxy_pass http://docker-registry; # the docker container is the domain name
}
location /v1/_ping {
auth_basic off;
proxy_pass http://docker-registry;
}
}
}
Example of #2 within ./nginx.conf file:
server {
listen 80;
listen [::]:80;
return 301 https://$host#request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
error_log /var/log/nginx/error.log info;
access_log /var/log/nginx/access.log main;
ssl_certificate /etc/ssl/private/{SSL_CERT_FILENAME};
ssl_certificate_key /etc/ssl/private/{SSL_CERT_KEY_FILENAME};
location / {
proxy_pass http://app1
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr; # could also be `$proxy_add_x_forwarded_for`
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Request-Start $msec;
}
}
I dont quite understand your question, but it seems to me that the second example is missing the http {}, I dont think that nginx will start without it.
unless your example2 file is included somehow in the nginx.conf that has the http{}

Rails Application using Nginx working on HTTP but not HTTPS

I have been trying to get my application working in production. I was able to access the site before changing config.force_ssl = true in my config\environments\production.rb.
I have seen many others with this problem need to add proxy_set_header X-Fowarded-Proto https;
I have tried adding this in my /etc/nginx/sites-available/default but haven't seen a difference.
My full default is below:
upstream puma {
server unix:///home/deploy/apps/appname/shared/tmp/sockets/appname-puma.sock;
}
server {
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
root /var/www/html;
index index.html index.htm index.nginx-debian.html
server_name appname.com www.appname.com
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;
}
}
After making changes I reloaded nginx using sudo service nginx reload followed by sudo service nginx stop and sudo service nginx start
Am I missing something?
EDIT:
I updated my default and removed the config.force_ssl = true:
upstream puma {
server unix:///home/kiui/apps/appnamw/shared/tmp/sockets/appname-puma.sock;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
keepalive_timeout 70;
server_name appname.com www.appname.com;
ssl on;
ssl_certificate /root/appname.com.chain.cer;
ssl_certificate_key /root/appname.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
root /home/deploy/apps/appname/current/public;
access_log /home/deploy/apps/appname/current/log/nginx.access.log;
error_log /home/deploy/apps/appname/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-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://puma;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 10M;
}
I can now access the site with http but not https.
Could you try the following:
upstream puma {
  server unix:///home/deploy/apps/appname/shared/tmp/sockets/appname-puma.sock;
}
server {
 listen 80;
 server_name appname.com www.appname.com;
 return 301 https://$host$request_uri;
}
server {
  # SSL configuration
  ssl on;
 listen 443 ssl;
 ssl_certificate path-to-your-crt-file;
 ssl_certificate_key path-to-your-key-file;
 server_name appname.com www.appname.com;
...
}
My problem was where I was adding the code above. I was adding it in default rather than nginx.conf. Moving the code above solved the problem.

serve ssl webpage via nginx, ssl_error_rx_record_too_long

Please help me: there is something wrong in this virtual.conf nginx conf file that means that when viewing the site, I get the error ssl_error_rx_record_too_long rather than being able to view my site. I am using aws, nginx to serve some rails apps - this was working yesterday, but I crashed my entire server and am frantically trying to fix this at 2am, ready for production at 9:30am.
ssl_certificate /etc/ssl/star_my_site.pem;
ssl_certificate_key /etc/ssl/star_my_site.key;
# ------------------
# rails app one
# ------------------
upstream my_app {
server unix:///var/run/puma/my_app.sock;
}
server {
listen 80;
# server_name rails_app_one.my_site.com.au www.rails_app_one.my_site.com.au;
server_name _ localhost;
return 301 https://rails_app_one.my_site.com.au;
}
server {
listen 443;
server_name _ localhost;
location / {
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://my_app;
}
location ~ "^/assets/" {
root /var/app/current/public;
gzip_static on;
expires max;
add_header Cache-Control public;
}
}
# ------------------
# rails app two
# ------------------
upstream rails_app_two_app {
server unix:///var/run/puma/rails_app_two_app.sock;
}
server {
listen 80;
server_name rails_app_two.my_site.com.au www.rails_app_two.my_site.com.au;
return 301 https://rails_app_two.my_site.com.au;
}
server {
listen 443;
server_name rails_app_two.my_site.com.au;
location / {
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://rails_app_two_app;
}
location ~ "^/assets/" {
root /var/app/rails_app_two.my_site.com.au/current/public;
gzip_static on;
expires max;
add_header Cache-Control public;
}
}
ssl_error_rx_record_too_long usually means the server is returning plain HTTP content when the browser is expecting HTTPS content. (You can probably verify this by going to http://your.site:443 and seeing your site.)
You need to enable SSL in nginx - simply declaring ssl_certificate isn't enough.
Change listen 443; to listen 443 ssl;
(Also, I'd suggest putting ssl_certificate in the server block, so you can use different ssl certs with other domains.)
Edit: https://serverfault.com/questions/497430/error-code-ssl-error-rx-record-too-long has almost the exact same issue, hopefully you found it before your 9am production release!

Connection Time Out: Private_Pub + SSL + Nginx

I have implemented Private_Pub with SSL and I am running that over port 4443 as recommended in: https://github.com/ryanb/private_pub#serving-faye-over-https-with-thin
However, whenever I actually use the private_pub service I receive the following error:
Errno::ETIMEDOUT: Connection timed out - connect(2) for "www.mysite.com" port 4443
The really odd thing is that it was working on another server before we migrated providers.
I have ensured port 4443 is open by telnet-ing to it successfully. I have played around with the nginx config without any luck. I have restarted the thin server several times just to see if I get lucky.
I am able to access: https://www.mysite.com:4443/faye/faye.js
Can anybody point me in the right direction here?
Edit: Added my nginx config file:
worker_processes 1;
error_log /var/log/nginx.log debug;
events {
worker_connections 1024;
}
http {
passenger_root /home/me/.rvm/gems/ruby-2.1.2/gems/passenger-4.0.52;
passenger_ruby /home/me/.rvm/gems/ruby-2.1.2/wrappers/ruby;
passenger_app_env production;
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
root /var/www/mysite/current/public;
rails_env production;
passenger_enabled on;
}
server {
listen 443;
root /var/www/mysite/current/public;
rails_env production;
proxy_read_timeout 1200;
client_max_body_size 20m;
error_log /var/log/nginx.log debug;
ssl on;
ssl_certificate /var/server.crt;
ssl_certificate_key /var/server.key;
proxy_set_header X-Forwarded-Proto: https;
passenger_pass_header X-Forwarded-Proto;
passenger_enabled on;
# Tried it also without this part to no avail
location /faye {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
root /var/applications/current/faye;
proxy_pass http://127.0.0.1:4443;
break;
}
}
server {
listen 80;
listen 443;
server_name www.mysite.co mysite.co;
return 302 $scheme://www.mysite.com$request_uri;
}
}

How to set up redirect from http to https with nginx?

I am fighting with this issue the whole day.
Here's my nginx.cong:
upstream my_website.co {
server 127.0.0.1:8080;
}
server{
listen 80;
listen 443 default ssl;
# return 301 https://www.my_website.co; - I put it here, but it didn't work
ssl on;
ssl_certificate /etc/nginx/certs/my_website.co.crt;
ssl_certificate_key /etc/nginx/certs/my_website.co.private.key;
server_name my_website.co _;
root /home/deployer/my_website/public;
location / {
proxy_set_header X_FORWARDED_PROTO $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header CLIENT_IP $remote_addr;
if (!-f $request_filename) {
proxy_pass http://my_website.co;
break;
}
if (-f $document_root/system/maintenance.html) {
return 503;
}
}
# return 301 https://www.my_website.co; - I put it here, but it didn't work
Could you help me, please, how to redirect everything from http to https?
My Rails code:
ApplicationController
before_filter :ensure_domain
APP_DOMAIN = 'www.my_website.co'
def ensure_domain
if request.env['HTTP_HOST'] != APP_DOMAIN && Rails.env != 'development'
redirect_to "https://#{APP_DOMAIN}#{request.env['REQUEST_PATH']}", :status => 301
end
end
I'll be grateful for every help, I am lost in here.
Thank you
I do something like this in the nginx config file on one of my sites and it works without a problem. I do not have anything in my ApplicationController to force the redirect either.
server {
listen 80;
server_name my_website.co;
rewrite ^ https://server_name$request_uri? permanent;
}
server {
listen 80;
server_name www.my_website.co;
rewrite ^ https://server_name$request_uri? permanent;
}
server {
listen 443;
server_name my_website.co;
root /home/deployer/my_website/public;
ssl on;
ssl_certificate /etc/nginx/certs/my_website.co.crt;
ssl_certificate_key /etc/nginx/certs/my_website.co.private.key;
// rest of your config file below
}
The server section you have should only listen to port 443, so remove the port 80 from it in your Nginx configuration. Then add a new server section like this to your Nginx configuration file:
server {
listen 80;
server_name my_site.co;
rewrite ^ https://$server_name$request_uri? permanent;
}
Then Nginx will redirect everything to SSL version for you. You can remove the code in your ApplicationController.
In Rails 4 We can directly do this in application.rb
config.force_ssl = true
No Need To play with nginx configuration file.

Resources