From c8a9a288fb7d68d6b035958c779f1aa10103da95 Mon Sep 17 00:00:00 2001 From: Michael Kozono Date: Tue, 19 Mar 2019 16:19:31 -0700 Subject: [PATCH] WIP --- app/controllers/application_controller.rb | 6 ++- config/application.rb | 4 ++ lib/gitlab/middleware/geo_proxy.rb | 64 +++++++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 lib/gitlab/middleware/geo_proxy.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1773febfeaf357..0089c9982268cb 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -31,7 +31,11 @@ class ApplicationController < ActionController::Base after_action :set_page_title_header, if: :json_request? after_action :limit_unauthenticated_session_times - protect_from_forgery with: :exception, prepend: true + # protect_from_forgery with: :exception, prepend: true + + # defining this so `skip_before_action :verify_authenticity_token` doesn't raise anything + before_action :verify_authenticity_token + def verify_authenticity_token; end helper_method :can? helper_method :import_sources_enabled?, :github_import_enabled?, diff --git a/config/application.rb b/config/application.rb index 62f7275d8c8f91..5b215920e22f2d 100644 --- a/config/application.rb +++ b/config/application.rb @@ -16,6 +16,7 @@ class Application < Rails::Application require_dependency Rails.root.join('lib/gitlab/current_settings') require_dependency Rails.root.join('lib/gitlab/middleware/read_only') require_dependency Rails.root.join('lib/gitlab/middleware/basic_health_check') + require_dependency Rails.root.join('lib/gitlab/middleware/geo_proxy') # This needs to be loaded before DB connection is made # to make sure that all connections have NO_ZERO_DATE @@ -254,6 +255,9 @@ class Application < Rails::Application # GitLab Read-only middleware support config.middleware.insert_after ActionDispatch::Flash, ::Gitlab::Middleware::ReadOnly + # Geo proxy write actions to primary + config.middleware.insert_before ::Gitlab::Middleware::ReadOnly, ::Gitlab::Middleware::GeoProxy + config.generators do |g| g.factory_bot false end diff --git a/lib/gitlab/middleware/geo_proxy.rb b/lib/gitlab/middleware/geo_proxy.rb new file mode 100644 index 00000000000000..2e05a5a6e652da --- /dev/null +++ b/lib/gitlab/middleware/geo_proxy.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +module Gitlab + module Middleware + class GeoProxy < Rack::Proxy + def perform_request(env) + request = Rack::Request.new(env) + if write_action?(request) && Gitlab::Geo.secondary? + primary_http_host = Gitlab::Geo.primary_node.uri.host + primary_http_host += ":#{Gitlab::Geo.primary_node.uri.port}" unless on_standard_port?(Gitlab::Geo.primary_node.uri.port) + + Rails.logger.info "[GeoProxy] perform_request: Replacing #{env["HTTP_HOST"]} with #{primary_http_host}" + env["HTTP_HOST"] = primary_http_host + + env['content-length'] = nil + + super(env) + else + @app.call(env) + end + end + + def rewrite_response(triplet) + status, headers, body = triplet + location = headers["location"] + location = location.first if location.is_a?(Array) + + if location.present? && rewritable_location?(location) + primary_http_host = Gitlab::Geo.primary_node.uri.host + primary_http_host += ":#{Gitlab::Geo.primary_node.uri.port}" unless on_standard_port?(Gitlab::Geo.primary_node.uri.port) + this_http_host = Settings.gitlab.url.sub(%r{.*://}, '') + + Rails.logger.info "[GeoProxy] rewrite_response: Replacing #{primary_http_host} with #{this_http_host} in #{location}" + headers["location"] = location.sub(primary_http_host, this_http_host) + end + + # if you proxy depending on the backend, it appears that content-length isn't calculated correctly + # resulting in only partial responses being sent to users + # you can remove it or recalculate it here + headers["content-length"] = nil + + triplet + end + + def write_action?(request) + %w{post patch put delete}.include?(request.request_method.downcase) + end + + def on_standard_port?(port) + ["80", "443"].include?(port) + end + + def rewritable_location?(location) + valid_locations = %w{ + /users/sign_in + /oauth/authorize + /oauth/geo/logout + } + + valid_locations.none? { |v| location.match(v) } + end + end + end +end -- GitLab