diff options
-rw-r--r-- | app/controllers/jwt_controller.rb | 39 | ||||
-rw-r--r-- | app/models/project.rb | 2 | ||||
-rw-r--r-- | app/services/jwt/container_registry_authentication_service.rb | 119 | ||||
-rw-r--r-- | lib/jwt/rsa_token.rb | 10 | ||||
-rw-r--r-- | spec/features/container_registry_spec.rb | 2 | ||||
-rw-r--r-- | spec/services/jwt/container_registry_authentication_service_spec.rb | 2 |
6 files changed, 95 insertions, 79 deletions
diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb index 07a842970b8..568f14a713a 100644 --- a/app/controllers/jwt_controller.rb +++ b/app/controllers/jwt_controller.rb @@ -1,22 +1,13 @@ class JwtController < ApplicationController skip_before_action :authenticate_user! skip_before_action :verify_authenticity_token + before_action :authenticate_project_or_user SERVICES = { - Jwt::ContainerRegistryAuthenticationService::AUDIENCE => Jwt::ContainerRegistryAuthenticationService, + ::Gitlab::JWT::ContainerRegistryAuthenticationService::AUDIENCE => ::Gitlab::JWT::ContainerRegistryAuthenticationService, } def auth - @authenticated = authenticate_with_http_basic do |login, password| - # if it's possible we first try to authenticate project with login and password - @project = authenticate_project(login, password) - @user = authenticate_user(login, password) unless @project - end - - unless @authenticated - head :forbidden if ActionController::HttpAuthentication::Basic.has_basic_credentials?(request) - end - service = SERVICES[params[:service]] head :not_found unless service @@ -28,19 +19,28 @@ class JwtController < ApplicationController private + def authenticate_project_or_user + authenticate_with_http_basic do |login, password| + # if it's possible we first try to authenticate project with login and password + @project = authenticate_project(login, password) + return if @project + + @user = authenticate_user(login, password) + return if @user + end + + if ActionController::HttpAuthentication::Basic.has_basic_credentials?(request) + head :forbidden + end + end + def auth_params params.permit(:service, :scope, :offline_token, :account, :client_id) end def authenticate_project(login, password) - matched_login = /(?<s>^[a-zA-Z]*-ci)-token$/.match(login) - - if matched_login.present? - underscored_service = matched_login['s'].underscore - - if underscored_service == 'gitlab_ci' - Project.find_by(builds_enabled: true, runners_token: password) - end + if login == 'gitlab_ci_token' + Project.find_by(builds_enabled: true, runners_token: password) end end @@ -77,6 +77,7 @@ class JwtController < ApplicationController if banned Rails.logger.info "IP #{request.ip} failed to login " \ "as #{login} but has been temporarily banned from Git auth" + return end end end diff --git a/app/models/project.rb b/app/models/project.rb index e5ace7d755b..fb90102db73 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -377,7 +377,7 @@ class Project < ActiveRecord::Base def container_registry_repository @container_registry_repository ||= begin - token = JWT::ContainerRegistryAuthenticationService.full_access_token(path_with_namespace) + token = Gitlab::JWT::ContainerRegistryAuthenticationService.full_access_token(path_with_namespace) url = Gitlab.config.registry.api_url host_port = Gitlab.config.registry.host_port registry = ContainerRegistry::Registry.new(url, token: token, path: host_port) diff --git a/app/services/jwt/container_registry_authentication_service.rb b/app/services/jwt/container_registry_authentication_service.rb index b60cd3c57e5..46b30ec9202 100644 --- a/app/services/jwt/container_registry_authentication_service.rb +++ b/app/services/jwt/container_registry_authentication_service.rb @@ -1,71 +1,73 @@ -module JWT - class ContainerRegistryAuthenticationService < BaseService - AUDIENCE = 'container_registry' +module Gitlab + module JWT + class ContainerRegistryAuthenticationService < BaseService + AUDIENCE = 'container_registry' - def execute - return error('not found', 404) unless registry.enabled + def execute + return error('not found', 404) unless registry.enabled - if params[:offline_token] - return error('forbidden', 403) unless current_user - end + if params[:offline_token] + return error('forbidden', 403) unless current_user + end - return error('forbidden', 401) if scopes.blank? + return error('forbidden', 401) if scopes.blank? - { token: authorized_token(scopes).encoded } - end + { token: authorized_token(scopes).encoded } + end - def self.full_access_token(*names) - registry = Gitlab.config.registry - token = ::JWT::RSAToken.new(registry.key) - token.issuer = registry.issuer - token.audience = AUDIENCE - token[:access] = names.map do |name| - { type: 'repository', name: name, actions: %w(pull push) } + def self.full_access_token(*names) + registry = Gitlab.config.registry + token = ::JWT::RSAToken.new(registry.key) + token.issuer = registry.issuer + token.audience = AUDIENCE + token[:access] = names.map do |name| + { type: 'repository', name: name, actions: %w(pull push) } + end + token.encoded end - token.encoded - end - private + private - def authorized_token(access) - token = ::JWT::RSAToken.new(registry.key) - token.issuer = registry.issuer - token.audience = AUDIENCE - token.subject = current_user.try(:username) - token[:access] = access - token - end + def authorized_token(access) + token = ::JWT::RSAToken.new(registry.key) + token.issuer = registry.issuer + token.audience = params[:service] + token.subject = current_user.try(:username) + token[:access] = access + token + end - def scopes - return unless params[:scope] + def scopes + return unless params[:scope] - @scopes ||= begin - scope = process_scope(params[:scope]) - [scope].compact + @scopes ||= begin + scope = process_scope(params[:scope]) + [scope].compact + end end - end - def process_scope(scope) - type, name, actions = scope.split(':', 3) - actions = actions.split(',') + def process_scope(scope) + type, name, actions = scope.split(':', 3) + actions = actions.split(',') - case type - when 'repository' - process_repository_access(type, name, actions) + case type + when 'repository' + process_repository_access(type, name, actions) + end end - end - def process_repository_access(type, name, actions) - requested_project = Project.find_with_namespace(name) - return unless requested_project + def process_repository_access(type, name, actions) + requested_project = Project.find_with_namespace(name) + return unless requested_project - actions = actions.select do |action| - can_access?(requested_project, action) - end + actions = actions.select do |action| + can_access?(requested_project, action) + end - { type: type, name: name, actions: actions } if actions.present? - end + { type: type, name: name, actions: actions } if actions.present? + end +<<<<<<< HEAD def can_access?(requested_project, requested_action) return false unless requested_project.container_registry_enabled? @@ -76,11 +78,22 @@ module JWT requested_project == project || can?(current_user, :create_container_registry, requested_project) else false +======= + def can_access?(requested_project, requested_action) + case requested_action + when 'pull' + requested_project.public? || requested_project == project || can?(current_user, :read_container_registry, requested_project) + when 'push' + requested_project == project || can?(current_user, :create_container_registry, requested_project) + else + false + end +>>>>>>> docker-registry end - end - def registry - Gitlab.config.registry + def registry + Gitlab.config.registry + end end end end diff --git a/lib/jwt/rsa_token.rb b/lib/jwt/rsa_token.rb index 0438135ad54..4de89bf0d37 100644 --- a/lib/jwt/rsa_token.rb +++ b/lib/jwt/rsa_token.rb @@ -24,11 +24,13 @@ module JWT @key ||= OpenSSL::PKey::RSA.new(key_data) end + def public_key + key.public_key + end + def kid - sha256 = Digest::SHA256.new - sha256.update(key.public_key.to_der) - payload = StringIO.new(sha256.digest).read(30) - Base32.encode(payload).split('').each_slice(4).each_with_object([]) do |slice, mem| + fingerprint = Digest::SHA256.digest(public_key.to_der) + Base32.encode(fingerprint).split('').each_slice(4).each_with_object([]) do |slice, mem| mem << slice.join end.join(':') end diff --git a/spec/features/container_registry_spec.rb b/spec/features/container_registry_spec.rb index 6c4d675fd6a..992da289028 100644 --- a/spec/features/container_registry_spec.rb +++ b/spec/features/container_registry_spec.rb @@ -16,7 +16,7 @@ describe "Container Registry" do project.team << [@user, :developer] stub_container_registry(*tags) allow(Gitlab.config.registry).to receive_messages(registry_settings) - allow(JWT::ContainerRegistryAuthenticationService).to receive(:full_access_token).and_return('token') + allow(Gitlab::JWT::ContainerRegistryAuthenticationService).to receive(:full_access_token).and_return('token') end describe 'GET /:project/container_registry' do diff --git a/spec/services/jwt/container_registry_authentication_service_spec.rb b/spec/services/jwt/container_registry_authentication_service_spec.rb index 672a7579dd3..c8a0ba958dd 100644 --- a/spec/services/jwt/container_registry_authentication_service_spec.rb +++ b/spec/services/jwt/container_registry_authentication_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe JWT::ContainerRegistryAuthenticationService, services: true do +describe Gitlab::JWT::ContainerRegistryAuthenticationService, services: true do let(:current_project) { nil } let(:current_user) { nil } let(:current_params) { {} } |