Skip to content
Snippets Groups Projects
Commit 9e9c1201 authored by Igor Drozdov's avatar Igor Drozdov
Browse files

Gitlab Shell: Authenticate via JWT token

Before:

- Gitlab Shell sent a shared secret_token for Rails to verify

Now:

- Gitlab Shell sends a shared secret_token
- Gitlab Shell sends JWT singed by the shared secret_token
- Gitlab Rails verifies either JWT or secret token (based on FF)

Then:

- FF is removed
- Only JWT is verified

Changelog: added
parent d0793df8
No related branches found
No related tags found
No related merge requests found
---
name: gitlab_shell_jwt_token
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86148
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/360808
milestone: '15.0'
type: development
group: group::source code
default_enabled: false
Loading
Loading
@@ -3,6 +3,7 @@
require 'spec_helper'
 
RSpec.describe API::Geo do
include GitlabShellHelpers
include TermsHelper
include ApiHelpers
include WorkhorseHelpers
Loading
Loading
@@ -230,7 +231,7 @@
describe 'POST /geo/proxy_git_ssh/info_refs_upload_pack' do
context 'with all required params missing' do
it 'responds with 400' do
post api('/geo/proxy_git_ssh/info_refs_upload_pack'), params: nil
post api('/geo/proxy_git_ssh/info_refs_upload_pack'), params: nil, headers: gitlab_shell_internal_api_request_header
 
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eql('secret_token is missing, data is missing, data[gl_id] is missing, data[primary_repo] is missing')
Loading
Loading
@@ -244,9 +245,9 @@
allow(Gitlab::Geo::GitSSHProxy).to receive(:new).with(data).and_return(git_push_ssh_proxy)
end
 
context 'with an invalid secret_token' do
context 'with an invalid jwt token' do
it 'responds with 401' do
post(api('/geo/proxy_git_ssh/info_refs_upload_pack'), params: { secret_token: 'invalid', data: data })
post(api('/geo/proxy_git_ssh/info_refs_upload_pack'), params: { secret_token: 'invalid', data: data }, headers: gitlab_shell_internal_api_request_header(issuer: 'gitlab-workhorse'))
 
expect(response).to have_gitlab_http_status(:unauthorized)
expect(json_response['error']).to be_nil
Loading
Loading
@@ -257,7 +258,7 @@
it 'responds with 500' do
expect(git_push_ssh_proxy).to receive(:info_refs_upload_pack).and_raise('deliberate exception raised')
 
post api('/geo/proxy_git_ssh/info_refs_upload_pack'), params: { secret_token: secret_token, data: data }
post api('/geo/proxy_git_ssh/info_refs_upload_pack'), params: { secret_token: secret_token, data: data }, headers: gitlab_shell_internal_api_request_header
 
expect(response).to have_gitlab_http_status(:internal_server_error)
expect(json_response['message']).to include('RuntimeError (deliberate exception raised)')
Loading
Loading
@@ -278,7 +279,7 @@
it 'responds with 200' do
expect(git_push_ssh_proxy).to receive(:info_refs_upload_pack).and_return(api_response)
 
post api('/geo/proxy_git_ssh/info_refs_upload_pack'), params: { secret_token: secret_token, data: data }
post api('/geo/proxy_git_ssh/info_refs_upload_pack'), params: { secret_token: secret_token, data: data }, headers: gitlab_shell_internal_api_request_header
 
expect(response).to have_gitlab_http_status(:ok)
expect(Base64.decode64(json_response['result'])).to eql('something here')
Loading
Loading
@@ -305,9 +306,9 @@
allow(Gitlab::Geo::GitSSHProxy).to receive(:new).with(data).and_return(git_push_ssh_proxy)
end
 
context 'with an invalid secret_token' do
context 'with an invalid jwt token' do
it 'responds with 401' do
post(api('/geo/proxy_git_ssh/upload_pack'), params: { secret_token: 'invalid', data: data, output: output })
post(api('/geo/proxy_git_ssh/upload_pack'), params: { secret_token: 'invalid', data: data, output: output }, headers: gitlab_shell_internal_api_request_header(issuer: 'gitlab-workhorse'))
 
expect(response).to have_gitlab_http_status(:unauthorized)
expect(json_response['error']).to be_nil
Loading
Loading
@@ -317,7 +318,7 @@
context 'where an exception occurs' do
it 'responds with 500' do
expect(git_push_ssh_proxy).to receive(:upload_pack).and_raise('deliberate exception raised')
post api('/geo/proxy_git_ssh/upload_pack'), params: { secret_token: secret_token, data: data, output: output }
post api('/geo/proxy_git_ssh/upload_pack'), params: { secret_token: secret_token, data: data, output: output }, headers: gitlab_shell_internal_api_request_header
 
expect(response).to have_gitlab_http_status(:internal_server_error)
expect(json_response['message']).to include('RuntimeError (deliberate exception raised)')
Loading
Loading
@@ -338,7 +339,7 @@
it 'responds with 201' do
expect(git_push_ssh_proxy).to receive(:upload_pack).with(output).and_return(api_response)
 
post api('/geo/proxy_git_ssh/upload_pack'), params: { secret_token: secret_token, data: data, output: output }
post api('/geo/proxy_git_ssh/upload_pack'), params: { secret_token: secret_token, data: data, output: output }, headers: gitlab_shell_internal_api_request_header
 
expect(response).to have_gitlab_http_status(:created)
expect(Base64.decode64(json_response['result'])).to eql('something here')
Loading
Loading
@@ -350,7 +351,7 @@
describe 'POST /geo/proxy_git_ssh/info_refs_receive_pack' do
context 'with all required params missing' do
it 'responds with 400' do
post api('/geo/proxy_git_ssh/info_refs_receive_pack'), params: nil
post api('/geo/proxy_git_ssh/info_refs_receive_pack'), params: nil, headers: gitlab_shell_internal_api_request_header
 
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eql('secret_token is missing, data is missing, data[gl_id] is missing, data[primary_repo] is missing')
Loading
Loading
@@ -364,9 +365,9 @@
allow(Gitlab::Geo::GitSSHProxy).to receive(:new).with(data).and_return(git_push_ssh_proxy)
end
 
context 'with an invalid secret_token' do
context 'with an invalid jwt token' do
it 'responds with 401' do
post(api('/geo/proxy_git_ssh/info_refs_receive_pack'), params: { secret_token: 'invalid', data: data })
post(api('/geo/proxy_git_ssh/info_refs_receive_pack'), params: { secret_token: 'invalid', data: data }, headers: gitlab_shell_internal_api_request_header(issuer: 'gitlab-workhorse'))
 
expect(response).to have_gitlab_http_status(:unauthorized)
expect(json_response['error']).to be_nil
Loading
Loading
@@ -377,7 +378,7 @@
it 'responds with 500' do
expect(git_push_ssh_proxy).to receive(:info_refs_receive_pack).and_raise('deliberate exception raised')
 
post api('/geo/proxy_git_ssh/info_refs_receive_pack'), params: { secret_token: secret_token, data: data }
post api('/geo/proxy_git_ssh/info_refs_receive_pack'), params: { secret_token: secret_token, data: data }, headers: gitlab_shell_internal_api_request_header
 
expect(response).to have_gitlab_http_status(:internal_server_error)
expect(json_response['message']).to include('RuntimeError (deliberate exception raised)')
Loading
Loading
@@ -398,7 +399,7 @@
it 'responds with 200' do
expect(git_push_ssh_proxy).to receive(:info_refs_receive_pack).and_return(api_response)
 
post api('/geo/proxy_git_ssh/info_refs_receive_pack'), params: { secret_token: secret_token, data: data }
post api('/geo/proxy_git_ssh/info_refs_receive_pack'), params: { secret_token: secret_token, data: data }, headers: gitlab_shell_internal_api_request_header
 
expect(response).to have_gitlab_http_status(:ok)
expect(Base64.decode64(json_response['result'])).to eql('something here')
Loading
Loading
@@ -425,9 +426,9 @@
allow(Gitlab::Geo::GitSSHProxy).to receive(:new).with(data).and_return(git_push_ssh_proxy)
end
 
context 'with an invalid secret_token' do
context 'with an invalid jwt token' do
it 'responds with 401' do
post(api('/geo/proxy_git_ssh/receive_pack'), params: { secret_token: 'invalid', data: data, output: output })
post(api('/geo/proxy_git_ssh/receive_pack'), params: { secret_token: 'invalid', data: data, output: output }, headers: gitlab_shell_internal_api_request_header(issuer: 'gitlab-workhorse'))
 
expect(response).to have_gitlab_http_status(:unauthorized)
expect(json_response['error']).to be_nil
Loading
Loading
@@ -437,7 +438,7 @@
context 'where an exception occurs' do
it 'responds with 500' do
expect(git_push_ssh_proxy).to receive(:receive_pack).and_raise('deliberate exception raised')
post api('/geo/proxy_git_ssh/receive_pack'), params: { secret_token: secret_token, data: data, output: output }
post api('/geo/proxy_git_ssh/receive_pack'), params: { secret_token: secret_token, data: data, output: output }, headers: gitlab_shell_internal_api_request_header
 
expect(response).to have_gitlab_http_status(:internal_server_error)
expect(json_response['message']).to include('RuntimeError (deliberate exception raised)')
Loading
Loading
@@ -458,7 +459,7 @@
it 'responds with 201' do
expect(git_push_ssh_proxy).to receive(:receive_pack).with(output).and_return(api_response)
 
post api('/geo/proxy_git_ssh/receive_pack'), params: { secret_token: secret_token, data: data, output: output }
post api('/geo/proxy_git_ssh/receive_pack'), params: { secret_token: secret_token, data: data, output: output }, headers: gitlab_shell_internal_api_request_header
 
expect(response).to have_gitlab_http_status(:created)
expect(Base64.decode64(json_response['result'])).to eql('something here')
Loading
Loading
Loading
Loading
@@ -2,6 +2,7 @@
require 'spec_helper'
 
RSpec.describe API::Internal::Base do
include GitlabShellHelpers
include EE::GeoHelpers
include APIInternalBaseHelpers
 
Loading
Loading
@@ -11,8 +12,6 @@
let_it_be(:secondary_node, reload: true) { create(:geo_node, url: secondary_url) }
let_it_be(:user) { create(:user) }
 
let(:secret_token) { Gitlab::Shell.secret_token }
describe 'POST /internal/post_receive', :geo do
let(:key) { create(:key, user: user) }
let_it_be(:project, reload: true) { create(:project, :repository, :wiki_repo) }
Loading
Loading
@@ -25,7 +24,6 @@
let(:valid_params) do
{
gl_repository: gl_repository,
secret_token: secret_token,
identifier: identifier,
changes: changes,
push_options: {}
Loading
Loading
@@ -63,7 +61,7 @@
http://primary.example.com/#{project.full_path}.git
STR
 
post api('/internal/post_receive'), params: valid_params
post api('/internal/post_receive'), params: valid_params, headers: gitlab_shell_internal_api_request_header
 
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['messages']).to include({
Loading
Loading
@@ -88,9 +86,9 @@ def check_access_by_alias(alias_name)
action: "git-upload-pack",
key_id: key.id,
project: alias_name,
protocol: 'ssh',
secret_token: secret_token
}
protocol: 'ssh'
},
headers: gitlab_shell_internal_api_request_header
)
end
 
Loading
Loading
@@ -143,8 +141,9 @@ def check_access_by_alias(alias_name)
project: project.full_path,
gl_repository: "project-#{project.id}",
action: 'git-upload-pack',
secret_token: secret_token,
protocol: 'ssh' })
protocol: 'ssh' },
headers: gitlab_shell_internal_api_request_header
)
end
 
before do
Loading
Loading
@@ -206,7 +205,6 @@ def check_access_by_alias(alias_name)
project: project.full_path,
gl_repository: "project-#{project.id}",
action: 'git-upload-pack',
secret_token: secret_token,
protocol: 'ssh'
}
end
Loading
Loading
@@ -233,7 +231,8 @@ def check_access_by_alias(alias_name)
subject do
post(
api('/internal/allowed'),
params: check_ip_present ? params.merge(check_ip: ip) : params
params: check_ip_present ? params.merge(check_ip: ip) : params,
headers: gitlab_shell_internal_api_request_header
)
end
 
Loading
Loading
@@ -310,9 +309,9 @@ def lfs_auth_user(user_id, project)
api("/internal/lfs_authenticate"),
params: {
user_id: user_id,
secret_token: secret_token,
project: project.full_path
}
},
headers: gitlab_shell_internal_api_request_header
)
end
end
Loading
Loading
@@ -333,12 +332,12 @@ def lfs_auth_user(user_id, project)
it 'returns an error message when the expiry date exceeds the max token lifetime' do
post api('/internal/personal_access_token'),
params: {
secret_token: secret_token,
key_id: key.id,
name: 'newtoken',
scopes: %w(read_api read_repository),
expires_at: (instance_level_max_personal_access_token_lifetime + 1).days.from_now.to_date.to_s
}
},
headers: gitlab_shell_internal_api_request_header
 
aggregate_failures do
expect(json_response['success']).to eq(false)
Loading
Loading
@@ -352,12 +351,12 @@ def lfs_auth_user(user_id, project)
 
