Setting Heroku to accept traffic only from CloudFlare - heroku

I have a website deployed on Heroku (e.g. www.example.com) and I have set up CloudFlare to work as my CDN, therefore all traffic to my website goes through CloudFlare.
But I still have the link to my app on Heroku sub-domain (example.heroku.com), and if somebody tries this address, it wouldn't pass through CloudFlare anymore.
How can I hide my Heroku app address (example.heroku.com) and make my website to accept traffic only from CloudFlare?

My answer is based on the assumption that you're using Heroku to host a Ruby Rack application, since I believe that is the profile of most of Heroku's users. Otherwise, please skip.
If you're hosting a Rack app on Heroku, you could potentially insert a small piece of Rack middleware to do redirects for you.
# lib/rack/domain_redirect.rb
# encoding utf-8
# Rack Middleware that was created to handle
# autoredirecting requests away from *.herokuapp.com to
# the equivalent *.example.com. That said, it does allow you to configure
# what domain to redirect from and what domain to redirect to as well
module Rack
class DomainRedirect
attr_accessor :redirect_from_domain, :redirect_to_domain
def initialize(app, redirect_from_domain = "herokuapp.com", redirect_to_domain = "example.com")
self.redirect_from_domain = redirect_from_domain
self.redirect_to_domain = redirect_to_domain
#app = app
end
def call(env)
request = Rack::Request.new(env)
if request.host.include?(redirect_from_domain)
[301, {"Location" => request.url.sub(redirect_from_domain, redirect_to_domain)}, []]
else
#app.call(env)
end
end
end
end
Then in your config.ru
# some other middlewares and requires
require File.expand_path("../lib/rack/domain_redirect.rb", __FILE__)
use Rack::DomainRedirect
# run your app
run MyApp

Related

Intercept WEBrick request

I have a web app that runs on different pieces of hardware, that for the most part consists of smart TVs and set-top boxes.
My web app contains a ruby script to setup the app for local debugging. This script builds my app, listens for file changes, and hosts the app using a simple WEBrick server.
Now I'm running into a problem on a specific piece of hardware. This hardware expects to get a success response from a POST request to a health_check API running on the same host as the web app, before it will load up the web app.
I'm simply hoping to intercept this request and spoof it so that the hardware will load my client. So far I've gotten as far as this:
def start_server
require 'webrick'
root = File.expand_path 'public'
request_callback = Proc.new { |req, res|
if req.path =~ /health_check/
# return 200 response somehow?
end
}
server = WEBrick::HTTPServer.new :Port => 5000, :DocumentRoot => root, :RequestCallback => request_callback
server.start
end
I can modify the response object to set status to 200, but it still ends up returning a 404.
You don't need to "intercept" all requests and check for a specific path. You simply want mount_proc, to handle a specific route with a proc.
Add the following before server.start:
server.mount_proc '/health_check' do |req, res|
res.body = 'what what' # your content here
end
You'll probably want to wrap this in a check to determine if you're running on whatever custom hardware requires this behavior.
See Custom Behavior in the WEBrick docs.

Simplest method of enforcing HTTPS for Heroku Ruby Sinatra app

I have an app I created on Heroku which is written in Ruby (not rails) and Sinatra.
It is hosted on the default herokuapp domain so I can address the app with both HTTP and HTTPS.
The app requests user credentials which I forward on to an HTTPS call so the forwarding part is secure.
I want to ensure my users always connect securely to my app so the credentials aren't passed in clear text.
Despite lots of research, I've not found a solution to this simple requirement.
Is there a simple solution without changing my app to Ruby rails or otherwise?
Thanks,
Alan
I use a helper that looks like this:
def https_required!
if settings.production? && request.scheme == 'http'
headers['Location'] = request.url.sub('http', 'https')
halt 301, "https required\n"
end
end
I can then add it to any single route I want to force to https, or use it in the before filter to force on a set of urls:
before "/admin/*" do
https_required!
end
Redirect in a Before Filter
This is untested, but it should work. If not, or if it needs additional refinement, it should at least give you a reasonable starting point.
before do
redirect request.url.sub('http', 'https') unless request.secure?
end
See Also
Filters
Request Object
RackSsl::Enforcer

How To Run EventMachine and Serve Pages In Sinatra?

