

Rack apps mounted in Rails — how to protect access to them?
source link: https://blog.arkency.com/common-authentication-for-mounted-rack-apps-in-rails/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Rack apps mounted in Rails — how to protect access to them?
Sidekiq, Flipper, RailsEventStore — what do these Rails gems have in common? They all ship web apps with UI to enhance their usefulness in the application. Getting an overview of processed jobs, managing visibility of feature toggles, browsing events, their correlations and streams — nothing you could not do in code or Rails console already. But never really wanted to do there 😉
In Rails apps we add those UI apps with mount
in routing:
# config/routes.rb
Rails.application.routes.draw do
mount Sidekiq::Web, at: "/sidekiq"
end
In production application you’ll want to protect access to this Sidekiq dashboard. Let’s assume this Rails application is API-only. There’s no Devise nor any other authentication library of your choice there. Fair scenario to rely on HTTP Basic Auth, as illustrated with wonderfully commented example from Sidekiq wiki:
# config/routes.rb
Rails.application.routes.draw do
Sidekiq::Web.use Rack::Auth::Basic do |username, password|
# Protect against timing attacks:
# - See https://codahale.com/a-lesson-in-timing-attacks/
# - See https://thisdata.com/blog/timing-attacks-against-string-comparison/
# - Use & (do not use &&) so that it doesn't short circuit.
# - Use digests to stop length information leaking (see also ActiveSupport::SecurityUtils.variable_size_secure_compare)
ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(username), ::Digest::SHA256.hexdigest(ENV["SIDEKIQ_USERNAME"])) &
ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(password), ::Digest::SHA256.hexdigest(ENV["SIDEKIQ_PASSWORD"]))
end
mount Sidekiq::Web, at: "/sidekiq"
end
Let’s transform this example a bit to not rely on Sidekiq::Web.use
. That’s very convenient to provide such interface from a library. I want to show you something else here — Sidekiq::Web
is a Rack application and can be treated as such.
# config/routes.rb
Rails.application.routes.draw do
mount Rack::Builder.new do
use Rack::Auth::Basic do |username, password|
ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(username), ::Digest::SHA256.hexdigest(ENV.fetch("DEV_UI_USERNAME"))) &
ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(password), ::Digest::SHA256.hexdigest(ENV.fetch("DEV_UI_PASSWORD")))
end
run Sidekiq::Web
end, at: "/sidekiq"
end
Little explanation:
- Rack::Builder is a DSL to create new Rack application
- Rack applications can wrap each other, like layers
- when the request comes in, a chain of Rack apps is executed from outermost to innermost
- when the response is to be returned, it goes from innermost Rack app to outermost
Right. Wasn’t it supposed to be about protecting access?
Imagine now that aforementioned Rails application includes all those UIs for Sidekiq, Flipper, RailsEventStore at the same time. How can we have common protection for them without boring copying and pasting same wrapper again and again?
Let’s extract (bad word detected) a factory!
# config/routes.rb
Rails.application.routes.draw do
with_dev_auth =
lambda do |app|
Rack::Builder.new do
use Rack::Auth::Basic do |username, password|
ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(username), ::Digest::SHA256.hexdigest(ENV.fetch("DEV_UI_USERNAME"))) &
ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(password), ::Digest::SHA256.hexdigest(ENV.fetch("DEV_UI_PASSWORD")))
end
run app
end
end
mount with_dev_auth.call(Sidekiq::Web), at: "sidekiq"
end
Fun fact is that a Proc#[]
is an equivalent to Proc#call
.
The last line can be as well written as:
mount with_dev_auth[Sidekiq::Web], at: "sidekiq"
And with all those UIs in place we receive:
mount with_dev_auth[Sidekiq::Web], at: "/sidekiq"
mount with_dev_auth[RailsEventStore::Browser], at: "/res"
mount with_dev_auth[Flipper::UI.app(Flipper)], at: "/flipper"
In the future we could swap Basic Auth to one or another authentication mechanism. The with_dev_auth
factory would remain useful and probably survive them all.
Now, a plug 🔌. Join ARKADEMY.DEV and get access to our best courses: Rails Architect Masterclass, Anti-IF course, Blogging for busy programmers, Async Remote, TDD video class, Domain-Driven Rails video course and growing!
Recommend
-
63
README.md React Scroll Lock Prevent scroll on the <body /> when a component is mounted. Install yarn add react-scrolllock Usage
-
56
This guide explains how to find the mounted filesystem type in Linux operating systems.
-
30
Side-mounted fingerprint sensors are completely underrated After trying it on the side, I can’t go back By...
-
16
Introduction This tutorial demonstrates how to deploy a Ruby web app using the Sinatra framework using uWSGI and nginx as the web server...
-
18
Ruby Magic Running Rack: How Ruby HTTP servers run Rails apps Jeff Kreeftmeijer on Jan 24, 2017 “I absolutely love AppSignal.”
-
8
This is the World’s First Ceiling-Mounted, Motion-Controlled Camera DollyYouTuber, actor, and content creator Josh Yeo created a device that he says is
-
16
Home Assistant Configuration Home Assistant Core in docker on a Synology DiskStation DS918+....
-
18
July 16, 2021 ...
-
5
Saturday, September 10, 2022 Rack 3 and Rails 7.0.4, 6.1.7, and 6.0.6 releases, ActionDispatch::Cookies, etc Posted by CHANGELOG.md 👋🏾… This is
-
5
Where are iMessage apps in iOS 17? Here’s how to access and customize them ...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK