Commit fda23577 authored by Yorick Peterse's avatar Yorick Peterse

Merge branch 'use-ssh-key-internal-api' into 'master'

Use ssh key internal api to build the authorized-keys command on openssh 6.9

See merge request !42
parents 91e72255 38b8600f
v2.6.14
- Add support for ssh AuthorizedKeysCommand query by key
v2.6.13
- Add push-branches command
- Add delete-remote-branches command
......
#!/usr/bin/env ruby
#
# GitLab shell authorized_keys. Query GitLab API to get the authorized command for a given ssh key fingerprint
#
# Ex.
# /bin/authorized_keys BASE64-KEY
#
# Returns
# command="/bin/gitlab-shell key-#",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1yc2EAAAADAQA...
#
key = ARGV[0]
abort "# No key provided" if key.nil? || key.empty?
require_relative "../lib/gitlab_init"
require_relative "../lib/gitlab_net"
require_relative "../lib/gitlab_keys"
authorized_key = GitlabNet.new.authorized_key(key)
if authorized_key.nil?
puts "# No key was found for #{key}"
else
puts GitlabKey.new.key_line("key-#{authorized_key['id']}", authorized_key["key"])
end
......@@ -11,6 +11,7 @@ class GitlabKeys
@key_id = ARGV.shift
@key = ARGV.shift
@auth_file = GitlabConfig.new.auth_file
@gitlab_key = GitlabKey.new
end
def exec
......@@ -32,7 +33,7 @@ class GitlabKeys
def add_key
lock do
$logger.info "Adding key #{@key_id} => #{@key.inspect}"
auth_line = key_line(@key_id, @key)
auth_line = @gitlab_key.key_line(@key_id, @key)
open(auth_file, 'a') { |file| file.puts(auth_line) }
end
true
......@@ -59,7 +60,7 @@ class GitlabKeys
abort("#{$0}: invalid input #{input.inspect}") unless tokens.count == 2
key_id, public_key = tokens
$logger.info "Adding key #{key_id} => #{public_key.inspect}"
file.puts(key_line(key_id, public_key))
file.puts(@gitlab_key.key_line(key_id, public_key))
end
end
end
......@@ -70,20 +71,12 @@ class GitlabKeys
$stdin
end
def key_command(key_id)
"#{ROOT_PATH}/bin/gitlab-shell #{key_id}"
end
def key_line(key_id, public_key)
auth_line = "command=\"#{key_command(key_id)}\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty #{public_key}"
end
def rm_key
lock do
$logger.info "Removing key #{@key_id}"
open(auth_file, 'r+') do |f|
while line = f.gets do
next unless line.start_with?("command=\"#{key_command(@key_id)}\"")
next unless line.start_with?("command=\"#{@gitlab_key.command(@key_id)}\"")
f.seek(-line.length, IO::SEEK_CUR)
# Overwrite the line with #'s. Because the 'line' variable contains
# a terminating '\n', we write line.length - 1 '#' characters.
......@@ -115,3 +108,14 @@ class GitlabKeys
@lock_file ||= auth_file + '.lock'
end
end
class GitlabKey
def command(key_id)
"#{ROOT_PATH}/bin/gitlab-shell #{key_id}"
end
def key_line(key_id, public_key)
"command=\"#{command(key_id)}\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty #{public_key}"
end
end
......@@ -56,6 +56,13 @@ class GitlabNet
get("#{host}/check", read_timeout: CHECK_TIMEOUT)
end
def authorized_key(key)
resp = get("#{host}/authorized_keys?key=#{URI.escape(key, '+/=')}")
JSON.parse(resp.body) if resp.code == "200"
rescue
nil
end
protected
def config
......
......@@ -8,8 +8,8 @@ describe GitlabNet, vcr: true do
let(:changes) { ['0000000000000000000000000000000000000000 92d0970eefd7acb6d548878925ce2208cfe2d2ec refs/heads/branch4'] }
before do
gitlab_net.stub!(:host).and_return('https://dev.gitlab.org/api/v3/internal')
gitlab_net.stub!(:secret_token).and_return('a123')
gitlab_net.stub(:host).and_return('https://dev.gitlab.org/api/v3/internal')
gitlab_net.stub(:secret_token).and_return('a123')
end
describe :check do
......@@ -76,6 +76,36 @@ describe GitlabNet, vcr: true do
end
end
describe :authorized_key do
let (:ssh_key) { "AAAAB3NzaC1yc2EAAAADAQABAAACAQDPKPqqnqQ9PDFw65cO7iHXrKw6ucSZg8Bd2CZ150Yy1YRDPJOWeRNCnddS+M/Lk" }
it "should return nil when the resource is not implemented" do
VCR.use_cassette("ssh-key-not-implemented") do
result = gitlab_net.authorized_key("whatever")
result.should be_nil
end
end
it "should return nil when the fingerprint is not found" do
VCR.use_cassette("ssh-key-not-found") do
result = gitlab_net.authorized_key("whatever")
result.should be_nil
end
end
it "should return a ssh key with a valid fingerprint" do
VCR.use_cassette("ssh-key-ok") do
result = gitlab_net.authorized_key(ssh_key)
result.should eq({
"created_at" => "2016-03-04T18:27:36.959Z",
"id" => 2,
"key" => "ssh-rsa a-made=up-rsa-key dummy@gitlab.com",
"title" => "some key title"
})
end
end
end
describe :check_access do
context 'ssh key with access to project' do
it 'should allow pull access for dev.gitlab.org' do
......@@ -142,7 +172,7 @@ describe GitlabNet, vcr: true do
describe :http_client_for do
subject { gitlab_net.send :http_client_for, URI('https://localhost/') }
before do
gitlab_net.stub! :cert_store
gitlab_net.stub :cert_store
gitlab_net.send(:config).stub(:http_settings) { {'self_signed_cert' => true} }
end
......
---
http_interactions:
- request:
method: get
uri: https://dev.gitlab.org/api/v3/internal/authorized_keys?key=whatever
body:
encoding: US-ASCII
string: 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: 404
message: Not Found
headers:
Server:
- nginx
Date:
- Mon, 07 Mar 2016 12:09:59 GMT
Content-Type:
- text/html; charset=utf-8
Connection:
- keep-alive
Cache-Control:
- no-cache
Set-Cookie:
- _gitlab_session=a924e63729e538c9efe10fa8338077d7; path=/; expires=Mon, 14
Mar 2016 12:09:59 -0000; secure; HttpOnly
Status:
- 404 Not Found
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 275ad011-515f-4239-80be-e537fd7c9086
X-Runtime:
- '2.169401'
X-Xss-Protection:
- 1; mode=block
http_version:
recorded_at: Mon, 07 Mar 2016 12:10:00 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: get
uri: https://dev.gitlab.org/api/v3/internal/authorized_keys?key=whatever
body:
encoding: US-ASCII
string: 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: 501
message: Not Implemented
headers:
Server:
- nginx
Date:
- Mon, 07 Mar 2016 12:09:59 GMT
Content-Type:
- text/html; charset=utf-8
Connection:
- keep-alive
Cache-Control:
- no-cache
Set-Cookie:
- _gitlab_session=a924e63729e538c9efe10fa8338077d7; path=/; expires=Mon, 14
Mar 2016 12:09:59 -0000; secure; HttpOnly
Status:
- 501 Not Implemented
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 275ad011-515f-4239-80be-e537fd7c9086
X-Runtime:
- '2.169401'
X-Xss-Protection:
- 1; mode=block
http_version:
recorded_at: Mon, 07 Mar 2016 12:10:00 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: get
uri: https://dev.gitlab.org/api/v3/internal/authorized_keys?key=AAAAB3NzaC1yc2EAAAADAQABAAACAQDPKPqqnqQ9PDFw65cO7iHXrKw6ucSZg8Bd2CZ150Yy1YRDPJOWeRNCnddS+M/Lk
body:
encoding: US-ASCII
string: 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:
Server:
- nginx/1.1.19
Date:
- Wed, 03 Sep 2014 11:27:35 GMT
Content-Type:
- application/json
Connection:
- keep-alive
Status:
- 200 OK
Cache-Control:
- max-age=0, private, must-revalidate
body:
encoding: UTF-8
string: '{"id":2, "title":"some key title", "key":"ssh-rsa a-made=up-rsa-key dummy@gitlab.com", "created_at":"2016-03-04T18:27:36.959Z"}'
http_version:
recorded_at: Mon, 07 Mar 2016 12:10:00 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