InfoGrab Docs

활성 세션

요약

GitLab은 계정에 로그인한 모든 기기를 나열합니다. GitLab에서는 사용자가 한 번에 최대 100개의 활성 세션을 보유할 수 있습니다. 세션이 취소되면 모든 기기의 로그인 상태 유지 토큰이 모두 취소됩니다. Rails 콘솔을 통해서도 사용자 세션을 취소할 수 있습니다.

GitLab은 계정에 로그인한 모든 기기를 나열합니다. 세션을 검토하고 인식하지 못하는 세션은 취소할 수 있습니다.

모든 활성 세션 나열#

모든 활성 세션을 나열하려면:

  1. 오른쪽 상단에서 아바타를 선택합니다.
  2. 프로필 편집을 선택합니다.
  3. 왼쪽 사이드바에서 액세스 > 활성 세션을 선택합니다.

활성 세션 목록

활성 세션 제한#

GitLab에서는 사용자가 한 번에 최대 100개의 활성 세션을 보유할 수 있습니다. 활성 세션 수가 100개를 초과하면 가장 오래된 세션이 삭제됩니다.

세션 취소#

활성 세션을 취소하려면:

  1. 오른쪽 상단에서 아바타를 선택합니다.
  2. 프로필 편집을 선택합니다.
  3. 왼쪽 사이드바에서 액세스 > 활성 세션을 선택합니다.
  4. 세션 옆에서 취소를 선택합니다. 현재 세션은 취소할 수 없습니다. 취소하면 GitLab에서 로그아웃됩니다.
Note

세션이 취소되면 모든 기기의 로그인 상태 유지 토큰이 모두 취소됩니다. 로그인 상태 유지에 대한 자세한 내용은 로그인에 사용되는 쿠키를 참조하세요.

Rails 콘솔을 통해 세션 취소#

Rails 콘솔을 통해서도 사용자 세션을 취소할 수 있습니다. 이를 사용하여 동시에 여러 세션을 취소할 수 있습니다.

모든 사용자의 모든 세션 취소#

모든 사용자의 모든 세션을 취소하려면:

  1. Rails 콘솔 세션 시작.

  2. 선택 사항. 다음 명령으로 모든 활성 세션을 나열합니다:

    # Show all users with active sessions
     puts "=== Currently Logged In Users ==="
     User.find_each do |user|
         sessions = ActiveSession.list(user)
         if sessions.any?
             puts "\n#{user.username} (#{user.name}):"
             sessions.each do |session|
                 puts "  - IP: #{session.ip_address}, Browser: #{session.browser}, Last active: #{session.updated_at}"
             end
         end
     end
    
  3. 다음 명령으로 모든 세션을 취소합니다:

    User.find_each do |user|
       ActiveSession.destroy_all_but_current(user, nil)
    end
    
  4. 선택 사항. "모든 활성 세션 나열" 명령을 다시 실행하여 모든 세션이 취소되었는지 확인합니다.

그룹의 모든 사용자 세션 취소#

  1. 다음 스크립트를 GitLab 인스턴스에 저장합니다. 예: scripts/session_revocation/revoke_group_sessions.rb.

    # frozen_string_literal: true
    #
    # Revoke all active sessions for members of a group, including:
    # - Direct and inherited members of the top-level group
    # - Direct members of all subgroups
    # - Members invited via group shares at the top-level and subgroup level
    #
    # Usage (Rails console):
    #   DRY_RUN = true
    #   GROUP_IDENTIFIER = 'your-group-path' # or numeric ID
    #   load 'scripts/session_revocation/revoke_group_sessions.rb'
    
    DRY_RUN = true unless defined?(DRY_RUN)
    # Replace `your-group-path` with your group ID or the full path to your group
    GROUP_IDENTIFIER = 'your-group-path' unless defined?(GROUP_IDENTIFIER)
    
    # ---------------------------------------------------------------
    
    def find_group(identifier)
       # Try finding by full path first (handles numeric group names)
       group = Group.find_by_full_path(identifier.to_s)
       return group if group
    
       # Fallback to ID lookup if path not found and identifier is numeric
       if identifier.is_a?(Integer) || identifier.to_s.match?(/\A\d+\z/)
          Group.find_by(id: identifier)
       end
    end
    
    def collect_member_user_ids(group)
       user_ids = Set.new
    
       # Direct and inherited members of the top-level group
       user_ids.merge(group.members_with_parents.pluck(:user_id))
    
       # Members invited via group shares into the top-level group
       group.shared_with_group_links.each do |link|
          user_ids.merge(link.shared_with_group.members_with_parents.pluck(:user_id))
       end
    
       # Traverse all subgroups
       group.descendants.find_each do |subgroup|
          # Direct members of each subgroup
          user_ids.merge(subgroup.members.pluck(:user_id))
    
          # Members invited via group shares into each subgroup
          subgroup.shared_with_group_links.each do |link|
             user_ids.merge(link.shared_with_group.members_with_parents.pluck(:user_id))
          end
       end
    
       user_ids.to_a
    end
    
    def revoke_sessions_for_group(group, dry_run:)
       member_user_ids = collect_member_user_ids(group)
    
       puts "Found #{member_user_ids.count} unique members in group '#{group.full_path}' (including subgroups and group shares)"
    
       # Only process active, non-bot human users to avoid unnecessary Redis lookups
       users = User.active.human.id_in(member_user_ids)
    
       revoked_sessions = 0
       affected_users = []
       skipped_users = []
    
       users.find_each do |user|
          sessions = ActiveSession.list(user)
    
          if sessions.empty?
             skipped_users << user.username
             next
          end
    
          session_ids = sessions.map(&:session_private_id).compact
    
          if session_ids.empty?
             puts "  [WARN] User #{user.username} has sessions but all session_private_ids are nil, skipping."
             skipped_users << user.username
             next
          end
    
          unless dry_run
             Gitlab::Redis::Sessions.with do |redis|
                ActiveSession.destroy_sessions(redis, user, session_ids)
             end
    
             # Emit audit event for security traceability
             Gitlab::AppLogger.info(
                message: "Sessions revoked via admin script",
                user_id: user.id,
                username: user.username,
                session_count: session_ids.size,
                group: group.full_path,
                performed_at: Time.current.iso8601
             )
          end
    
          revoked_sessions += session_ids.size
          affected_users << user.username
       end
    
       [revoked_sessions, affected_users, skipped_users]
    end
    
    # ---------------------------------------------------------------
    
    group = find_group(GROUP_IDENTIFIER)
    
    if group.nil?
       puts "ERROR: Group '#{GROUP_IDENTIFIER}' not found. Aborting."
    end
    
    puts "=== Session Revocation #{DRY_RUN ? '(DRY RUN)' : '(LIVE)'} ==="
    puts "Group: #{group.full_path} (ID: #{group.id})"
    puts
    
    revoked_sessions, affected_users, skipped_users = revoke_sessions_for_group(group, dry_run: DRY_RUN)
    
    prefix = DRY_RUN ? "[DRY RUN] Would revoke" : "Revoked"
    puts "#{prefix} #{revoked_sessions} sessions for #{affected_users.size} users"
    puts "Users affected: #{affected_users.sort.join(', ')}" if affected_users.any?
    puts "Users skipped (no active sessions): #{skipped_users.size}" if skipped_users.any?
    
    if DRY_RUN && revoked_sessions.positive?
       puts "\nTo actually revoke sessions, set DRY_RUN = false and run again."
    end
    
  2. Rails 콘솔 세션을 시작합니다.

  3. 다음 명령을 실행하여 그룹을 지정합니다. your-group-path를 그룹 ID 또는 그룹의 전체 경로로 바꿉니다:

    GROUP_IDENTIFIER = 'your-group-path'
    
  4. 다음 명령을 실행하여 그룹의 모든 활성 세션을 나열합니다:

    DRY_RUN = true
    load 'scripts/session_revocation/revoke_group_sessions.rb'
    
  5. 다음 명령을 실행하여 그룹의 모든 세션을 취소합니다:

    DRY_RUN = false
    load 'scripts/session_revocation/revoke_group_sessions.rb'
    
  6. 다음 명령을 실행하여 모든 세션이 종료되었는지 확인합니다. 출력에 활성 세션이 0개로 표시되어야 합니다:

    DRY_RUN = true
    load 'scripts/session_revocation/revoke_group_sessions.rb'
    

사용자의 모든 세션 취소#

특정 사용자의 모든 세션을 취소하려면:

  1. Rails 콘솔 세션 시작.

  2. 다음 명령으로 사용자를 찾습니다:

    • 사용자 이름으로:

      user = User.find_by_username 'exampleuser'
      
    • 사용자 ID로:

      user = User.find(123)
      
    • 이메일 주소로:

      user = User.find_by(email: 'user@example.com')
      
  3. 선택 사항. 다음 명령으로 사용자의 모든 활성 세션을 나열합니다:

    ActiveSession.list(user)
    
  4. 다음 명령으로 모든 세션을 취소합니다:

    ActiveSession.list(user).each { |session| ActiveSession.destroy_session(user, session.session_private_id) }
    
  5. 다음 명령으로 모든 세션이 종료되었는지 확인합니다:

    # If all sessions are closed, returns an empty array.
    ActiveSession.list(user)
    

활성 세션