post api('/internal/personal_access_token'),
params: {
secret_token: secret_token,
key_id: key.id,
name: 'newtoken',
scopes: %w(read_api read_repository),
expires_at: expires_at
}
},
headers: gitlab_shell_internal_api_request_header
 
aggregate_failures do
expect(json_response['success']).to eq(true)
Loading
Loading
@@ -383,10 +382,10 @@ def lfs_auth_user(user_id, project)
subject do
post api('/internal/two_factor_otp_check'),
params: {
secret_token: secret_token,
key_id: key_id,
otp_attempt: otp
}
},
headers: gitlab_shell_internal_api_request_header
end
 
it_behaves_like 'actor key validations'
Loading
Loading
Loading
Loading
@@ -9,6 +9,8 @@ module Helpers
 
SUDO_HEADER = "HTTP_SUDO"
GITLAB_SHARED_SECRET_HEADER = "Gitlab-Shared-Secret"
GITLAB_SHELL_API_HEADER = "Gitlab-Shell-Api-Request"
GITLAB_SHELL_JWT_ISSUER = "gitlab-shell"
SUDO_PARAM = :sudo
API_USER_ENV = 'gitlab.api.user'
API_EXCEPTION_ENV = 'gitlab.api.exception'
Loading
Loading
@@ -264,12 +266,22 @@ def authenticate_non_get!
end
 
def authenticate_by_gitlab_shell_token!
input = params['secret_token']
input ||= Base64.decode64(headers[GITLAB_SHARED_SECRET_HEADER]) if headers.key?(GITLAB_SHARED_SECRET_HEADER)
if Feature.enabled?(:gitlab_shell_jwt_token, default_enabled: :yaml)
begin
payload, _ = JSONWebToken::HMACToken.decode(headers[GITLAB_SHELL_API_HEADER], secret_token)
unauthorized! unless payload['iss'] == GITLAB_SHELL_JWT_ISSUER
rescue JWT::DecodeError, JWT::ExpiredSignature, JWT::ImmatureSignature => ex
Gitlab::ErrorTracking.track_exception(ex)
unauthorized!
end
else
input = params['secret_token']
input ||= Base64.decode64(headers[GITLAB_SHARED_SECRET_HEADER]) if headers.key?(GITLAB_SHARED_SECRET_HEADER)
 
input&.chomp!
input&.chomp!
 
unauthorized! unless Devise.secure_compare(secret_token, input)
unauthorized! unless Devise.secure_compare(secret_token, input)
end
end
 
def authenticated_with_can_read_all_resources!
Loading
Loading
Loading
Loading
@@ -3,6 +3,7 @@
require 'spec_helper'
 
RSpec.describe API::Internal::Base do
include GitlabShellHelpers
include APIInternalBaseHelpers
 
let_it_be(:user, reload: true) { create(:user) }
Loading
Loading
@@ -17,10 +18,14 @@
let(:snippet_changes) { "#{TestEnv::BRANCH_SHA['snippet/single-file']} #{TestEnv::BRANCH_SHA['snippet/edit-file']} refs/heads/snippet/edit-file" }
 
describe "GET /internal/check" do
def perform_request(headers: gitlab_shell_internal_api_request_header)
get api("/internal/check"), headers: headers
end
it do
expect_any_instance_of(Redis).to receive(:ping).and_return('PONG')
 
get api("/internal/check"), params: { secret_token: secret_token }
perform_request
 
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['api_version']).to eq(API::API.version)
Loading
Loading
@@ -30,24 +35,57 @@
it 'returns false for field `redis` when redis is unavailable' do
expect_any_instance_of(Redis).to receive(:ping).and_raise(Errno::ENOENT)
 
get api("/internal/check"), params: { secret_token: secret_token }
perform_request
 
expect(json_response['redis']).to be(false)
end
 
context 'authenticating' do
it 'authenticates using a header' do
get api("/internal/check"),
headers: { API::Helpers::GITLAB_SHARED_SECRET_HEADER => Base64.encode64(secret_token) }
it 'authenticates using a jwt token in a header' do
perform_request
 
expect(response).to have_gitlab_http_status(:ok)
end
 
it 'returns 401 when no credentials provided' do
get(api("/internal/check"))
it 'returns 401 when jwt token is invalid' do
headers = gitlab_shell_internal_api_request_header
travel_to(2.minutes.since) do
perform_request(headers: headers)
end
expect(response).to have_gitlab_http_status(:unauthorized)
end
it 'returns 401 when jwt issuer is not Gitlab-Shell' do
perform_request(headers: gitlab_shell_internal_api_request_header(issuer: "gitlab-workhorse"))
 
expect(response).to have_gitlab_http_status(:unauthorized)
end
it 'returns 401 when plain secret is provided in a header' do
perform_request(headers: { API::Helpers::GITLAB_SHARED_SECRET_HEADER => Base64.encode64(secret_token) })
expect(response).to have_gitlab_http_status(:unauthorized)
end
context 'when gitlab_shell_jwt_token is disabled' do
before do
stub_feature_flags(gitlab_shell_jwt_token: false)
end
it 'authenticates using a header' do
perform_request(headers: { API::Helpers::GITLAB_SHARED_SECRET_HEADER => Base64.encode64(secret_token) })
expect(response).to have_gitlab_http_status(:ok)
end
it 'returns 401 when no credentials provided' do
get(api("/internal/check"))
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
end
end
 
Loading
Loading
@@ -56,10 +94,8 @@
 
subject do
post api('/internal/two_factor_recovery_codes'),
params: {
secret_token: secret_token,
key_id: key_id
}
params: { key_id: key_id },
headers: gitlab_shell_internal_api_request_header
end
 
it_behaves_like 'actor key validations'
Loading
Loading
@@ -105,10 +141,8 @@
 
subject do
post api('/internal/personal_access_token'),
params: {
secret_token: secret_token,
key_id: key_id
}
params: { key_id: key_id },
headers: gitlab_shell_internal_api_request_header
end
 
it_behaves_like 'actor key validations'
Loading
Loading
@@ -126,10 +160,8 @@
 
it 'returns an error message when given an non existent user' do
post api('/internal/personal_access_token'),
params: {
secret_token: secret_token,
user_id: 0
}
params: { user_id: 0 },
headers: gitlab_shell_internal_api_request_header
 
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq("Could not find the given user")
Loading
Loading
@@ -137,10 +169,8 @@
 
it 'returns an error message when no name parameter is received' do
post api('/internal/personal_access_token'),
params: {
secret_token: secret_token,
key_id: key.id
}
params: { key_id: key.id },
headers: gitlab_shell_internal_api_request_header
 
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq("No token name specified")
Loading
Loading
@@ -148,11 +178,8 @@
 
it 'returns an error message when no scopes parameter is received' do
post api('/internal/personal_access_token'),
params: {
secret_token: secret_token,
key_id: key.id,
name: 'newtoken'
}
params: { key_id: key.id, name: 'newtoken' },
headers: gitlab_shell_internal_api_request_header
 
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq("No token scopes specified")
Loading
Loading
@@ -161,12 +188,12 @@
it 'returns an error message when expires_at contains an invalid date' do
post api('/internal/personal_access_token'),
params: {
secret_token: secret_token,
key_id: key.id,
name: 'newtoken',
scopes: ['api'],
expires_at: 'invalid-date'
}
},
headers: gitlab_shell_internal_api_request_header
 
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq("Invalid token expiry date: 'invalid-date'")
Loading
Loading
@@ -175,11 +202,11 @@
it 'returns an error message when it receives an invalid scope' do
post api('/internal/personal_access_token'),
params: {
secret_token: secret_token,
key_id: key.id,
name: 'newtoken',
scopes: %w(read_api badscope read_repository)
}
},
headers: gitlab_shell_internal_api_request_header
 
expect(json_response['success']).to be_falsey
expect(json_response['message']).to match(/\AInvalid scope: 'badscope'. Valid scopes are: /)
Loading
Loading
@@ -190,11 +217,11 @@
 
post api('/internal/personal_access_token'),
params: {
secret_token: secret_token,
key_id: key.id,
name: 'newtoken',
scopes: %w(read_api read_repository)
}
},
headers: gitlab_shell_internal_api_request_header
 
