diff options
author | Nick Thomas <nick@gitlab.com> | 2017-07-28 16:45:26 +0100 |
---|---|---|
committer | Nick Thomas <nick@gitlab.com> | 2017-07-31 10:18:56 +0100 |
commit | 2a31b8df645d746618cd8400360bebedeeb6db47 (patch) | |
tree | 96e2a9d18d63a4ba81d4abe8419fe4009346e088 /lib/gitlab_projects.rb | |
parent | cdea8630d0e0f2a726fcd0d377d815fe0bedd99f (diff) | |
download | gitlab-shell-2a31b8df645d746618cd8400360bebedeeb6db47.tar.gz |
Implement SSH authentication support in Ruby
Diffstat (limited to 'lib/gitlab_projects.rb')
-rw-r--r-- | lib/gitlab_projects.rb | 78 |
1 files changed, 67 insertions, 11 deletions
diff --git a/lib/gitlab_projects.rb b/lib/gitlab_projects.rb index 4c63a40..267c679 100644 --- a/lib/gitlab_projects.rb +++ b/lib/gitlab_projects.rb @@ -211,20 +211,23 @@ class GitlabProjects cmd = %W(git --git-dir=#{full_path} fetch #{@name} --prune --quiet) cmd << '--force' if forced cmd << tags_option - pid = Process.spawn(*cmd) - begin - Timeout.timeout(timeout) do - Process.wait(pid) - end + setup_ssh_auth do |env| + pid = Process.spawn(env, *cmd) - $?.exitstatus.zero? - rescue => exception - $logger.error "Fetching remote #{@name} for project #{@project_name} failed due to: #{exception.message}." + begin + _, status = Timeout.timeout(timeout) do + Process.wait2(pid) + end - Process.kill('KILL', pid) - Process.wait - false + status.success? + rescue => exception + $logger.error "Fetching remote #{@name} for project #{@project_name} failed due to: #{exception.message}." + + Process.kill('KILL', pid) + Process.wait + false + end end end @@ -406,6 +409,59 @@ class GitlabProjects false end + # Builds a small shell script that can be used to execute SSH with a set of + # custom options. + # + # Options are expanded as `'-oKey="Value"'`, so SSH will correctly interpret + # paths with spaces in them. We trust the user not to embed single or double + # quotes in the key or value. + def custom_ssh_script(options = {}) + args = options.map { |k, v| "'-o#{k}=\"#{v}\"'" }.join(' ') + + [ + "#!/bin/sh", + "exec ssh #{args} \"$@\"" + ].join("\n") + end + + # Known hosts data and private keys can be passed to gitlab-shell in the + # environment. If present, this method puts them into temporary files, writes + # a script that can substitute as `ssh`, setting the options to respect those + # files, and yields: { "GIT_SSH" => "/tmp/myScript" } + def setup_ssh_auth + options = {} + + if ENV.key?('GITLAB_SHELL_SSH_KEY') + key_file = Tempfile.new('gitlab-shell-key-file', mode: 0o400) + key_file.write(ENV['GITLAB_SHELL_SSH_KEY']) + key_file.close + + options['IdentityFile'] = key_file.path + options['IdentitiesOnly'] = true + end + + if ENV.key?('GITLAB_SHELL_KNOWN_HOSTS') + known_hosts_file = Tempfile.new('gitlab-shell-known-hosts', mode: 0o400) + known_hosts_file.write(ENV['GITLAB_SHELL_KNOWN_HOSTS']) + known_hosts_file.close + + options['StrictHostKeyChecking'] = true + options['UserKnownHostsFile'] = known_hosts_file.path + end + + return yield({}) if options.empty? + + script = Tempfile.new('gitlab-shell-ssh-wrapper', mode: 0o755) + script.write(custom_ssh_script(options)) + script.close + + yield('GIT_SSH' => script.path) + ensure + key_file.close! unless key_file.nil? + known_hosts_file.close! unless known_hosts_file.nil? + script.close! unless script.nil? + end + def gitlab_reference_counter @gitlab_reference_counter ||= begin # Defer loading because this pulls in gitlab_net, which takes 100-200 ms |