Rails 'unsafe redirect' from strange curl request - ruby-on-rails

I am getting occasional errors of this type in production and staging:
Unsafe redirect to "https://${ip}:${port}/businesses/new", pass allow_other_host: true to redirect anyway.
It is being caused by Curl requests from a random IP:
User-Agent: "curl/7.64.1"
Accept: "*/*"
Host: "${ip}:${port}"
Version: "HTTP/1.1"
I do not get it with any other URLs processed within the app.
When I try the same curl request in development I get this (which is correct):
Rails - [ActionDispatch::HostAuthorization::DefaultResponseApp] Blocked host: "${ip}:${port}"
I cannot find where the difference is that makes this throw an exception in production.
Any insights on this issue would be appreciated.

Related

Where do I find the error log of a Rails app on the production server?

My app is working fine on a local server. After deploying it on production (AWS EC2), I see this "classic" Rails error page:
I thought that the errors are logged to the file current/log/production.log, but when I looked in it, there's no error captured. I can only see there the following:
I, [2019-06-09T12:12:04.353438 #12855] INFO -- : Started GET "/constact-us" for 185.44.76.84 at 2019-06-09 12:12:04 +0000
I, [2019-06-09T12:12:04.355034 #12855] INFO -- : Processing by MyAppSite::SiteController#contact_us as HTML
There's logged accessing the URL, but not the error message. Where do I find it? I added some pure HTML/image to that template, so I think the error must be related to some issue with assets (and precompilation).
However, where do I find the full error message?
I am looking to the config/environments/production.rb file and regarding logs, there's "only" this line:
config.log_level = :info
Any advise how to figure out the error message?
EDIT: I just realized that I also have integrated Rollbar to the app and it hasn't caught the error either.
EDIT 2: error from the nginx log:
2019/06/09 13:47:14 [error] 987#0: *7824941 upstream prematurely closed connection while reading response header from upstream, client: IP, server: www.my_website.com, request: "GET /contact-us HTTP/1.1", upstream: "http://unix:/tmp/unicorn.myapp_production.sock:/contact-us", host: "www.my_website.com
Thank you

Passing remote_user to lua file

I am following the next tuto section LDAP Authentication. The configuration nginx file and the lua script are here and here. After the commands
sbin/nginx -p $PWD -c conf/nginx-ldap-auth.conf
python backend-sample-app.py
python nginx-ldap-auth-daemon.py
According the log of nginx-ldap-auth-daemon.py I have success with login, i.e. 200 OK auth user admin. But I get a 500 Internal Server Error. In the lua.log I get
/usr/local/openresty/nginx/authorize_es_ldap.lua: in function </usr/local/openresty/nginx/authorize_es_ldap.lua:1> while sending to client, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", host: "localhost:8881", referrer: "http://localhost:8881/"
2016/09/29 23:35:27 [error] 23987#0: *10 lua entry thread aborted: runtime error: /usr/local/openresty/nginx/authorize_es_ldap.lua:50: attempt to concatenate global 'role' (a nil value)
I think that the problem is because in the tutorial there is a gap, that is how to pass the remote_user variable to lua script. I am trying to add self.send_header('LDAPUser',ctx['user']) around the line 204, before to end_headers and after to seld.send_response(200).
Could you help me please?

rails api: send curl in controller test

I'm building a Rails API, and I'm writing a test that sending a curl request works. This seems to be a testing issue, because an actual curl request works:
$ curl -X POST -d temperature=68 localhost:3000/temperature_readings.json
# => {"status":200}
Here's the controller method:
def create
TemperatureReading.create(temperature: params[:temperature])
render json: { status: 200 }
end
Here's the test:
context 'works via a curl request' do
it 'works' do
system "curl -X POST -d 'temperature=68' #{temperature_readings_url(format: 'json')}"
expect(TemperatureReading.last.temperature).to eq(68)
end
end
I'm getting an error from curl that test.host does not resolve:
curl: (6) Could not resolve host: test.host
That makes sense, because when I call temperature_readings_url(format: 'json') from within pry in that method, I get http://test.host/temperatures.json.
Is there a better way to test that my controller can successfully handle a curl request and create the correct record with the correct attributes?
What I've Tried
I made sure that protect_from_forgery with: :null_session is set in ApplicationController so that it doesn't fail with an exception when it can't find the token
I tried adding -H 'Content-Type:application/json' to the curl request to make sure it's hitting the json format in the controller. But it really seems to boil down to the fact that the working URL in the testing environment is test.host.
So I explicitly set request.host to localhost:3000 in the test, which felt a little hacky. But then of course it just posted to my dev database and not my test database, so the test still failed.
Your app doesn't know its host outside the request context, so you have to provide it anyway. This is also hacky:
temperature_readings_url(format: 'json', host: 'localhost:3000').
But you can define it globally in config/enviroments/test.rb:
Rails.application.routes.default_url_options[:host] = 'localhost:3000'
so in test enviroment all url helpers will return localhost:3000 as host by default.

File upload not working with Rails 4 in development using Pow and Nginx

I am using Pow and Nginx to serve my Rails 4 app in development. A simple file upload is returning 500 error and the request is not reaching the Rails controller. I assume this is the case because there is no mention of the request in the Rails log. Without any mention of the error, I am not sure what is going wrong here.
I started with an Ajax file upload but replaced it with a simple form which is also not working.
Tried this
and then went to this
Current avatar.html.erb
<%= form_for #user, html: { multipart: true }, method: "post", url: '/settings/avatar/update', class: "", id: "update_avatar" do |f| %>
<%= f.file_field :avatar, class: 'js-upload-photo-button js-change-avatar-btn', accept: 'image/png,image/gif,image/jpeg,image/jpg' %>
<%= f.submit "Upload" %>
<% end %>
Request headers in Chrome
Expanded Request Headers section
nginx.log
127.0.0.1 - - [01/Feb/2014:11:28:26 +0530] "POST /settings/avatar/update HTTP/1.1" 500 643 "https://allotrop.dev/settings/avatar" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.77 Safari/537.36"
pow access.log
[Sat Feb 01 2014 11:28:26 GMT+0530 (IST)] INFO [127.0.0.1] GET allotrop.dev /500.html
There are other questions here about Rails returning 500 error with no mention in log rails 500 error no production log entry
, Rails 3.2.13, 500 error in development with no log
and How to properly diagnose a 500 error (Rails, Passenger, Nginx, Postgres)
But these are one-off errors and do not seem related to the problem I am facing. It would be great if anyone can point me in the right direction.
Update 1
Relevant line from routes.rb
post '/settings/avatar/update', to: 'settings#update_avatar'
Update 2
Found my nginx error log. BTW, if you are using Homebrew, it is at /usr/local/Cellar/nginx/1.4.0/logs/error.log
nginx/error.log
2014/02/01 13:05:54 [crit] 8787#0: *85813 open() "/usr/local/var/run/nginx/client_body_temp/0000000010" failed (13: Permission denied), client: 127.0.0.1, server: *.dev, request: "POST /settings/avatar/update HTTP/1.1", host: "allotrop.dev", referrer: "https://allotrop.dev/settings/avatar"
The problem is not with Rails but with Nginx which is pretty evident from the nginx error.log. This question helped me understand what I was dealing with - Rails 3 + carrierwave + nginx = permission denied.
Nginx uses the client_body_temp_path directive to specify the location where it will temporarily store the uploaded files from the user request. Homebrew had set it by default to /usr/local/var/run/nginx. This folder also contains fastcgi_temp, proxy_temp, scgi_temp and uwsgi_temp for me. Nginx worker processes run with user nobody and they were not able to access these folders. I chowned all these folders to the nobody user, but that did not help.
Finally, I did
client_body_temp_path /tmp/nginx/; inside the HTTP module of my nginx.conf to make it work.
Doing a ls -l shows
drwx------ 2 nobody wheel 68 Feb 1 14:44 nginx
I am not sure why this worked inside /tmp and not inside the original /var/run/nginx. I belive I will face similar issue when I use other temp folders or in production. Will update this thread if and when that happens.
I recommend symlinking the other relevant logs like the nginx access and error log, pow access and app log to the /log directory of your Rails app. It helps in looking up errors in one of these when you face a tricky bug.

I keep getting QUIT and CONNECT HTTP methods sent to my server, what do they mean?

I keep getting the two following errors from my server, I assumed they were just bots looking for potential targets, but does anyone know specifically why I'm getting these? I'm using the SslRequirement plugin to make sure all hits to the login/signup page are redirected to SSL, so all of these weird https requests to root should just be redirected to regular http.
A ActionController::UnknownHttpMethod occurred in application#index:
quit, accepted HTTP methods are get, head, put, post, delete, and options
/usr/local/lib/ruby/gems/1.9.1/gems/actionpack-2.3.4/lib/action_controller/request.rb:35:in `request_method'
PATH_INFO : /
REMOTE_ADDR : 99.19.208.249
REMOTE_PORT : 6376
REQUEST_METHOD : CONNECT
REQUEST_URI : /
SERVER_PORT : 443
SERVER_PROTOCOL : HTTP/1.0
SERVER_SOFTWARE : Apache
A ActionController::UnknownHttpMethod occurred in application#index:
CONNECT, accepted HTTP methods are get, head, put, post, delete, and options
/usr/local/lib/ruby/gems/1.9.1/gems/actionpack-2.3.4/lib/action_controller/request.rb:35:in `request_method'
HTTPS : on
HTTP_X_FORWARDED_PROTO : https
PATH_INFO : /
REMOTE_ADDR : 91.209.196.76
REMOTE_PORT : 50751
REQUEST_METHOD : quit
REQUEST_URI : /
SERVER_PORT : 443
SERVER_PROTOCOL : HTTP/0.9
The CONNECT command is used by HTTP proxy servers to indicate that the client wants to just connect a socket directly to another server; this is usually used for tunneling TLS over an HTTP proxy, but could be used for tunneling almost any protocol.
QUIT is not an HTTP command, but it is an SMTP command. It is possible that you are getting these commands from a bot that is trying to find open relays for sending spam; it's trying to figure out if you have an open SMTP relay, or an open HTTP proxy that allows the CONNECT command which could also be used to tunnel SMTP traffic.
So, likely you're just being hit by a spam botnet trying to find open relays. My advice would be to drop such requests as early as possible, and not worry about them.
CONNECT: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.9
QUIT usually means 'close the connection'.
# Avoid annoying ActionController::UnknownHttpMethod exceptions like:
#
# ActionController::UnknownHttpMethod) "CONNECT, accepted HTTP methods are get, head, put, post, delete, and options"
#
# Install this file in app/metal and these requests will receive a 405
# "Method Not Allowed" status and will be logged under `info'.
class IgnoreUnknownHttpMethod
def self.call(env)
[
if ActionController::Request::HTTP_METHODS.include?(env["REQUEST_METHOD"].downcase)
404 # Not Found
else
Rails.logger.info("Ignoring unknown HTTP method; #{env.inspect}")
405 # Method Not Allowed
end, {"Content-Type" => "text/plain"}, []]
end
end
Credit https://gist.github.com/remvee/600569
I just noticed a few attempt to connect to my servers as follow, we see that it ends with a QUIT...
198.20.87.98 - - [22/Dec/2015:21:43:42 -0800] "GET / HTTP/1.1" 444 5666 "-" "-"
198.20.87.98 - - [22/Dec/2015:21:43:42 -0800] "GET /robots.txt HTTP/1.1" 444 5666 "-" "-"
198.20.87.98 - - [22/Dec/2015:21:43:42 -0800] "GET /sitemap.xml HTTP/1.1" 444 5666 "-" "-"
198.20.87.98 - - [22/Dec/2015:21:43:58 -0800] "quit" 405 5461 "-" "-"
As a side note, my server returns 444 which is not a legal HTTP code. It means NO RESPONSE and I do that because their "Agent String" is empty.
Looking at the IP address, I found a search engine that searches for things on the internet. Not a hacker trying to break in per se. (There intend is not evil, it seems.) I suppose that some of the applications they are checking for must understand the QUIT. So as a robot, it makes sense that you would want to try what would otherwise looks like weird commands such as CONNNECT and QUIT. There is no real limit to the commands an application can support, even if those commands are not official HTTP commands.
If your application does not understand these methods, then nothing will happen so you should not bother too much about these.
If your application receives those methods, then you may want to write a little something at the start to get the method and compare to GET and POST (and whatever else you support, like DELETE and PUT) and if it matches none of these, then reply with a 405 error code: "Method Not Allowed".
https://www.rfc-editor.org/rfc/rfc7231#section-6.5.5
If you cannot change your application and you are not sure whether it could react to a CONNECT / QUIT hit, then you could look into using a setup such as mod_security for Apache2.
as per jturkel at https://gist.github.com/remvee/600569.
In Rails 3.2 for non http actions I added the following at the end of config/application.rb and solved the problem for quit.
# silence ActionController::UnknownHttpMethod exceptions
ActionDispatch::ExceptionWrapper.rescue_responses.merge!( 'ActionController::UnknownHttpMethod' => :method_not_allowed )

Resources