expect(json_response['success']).to be_truthy
expect(json_response['token']).to match(/\A\S{#{token_size}}\z/)
Loading
Loading
@@ -207,12 +234,12 @@
 
post api('/internal/personal_access_token'),
params: {
secret_token: secret_token,
key_id: key.id,
name: 'newtoken',
scopes: %w(read_api read_repository),
expires_at: '9001-11-17'
}
},
headers: gitlab_shell_internal_api_request_header
 
expect(json_response['success']).to be_truthy
expect(json_response['token']).to match(/\A\S{#{token_size}}\z/)
Loading
Loading
@@ -309,7 +336,7 @@
 
describe "GET /internal/discover" do
it "finds a user by key id" do
get(api("/internal/discover"), params: { key_id: key.id, secret_token: secret_token })
get(api("/internal/discover"), params: { key_id: key.id }, headers: gitlab_shell_internal_api_request_header)
 
expect(response).to have_gitlab_http_status(:ok)
 
Loading
Loading
@@ -317,7 +344,7 @@
end
 
it "finds a user by username" do
get(api("/internal/discover"), params: { username: user.username, secret_token: secret_token })
get(api("/internal/discover"), params: { username: user.username }, headers: gitlab_shell_internal_api_request_header)
 
expect(response).to have_gitlab_http_status(:ok)
 
Loading
Loading
@@ -325,7 +352,7 @@
end
 
it 'responds successfully when a user is not found' do
get(api('/internal/discover'), params: { username: 'noone', secret_token: secret_token })
get(api('/internal/discover'), params: { username: 'noone' }, headers: gitlab_shell_internal_api_request_header)
 
expect(response).to have_gitlab_http_status(:ok)
 
Loading
Loading
@@ -333,7 +360,7 @@
end
 
it 'response successfully when passing invalid params' do
get(api('/internal/discover'), params: { nothing: 'to find a user', secret_token: secret_token })
get(api('/internal/discover'), params: { nothing: 'to find a user' }, headers: gitlab_shell_internal_api_request_header)
 
expect(response).to have_gitlab_http_status(:ok)
 
Loading
Loading
@@ -344,7 +371,7 @@
describe "GET /internal/authorized_keys" do
context "using an existing key" do
it "finds the key" do
get(api('/internal/authorized_keys'), params: { key: key.key.split[1], secret_token: secret_token })
get(api('/internal/authorized_keys'), params: { key: key.key.split[1] }, headers: gitlab_shell_internal_api_request_header)
 
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['id']).to eq(key.id)
Loading
Loading
@@ -352,7 +379,7 @@
end
 
it 'exposes the comment of the key as a simple identifier of username + hostname' do
get(api('/internal/authorized_keys'), params: { key: key.key.split[1], secret_token: secret_token })
get(api('/internal/authorized_keys'), params: { key: key.key.split[1] }, headers: gitlab_shell_internal_api_request_header)
 
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['key']).to include("#{key.user_name} (#{Gitlab.config.gitlab.host})")
Loading
Loading
@@ -360,13 +387,13 @@
end
 
it "returns 404 with a partial key" do
get(api('/internal/authorized_keys'), params: { key: key.key.split[1][0...-3], secret_token: secret_token })
get(api('/internal/authorized_keys'), params: { key: key.key.split[1][0...-3] }, headers: gitlab_shell_internal_api_request_header)
 
expect(response).to have_gitlab_http_status(:not_found)
end
 
it "returns 404 with an not valid base64 string" do
get(api('/internal/authorized_keys'), params: { key: "whatever!", secret_token: secret_token })
get(api('/internal/authorized_keys'), params: { key: "whatever!" }, headers: gitlab_shell_internal_api_request_header)
 
expect(response).to have_gitlab_http_status(:not_found)
end
Loading
Loading
@@ -580,9 +607,9 @@ def request
project: full_path_for(project),
gl_repository: gl_repository_for(project),
action: 'git-upload-pack',
secret_token: secret_token,
protocol: 'ssh'
}
},
headers: gitlab_shell_internal_api_request_header
)
end
end
Loading
Loading
@@ -965,9 +992,9 @@ def request
key_id: key.id,
project: 'project/does-not-exist.git',
action: 'git-upload-pack',
secret_token: secret_token,
protocol: 'ssh'
}
},
headers: gitlab_shell_internal_api_request_header
)
 
expect(response).to have_gitlab_http_status(:not_found)
Loading
Loading
@@ -1141,9 +1168,9 @@ def request
key_id: key.id,
project: project.full_path,
gl_repository: gl_repository,
secret_token: secret_token,
protocol: 'ssh'
})
}, headers: gitlab_shell_internal_api_request_header
)
 
