diff --git a/app/controllers/projects/git_http_controller.rb b/app/controllers/projects/git_http_controller.rb
index f907d63258b19e20e27be07f83c365dde9f5cd67..62c3fa8de531a75f4ac77067b99cd299c61b2d72 100644
--- a/app/controllers/projects/git_http_controller.rb
+++ b/app/controllers/projects/git_http_controller.rb
@@ -1,4 +1,9 @@
+# This file should be identical in GitLab Community Edition and Enterprise Edition
+
 class Projects::GitHttpController < Projects::ApplicationController
+  include ActionController::HttpAuthentication::Basic
+  include KerberosSpnegoHelper
+
   attr_reader :user
 
   # Git clients will not know what authenticity token to send along
@@ -40,9 +45,12 @@ class Projects::GitHttpController < Projects::ApplicationController
   private
 
   def authenticate_user
-    return if project && project.public? && upload_pack?
+    if project && project.public? && upload_pack?
+      return # Allow access
+    end
 
-    authenticate_or_request_with_http_basic do |login, password|
+    if allow_basic_auth? && basic_auth_provided?
+      login, password = user_name_and_password(request)
       auth_result = Gitlab::Auth.find_for_git_client(login, password, project: project, ip: request.ip)
 
       if auth_result.type == :ci && upload_pack?
@@ -53,8 +61,31 @@ class Projects::GitHttpController < Projects::ApplicationController
         @user = auth_result.user
       end
 
-      ci? || user
+      if ci? || user
+        return # Allow access
+      end
+    elsif allow_kerberos_spnego_auth? && spnego_provided?
+      @user = find_kerberos_user
+
+      if user
+        send_final_spnego_response
+        return # Allow access
+      end
     end
+
+    send_challenges
+    render plain: "HTTP Basic: Access denied\n", status: 401
+  end
+
+  def basic_auth_provided?
+    has_basic_credentials?(request)
+  end
+
+  def send_challenges
+    challenges = []
+    challenges << 'Basic realm="GitLab"' if allow_basic_auth?
+    challenges << spnego_challenge if allow_kerberos_spnego_auth?
+    headers['Www-Authenticate'] = challenges.join("\n") if challenges.any?
   end
 
   def ensure_project_found!
@@ -120,7 +151,7 @@ class Projects::GitHttpController < Projects::ApplicationController
   end
 
   def render_not_found
-    render text: 'Not Found', status: :not_found
+    render plain: 'Not Found', status: :not_found
   end
 
   def ci?
diff --git a/app/helpers/kerberos_spnego_helper.rb b/app/helpers/kerberos_spnego_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f5b0aa7549a23643de7f60c15999c1f73852ca72
--- /dev/null
+++ b/app/helpers/kerberos_spnego_helper.rb
@@ -0,0 +1,9 @@
+module KerberosSpnegoHelper
+  def allow_basic_auth?
+    true # different behavior in GitLab Enterprise Edition
+  end
+
+  def allow_kerberos_spnego_auth?
+    false # different behavior in GitLab Enterprise Edition
+  end
+end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index bae56334be48a2432f980060a757f80759ab00b3..82ab582beace731f4a57baee10450cca3f5691a1 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -350,23 +350,23 @@ describe 'Git HTTP requests', lib: true do
   end
 
   def clone_get(project, options={})
-    get "/#{project}/info/refs", { service: 'git-upload-pack' }, auth_env(*options.values_at(:user, :password))
+    get "/#{project}/info/refs", { service: 'git-upload-pack' }, auth_env(*options.values_at(:user, :password, :spnego_request_token))
   end
 
   def clone_post(project, options={})
-    post "/#{project}/git-upload-pack", {}, auth_env(*options.values_at(:user, :password))
+    post "/#{project}/git-upload-pack", {}, auth_env(*options.values_at(:user, :password, :spnego_request_token))
   end
 
   def push_get(project, options={})
-    get "/#{project}/info/refs", { service: 'git-receive-pack' }, auth_env(*options.values_at(:user, :password))
+    get "/#{project}/info/refs", { service: 'git-receive-pack' }, auth_env(*options.values_at(:user, :password, :spnego_request_token))
   end
 
   def push_post(project, options={})
-    post "/#{project}/git-receive-pack", {}, auth_env(*options.values_at(:user, :password))
+    post "/#{project}/git-receive-pack", {}, auth_env(*options.values_at(:user, :password, :spnego_request_token))
   end
 
-  def download(project, user: nil, password: nil)
-    args = [project, { user: user, password: password }]
+  def download(project, user: nil, password: nil, spnego_request_token: nil)
+    args = [project, { user: user, password: password, spnego_request_token: spnego_request_token }]
 
     clone_get(*args)
     yield response
@@ -375,8 +375,8 @@ describe 'Git HTTP requests', lib: true do
     yield response
   end
 
-  def upload(project, user: nil, password: nil)
-    args = [project, { user: user, password: password }]
+  def upload(project, user: nil, password: nil, spnego_request_token: nil)
+    args = [project, { user: user, password: password, spnego_request_token: spnego_request_token }]
 
     push_get(*args)
     yield response
@@ -385,11 +385,14 @@ describe 'Git HTTP requests', lib: true do
     yield response
   end
 
-  def auth_env(user, password)
+  def auth_env(user, password, spnego_request_token)
+    env = {}
     if user && password
-      { 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user, password) }
-    else
-      {}
+      env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user, password)
+    elsif spnego_request_token
+      env['HTTP_AUTHORIZATION'] = "Negotiate #{::Base64.strict_encode64('opaque_request_token')}"
     end
+
+    env
   end
 end