I'm building a Sinatra app that uses TweetStream (which listens for Tweets using EventMachine). I would also like the app to serve pages like a normal Sinatra app but it seems like Sinatra can't "listen" for page requests when it's "listening" for Tweets.
Is this something I can fix by using a different server or structuring my app in a different way? I've tried using WebBrick and Thin.
Here is basically what I'm doing:
class App < Sinatra::Base
# listening for tweets
#client = TweetStream::Client.new
#client.track(terms) do |status|
# do some stuff when I detect terms
end
get '/' do
"Here's some page content!"
end
end
You can mount Sinatra apps within eventmachine (providing you you a awebserver that supports EM i.e., Thin). You should then have full access to the EM reactor loop from your Sinatra app, as well as allowing any other EM plugins to run too.
The Sinatra recipes have a good example:
http://recipes.sinatrarb.com/p/embed/event-machine
here is a very stripped down version of the code:
require 'eventmachine'
require 'sinatra/base'
require 'thin'
def run(opts)
EM.run do
server = opts[:server] || 'thin'
host = opts[:host] || '0.0.0.0'
port = opts[:port] || '8181'
web_app = opts[:app]
dispatch = Rack::Builder.app do
map '/' do
run web_app
end
end
unless ['thin', 'hatetepe', 'goliath'].include? server
raise "Need an EM webserver, but #{server} isn't"
end
Rack::Server.start({
app: dispatch,
server: server,
Host: host,
Port: port
})
end
end
class HelloApp < Sinatra::Base
configure do
set :threaded, false
end
get '/hello' do
'Hello World'
end
get '/delayed-hello' do
EM.defer do
sleep 5
end
'I\'m doing work in the background, but I am still free to take requests'
end
end
run app: HelloApp.new
if you really want to use the tweets streaming feature then you need to run the streaming part as a separate process and write it's results say into a database, then read those records from your sinatra app.
that's how it works the twitter stream listener is a separate thing from your sinatra app and you need some sort of a queue to join them, say redis or db, or something like that.

Rack apps mounted in different subdomains

I'm building a Grape API alongside Sinatra. So far I've been mounting them in separate routes like this:
run Rack::URLMap.new("/" => Frontend::Server.new,
"/api" => API::Server.new)
Where the "/api" is served by a Grape app and "/" by a Sinatra app. But I wanted to use subdomains to separate those concerns instead of the actual "sub-URL". Any clues on how to do this?
Thanks in advance for the help.
There is a rack-subdomain gem, but it only handles redirection to paths, not rack apps. You could fork it and make it redirect to rack apps instead.
You could also just roll your own :
class SubdomainDispatcher
def initialize
#frontend = Frontend::Server.new
#api = API::Server.new
end
def call(env)
if subdomain == 'api'
return #api.call(env)
else
return #frontend.call(env)
end
end
private
# If may be more robust to use a 3rd party plugin to extract the subdomain
# e.g ActionDispatch::Http::URL.extract_subdomain(#env['HTTP_HOST'])
def subdomain
#env['HTTP_HOST'].split('.').first
end
end
run SubdomainDispatcher.new

HTTParty with Proxy

I am on heroku trying to access an API that requires my apps ip to be whitelisted. So, I used the heroku add-on proximo to get host/ip for the api's whitelist.
A quick test I set up to test connectivity using HTTParty is failing.
class FakeRequest
include HTTParty
http_proxy 'XX.XXX.XX.XX', 80, 'user', 'pass'
def set_defaults
{:api_key=>"BLARG_BLARG",
:login_name=>"user",
:method => "do_something",
:response_format => "json",
:v => "1.0",
:login_password=>"pass"}
end
def make_post
HTTParty.post "https://test.com", :query => set_defaults
end
end
Going this like:
req = FakeRequest.new
req.make_post
Returns an error message from the api complaining that the source IP is not whitelisted. I looked at the source IP and it is not using the proxy. How can I make HTTParty post using the proxy and not my ISP's IP.
This is the module I built to do just this:
module ProximoParty
PROXIMO = URI.parse(ENV['PROXIMO_URL'])
def self.included(base)
base.send(:include, HTTParty)
base.http_proxy(PROXIMO.host, 80, PROXIMO.user, PROXIMO.password)
end
end
This uses the PROXIMO URL as it is added to your heroku app when you install the addon. So you can drop this file into your app and include ProximoParty into your FakeRequest class instead of HTTParty and it should "just work".
It looks like my code is doing the same thing your code is doing though, so what I'm guessing is that you may not be manually carrying over the credentials properly for proximo.
I ran into a similar problem where it wasn't quite working for me right off the bat. I believe the problem was that I was getting tripped up that there looked to be a "proxy:" protocol in the proximo URL but that was just the username part of the URL.
Anyways, this may or may not help, but please let me know if it does!
It blows my mind how many answers you can get on this question that don't work. Here is how I got it working:
HTTParty::Basement.http_proxy('localhost', 8000, nil, nil)
Put this as a global override in your env.rb file. That's it. Done.
With recent versions of HTTParty it's as simple as using the
http_proxy method:
class Foo
include HTTParty
http_proxy 'http://example.com', 80, 'user', 'pass'
end
HTTParty can use a proxy server address using the following proxy options.
[:+http_proxyaddr+:] Address of proxy server to use
[:+http_proxyport+:] Port of proxy server to use.
[:+http_proxyuser+:] User for proxy server authentication
[:+http_proxypass+:] Password for proxy server authentication.
As you configure HTTParty as an inclusion in your module, you have to call HTTParty method through your class, so:
def make_post
self.class.post "https://test.com", :query => set_defaults
end
I included a link because it include lengthy setup steps. But point taken about the link changing and page becoming invalid. We used squid proxy server on a EC2 AMI and called it a day.
I think you need to call the local httparty, like this:
def make_post
self.post "https://test.com", :query => set_defaults
end

Resources