expect(response).to have_gitlab_http_status(:unauthorized)
end
Loading
Loading
@@ -1256,7 +1283,6 @@ def request
let(:valid_params) do
{
gl_repository: gl_repository,
secret_token: secret_token,
identifier: identifier,
changes: changes,
push_options: push_options
Loading
Loading
@@ -1267,7 +1293,7 @@ def request
"#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/#{branch_name}"
end
 
subject { post api('/internal/post_receive'), params: valid_params }
subject { post api('/internal/post_receive'), params: valid_params, headers: gitlab_shell_internal_api_request_header }
 
before do
project.add_developer(user)
Loading
Loading
@@ -1368,7 +1394,7 @@ def request
 
describe 'POST /internal/pre_receive' do
let(:valid_params) do
{ gl_repository: gl_repository, secret_token: secret_token }
{ gl_repository: gl_repository }
end
 
it 'decreases the reference counter and returns the result' do
Loading
Loading
@@ -1376,7 +1402,7 @@ def request
.and_return(reference_counter)
expect(reference_counter).to receive(:increase).and_return(true)
 
post api("/internal/pre_receive"), params: valid_params
post api("/internal/pre_receive"), params: valid_params, headers: gitlab_shell_internal_api_request_header
 
expect(json_response['reference_counter_increased']).to be(true)
end
Loading
Loading
@@ -1391,10 +1417,8 @@ def request
 
subject do
post api('/internal/two_factor_config'),
params: {
secret_token: secret_token,
key_id: key_id
}
params: { key_id: key_id },
headers: gitlab_shell_internal_api_request_header
end
 
it_behaves_like 'actor key validations'
Loading
Loading
@@ -1455,11 +1479,8 @@ def request
 
subject do
post api('/internal/two_factor_otp_check'),
params: {
secret_token: secret_token,
key_id: key_id,
otp_attempt: otp
}
params: { key_id: key_id, otp_attempt: otp },
headers: gitlab_shell_internal_api_request_header
end
 
it 'is not available' do
Loading
Loading
@@ -1555,32 +1576,24 @@ def request
def lfs_auth_project(project)
post(
api("/internal/lfs_authenticate"),
params: {
secret_token: secret_token,
project: project.full_path
}
params: { project: project.full_path },
headers: gitlab_shell_internal_api_request_header
)
end
 
def lfs_auth_key(key_id, project)
post(
api("/internal/lfs_authenticate"),
params: {
key_id: key_id,
secret_token: secret_token,
project: project.full_path
}
params: { key_id: key_id, project: project.full_path },
headers: gitlab_shell_internal_api_request_header
)
end
 
def lfs_auth_user(user_id, project)
post(
api("/internal/lfs_authenticate"),
params: {
user_id: user_id,
secret_token: secret_token,
project: project.full_path
}
params: { user_id: user_id, project: project.full_path },
headers: gitlab_shell_internal_api_request_header
)
end
end
Loading
Loading
@@ -3,6 +3,7 @@
require 'spec_helper'
 
RSpec.describe API::Internal::Lfs do
include GitlabShellHelpers
include APIInternalBaseHelpers
 
let_it_be(:project) { create(:project) }
Loading
Loading
@@ -11,25 +12,23 @@
let_it_be(:gl_repository) { "project-#{project.id}" }
let_it_be(:filename) { lfs_object.file.path }
 
let(:secret_token) { Gitlab::Shell.secret_token }
describe 'GET /internal/lfs' do
let(:valid_params) do
{ oid: lfs_object.oid, gl_repository: gl_repository, secret_token: secret_token }
{ oid: lfs_object.oid, gl_repository: gl_repository }
end
 
context 'with invalid auth' do
let(:invalid_params) { valid_params.merge!(secret_token: 'invalid_tokne') }
it 'returns 401' do
get api("/internal/lfs"), params: invalid_params
get api("/internal/lfs"),
params: valid_params,
headers: gitlab_shell_internal_api_request_header(issuer: 'gitlab-workhorse')
end
end
 
context 'with valid auth' do
context 'LFS in local storage' do
it 'sends the file' do
get api("/internal/lfs"), params: valid_params
get api("/internal/lfs"), params: valid_params, headers: gitlab_shell_internal_api_request_header
 
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['Content-Type']).to eq('application/octet-stream')
Loading
Loading
@@ -39,7 +38,10 @@
 
# https://www.rubydoc.info/github/rack/rack/master/Rack/Sendfile
it 'delegates sending to Web server' do
get api("/internal/lfs"), params: valid_params, env: { 'HTTP_X_SENDFILE_TYPE' => 'X-Sendfile' }
get api("/internal/lfs"),
params: valid_params,
env: { 'HTTP_X_SENDFILE_TYPE' => 'X-Sendfile' },
headers: gitlab_shell_internal_api_request_header
 
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['Content-Type']).to eq('application/octet-stream')
Loading
Loading
@@ -51,7 +53,7 @@
it 'retuns 404 for unknown file' do
params = valid_params.merge(oid: SecureRandom.hex)
 
