Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
ebb6a075
Commit
ebb6a075
authored
Jun 29, 2016
by
Jacob Vosmaer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support Kerberos SPNEGO in GitHttpController
parent
4f5478fd
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
125 additions
and
14 deletions
+125
-14
app/controllers/projects/git_http_controller.rb
app/controllers/projects/git_http_controller.rb
+35
-4
app/helpers/kerberos_spnego_helper.rb
app/helpers/kerberos_spnego_helper.rb
+73
-0
config/routes.rb
config/routes.rb
+9
-2
spec/requests/git_http_spec.rb
spec/requests/git_http_spec.rb
+8
-8
No files found.
app/controllers/projects/git_http_controller.rb
View file @
ebb6a075
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
skip_before_action
:verify_authenticity_token
skip_before_action
:repository
before
_action
:authenticate_user
around
_action
:authenticate_user
before_action
:ensure_project_found!
# GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
...
...
@@ -40,9 +43,13 @@ class Projects::GitHttpController < Projects::ApplicationController
private
def
authenticate_user
return
if
project
&&
project
.
public?
&&
upload_pack?
if
project
&&
project
.
public?
&&
upload_pack?
yield
return
end
authenticate_or_request_with_http_basic
do
|
login
,
password
|
if
allow_basic_auth?
&&
has_basic_credentials?
(
request
)
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 +60,32 @@ class Projects::GitHttpController < Projects::ApplicationController
@user
=
auth_result
.
user
end
ci?
||
user
if
ci?
||
user
yield
return
end
elsif
allow_kerberos_spnego_auth?
&&
has_spnego_credentials?
(
request
)
spnego_token
=
Base64
.
strict_decode64
(
auth_param
(
request
))
@user
=
find_kerberos_user
(
spnego_token
)
if
user
set_www_authenticate
(
spnego_challenge
)
if
spnego_response_token
yield
return
end
end
# Authentication failed. Challenge the client to provide credentials.
challenges
=
[]
challenges
<<
'Basic realm="GitLab"'
if
allow_basic_auth?
challenges
<<
spnego_challenge
if
allow_kerberos_spnego_auth?
set_www_authenticate
(
challenges
.
join
(
"
\n
"
))
render
plain:
"HTTP Basic: Access denied
\n
"
,
status:
401
end
def
set_www_authenticate
(
value
)
headers
[
'Www-Authenticate'
]
=
value
end
def
ensure_project_found!
...
...
app/helpers/kerberos_spnego_helper.rb
0 → 100644
View file @
ebb6a075
module
KerberosSpnegoHelper
attr_reader
:spnego_response_token
def
allow_basic_auth?
if
Gitlab
.
config
.
kerberos
.
enabled
&&
Gitlab
.
config
.
kerberos
.
use_dedicated_port
!
request_uses_kerberos_dedicated_port?
else
true
end
end
def
allow_kerberos_spnego_auth?
return
false
unless
Gitlab
.
config
.
kerberos
.
enabled
if
Gitlab
.
config
.
kerberos
.
use_dedicated_port
request_uses_kerberos_dedicated_port?
else
true
end
end
def
request_uses_kerberos_dedicated_port?
request
.
env
[
'SERVER_PORT'
]
==
Gitlab
.
config
.
kerberos
.
port
.
to_s
end
def
spnego_challenge
if
spnego_response_token
"Negotiate
#{
::
Base64
.
strict_encode64
(
spnego_response_token
)
}
"
else
'Negotiate'
end
end
def
has_spnego_credentials?
(
request
)
request
.
authorization
.
present?
&&
(
auth_scheme
(
request
)
==
'Negotiate'
)
end
def
find_kerberos_user
(
spnego_token
)
krb_principal
=
spnego_credentials!
(
spnego_token
)
return
unless
krb_principal
identity
=
::
Identity
.
find_by
(
provider: :kerberos
,
extern_uid:
krb_principal
)
identity
.
user
if
identity
end
# The Kerberos backend will translate spnego_token into a Kerberos
# principal and/or provide a value for @spnego_response_token.
def
spnego_credentials!
(
spnego_token
)
require
'gssapi'
gss
=
GSSAPI
::
Simple
.
new
(
nil
,
nil
,
Gitlab
.
config
.
kerberos
.
keytab
)
# the GSSAPI::Simple constructor transforms a nil service name into a default value, so
# pass service name to acquire_credentials explicitly to support the special meaning of nil
gss_service_name
=
if
Gitlab
.
config
.
kerberos
.
service_principal_name
.
present?
gss
.
import_name
(
Gitlab
.
config
.
kerberos
.
service_principal_name
)
else
nil
# accept any valid service principal name from keytab
end
gss
.
acquire_credentials
(
gss_service_name
)
# grab credentials from keytab
# Decode token
gss_result
=
gss
.
accept_context
(
spnego_token
)
# gss_result will be 'true' if nothing has to be returned to the client
@spnego_response_token
=
gss_result
if
gss_result
&&
gss_result
!=
true
# Return user principal name if authentication succeeded
gss
.
display_name
rescue
GSSAPI
::
GssApiError
=>
ex
Rails
.
logger
.
error
"
#{
self
.
class
.
name
}
: failed to process Negotiate/Kerberos authentication:
#{
ex
.
message
}
"
false
end
end
config/routes.rb
View file @
ebb6a075
...
...
@@ -93,8 +93,8 @@ Rails.application.routes.draw do
# Health check
get
'health_check(/:checks)'
=>
'health_check#index'
,
as: :health_check
# Enable Grack support
mount
Grack
::
AuthSpawner
,
at:
'/'
,
constraints:
lambda
{
|
request
|
/[-\/\w\.]+\.git\//
.
match
(
request
.
path_info
)
},
via:
[
:get
,
:post
,
:put
]
# Enable Grack support
(for LFS only)
mount
Grack
::
AuthSpawner
,
at:
'/'
,
constraints:
lambda
{
|
request
|
/[-\/\w\.]+\.git\/
(info\/lfs|gitlab-lfs)
/
.
match
(
request
.
path_info
)
},
via:
[
:get
,
:post
,
:put
]
# Help
get
'help'
=>
'help#index'
...
...
@@ -502,6 +502,13 @@ Rails.application.routes.draw do
end
scope
module: :projects
do
# Git HTTP clients ('git clone' etc.)
scope
constraints:
{
id:
/.+\.git/
,
format:
nil
}
do
get
'/info/refs'
,
to:
'git_http#info_refs'
post
'/git-upload-pack'
,
to:
'git_http#git_upload_pack'
post
'/git-receive-pack'
,
to:
'git_http#git_receive_pack'
end
# Allow /info/refs, /info/refs?service=git-upload-pack, and
# /info/refs?service=git-receive-pack, but nothing else.
#
...
...
spec/requests/git_http_spec.rb
View file @
ebb6a075
...
...
@@ -97,7 +97,7 @@ describe 'Git HTTP requests', lib: true do
allow
(
Gitlab
.
config
.
gitlab_shell
).
to
receive
(
:upload_pack
).
and_return
(
false
)
download
(
path
,
{})
do
|
response
|
expect
(
response
.
status
).
to
eq
(
40
1
)
expect
(
response
.
status
).
to
eq
(
40
4
)
end
end
end
...
...
@@ -126,12 +126,12 @@ describe 'Git HTTP requests', lib: true do
let
(
:env
)
{
{
spnego_request_token:
'opaque_request_token'
}
}
before
do
allow_any_instance_of
(
Grack
::
Auth
).
to
receive
(
:allow_kerberos
_auth?
).
and_return
(
true
)
allow_any_instance_of
(
Projects
::
GitHttpController
).
to
receive
(
:allow_kerberos_spnego
_auth?
).
and_return
(
true
)
end
context
"when authentication fails because of invalid Kerberos token"
do
before
do
allow_any_instance_of
(
Grack
::
Auth
::
Request
).
to
receive
(
:spnego_credentials!
).
and_return
(
nil
)
allow_any_instance_of
(
Projects
::
GitHttpController
).
to
receive
(
:spnego_credentials!
).
and_return
(
nil
)
end
it
"responds with status 401"
do
...
...
@@ -143,7 +143,7 @@ describe 'Git HTTP requests', lib: true do
context
"when authentication fails because of unknown Kerberos identity"
do
before
do
allow_any_instance_of
(
Grack
::
Auth
::
Request
).
to
receive
(
:spnego_credentials!
).
and_return
(
"mylogin@FOO.COM"
)
allow_any_instance_of
(
Projects
::
GitHttpController
).
to
receive
(
:spnego_credentials!
).
and_return
(
"mylogin@FOO.COM"
)
end
it
"responds with status 401"
do
...
...
@@ -156,8 +156,8 @@ describe 'Git HTTP requests', lib: true do
context
"when authentication succeeds"
do
before
do
allow_any_instance_of
(
Grack
::
Auth
::
Request
).
to
receive
(
:spnego_credentials!
).
and_return
(
"mylogin@FOO.COM"
)
user
.
identities
.
build
(
provider:
"kerberos"
,
extern_uid
:"mylogin@FOO.COM"
).
save
allow_any_instance_of
(
Projects
::
GitHttpController
).
to
receive
(
:spnego_credentials!
).
and_return
(
"mylogin@FOO.COM"
)
user
.
identities
.
create!
(
provider:
"kerberos"
,
extern_uid
:"mylogin@FOO.COM"
)
end
context
"when the user has access to the project"
do
...
...
@@ -187,7 +187,7 @@ describe 'Git HTTP requests', lib: true do
end
it
"complies with RFC4559"
do
allow_any_instance_of
(
Grack
::
Auth
::
Request
).
to
receive
(
:spnego_response_token
).
and_return
(
"opaque_response_token"
)
allow_any_instance_of
(
Projects
::
GitHttpController
).
to
receive
(
:spnego_response_token
).
and_return
(
"opaque_response_token"
)
download
(
path
,
env
)
do
|
response
|
expect
(
response
.
headers
[
'WWW-Authenticate'
].
split
(
"
\n
"
)).
to
include
(
"Negotiate
#{
::
Base64
.
strict_encode64
(
'opaque_response_token'
)
}
"
)
end
...
...
@@ -202,7 +202,7 @@ describe 'Git HTTP requests', lib: true do
end
it
"complies with RFC4559"
do
allow_any_instance_of
(
Grack
::
Auth
::
Request
).
to
receive
(
:spnego_response_token
).
and_return
(
"opaque_response_token"
)
allow_any_instance_of
(
Projects
::
GitHttpController
).
to
receive
(
:spnego_response_token
).
and_return
(
"opaque_response_token"
)
download
(
path
,
env
)
do
|
response
|
expect
(
response
.
headers
[
'WWW-Authenticate'
].
split
(
"
\n
"
)).
to
include
(
"Negotiate
#{
::
Base64
.
strict_encode64
(
'opaque_response_token'
)
}
"
)
end
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment