diff --git a/app/models/deploy_token.rb b/app/models/deploy_token.rb
index b4df44d295a05d7afee018aa68e4a10fda188998..c70d1457afb5f1be3445105814a66f7a24db0d5f 100644
--- a/app/models/deploy_token.rb
+++ b/app/models/deploy_token.rb
@@ -29,6 +29,10 @@ def active?
   end
 
   def username
-    User.ghost.username
+    "gitlab+deploy-token-#{id}"
+  end
+
+  def has_access_to?(project)
+    self.project == project
   end
 end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 2f9dd0384bca2a58dd4e4af2ad63242151649b99..21bb0934dee9752b2cfca819249e8d6449c04e56 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -145,7 +145,7 @@ def self.create_read_update_admin(name)
   # These abilities are not allowed to admins that are not members of the project,
   # that's why they are defined separately.
   rule { guest & can?(:download_code) }.enable :build_download_code
-  rule { guest & can?(:read_container_image) }.enable :project_read_container_image
+  rule { guest & can?(:read_container_image) }.enable :build_read_container_image
 
   rule { can?(:reporter_access) }.policy do
     enable :download_code
@@ -179,7 +179,7 @@ def self.create_read_update_admin(name)
 
     enable :fork_project
     enable :build_download_code
-    enable :project_read_container_image
+    enable :build_read_container_image
     enable :request_access
   end
 
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index d70ac7b1b3df77efda7e36b08879c4f6a2a21118..2ac35f5bd64847460e87c4c5458796ac43829937 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -109,7 +109,7 @@ def can_access?(requested_project, requested_action)
 
       case requested_action
       when 'pull'
-        build_can_pull?(requested_project) || user_can_pull?(requested_project)
+        build_can_pull?(requested_project) || user_can_pull?(requested_project) || deploy_token_can_pull?(requested_project)
       when 'push'
         build_can_push?(requested_project) || user_can_push?(requested_project)
       when '*'
@@ -123,22 +123,33 @@ def registry
       Gitlab.config.registry
     end
 
+    def can_user?(ability, project)
+      current_user.is_a?(User) &&
+        can?(current_user, ability, project)
+    end
+
     def build_can_pull?(requested_project)
       # Build can:
       # 1. pull from its own project (for ex. a build)
       # 2. read images from dependent projects if creator of build is a team member
-      has_authentication_ability?(:project_read_container_image) &&
-        (requested_project == project || can?(current_user, :project_read_container_image, requested_project))
+      has_authentication_ability?(:build_read_container_image) &&
+        (requested_project == project || can_user?(:build_read_container_image, requested_project))
     end
 
     def user_can_admin?(requested_project)
       has_authentication_ability?(:admin_container_image) &&
-        can?(current_user, :admin_container_image, requested_project)
+        can_user?(:admin_container_image, requested_project)
     end
 
     def user_can_pull?(requested_project)
       has_authentication_ability?(:read_container_image) &&
-        can?(current_user, :read_container_image, requested_project)
+        can_user?(:read_container_image, requested_project)
+    end
+    
+    def deploy_token_can_pull?(requested_project)
+      has_authentication_ability?(:read_container_image) &&
+        current_user.is_a?(DeployToken) &&
+        current_user.has_access_to?(requested_project)
     end
 
     ##
@@ -154,7 +165,7 @@ def build_can_push?(requested_project)
 
     def user_can_push?(requested_project)
       has_authentication_ability?(:create_container_image) &&
-        can?(current_user, :create_container_image, requested_project)
+        can_user?(current_user, :create_container_image, requested_project)
     end
 
     def error(code, status:, message: '')
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index 35458f607c60b5ea045f0c3873cd30bf3798dba2..336cdbab5f08b133518f2ee4092681202ca0db9c 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -26,7 +26,7 @@ def find_for_git_client(login, password, project:, ip:)
           lfs_token_check(login, password, project) ||
           oauth_access_token_check(login, password) ||
           personal_access_token_check(password) ||
-          deploy_token_check(project, password) ||
+          deploy_token_check(login, password) ||
           user_with_password_for_git(login, password) ||
           Gitlab::Auth::Result.new
 