get api("/internal/lfs"), params: params
get api("/internal/lfs"), params: params, headers: gitlab_shell_internal_api_request_header
 
expect(response).to have_gitlab_http_status(:not_found)
end
Loading
Loading
@@ -60,7 +62,7 @@
other_lfs = create(:lfs_object, :with_file)
params = valid_params.merge(oid: other_lfs.oid)
 
get api("/internal/lfs"), params: params
get api("/internal/lfs"), params: params, headers: gitlab_shell_internal_api_request_header
 
expect(response).to have_gitlab_http_status(:not_found)
end
Loading
Loading
@@ -70,7 +72,7 @@
let!(:lfs_object2) { create(:lfs_object, :with_file) }
let!(:lfs_objects_project2) { create(:lfs_objects_project, project: project, lfs_object: lfs_object2) }
let(:valid_params) do
{ oid: lfs_object2.oid, gl_repository: gl_repository, secret_token: secret_token }
{ oid: lfs_object2.oid, gl_repository: gl_repository }
end
 
before do
Loading
Loading
@@ -79,7 +81,7 @@
end
 
it 'notifies Workhorse to send the file' do
get api("/internal/lfs"), params: valid_params
get api("/internal/lfs"), params: valid_params, headers: gitlab_shell_internal_api_request_header
 
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("send-url:")
Loading
Loading
Loading
Loading
@@ -3,6 +3,7 @@
require 'spec_helper'
 
RSpec.describe PostReceiveService do
include GitlabShellHelpers
include Gitlab::Routing
 
let_it_be(:user) { create(:user) }
Loading
Loading
@@ -13,7 +14,6 @@
let(:identifier) { 'key-123' }
let(:gl_repository) { "project-#{project.id}" }
let(:branch_name) { 'feature' }
let(:secret_token) { Gitlab::Shell.secret_token }
let(:reference_counter) { double('ReferenceCounter') }
let(:push_options) { ['ci.skip', 'another push option'] }
let(:repository) { project.repository }
Loading
Loading
@@ -25,7 +25,6 @@
let(:params) do
{
gl_repository: gl_repository,
secret_token: secret_token,
identifier: identifier,
changes: changes,
push_options: push_options
Loading
Loading
# frozen_string_literal: true
 
require_relative 'gitlab_shell_helpers'
module APIInternalBaseHelpers
include GitlabShellHelpers
def gl_repository_for(container)
case container
when ProjectWiki
Loading
Loading
@@ -33,9 +37,9 @@ def pull(key, container, protocol = 'ssh')
project: full_path_for(container),
gl_repository: gl_repository_for(container),
action: 'git-upload-pack',
secret_token: secret_token,
protocol: protocol
}
},
headers: gitlab_shell_internal_api_request_header
)
end
 
Loading
Loading
@@ -56,7 +60,6 @@ def push_with_path(key, full_path:, gl_repository: nil, protocol: 'ssh', env: ni
key_id: key.id,
project: full_path,
action: 'git-receive-pack',
secret_token: secret_token,
protocol: protocol,
env: env
}
Loading
Loading
@@ -64,7 +67,8 @@ def push_with_path(key, full_path:, gl_repository: nil, protocol: 'ssh', env: ni
 
post(
api("/internal/allowed"),
params: params
params: params,
headers: gitlab_shell_internal_api_request_header
)
end
 
Loading
Loading
@@ -77,9 +81,9 @@ def archive(key, container)
project: full_path_for(container),
gl_repository: gl_repository_for(container),
action: 'git-upload-archive',
secret_token: secret_token,
protocol: 'ssh'
}
},
headers: gitlab_shell_internal_api_request_header
)
end
end
# frozen_string_literal: true
module GitlabShellHelpers
extend self
def gitlab_shell_internal_api_request_header(issuer: API::Helpers::GITLAB_SHELL_JWT_ISSUER)
jwt_token = JSONWebToken::HMACToken.new(Gitlab::Shell.secret_token).tap do |token|
token.issuer = issuer
end
{ API::Helpers::GITLAB_SHELL_API_HEADER => jwt_token.encoded }
end
end
Loading
Loading
@@ -518,7 +518,7 @@ def reset_rack_attack
context 'when the request is to the api internal endpoints' do
it 'allows requests over the rate limit' do
(1 + requests_per_period).times do
get '/api/v4/internal/check', params: { secret_token: Gitlab::Shell.secret_token }
get '/api/v4/internal/check', headers: GitlabShellHelpers.gitlab_shell_internal_api_request_header
expect(response).to have_gitlab_http_status(:ok)
end
end
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment