diff options
author | Ævar Arnfjörð Bjarmason <avarab@gmail.com> | 2018-06-14 15:54:38 +0200 |
---|---|---|
committer | Ævar Arnfjörð Bjarmason <avarab@gmail.com> | 2018-07-26 12:35:55 +0200 |
commit | 2e8b67027067761034f36dadb3c2208ce66d2552 (patch) | |
tree | 1f35c43611dcd0041d3f30fe7a86eac507912b75 /spec | |
parent | dc67cf1a62529bf7aecc8e350994ac40d5f4a068 (diff) | |
download | gitlab-shell-2e8b67027067761034f36dadb3c2208ce66d2552.tar.gz |
Add support for SSH certificate authentication
This along with the code submitted to gitlab-ce in the
gitlab-org/gitlab-ce! MR implements SSH certificate
authentication. See the docs added to gitlab-ce for why and how to
enable this. This, along with that MR, closes
gitlab-org/gitlab-ce#3457
Implementation notes:
- Because it's easy to do, and because an earlier nascent version of
this would pass user-ID to gitlab-shell, that's now supported, even
though the SSH certificate authentication uses username-USERNAME.
- The astute reader will notice that not all the API calls in
gitlab-ce's lib/api/internal.rb support a "username" argument, some
only support "user_id".
There's a few reasons for this:
a) For this to be efficient, I am bending over backwards to avoid
extra API calls when using SSH certificates.
Therefore the /allowed API call will now return a "user id" to
us if we're allowed to proceed further. This is then fed to
existing APIs that would only be called after a successful
call to /allowed.
b) Not all of the git-shell codepaths go through
/internal/allowed, or ever deal with a repository, e.g. the
argument-less "Welcome to GitLab", and
/internal/2fa_recovery_codes. These need to use
/internal/discover to figure out details about the user, so
support looking that up by username.
c) Once we have the "user id", the GL_ID gets passed down to
e.g. user-authored hooks. I don't want to have those all break
by having to handle a third GL_ID mode of "username" in
addition to the current "key id" and "user id".
Diffstat (limited to 'spec')
-rw-r--r-- | spec/gitlab_access_spec.rb | 2 | ||||
-rw-r--r-- | spec/gitlab_keys_spec.rb | 32 | ||||
-rw-r--r-- | spec/gitlab_shell_spec.rb | 38 |
3 files changed, 53 insertions, 19 deletions
diff --git a/spec/gitlab_access_spec.rb b/spec/gitlab_access_spec.rb index 8882e01..d082176 100644 --- a/spec/gitlab_access_spec.rb +++ b/spec/gitlab_access_spec.rb @@ -10,6 +10,7 @@ describe GitlabAccess do api.stub(check_access: GitAccessStatus.new(true, 'ok', gl_repository: 'project-1', + gl_id: 'user-123', gl_username: 'testuser', repository_path: '/home/git/repositories', gitaly: nil)) @@ -47,6 +48,7 @@ describe GitlabAccess do false, 'denied', gl_repository: nil, + gl_id: nil, gl_username: nil, repository_path: nil, gitaly: nil diff --git a/spec/gitlab_keys_spec.rb b/spec/gitlab_keys_spec.rb index d6583b8..7011ca0 100644 --- a/spec/gitlab_keys_spec.rb +++ b/spec/gitlab_keys_spec.rb @@ -8,13 +8,25 @@ describe GitlabKeys do end describe '.command' do + it 'the internal "command" utility function' do + command = "#{ROOT_PATH}/bin/gitlab-shell does-not-validate" + expect(described_class.command('does-not-validate')).to eq(command) + end + + it 'does not raise a KeyError on invalid input' do + command = "#{ROOT_PATH}/bin/gitlab-shell foo\nbar\nbaz\n" + expect(described_class.command("foo\nbar\nbaz\n")).to eq(command) + end + end + + describe '.command_key' do it 'returns the "command" part of the key line' do command = "#{ROOT_PATH}/bin/gitlab-shell key-123" - expect(described_class.command('key-123')).to eq(command) + expect(described_class.command_key('key-123')).to eq(command) end it 'raises KeyError on invalid input' do - expect { described_class.command("\nssh-rsa AAA") }.to raise_error(described_class::KeyError) + expect { described_class.command_key("\nssh-rsa AAA") }.to raise_error(described_class::KeyError) end end @@ -34,6 +46,22 @@ describe GitlabKeys do end end + describe '.principal_line' do + let(:line) { %(command="#{ROOT_PATH}/bin/gitlab-shell username-someuser",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty sshUsers) } + + it 'returns the key line' do + expect(described_class.principal_line('username-someuser', 'sshUsers')).to eq(line) + end + + it 'silently removes a trailing newline' do + expect(described_class.principal_line('username-someuser', "sshUsers\n")).to eq(line) + end + + it 'raises KeyError on invalid input' do + expect { described_class.principal_line('username-someuser', "sshUsers\nloginUsers") }.to raise_error(described_class::KeyError) + end + end + describe :initialize do let(:gitlab_keys) { build_gitlab_keys('add-key', 'key-741', 'ssh-rsa AAAAB3NzaDAxx2E') } diff --git a/spec/gitlab_shell_spec.rb b/spec/gitlab_shell_spec.rb index af84b29..3f7c962 100644 --- a/spec/gitlab_shell_spec.rb +++ b/spec/gitlab_shell_spec.rb @@ -13,8 +13,8 @@ describe GitlabShell do end subject do - ARGV[0] = key_id - GitlabShell.new(key_id).tap do |shell| + ARGV[0] = gl_id + GitlabShell.new(gl_id).tap do |shell| shell.stub(exec_cmd: :exec_called) shell.stub(api: api) end @@ -24,6 +24,7 @@ describe GitlabShell do true, 'ok', gl_repository: gl_repository, + gl_id: gl_id, gl_username: gl_username, repository_path: repo_path, gitaly: { 'repository' => { 'relative_path' => repo_name, 'storage_name' => 'default'} , 'address' => 'unix:gitaly.socket' } @@ -37,6 +38,7 @@ describe GitlabShell do true, 'ok', gl_repository: gl_repository, + gl_id: gl_id, gl_username: gl_username, repository_path: repo_path, gitaly: nil)) @@ -47,13 +49,14 @@ describe GitlabShell do end end - let(:key_id) { "key-#{rand(100) + 100}" } + let(:gl_id) { "key-#{rand(100) + 100}" } let(:ssh_cmd) { nil } let(:tmp_repos_path) { File.join(ROOT_PATH, 'tmp', 'repositories') } let(:repo_name) { 'gitlab-ci.git' } let(:repo_path) { File.join(tmp_repos_path, repo_name) } let(:gl_repository) { 'project-1' } + let(:gl_id) { 'user-1' } let(:gl_username) { 'testuser' } before do @@ -63,7 +66,7 @@ describe GitlabShell do describe :initialize do let(:ssh_cmd) { 'git-receive-pack' } - its(:key_id) { should == key_id } + its(:gl_id) { should == gl_id } end describe :parse_cmd do @@ -146,7 +149,7 @@ describe GitlabShell do end describe :exec do - let(:gitaly_message) { JSON.dump({ 'repository' => { 'relative_path' => repo_name, 'storage_name' => 'default' }, 'gl_repository' => gl_repository, 'gl_id' => key_id, 'gl_username' => gl_username}) } + let(:gitaly_message) { JSON.dump({ 'repository' => { 'relative_path' => repo_name, 'storage_name' => 'default' }, 'gl_repository' => gl_repository, 'gl_id' => gl_id, 'gl_username' => gl_username}) } shared_examples_for 'upload-pack' do |command| let(:ssh_cmd) { "#{command} gitlab-ci.git" } @@ -162,7 +165,7 @@ describe GitlabShell do it "should log the command execution" do message = "executing git command" - user_string = "user with key #{key_id}" + user_string = "user with id #{gl_id}" $logger.should_receive(:info).with(message, command: "git-upload-pack #{repo_path}", user: user_string) end @@ -197,7 +200,7 @@ describe GitlabShell do it "should log the command execution" do message = "executing git command" - user_string = "user with key #{key_id}" + user_string = "user with id #{gl_id}" $logger.should_receive(:info).with(message, command: "gitaly-upload-pack unix:gitaly.socket #{gitaly_message}", user: user_string) end @@ -221,7 +224,7 @@ describe GitlabShell do it "should log the command execution" do message = "executing git command" - user_string = "user with key #{key_id}" + user_string = "user with id #{gl_id}" $logger.should_receive(:info).with(message, command: "git-receive-pack #{repo_path}", user: user_string) end end @@ -243,7 +246,7 @@ describe GitlabShell do it "should log the command execution" do message = "executing git command" - user_string = "user with key #{key_id}" + user_string = "user with id #{gl_id}" $logger.should_receive(:info).with(message, command: "gitaly-receive-pack unix:gitaly.socket #{gitaly_message}", user: user_string) end @@ -270,7 +273,7 @@ describe GitlabShell do it "should log the command execution" do message = "executing git command" - user_string = "user with key #{key_id}" + user_string = "user with id #{gl_id}" $logger.should_receive(:info).with(message, command: exec_cmd_log_params.join(' '), user: user_string) end @@ -322,7 +325,7 @@ describe GitlabShell do it "should log the attempt" do message = 'Denied disallowed command' - user_string = "user with key #{key_id}" + user_string = "user with id #{gl_id}" $logger.should_receive(:warn).with(message, command: 'arbitrary command', user: user_string) end end @@ -331,7 +334,7 @@ describe GitlabShell do after { subject.exec(nil) } it "should call api.discover" do - api.should_receive(:discover).with(key_id) + api.should_receive(:discover).with(gl_id) end end @@ -398,7 +401,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', nil, 'gitlab-ci.git', key_id, '_any', 'ssh') + api.should_receive(:check_access).with('git-upload-pack', nil, 'gitlab-ci.git', gl_id, '_any', 'ssh') end it "should disallow access and log the attempt if check_access returns false status" do @@ -406,11 +409,12 @@ describe GitlabShell do false, 'denied', gl_repository: nil, + gl_id: nil, gl_username: nil, repository_path: nil, gitaly: nil)) message = 'Access denied' - user_string = "user with key #{key_id}" + user_string = "user with id #{gl_id}" $logger.should_receive(:warn).with(message, command: 'git-upload-pack gitlab-ci.git', user: user_string) end end @@ -436,14 +440,14 @@ describe GitlabShell do end describe :exec_cmd do - let(:shell) { GitlabShell.new(key_id) } + let(:shell) { GitlabShell.new(gl_id) } let(:env) do { 'HOME' => ENV['HOME'], 'PATH' => ENV['PATH'], 'LD_LIBRARY_PATH' => ENV['LD_LIBRARY_PATH'], 'LANG' => ENV['LANG'], - 'GL_ID' => key_id, + 'GL_ID' => gl_id, 'GL_PROTOCOL' => 'ssh', 'GL_REPOSITORY' => gl_repository, 'GL_USERNAME' => 'testuser' @@ -543,7 +547,7 @@ describe GitlabShell do end describe :api do - let(:shell) { GitlabShell.new(key_id) } + let(:shell) { GitlabShell.new(gl_id) } subject { shell.send :api } it { should be_a(GitlabNet) } |