From 44e7804ddb408d85f091c7a5cd36e0fdbec63d13 Mon Sep 17 00:00:00 2001 From: Patricio Cano <suprnova32@gmail.com> Date: Mon, 20 Jun 2016 21:13:53 -0500 Subject: [PATCH 1/7] Allow GitLab Shell to check for allowed access based on the used Git protocol. --- hooks/pre-receive | 5 ++++- lib/gitlab_access.rb | 7 ++++--- lib/gitlab_net.rb | 3 ++- lib/gitlab_shell.rb | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/hooks/pre-receive b/hooks/pre-receive index 1f8a9d5..6ed9a2c 100755 --- a/hooks/pre-receive +++ b/hooks/pre-receive @@ -5,12 +5,15 @@ refs = $stdin.read key_id = ENV['GL_ID'] +protocol = ENV['PROTOCOL'] repo_path = Dir.pwd require_relative '../lib/gitlab_custom_hook' require_relative '../lib/gitlab_access' -if GitlabAccess.new(repo_path, key_id, refs).exec && +protocol ||= 'http' + +if GitlabAccess.new(repo_path, key_id, refs, protocol).exec && GitlabCustomHook.new.pre_receive(refs, repo_path) exit 0 else diff --git a/lib/gitlab_access.rb b/lib/gitlab_access.rb index 10afeef..bab2c4c 100644 --- a/lib/gitlab_access.rb +++ b/lib/gitlab_access.rb @@ -9,18 +9,19 @@ class GitlabAccess 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 = nil) @config = GitlabConfig.new @repo_path = repo_path.strip @actor = actor @repo_name = extract_repo_name(@repo_path.dup) @changes = changes.lines + @protocol = protocol end 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? diff --git a/lib/gitlab_net.rb b/lib/gitlab_net.rb index dd9a4b0..24e97be 100644 --- a/lib/gitlab_net.rb +++ b/lib/gitlab_net.rb @@ -14,7 +14,7 @@ class GitlabNet CHECK_TIMEOUT = 5 READ_TIMEOUT = 300 - def check_access(cmd, repo, actor, changes) + def check_access(cmd, repo, actor, changes, protocol = nil) project_name = repo.gsub("'", "") project_name = project_name.gsub(/\.git\Z/, "") project_name = project_name.gsub(/\A\//, "") @@ -24,6 +24,7 @@ class GitlabNet action: cmd, changes: changes, project: project_name, + protocol: protocol } if actor =~ /\Akey\-\d+\Z/ diff --git a/lib/gitlab_shell.rb b/lib/gitlab_shell.rb index c5d5c02..2bb8a4d 100644 --- a/lib/gitlab_shell.rb +++ b/lib/gitlab_shell.rb @@ -85,7 +85,7 @@ class GitlabShell end 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', 'ssh') raise AccessDeniedError, status.message unless status.allowed? -- GitLab From 1c18095c9f2b80d380122c8c71f98a07387c1e66 Mon Sep 17 00:00:00 2001 From: Patricio Cano <suprnova32@gmail.com> Date: Mon, 20 Jun 2016 21:14:44 -0500 Subject: [PATCH 2/7] Added CHANGELOG item --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 58a9e39..a806079 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ v3.1.0 - Refactor repository paths handling to allow multiple git mount points + - Allow GitLab Shell to check for allowed access based on the used Git protocol v3.0.1 - Update PostReceive worker to provide enqueued_at time. -- GitLab From fd41b8a433164f36f7cf70b358115c5f56f06670 Mon Sep 17 00:00:00 2001 From: Patricio Cano <suprnova32@gmail.com> Date: Mon, 20 Jun 2016 21:57:37 -0500 Subject: [PATCH 3/7] Simplify protocol assign, and populate ENV['PROTOCOL'] variable when calling hooks via SSH --- hooks/pre-receive | 4 +--- lib/gitlab_shell.rb | 3 ++- spec/gitlab_shell_spec.rb | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/hooks/pre-receive b/hooks/pre-receive index 6ed9a2c..a7eeb30 100755 --- a/hooks/pre-receive +++ b/hooks/pre-receive @@ -5,14 +5,12 @@ refs = $stdin.read key_id = ENV['GL_ID'] -protocol = ENV['PROTOCOL'] +protocol = ENV['PROTOCOL'] || 'http' repo_path = Dir.pwd require_relative '../lib/gitlab_custom_hook' require_relative '../lib/gitlab_access' -protocol ||= 'http' - if GitlabAccess.new(repo_path, key_id, refs, protocol).exec && GitlabCustomHook.new.pre_receive(refs, repo_path) exit 0 diff --git a/lib/gitlab_shell.rb b/lib/gitlab_shell.rb index 2bb8a4d..3dc10b6 100644 --- a/lib/gitlab_shell.rb +++ b/lib/gitlab_shell.rb @@ -131,7 +131,8 @@ class GitlabShell 'PATH' => ENV['PATH'], 'LD_LIBRARY_PATH' => ENV['LD_LIBRARY_PATH'], 'LANG' => ENV['LANG'], - 'GL_ID' => @key_id + 'GL_ID' => @key_id, + 'PROTOCOL' => 'ssh' } if @config.git_annex_enabled? diff --git a/spec/gitlab_shell_spec.rb b/spec/gitlab_shell_spec.rb index 79fa49b..0b0a817 100644 --- a/spec/gitlab_shell_spec.rb +++ b/spec/gitlab_shell_spec.rb @@ -242,7 +242,7 @@ describe GitlabShell do after { subject.exec(ssh_cmd) } 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 it "should disallow access and log the attempt if check_access returns false status" do -- GitLab From 4cd4cf673844f8f381609f9c3264c33f2843935d Mon Sep 17 00:00:00 2001 From: Patricio Cano <suprnova32@gmail.com> Date: Tue, 21 Jun 2016 21:10:09 -0500 Subject: [PATCH 4/7] Added better tests for the protocol check --- spec/gitlab_access_spec.rb | 3 +- spec/gitlab_net_spec.rb | 36 +++++++++++++++++ spec/vcr_cassettes/http-access-disabled.yml | 44 +++++++++++++++++++++ spec/vcr_cassettes/ssh-access-disabled.yml | 44 +++++++++++++++++++++ 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 spec/vcr_cassettes/http-access-disabled.yml create mode 100644 spec/vcr_cassettes/ssh-access-disabled.yml diff --git a/spec/gitlab_access_spec.rb b/spec/gitlab_access_spec.rb index 98848ae..2781aa9 100644 --- a/spec/gitlab_access_spec.rb +++ b/spec/gitlab_access_spec.rb @@ -11,7 +11,7 @@ describe GitlabAccess do end end 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(api: api) end @@ -25,6 +25,7 @@ describe GitlabAccess do it { subject.repo_name.should == repo_name } it { subject.repo_path.should == repo_path } it { subject.changes.should == ['wow'] } + it { subject.protocol.should == 'ssh' } end describe "#exec" do diff --git a/spec/gitlab_net_spec.rb b/spec/gitlab_net_spec.rb index 0643868..b236247 100644 --- a/spec/gitlab_net_spec.rb +++ b/spec/gitlab_net_spec.rb @@ -130,6 +130,42 @@ describe GitlabNet, vcr: true do 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 it 'should deny pull access for dev.gitlab.org' do VCR.use_cassette("denied-pull") do diff --git a/spec/vcr_cassettes/http-access-disabled.yml b/spec/vcr_cassettes/http-access-disabled.yml new file mode 100644 index 0000000..36e27a9 --- /dev/null +++ b/spec/vcr_cassettes/http-access-disabled.yml @@ -0,0 +1,44 @@ +--- +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 diff --git a/spec/vcr_cassettes/ssh-access-disabled.yml b/spec/vcr_cassettes/ssh-access-disabled.yml new file mode 100644 index 0000000..656d0aa --- /dev/null +++ b/spec/vcr_cassettes/ssh-access-disabled.yml @@ -0,0 +1,44 @@ +--- +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 -- GitLab From 6aa601866c66a62b4ab3db3fa55ab1b5e84e444d Mon Sep 17 00:00:00 2001 From: Patricio Cano <suprnova32@gmail.com> Date: Wed, 22 Jun 2016 12:54:27 -0500 Subject: [PATCH 5/7] Rename ENV['PROTOCOL'] to ENV['GL_PROTOCOL'] and make it mandatory with no fallback value --- hooks/pre-receive | 2 +- lib/gitlab_access.rb | 2 +- lib/gitlab_net.rb | 2 +- lib/gitlab_shell.rb | 2 +- spec/gitlab_net_spec.rb | 14 +++++++------- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hooks/pre-receive b/hooks/pre-receive index a7eeb30..a4b2e32 100755 --- a/hooks/pre-receive +++ b/hooks/pre-receive @@ -5,7 +5,7 @@ refs = $stdin.read key_id = ENV['GL_ID'] -protocol = ENV['PROTOCOL'] || 'http' +protocol = ENV['GL_PROTOCOL'] repo_path = Dir.pwd require_relative '../lib/gitlab_custom_hook' diff --git a/lib/gitlab_access.rb b/lib/gitlab_access.rb index bab2c4c..04806b2 100644 --- a/lib/gitlab_access.rb +++ b/lib/gitlab_access.rb @@ -11,7 +11,7 @@ class GitlabAccess attr_reader :config, :repo_path, :repo_name, :changes, :protocol - def initialize(repo_path, actor, changes, protocol = nil) + def initialize(repo_path, actor, changes, protocol) @config = GitlabConfig.new @repo_path = repo_path.strip @actor = actor diff --git a/lib/gitlab_net.rb b/lib/gitlab_net.rb index 24e97be..e10a07a 100644 --- a/lib/gitlab_net.rb +++ b/lib/gitlab_net.rb @@ -14,7 +14,7 @@ class GitlabNet CHECK_TIMEOUT = 5 READ_TIMEOUT = 300 - def check_access(cmd, repo, actor, changes, protocol = nil) + def check_access(cmd, repo, actor, changes, protocol) project_name = repo.gsub("'", "") project_name = project_name.gsub(/\.git\Z/, "") project_name = project_name.gsub(/\A\//, "") diff --git a/lib/gitlab_shell.rb b/lib/gitlab_shell.rb index 3dc10b6..1e94369 100644 --- a/lib/gitlab_shell.rb +++ b/lib/gitlab_shell.rb @@ -132,7 +132,7 @@ class GitlabShell 'LD_LIBRARY_PATH' => ENV['LD_LIBRARY_PATH'], 'LANG' => ENV['LANG'], 'GL_ID' => @key_id, - 'PROTOCOL' => 'ssh' + 'GL_PROTOCOL' => 'ssh' } if @config.git_annex_enabled? diff --git a/spec/gitlab_net_spec.rb b/spec/gitlab_net_spec.rb index b236247..2bbf98b 100644 --- a/spec/gitlab_net_spec.rb +++ b/spec/gitlab_net_spec.rb @@ -110,7 +110,7 @@ describe GitlabNet, vcr: true do context 'ssh key with access to project' do it 'should allow pull access for dev.gitlab.org' 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 end end @@ -118,13 +118,13 @@ describe GitlabNet, vcr: true do it 'adds the secret_token to the request' do VCR.use_cassette("allowed-pull") do 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 it 'should allow push access for dev.gitlab.org' 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 end end @@ -169,21 +169,21 @@ describe GitlabNet, vcr: true do context 'ssh key without access to project' do it 'should deny pull access for dev.gitlab.org' 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 end end it 'should deny push access for dev.gitlab.org' 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 end end it 'should deny push access for dev.gitlab.org (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 end end @@ -192,7 +192,7 @@ describe GitlabNet, vcr: true do it "raises an exception if the connection fails" do Net::HTTP.any_instance.stub(:request).and_raise(StandardError) 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) end end -- GitLab From 52c3150ed3340c59faf88fd191af0f92fc318fc5 Mon Sep 17 00:00:00 2001 From: Patricio Cano <suprnova32@gmail.com> Date: Thu, 23 Jun 2016 09:34:39 -0500 Subject: [PATCH 6/7] Make use of a constant for the used SSH protocol --- lib/gitlab_shell.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/gitlab_shell.rb b/lib/gitlab_shell.rb index 1e94369..d9812ce 100644 --- a/lib/gitlab_shell.rb +++ b/lib/gitlab_shell.rb @@ -8,6 +8,7 @@ class GitlabShell class InvalidRepositoryPathError < StandardError; end 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_reader :repo_path @@ -85,7 +86,7 @@ class GitlabShell end def verify_access - status = api.check_access(@git_access, @repo_name, @key_id, '_any', 'ssh') + status = api.check_access(@git_access, @repo_name, @key_id, '_any', GL_PROTOCOL) raise AccessDeniedError, status.message unless status.allowed? @@ -132,7 +133,7 @@ class GitlabShell 'LD_LIBRARY_PATH' => ENV['LD_LIBRARY_PATH'], 'LANG' => ENV['LANG'], 'GL_ID' => @key_id, - 'GL_PROTOCOL' => 'ssh' + 'GL_PROTOCOL' => GL_PROTOCOL } if @config.git_annex_enabled? -- GitLab From 02b8071c60f26318d660864450ca869cc8e6b7cf Mon Sep 17 00:00:00 2001 From: Patricio Cano <suprnova32@gmail.com> Date: Tue, 5 Jul 2016 18:16:53 -0500 Subject: [PATCH 7/7] Bump VERSION to 3.2.0 --- CHANGELOG | 4 +++- VERSION | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a806079..31cda72 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ +v3.2.0 + - Allow GitLab Shell to check for allowed access based on the used Git protocol + v3.1.0 - Refactor repository paths handling to allow multiple git mount points - - Allow GitLab Shell to check for allowed access based on the used Git protocol v3.0.1 - Update PostReceive worker to provide enqueued_at time. diff --git a/VERSION b/VERSION index fd2a018..944880f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.1.0 +3.2.0 -- GitLab