Tier: Free, Premium, Ultimate
Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
원문 보기
요약

GitLab은 계정에 로그인한 모든 기기를 나열합니다. GitLab에서는 사용자가 한 번에 최대 100개의 활성 세션을 보유할 수 있습니다. 세션이 취소되면 모든 기기의 로그인 상태 유지 토큰이 모두 취소됩니다. Rails 콘솔을 통해서도 사용자 세션을 취소할 수 있습니다.

GitLab은 계정에 로그인한 모든 기기를 나열합니다. 세션을 검토하고 인식하지 못하는 세션은 취소할 수 있습니다.

모든 활성 세션 나열#

모든 활성 세션을 나열하려면:

  1. 오른쪽 상단에서 아바타를 선택합니다.
  2. 프로필 편집을 선택합니다.
  3. 왼쪽 사이드바에서 액세스 > 활성 세션을 선택합니다.

활성 세션 목록

활성 세션 제한#

GitLab에서는 사용자가 한 번에 최대 100개의 활성 세션을 보유할 수 있습니다. 활성 세션 수가 100개를 초과하면 가장 오래된 세션이 삭제됩니다.

세션 취소#

활성 세션을 취소하려면:

  1. 오른쪽 상단에서 아바타를 선택합니다.
  2. 프로필 편집을 선택합니다.
  3. 왼쪽 사이드바에서 액세스 > 활성 세션을 선택합니다.
  4. 세션 옆에서 취소를 선택합니다. 현재 세션은 취소할 수 없습니다. 취소하면 GitLab에서 로그아웃됩니다.
Note

세션이 취소되면 모든 기기의 로그인 상태 유지 토큰이 모두 취소됩니다. 로그인 상태 유지에 대한 자세한 내용은 로그인에 사용되는 쿠키를 참조하세요.

Rails 콘솔을 통해 세션 취소#

Rails 콘솔을 통해서도 사용자 세션을 취소할 수 있습니다. 이를 사용하여 동시에 여러 세션을 취소할 수 있습니다.

모든 사용자의 모든 세션 취소#

모든 사용자의 모든 세션을 취소하려면:

  1. Rails 콘솔 세션 시작.

  2. 선택 사항. 다음 명령으로 모든 활성 세션을 나열합니다:

    # Show all users with active sessions
     puts "=== Currently Logged In Users ==="
     User.find_each do |user|
         sessions = ActiveSession.list(user)
         if sessions.any?
             puts "\n#{user.username} (#{user.name}):"
             sessions.each do |session|
                 puts "  - IP: #{session.ip_address}, Browser: #{session.browser}, Last active: #{session.updated_at}"
             end
         end
     end
    
  3. 다음 명령으로 모든 세션을 취소합니다:

    User.find_each do |user|
       ActiveSession.destroy_all_but_current(user, nil)
    end
    
  4. 선택 사항. "모든 활성 세션 나열" 명령을 다시 실행하여 모든 세션이 취소되었는지 확인합니다.

그룹의 모든 사용자 세션 취소#

  1. 다음 스크립트를 GitLab 인스턴스에 저장합니다. 예: scripts/session_revocation/revoke_group_sessions.rb.

    # frozen_string_literal: true
    #
    # Revoke all active sessions for members of a group, including:
    # - Direct and inherited members of the top-level group
    # - Direct members of all subgroups
    # - Members invited via group shares at the top-level and subgroup level
    #
    # Usage (Rails console):
    #   DRY_RUN = true
    #   GROUP_IDENTIFIER = 'your-group-path' # or numeric ID
    #   load 'scripts/session_revocation/revoke_group_sessions.rb'
    
    DRY_RUN = true unless defined?(DRY_RUN)
    # Replace `your-group-path` with your group ID or the full path to your group
    GROUP_IDENTIFIER = 'your-group-path' unless defined?(GROUP_IDENTIFIER)
    
    # ---------------------------------------------------------------
    
    def find_group(identifier)
       # Try finding by full path first (handles numeric group names)
       group = Group.find_by_full_path(identifier.to_s)
       return group if group
    
       # Fallback to ID lookup if path not found and identifier is numeric
       if identifier.is_a?(Integer) || identifier.to_s.match?(/\A\d+\z/)
          Group.find_by(id: identifier)
       end
    end
    
    def collect_member_user_ids(group)
       user_ids = Set.new
    
       # Direct and inherited members of the top-level group
       user_ids.merge(group.members_with_parents.pluck(:user_id))
    
       # Members invited via group shares into the top-level group
       group.shared_with_group_links.each do |link|
          user_ids.merge(link.shared_with_group.members_with_parents.pluck(:user_id))
       end
    
       # Traverse all subgroups
       group.descendants.find_each do |subgroup|
          # Direct members of each subgroup
          user_ids.merge(subgroup.members.pluck(:user_id))
    
          # Members invited via group shares into each subgroup
          subgroup.shared_with_group_links.each do |link|
             user_ids.merge(link.shared_with_group.members_with_parents.pluck(:user_id))
          end
       end
    
       user_ids.to_a
    end
    
    def revoke_sessions_for_group(group, dry_run:)
       member_user_ids = collect_member_user_ids(group)
    
       puts "Found #{member_user_ids.count} unique members in group '#{group.full_path}' (including subgroups and group shares)"
    
       # Only process active, non-bot human users to avoid unnecessary Redis lookups
       users = User.active.human.id_in(member_user_ids)
    
       revoked_sessions = 0
       affected_users = []
       skipped_users = []
    
       users.find_each do |user|
          sessions = ActiveSession.list(user)
    
          if sessions.empty?
             skipped_users << user.username
             next
          end
    
          session_ids = sessions.map(&:session_private_id).compact
    
          if session_ids.empty?
             puts "  [WARN] User #{user.username} has sessions but all session_private_ids are nil, skipping."
             skipped_users << user.username
             next
          end
    
          unless dry_run
             Gitlab::Redis::Sessions.with do |redis|
                ActiveSession.destroy_sessions(redis, user, session_ids)
             end
    
             # Emit audit event for security traceability
             Gitlab::AppLogger.info(
                message: "Sessions revoked via admin script",
                user_id: user.id,
                username: user.username,
                session_count: session_ids.size,
                group: group.full_path,
                performed_at: Time.current.iso8601
             )
          end
    
          revoked_sessions += session_ids.size
          affected_users << user.username
       end
    
       [revoked_sessions, affected_users, skipped_users]
    end
    
    # ---------------------------------------------------------------
    
    group = find_group(GROUP_IDENTIFIER)
    
    if group.nil?
       puts "ERROR: Group '#{GROUP_IDENTIFIER}' not found. Aborting."
    end
    
    puts "=== Session Revocation #{DRY_RUN ? '(DRY RUN)' : '(LIVE)'} ==="
    puts "Group: #{group.full_path} (ID: #{group.id})"
    puts
    
    revoked_sessions, affected_users, skipped_users = revoke_sessions_for_group(group, dry_run: DRY_RUN)
    
    prefix = DRY_RUN ? "[DRY RUN] Would revoke" : "Revoked"
    puts "#{prefix} #{revoked_sessions} sessions for #{affected_users.size} users"
    puts "Users affected: #{affected_users.sort.join(', ')}" if affected_users.any?
    puts "Users skipped (no active sessions): #{skipped_users.size}" if skipped_users.any?
    
    if DRY_RUN && revoked_sessions.positive?
       puts "\nTo actually revoke sessions, set DRY_RUN = false and run again."
    end
    
  2. Rails 콘솔 세션을 시작합니다.

  3. 다음 명령을 실행하여 그룹을 지정합니다. your-group-path를 그룹 ID 또는 그룹의 전체 경로로 바꿉니다:

    GROUP_IDENTIFIER = 'your-group-path'
    
  4. 다음 명령을 실행하여 그룹의 모든 활성 세션을 나열합니다:

    DRY_RUN = true
    load 'scripts/session_revocation/revoke_group_sessions.rb'
    
  5. 다음 명령을 실행하여 그룹의 모든 세션을 취소합니다:

    DRY_RUN = false
    load 'scripts/session_revocation/revoke_group_sessions.rb'
    
  6. 다음 명령을 실행하여 모든 세션이 종료되었는지 확인합니다. 출력에 활성 세션이 0개로 표시되어야 합니다:

    DRY_RUN = true
    load 'scripts/session_revocation/revoke_group_sessions.rb'
    

사용자의 모든 세션 취소#

특정 사용자의 모든 세션을 취소하려면:

  1. Rails 콘솔 세션 시작.

  2. 다음 명령으로 사용자를 찾습니다:

    • 사용자 이름으로:

      user = User.find_by_username 'exampleuser'
      
    • 사용자 ID로:

      user = User.find(123)
      
    • 이메일 주소로:

      user = User.find_by(email: 'user@example.com')
      
  3. 선택 사항. 다음 명령으로 사용자의 모든 활성 세션을 나열합니다:

    ActiveSession.list(user)
    
  4. 다음 명령으로 모든 세션을 취소합니다:

    ActiveSession.list(user).each { |session| ActiveSession.destroy_session(user, session.session_private_id) }
    
  5. 다음 명령으로 모든 세션이 종료되었는지 확인합니다:

    # If all sessions are closed, returns an empty array.
    ActiveSession.list(user)