@@ -176,18 +176,18 @@ def abilities_for_scopes(scopes)
       # Project is always sent when using read_scope,
       # but is not sent when using read_registry scope
       # (since jwt is not context aware of the project)
-      def deploy_token_check(project, password)
+      def deploy_token_check(login, password)
         return unless password.present?
 
         token =
-          if project.present?
-            DeployToken.active.find_by(project: project, token: password)
-          else
-            DeployToken.active.find_by(token: password)
-          end
-
-        if token && valid_scoped_token?(token, available_scopes)
-          Gitlab::Auth::Result.new(token, token.project, :deploy_token, abilities_for_scopes(token.scopes))
+          DeployToken.active.find_by(token: password)
+
+        return unless token
+        return unless login != "gitlab+deploy-token-#{token.id}"
+        
+        scopes = abilities_for_scopes(token.scopes)
+        if valid_scoped_token?(token, scopes)
+          Gitlab::Auth::Result.new(token, token.project, :deploy_token, scopes)
         end
       end
 
@@ -242,7 +242,7 @@ def build_authentication_abilities
         [
           :read_project,
           :build_download_code,
-          :project_read_container_image,
+          :build_read_container_image,
           :build_create_container_image
         ]
       end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index e3c723ab2743d3952b0c3d5b7b15c086b5190444..0d1ee73ca1a34a7ef6e2221761f0b4298865581c 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -290,10 +290,10 @@ def ci?
     def can_read_project?
       if deploy_key?
         deploy_key.has_access_to?(project)
+      elsif deploy_token?
+        deploy_token.has_access_to?(project)
       elsif user
         user.can?(:read_project, project)
-      elsif deploy_token?
-        deploy_token.active? && deploy_token.project == project
       elsif ci?
         true # allow CI (build without a user) for backwards compatibility
       end || Guest.can?(:read_project, project)
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index f704c20f5984b642c2841b43c02e11ddab5f30c5..4ed554f06ec8faa7ded71a2229e95cec57ee8748 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -195,7 +195,7 @@ def operation
           personal_access_token = create(:personal_access_token, scopes: ['read_registry'])
 
           expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: '')
-          expect(gl_auth.find_for_git_client('', personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(personal_access_token.user, nil, :personal_access_token, [:read_project, :build_download_code, :project_read_container_image]))
+          expect(gl_auth.find_for_git_client('', personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(personal_access_token.user, nil, :personal_access_token, [:read_project, :build_download_code, :build_read_container_image]))
         end
       end
 
@@ -310,7 +310,7 @@ def operation
           end
 
           it 'succeeds if deploy token does have read_registry as scope' do
-            abilities = %i(read_project build_download_code project_read_container_image)
+            abilities = %i(read_project build_download_code build_read_container_image)
             auth_success = Gitlab::Auth::Result.new(deploy_token, project, :deploy_token, abilities)
 
             expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: '')
@@ -477,7 +477,7 @@ def build_authentication_abilities
     [
       :read_project,
       :build_download_code,
-      :project_read_container_image,
+      :build_read_container_image,
       :build_create_container_image
     ]
   end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index f5d9a58f83c12e5f68814072f884bcc93a117f2e..905d82b3bb1d87a1416bf0ff38d60354e5963a3f 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -28,7 +28,7 @@
   end
 
   let(:team_member_reporter_permissions) do
-    %i[build_download_code project_read_container_image]
+    %i[build_download_code build_read_container_image]
   end
 
   let(:developer_permissions) do
@@ -54,7 +54,7 @@
   let(:public_permissions) do
     %i[
       download_code fork_project read_commit_status read_pipeline
-      read_container_image build_download_code project_read_container_image
+      read_container_image build_download_code build_read_container_image
       download_wiki_code
     ]
   end
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
index 1cb0508cdf56f615c2114342ab287ba31129981b..290eeae828ee6bb88a73a6e6c0b5f5a2bc1ac827 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -373,7 +373,7 @@
     let(:current_user) { create(:user) }
 
     let(:authentication_abilities) do
-      [:project_read_container_image, :build_create_container_image]
+      [:build_read_container_image, :build_create_container_image]
     end
 
     before do