Ruby – Secure staging environment of your public app from users and bots

Context: App with public access, but you would like to prevent public access to staging (avoid google indexing and confused users).

Apache

One option, if you are enterprisey, you would keep the staging environment within the firewall, not publicly available.

A better option, IMO, given apache or relevant, I’d set basic auth in the apache config file, or add it to .htaccess if apache is configured to read that. Pretty simple and unobtrusive to the app code. On heroku however, we can’t do that.

Heroku

Heroku forces you to develop and deploy your app slightly differently. It has a read-only filesystem. Git is the only way to send heroku files, removing the possibility to use custom files at server that you have in .gitignore (normal with config/some_settings_x.yml files). In my opinion, heroku forces me to have a better architecture. Disk writes are heavy on the server, why spend server cpu time on disk-duty when it should be serving pages to your valuable customers?

What about the basic auth, this was already fairly simple with apache, wasn’t it?

You be the judge of what is best. I ended up doing this:

# config/environments/staging.rb

MyApp::Application.configure do
  config.middleware.insert_after(::Rack::Runtime, "::Rack::Auth::Basic", "Staging") do |u, p|
    [u, p] == ['username', 'password']
  end

  #... other config
end

beautiful and very readable, now it’s all there right in your environment config.

Now you have secured your staging environment from google bots, you don’t have to bother with robots.txt and it avoids people blindly stumbling into your staging app.

As a final note: To easily change password you should consider

   [u, p] == [ENV['MY_SITE_USERNAME'], ENV['MY_SITE_SECRET']]

   $ heroku config:add MY_SITE_USERNAME='username' --app myappstaging
   $ heroku config:add MY_SITE_SECRET='secret' --app myappstaging

About Ole Morten Amundsen

Developer, programmer, entrepreneur. Java, .Net, ruby, rails, agile, lean. Opinionated enthusiast!
This entry was posted in rails, ruby and tagged , , , , , , . Bookmark the permalink.

16 Responses to Ruby – Secure staging environment of your public app from users and bots

  1. Pingback: Link dump for June 14th | The Queue Incorporated

  2. Anonymous says:

    A problem I ran into configuring digest auth in apache was that I could not assign different credentials per staged application all running on the same server. This gives me a good alternative solution. Thanks.

  3. toliwaga says:

    regarding final note – missing left square bracket in :
    [u, p] == ENV[‘MY_SITE_USERNAME’], ENV[‘MY_SITE_SECRET’]]
    should be
    [u, p] == [ENV[‘MY_SITE_USERNAME’], ENV[‘MY_SITE_SECRET’]]

  4. marcmp says:

    I haven’t been able to have heroku show the username+password dialoge. However, it does work in local. This is my configuration:

    # My config.ru: file #
    require ::File.expand_path(‘../config/environment’, __FILE__)
    run MyApp::Application

    # My Procfile file #
    web: bundle exec rails server thin -p $PORT -e $RACK_ENV

    # RACK_ENV is set to “staging” by using the following #
    $ heroku config:add RACK_ENV=staging

    # Added you code in my config/staging.rb file (also in development and production just in case #
    config.middleware.insert_after(::Rack::Lock, “::Rack::Auth::Basic”, “Staging”) do |u, p|
    [u, p] == [‘kite’, ‘bit’]
    end

    Also, heroku app is using staging environment.

    Any ideas why this is not working in Heroku Cedar ?

    • marcmp says:

      I found out, index.html is served directly by the web server and not rails and thus the index.html url won’t be protected. Everything else served by Rails will be protected.

      • Ole Morten Amundsen says:

        Thanks for posting the solution, yielding closure.

      • Jeff Doyle says:

        I think that on cedar, all requests are sent to the web server, including static pages. I suspect that your problem is that ActionDispatch::Static is lower in your rack middleware stack then Rack::Auth::Basic

        try running:
        heroku run rake middleware

        I’m betting you will see something like this:

        use ActionDispatch::Static
        use Rack::Lock
        use Rack::Auth::Basic

        in which case you will have to adjust your insert_after call accordingly

  5. Joel says:

    Rack::Lock was not in my stack in staging (although it was in development), so I had to use insert_before with ::Rack::Runtime instead.
    I’m using Rails 4, so maybe there’s something new there?

    • Anonymous says:

      I think in Rails 4, you could simply call use instead of insert_before ::Rack::Something, since Rails 4 is thread-safe by default.

      • Anonymous says:

        Sorry, didn’t get you’re using insert_before instead of insert_after. This is definitely necessary.

    • Ole Morten Amundsen says:

      Thanks Joel. Yeah, thinks change and so does the middleware stack in rails.

  6. Outstanding. In case others need Heroku examples, here is how I have mine setup for development,test, and production:

    config.middleware.insert_before(::Rack::Runtime, “::Rack::Auth::Basic”, “production”) do |u, p|
    [u, p] == [ENV[‘MY_SITE_USERNAME’], ENV[‘MY_SITE_SECRET’]]
    end

    config.middleware.insert_before(::Rack::Runtime, “::Rack::Auth::Basic”, “test”) do |u, p|
    [u, p] == [ENV[‘MY_SITE_USERNAME’], ENV[‘MY_SITE_SECRET’]]
    end

    config.middleware.insert_before(::Rack::Runtime, “::Rack::Auth::Basic”, “development”) do |u, p|
    [u, p] == [ENV[‘MY_SITE_USERNAME’], ENV[‘MY_SITE_SECRET’]]
    end

  7. ibroadfo says:

    https://addons.heroku.com/wwwhisper is a super-simple site protection add-on (with a free plan) which uses mozilla’s persona system. (i’m just a happy customer, not an employee :))

Leave a comment