Commit dd6eed16 authored by Douwe Maan's avatar Douwe Maan

Merge branch 'add-allowed-protocols-support' into 'master'

Allow GitLab Shell to check for allowed access based on the used Git protocol.

Needed for gitlab-org/gitlab-ce!4696 and gitlab-org/gitlab-ce#18601

See merge request !62
parents 5afdd3f1 02b8071c
v3.2.0
- Allow GitLab Shell to check for allowed access based on the used Git protocol
v3.1.0 v3.1.0
- Refactor repository paths handling to allow multiple git mount points - Refactor repository paths handling to allow multiple git mount points
......
...@@ -5,12 +5,13 @@ ...@@ -5,12 +5,13 @@
refs = $stdin.read refs = $stdin.read
key_id = ENV['GL_ID'] key_id = ENV['GL_ID']
protocol = ENV['GL_PROTOCOL']
repo_path = Dir.pwd repo_path = Dir.pwd
require_relative '../lib/gitlab_custom_hook' require_relative '../lib/gitlab_custom_hook'
require_relative '../lib/gitlab_access' require_relative '../lib/gitlab_access'
if GitlabAccess.new(repo_path, key_id, refs).exec && if GitlabAccess.new(repo_path, key_id, refs, protocol).exec &&
GitlabCustomHook.new.pre_receive(refs, repo_path) GitlabCustomHook.new.pre_receive(refs, repo_path)
exit 0 exit 0
else else
......
...@@ -9,18 +9,19 @@ class GitlabAccess ...@@ -9,18 +9,19 @@ class GitlabAccess
include NamesHelper include NamesHelper
attr_reader :config, :repo_path, :repo_name, :changes attr_reader :config, :repo_path, :repo_name, :changes, :protocol
def initialize(repo_path, actor, changes) def initialize(repo_path, actor, changes, protocol)
@config = GitlabConfig.new @config = GitlabConfig.new
@repo_path = repo_path.strip @repo_path = repo_path.strip
@actor = actor @actor = actor
@repo_name = extract_repo_name(@repo_path.dup) @repo_name = extract_repo_name(@repo_path.dup)
@changes = changes.lines @changes = changes.lines
@protocol = protocol
end end
def exec def exec
status = api.check_access('git-receive-pack', @repo_name, @actor, @changes) status = api.check_access('git-receive-pack', @repo_name, @actor, @changes, @protocol)
raise AccessDeniedError, status.message unless status.allowed? raise AccessDeniedError, status.message unless status.allowed?
......
...@@ -14,7 +14,7 @@ class GitlabNet ...@@ -14,7 +14,7 @@ class GitlabNet
CHECK_TIMEOUT = 5 CHECK_TIMEOUT = 5
READ_TIMEOUT = 300 READ_TIMEOUT = 300
def check_access(cmd, repo, actor, changes) def check_access(cmd, repo, actor, changes, protocol)
project_name = repo.gsub("'", "") project_name = repo.gsub("'", "")
project_name = project_name.gsub(/\.git\Z/, "") project_name = project_name.gsub(/\.git\Z/, "")
project_name = project_name.gsub(/\A\//, "") project_name = project_name.gsub(/\A\//, "")
...@@ -24,6 +24,7 @@ class GitlabNet ...@@ -24,6 +24,7 @@ class GitlabNet
action: cmd, action: cmd,
changes: changes, changes: changes,
project: project_name, project: project_name,
protocol: protocol
} }
if actor =~ /\Akey\-\d+\Z/ if actor =~ /\Akey\-\d+\Z/
......
...@@ -8,6 +8,7 @@ class GitlabShell ...@@ -8,6 +8,7 @@ class GitlabShell
class InvalidRepositoryPathError < StandardError; end class InvalidRepositoryPathError < StandardError; end
GIT_COMMANDS = %w(git-upload-pack git-receive-pack git-upload-archive git-annex-shell git-lfs-authenticate).freeze GIT_COMMANDS = %w(git-upload-pack git-receive-pack git-upload-archive git-annex-shell git-lfs-authenticate).freeze
GL_PROTOCOL = 'ssh'.freeze
attr_accessor :key_id, :repo_name, :git_cmd attr_accessor :key_id, :repo_name, :git_cmd
attr_reader :repo_path attr_reader :repo_path
...@@ -85,7 +86,7 @@ class GitlabShell ...@@ -85,7 +86,7 @@ class GitlabShell
end end
def verify_access def verify_access
status = api.check_access(@git_access, @repo_name, @key_id, '_any') status = api.check_access(@git_access, @repo_name, @key_id, '_any', GL_PROTOCOL)
raise AccessDeniedError, status.message unless status.allowed? raise AccessDeniedError, status.message unless status.allowed?
...@@ -131,7 +132,8 @@ class GitlabShell ...@@ -131,7 +132,8 @@ class GitlabShell
'PATH' => ENV['PATH'], 'PATH' => ENV['PATH'],
'LD_LIBRARY_PATH' => ENV['LD_LIBRARY_PATH'], 'LD_LIBRARY_PATH' => ENV['LD_LIBRARY_PATH'],
'LANG' => ENV['LANG'], 'LANG' => ENV['LANG'],
'GL_ID' => @key_id 'GL_ID' => @key_id,
'GL_PROTOCOL' => GL_PROTOCOL
} }
if @config.git_annex_enabled? if @config.git_annex_enabled?
......
...@@ -11,7 +11,7 @@ describe GitlabAccess do ...@@ -11,7 +11,7 @@ describe GitlabAccess do
end end
end end
subject do subject do
GitlabAccess.new(repo_path, 'key-123', 'wow').tap do |access| GitlabAccess.new(repo_path, 'key-123', 'wow', 'ssh').tap do |access|
access.stub(exec_cmd: :exec_called) access.stub(exec_cmd: :exec_called)
access.stub(api: api) access.stub(api: api)
end end
...@@ -25,6 +25,7 @@ describe GitlabAccess do ...@@ -25,6 +25,7 @@ describe GitlabAccess do
it { subject.repo_name.should == repo_name } it { subject.repo_name.should == repo_name }
it { subject.repo_path.should == repo_path } it { subject.repo_path.should == repo_path }
it { subject.changes.should == ['wow'] } it { subject.changes.should == ['wow'] }
it { subject.protocol.should == 'ssh' }
end end
describe "#exec" do describe "#exec" do
......
...@@ -110,7 +110,7 @@ describe GitlabNet, vcr: true do ...@@ -110,7 +110,7 @@ describe GitlabNet, vcr: true do
context 'ssh key with access to project' do context 'ssh key with access to project' do
it 'should allow pull access for dev.gitlab.org' do it 'should allow pull access for dev.gitlab.org' do
VCR.use_cassette("allowed-pull") do VCR.use_cassette("allowed-pull") do
access = gitlab_net.check_access('git-receive-pack', 'gitlab/gitlabhq.git', 'key-126', changes) access = gitlab_net.check_access('git-receive-pack', 'gitlab/gitlabhq.git', 'key-126', changes, 'ssh')
access.allowed?.should be_true access.allowed?.should be_true
end end
end end
...@@ -118,36 +118,72 @@ describe GitlabNet, vcr: true do ...@@ -118,36 +118,72 @@ describe GitlabNet, vcr: true do
it 'adds the secret_token to the request' do it 'adds the secret_token to the request' do
VCR.use_cassette("allowed-pull") do VCR.use_cassette("allowed-pull") do
Net::HTTP::Post.any_instance.should_receive(:set_form_data).with(hash_including(secret_token: 'a123')) Net::HTTP::Post.any_instance.should_receive(:set_form_data).with(hash_including(secret_token: 'a123'))
gitlab_net.check_access('git-receive-pack', 'gitlab/gitlabhq.git', 'key-126', changes) gitlab_net.check_access('git-receive-pack', 'gitlab/gitlabhq.git', 'key-126', changes, 'ssh')
end end
end end
it 'should allow push access for dev.gitlab.org' do it 'should allow push access for dev.gitlab.org' do
VCR.use_cassette("allowed-push") do VCR.use_cassette("allowed-push") do
access = gitlab_net.check_access('git-upload-pack', 'gitlab/gitlabhq.git', 'key-126', changes) access = gitlab_net.check_access('git-upload-pack', 'gitlab/gitlabhq.git', 'key-126', changes, 'ssh')
access.allowed?.should be_true access.allowed?.should be_true
end end
end end
end end
context 'ssh access has been disabled' do
it 'should deny pull access for dev.gitlab.org' do
VCR.use_cassette('ssh-access-disabled') do
access = gitlab_net.check_access('git-receive-pack', 'gitlab/gitlabhq.git', 'key-2', changes, 'ssh')
access.allowed?.should be_false
access.message.should eq 'Git access over SSH is not allowed'
end
end
it 'should deny pull access for dev.gitlab.org' do
VCR.use_cassette('ssh-access-disabled') do
access = gitlab_net.check_access('git-receive-pack', 'gitlab/gitlabhq.git', 'key-2', changes, 'ssh')
access.allowed?.should be_false
access.message.should eq 'Git access over SSH is not allowed'
end
end
end
context 'http access has been disabled' do
it 'should deny pull access for dev.gitlab.org' do
VCR.use_cassette('http-access-disabled') do
access = gitlab_net.check_access('git-receive-pack', 'gitlab/gitlabhq.git', 'key-2', changes, 'http')
access.allowed?.should be_false
access.message.should eq 'Git access over HTTP is not allowed'
end
end
it 'should deny pull access for dev.gitlab.org' do
VCR.use_cassette('http-access-disabled') do
access = gitlab_net.check_access('git-receive-pack', 'gitlab/gitlabhq.git', 'key-2', changes, 'http')
access.allowed?.should be_false
access.message.should eq 'Git access over HTTP is not allowed'
end
end
end
context 'ssh key without access to project' do context 'ssh key without access to project' do
it 'should deny pull access for dev.gitlab.org' do it 'should deny pull access for dev.gitlab.org' do
VCR.use_cassette("denied-pull") do VCR.use_cassette("denied-pull") do
access = gitlab_net.check_access('git-receive-pack', 'gitlab/gitlabhq.git', 'key-2', changes) access = gitlab_net.check_access('git-receive-pack', 'gitlab/gitlabhq.git', 'key-2', changes, 'ssh')
access.allowed?.should be_false access.allowed?.should be_false
end end
end end
it 'should deny push access for dev.gitlab.org' do it 'should deny push access for dev.gitlab.org' do
VCR.use_cassette("denied-push") do VCR.use_cassette("denied-push") do
access = gitlab_net.check_access('git-upload-pack', 'gitlab/gitlabhq.git', 'key-2', changes) access = gitlab_net.check_access('git-upload-pack', 'gitlab/gitlabhq.git', 'key-2', changes, 'ssh')
access.allowed?.should be_false access.allowed?.should be_false
end end
end end
it 'should deny push access for dev.gitlab.org (with user)' do it 'should deny push access for dev.gitlab.org (with user)' do
VCR.use_cassette("denied-push-with-user") do VCR.use_cassette("denied-push-with-user") do
access = gitlab_net.check_access('git-upload-pack', 'gitlab/gitlabhq.git', 'user-1', changes) access = gitlab_net.check_access('git-upload-pack', 'gitlab/gitlabhq.git', 'user-1', changes, 'ssh')
access.allowed?.should be_false access.allowed?.should be_false
end end
end end
...@@ -156,7 +192,7 @@ describe GitlabNet, vcr: true do ...@@ -156,7 +192,7 @@ describe GitlabNet, vcr: true do
it "raises an exception if the connection fails" do it "raises an exception if the connection fails" do
Net::HTTP.any_instance.stub(:request).and_raise(StandardError) Net::HTTP.any_instance.stub(:request).and_raise(StandardError)
expect { expect {
gitlab_net.check_access('git-upload-pack', 'gitlab/gitlabhq.git', 'user-1', changes) gitlab_net.check_access('git-upload-pack', 'gitlab/gitlabhq.git', 'user-1', changes, 'ssh')
}.to raise_error(GitlabNet::ApiUnreachableError) }.to raise_error(GitlabNet::ApiUnreachableError)
end end
end end
......
...@@ -242,7 +242,7 @@ describe GitlabShell do ...@@ -242,7 +242,7 @@ describe GitlabShell do
after { subject.exec(ssh_cmd) } after { subject.exec(ssh_cmd) }
it "should call api.check_access" do it "should call api.check_access" do
api.should_receive(:check_access).with('git-upload-pack', 'gitlab-ci.git', key_id, '_any') api.should_receive(:check_access).with('git-upload-pack', 'gitlab-ci.git', key_id, '_any', 'ssh')
end end
it "should disallow access and log the attempt if check_access returns false status" do it "should disallow access and log the attempt if check_access returns false status" do
......
---
http_interactions:
- request:
method: post
uri: https://dev.gitlab.org/api/v3/internal/allowed
body:
encoding: US-ASCII
string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&project=gitlab%2Fgitlabhq&protocol=http&key_id=2&secret_token=a123
headers:
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Content-Type:
- application/x-www-form-urlencoded
response:
status:
code: 200
message: OK
headers:
Cache-Control:
- no-cache
Content-Length:
- '30'
Content-Type:
- application/json
Date:
- Wed, 22 Jun 2016 01:03:41 GMT
Status:
- 200 OK
Vary:
- Origin
X-Request-Id:
- 55b7af2c-3559-41d2-b301-9b86ad1d8fac
X-Runtime:
- '2.280895'
body:
encoding: UTF-8
string: '{"status": false, "message":"Git access over HTTP is not allowed"}'
http_version:
recorded_at: Wed, 22 Jun 2016 01:03:41 GMT
recorded_with: VCR 2.4.0
\ No newline at end of file
---
http_interactions:
- request:
method: post
uri: https://dev.gitlab.org/api/v3/internal/allowed
body:
encoding: US-ASCII
string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&project=gitlab%2Fgitlabhq&protocol=ssh&key_id=2&secret_token=a123
headers:
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Content-Type:
- application/x-www-form-urlencoded
response:
status:
code: 200
message: OK
headers:
Cache-Control:
- no-cache
Content-Length:
- '30'
Content-Type:
- application/json
Date:
- Wed, 22 Jun 2016 01:01:41 GMT
Status:
- 200 OK
Vary:
- Origin
X-Request-Id:
- 55b7af2c-3559-41d2-b301-9b86ad1d8fac
X-Runtime:
- '2.280895'
body:
encoding: UTF-8
string: '{"status": false, "message":"Git access over SSH is not allowed"}'
http_version:
recorded_at: Wed, 22 Jun 2016 01:01:41 GMT
recorded_with: VCR 2.4.0
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment