diff --git a/Gemfile b/Gemfile index 0e64277ba5..74f8b3e462 100644 --- a/Gemfile +++ b/Gemfile @@ -80,6 +80,11 @@ gem 'daemons' # (see https://community.openproject.org/work_packages/3029) gem 'rack-protection', :git => "https://github.com/finnlabs/rack-protection.git", :ref => '5a7d1bd' +# Rack::Attack is a rack middleware to protect your web app from bad clients. +# It allows whitelisting, blacklisting, throttling, and tracking based on arbitrary properties of the request. +# https://github.com/kickstarter/rack-attack +gem 'rack-attack' + gem 'syck', :platforms => [:ruby_20, :mingw_20, :ruby_21, :mingw_21], :require => false gem 'gon', '~> 4.0' diff --git a/Gemfile.lock b/Gemfile.lock index 3ccebfc8ce..1e5c5de5b3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -300,6 +300,8 @@ GEM rack (1.4.5) rack-accept (0.4.5) rack (>= 0.4) + rack-attack (4.2.0) + rack rack-cache (1.2) rack (>= 0.4) rack-mount (0.8.3) @@ -494,6 +496,7 @@ DEPENDENCIES pry-stack_explorer quiet_assets rabl (= 0.9.3) + rack-attack rack-protection! rack-test (~> 0.6.2) rack_session_access diff --git a/config/application.rb b/config/application.rb index 3173aea846..d5b5043674 100644 --- a/config/application.rb +++ b/config/application.rb @@ -82,6 +82,8 @@ module OpenProject env['PATH_INFO'] =~ /\/api\/v3/ } + config.middleware.use Rack::Attack + # Custom directories with classes and modules you want to be autoloadable. # config.autoload_paths += %W(#{config.root}/extras) config.autoload_paths << Rails.root.join('lib') diff --git a/config/configuration.yml.example b/config/configuration.yml.example index d08930116c..aaf79913e6 100644 --- a/config/configuration.yml.example +++ b/config/configuration.yml.example @@ -132,6 +132,25 @@ # - plugins # - info # +# Also there is a posibility to specify which routes are forbidden, +# they should be listed as an array in yml format more information +# regarding yml format you can find here: +# http://symfony.com/doc/current/components/yaml/yaml_format.html +# You can also use wildcards (*) in your url +# +# production: +# blacklisted_routes: +# - 'admin/info' +# - 'admin/plugins' +# - 'export_card_configurations' +# - 'project_types' +# - 'colors' +# - 'settings' +# - 'admin/enumerations' +# - 'workflows/*' +# - 'statuses' +# - 'types' +# - 'admin/roles' # default configuration options for all environments diff --git a/config/initializers/rack-attack.rb b/config/initializers/rack-attack.rb new file mode 100644 index 0000000000..4fa24a4408 --- /dev/null +++ b/config/initializers/rack-attack.rb @@ -0,0 +1,12 @@ +if OpenProject::Configuration.blacklisted_routes.any? + # Block logins from a bad user agent + Rack::Attack.blacklist('block forbidden routes') do |req| + regex = OpenProject::Configuration.blacklisted_routes.map! { |str| Regexp.new(str) } + regex.any? { |i| i =~ req.path } + end + + Rack::Attack.blacklisted_response = lambda do |_env| + # All blacklisted routes would return a 404. + [404, {}, ['Not found']] + end +end diff --git a/doc/CONFIGURATION.md b/doc/CONFIGURATION.md index 703fafb6f1..870e8480bd 100644 --- a/doc/CONFIGURATION.md +++ b/doc/CONFIGURATION.md @@ -85,6 +85,7 @@ storage config above like this: * [`attachments_storage`](#attachments-storage) (default: file) * [`hidden_menu_items`](#hidden-menu-items) (default: {}) * [`disabled_modules`](#disabled-modules) (default: []) +* [`blacklisted_routes`](#blacklisted-routes) (default: []) ### disable password login @@ -181,6 +182,34 @@ For instance 'Roles' and 'Types' under 'Administration' can be disabled by defin OPENPROJECT_HIDDEN__MENU__ITEMS_ADMIN__MENU='roles types' ``` +### blacklisted routes + +*default: []* + +You can blacklist specific routes +The following example forbid all routes for above disabled menu: + +``` +blacklisted_routes: + - 'admin/info' + - 'admin/plugins' + - 'export_card_configurations' + - 'project_types' + - 'colors' + - 'settings' + - 'admin/enumerations' + - 'workflows/*' + - 'statuses' + - 'types' + - 'admin/roles' +``` + +The configuration can be overridden through environment variables. + +``` +OPENPROJECT_BLACKLISTED__ROUTES='admin/info admin/plugins' +``` + ### disabled modules *default: []* diff --git a/lib/open_project/configuration.rb b/lib/open_project/configuration.rb index 9fcd77ec26..b61d645040 100644 --- a/lib/open_project/configuration.rb +++ b/lib/open_project/configuration.rb @@ -77,7 +77,8 @@ module OpenProject 'disable_password_choice' => false, 'disabled_modules' => [], # allow to disable default modules - 'hidden_menu_items' => {} + 'hidden_menu_items' => {}, + 'blacklisted_routes' => [] } @config = nil diff --git a/lib/open_project/configuration/helpers.rb b/lib/open_project/configuration/helpers.rb index 8b73b05390..6a0ac401ce 100644 --- a/lib/open_project/configuration/helpers.rb +++ b/lib/open_project/configuration/helpers.rb @@ -82,6 +82,10 @@ module OpenProject array self['disabled_modules'] end + def blacklisted_routes + array self['blacklisted_routes'] + end + def available_file_uploaders { fog: ::FogFileUploader,