check.rake 18 KB
Newer Older
1 2 3 4
# Temporary hack, until we migrate all checks to SystemCheck format
require 'system_check'
require 'system_check/helpers'

5
namespace :gitlab do
6
  desc 'GitLab | Check the configuration of GitLab and its environment'
7
  task check: %w{gitlab:gitlab_shell:check
Riyad Preukschas's avatar
Riyad Preukschas committed
8
                 gitlab:sidekiq:check
9
                 gitlab:incoming_email:check
10
                 gitlab:ldap:check
Riyad Preukschas's avatar
Riyad Preukschas committed
11 12
                 gitlab:app:check}

13
  namespace :app do
14
    desc 'GitLab | Check the configuration of the GitLab Rails app'
15
    task check: :environment  do
Riyad Preukschas's avatar
Riyad Preukschas committed
16
      warn_user_is_not_gitlab
17 18 19 20 21 22 23

      checks = [
        SystemCheck::App::GitConfigCheck,
        SystemCheck::App::DatabaseConfigExistsCheck,
        SystemCheck::App::MigrationsAreUpCheck,
        SystemCheck::App::OrphanedGroupMembersCheck,
        SystemCheck::App::GitlabConfigExistsCheck,
24
        SystemCheck::App::GitlabConfigUpToDateCheck,
25 26 27 28 29 30 31 32 33 34
        SystemCheck::App::LogWritableCheck,
        SystemCheck::App::TmpWritableCheck,
        SystemCheck::App::UploadsDirectoryExistsCheck,
        SystemCheck::App::UploadsPathPermissionCheck,
        SystemCheck::App::UploadsPathTmpPermissionCheck,
        SystemCheck::App::InitScriptExistsCheck,
        SystemCheck::App::InitScriptUpToDateCheck,
        SystemCheck::App::ProjectsHaveNamespaceCheck,
        SystemCheck::App::RedisVersionCheck,
        SystemCheck::App::RubyVersionCheck,
Gabriel Mazetto's avatar
Gabriel Mazetto committed
35
        SystemCheck::App::GitVersionCheck,
36
        SystemCheck::App::GitUserDefaultSSHConfigCheck,
Gabriel Mazetto's avatar
Gabriel Mazetto committed
37
        SystemCheck::App::ActiveUsersCheck
38 39
      ]

40 41 42
      # EE only
      checks << SystemCheck::App::ElasticsearchCheck

43
      SystemCheck.run('GitLab', checks)
44
    end
45 46
  end

47
  namespace :gitlab_shell do
48
    desc "GitLab | Check the configuration of GitLab Shell"
49
    task check: :environment  do
Riyad Preukschas's avatar
Riyad Preukschas committed
50
      warn_user_is_not_gitlab
Ben Bodenmiller's avatar
Ben Bodenmiller committed
51
      start_checking "GitLab Shell"
Riyad Preukschas's avatar
Riyad Preukschas committed
52

53
      check_gitlab_shell
Riyad Preukschas's avatar
Riyad Preukschas committed
54
      check_repo_base_exists
55
      check_repo_base_is_not_symlink
Riyad Preukschas's avatar
Riyad Preukschas committed
56 57
      check_repo_base_user_and_group
      check_repo_base_permissions
58
      check_repos_hooks_directory_is_link
59
      check_gitlab_shell_self_test
Riyad Preukschas's avatar
Riyad Preukschas committed
60

Ben Bodenmiller's avatar
Ben Bodenmiller committed
61
      finished_checking "GitLab Shell"
Riyad Preukschas's avatar
Riyad Preukschas committed
62 63 64 65 66 67
    end

    # Checks
    ########################

    def check_repo_base_exists
68
      puts "Repo base directory exists?"
Riyad Preukschas's avatar
Riyad Preukschas committed
69

70 71
      Gitlab.config.repositories.storages.each do |name, repository_storage|
        repo_base_path = repository_storage['path']
72
        print "#{name}... "
Riyad Preukschas's avatar
Riyad Preukschas committed
73

74
        if File.exist?(repo_base_path)
75 76 77 78 79 80 81 82 83 84 85 86 87 88
          puts "yes".color(:green)
        else
          puts "no".color(:red)
          puts "#{repo_base_path} is missing".color(:red)
          try_fixing_it(
            "This should have been created when setting up GitLab Shell.",
            "Make sure it's set correctly in config/gitlab.yml",
            "Make sure GitLab Shell is installed correctly."
          )
          for_more_information(
            see_installation_guide_section "GitLab Shell"
          )
          fix_and_rerun
        end
Riyad Preukschas's avatar
Riyad Preukschas committed
89 90 91
      end
    end

92
    def check_repo_base_is_not_symlink
93
      puts "Repo storage directories are symlinks?"
94

95 96
      Gitlab.config.repositories.storages.each do |name, repository_storage|
        repo_base_path = repository_storage['path']
97
        print "#{name}... "
98

99
        unless File.exist?(repo_base_path)
100
          puts "can't check because of previous errors".color(:magenta)
101
          break
102 103 104 105 106 107 108 109 110 111 112
        end

        unless File.symlink?(repo_base_path)
          puts "no".color(:green)
        else
          puts "yes".color(:red)
          try_fixing_it(
            "Make sure it's set to the real directory in config/gitlab.yml"
          )
          fix_and_rerun
        end
113 114 115
      end
    end

Riyad Preukschas's avatar
Riyad Preukschas committed
116
    def check_repo_base_permissions
117
      puts "Repo paths access is drwxrws---?"
Riyad Preukschas's avatar
Riyad Preukschas committed
118

119 120
      Gitlab.config.repositories.storages.each do |name, repository_storage|
        repo_base_path = repository_storage['path']
121
        print "#{name}... "
122

123
        unless File.exist?(repo_base_path)
124
          puts "can't check because of previous errors".color(:magenta)
125
          break
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
        end

        if File.stat(repo_base_path).mode.to_s(8).ends_with?("2770")
          puts "yes".color(:green)
        else
          puts "no".color(:red)
          try_fixing_it(
            "sudo chmod -R ug+rwX,o-rwx #{repo_base_path}",
            "sudo chmod -R ug-s #{repo_base_path}",
            "sudo find #{repo_base_path} -type d -print0 | sudo xargs -0 chmod g+s"
          )
          for_more_information(
            see_installation_guide_section "GitLab Shell"
          )
          fix_and_rerun
        end
Riyad Preukschas's avatar
Riyad Preukschas committed
142 143 144 145
      end
    end

    def check_repo_base_user_and_group
146
      gitlab_shell_ssh_user = Gitlab.config.gitlab_shell.ssh_user
147
      puts "Repo paths owned by #{gitlab_shell_ssh_user}:root, or #{gitlab_shell_ssh_user}:#{Gitlab.config.gitlab_shell.owner_group}?"
Riyad Preukschas's avatar
Riyad Preukschas committed
148

149 150
      Gitlab.config.repositories.storages.each do |name, repository_storage|
        repo_base_path = repository_storage['path']
151
        print "#{name}... "
152

153
        unless File.exist?(repo_base_path)
154
          puts "can't check because of previous errors".color(:magenta)
155
          break
156 157
        end

158 159 160 161
        user_id = uid_for(gitlab_shell_ssh_user)
        root_group_id = gid_for('root')
        group_ids = [root_group_id, gid_for(Gitlab.config.gitlab_shell.owner_group)]
        if File.stat(repo_base_path).uid == user_id && group_ids.include?(File.stat(repo_base_path).gid)
162 163 164
          puts "yes".color(:green)
        else
          puts "no".color(:red)
165
          puts "  User id for #{gitlab_shell_ssh_user}: #{user_id}. Groupd id for root: #{root_group_id}".color(:blue)
166
          try_fixing_it(
167
            "sudo chown -R #{gitlab_shell_ssh_user}:root #{repo_base_path}"
168 169 170 171 172 173
          )
          for_more_information(
            see_installation_guide_section "GitLab Shell"
          )
          fix_and_rerun
        end
Riyad Preukschas's avatar
Riyad Preukschas committed
174 175 176
      end
    end

177 178
    def check_repos_hooks_directory_is_link
      print "hooks directories in repos are links: ... "
Riyad Preukschas's avatar
Riyad Preukschas committed
179

180
      gitlab_shell_hooks_path = Gitlab.config.gitlab_shell.hooks_path
181

Riyad Preukschas's avatar
Riyad Preukschas committed
182
      unless Project.count > 0
183
        puts "can't check, you have no projects".color(:magenta)
Riyad Preukschas's avatar
Riyad Preukschas committed
184 185 186
        return
      end
      puts ""
187

Riyad Preukschas's avatar
Riyad Preukschas committed
188
      Project.find_each(batch_size: 100) do |project|
Marin Jankovski's avatar
Marin Jankovski committed
189
        print sanitized_message(project)
190
        project_hook_directory = File.join(project.repository.path_to_repo, "hooks")
191

Riyad Preukschas's avatar
Riyad Preukschas committed
192
        if project.empty_repo?
193
          puts "repository is empty".color(:magenta)
194 195
        elsif File.directory?(project_hook_directory) && File.directory?(gitlab_shell_hooks_path) &&
            (File.realpath(project_hook_directory) == File.realpath(gitlab_shell_hooks_path))
196
          puts 'ok'.color(:green)
Riyad Preukschas's avatar
Riyad Preukschas committed
197
        else
198
          puts "wrong or missing hooks".color(:red)
199
          try_fixing_it(
200
            sudo_gitlab("#{File.join(gitlab_shell_path, 'bin/create-hooks')} #{repository_storage_paths_args.join(' ')}"),
201 202 203 204 205 206 207
            'Check the hooks_path in config/gitlab.yml',
            'Check your gitlab-shell installation'
          )
          for_more_information(
            see_installation_guide_section "GitLab Shell"
          )
          fix_and_rerun
208 209
        end
      end
210
    end
Riyad Preukschas's avatar
Riyad Preukschas committed
211

212
    def check_gitlab_shell_self_test
213
      gitlab_shell_repo_base = gitlab_shell_path
214 215 216
      check_cmd = File.expand_path('bin/check', gitlab_shell_repo_base)
      puts "Running #{check_cmd}"
      if system(check_cmd, chdir: gitlab_shell_repo_base)
217
        puts 'gitlab-shell self-check successful'.color(:green)
218
      else
219
        puts 'gitlab-shell self-check failed'.color(:red)
220 221 222 223 224 225 226 227 228
        try_fixing_it(
          'Make sure GitLab is running;',
          'Check the gitlab-shell configuration file:',
          sudo_gitlab("editor #{File.expand_path('config.yml', gitlab_shell_repo_base)}")
        )
        fix_and_rerun
      end
    end

Riyad Preukschas's avatar
Riyad Preukschas committed
229 230 231
    # Helper methods
    ########################

232 233
    def gitlab_shell_path
      Gitlab.config.gitlab_shell.path
Riyad Preukschas's avatar
Riyad Preukschas committed
234 235
    end

236
    def gitlab_shell_version
237
      Gitlab::Shell.new.version
Riyad Preukschas's avatar
Riyad Preukschas committed
238 239
    end

240
    def gitlab_shell_major_version
241
      Gitlab::Shell.version_required.split('.')[0].to_i
242 243 244
    end

    def gitlab_shell_minor_version
245
      Gitlab::Shell.version_required.split('.')[1].to_i
246 247 248
    end

    def gitlab_shell_patch_version
249
      Gitlab::Shell.version_required.split('.')[2].to_i
250
    end
251
  end
252

Riyad Preukschas's avatar
Riyad Preukschas committed
253
  namespace :sidekiq do
254
    desc "GitLab | Check the configuration of Sidekiq"
255
    task check: :environment  do
Riyad Preukschas's avatar
Riyad Preukschas committed
256
      warn_user_is_not_gitlab
Riyad Preukschas's avatar
Riyad Preukschas committed
257
      start_checking "Sidekiq"
Riyad Preukschas's avatar
Riyad Preukschas committed
258

Riyad Preukschas's avatar
Riyad Preukschas committed
259
      check_sidekiq_running
260
      only_one_sidekiq_running
Riyad Preukschas's avatar
Riyad Preukschas committed
261

Riyad Preukschas's avatar
Riyad Preukschas committed
262
      finished_checking "Sidekiq"
Riyad Preukschas's avatar
Riyad Preukschas committed
263 264 265 266 267
    end

    # Checks
    ########################

Riyad Preukschas's avatar
Riyad Preukschas committed
268
    def check_sidekiq_running
Riyad Preukschas's avatar
Riyad Preukschas committed
269 270
      print "Running? ... "

271
      if sidekiq_process_count > 0
272
        puts "yes".color(:green)
Riyad Preukschas's avatar
Riyad Preukschas committed
273
      else
274
        puts "no".color(:red)
Riyad Preukschas's avatar
Riyad Preukschas committed
275
        try_fixing_it(
276
          sudo_gitlab("RAILS_ENV=production bin/background_jobs start")
Riyad Preukschas's avatar
Riyad Preukschas committed
277 278 279
        )
        for_more_information(
          see_installation_guide_section("Install Init Script"),
280
          "see log/sidekiq.log for possible errors"
Riyad Preukschas's avatar
Riyad Preukschas committed
281
        )
Riyad Preukschas's avatar
Riyad Preukschas committed
282
        fix_and_rerun
Riyad Preukschas's avatar
Riyad Preukschas committed
283 284
      end
    end
285 286

    def only_one_sidekiq_running
287 288
      process_count = sidekiq_process_count
      return if process_count.zero?
289 290

      print 'Number of Sidekiq processes ... '
291
      if process_count == 1
292
        puts '1'.color(:green)
293
      else
294
        puts "#{process_count}".color(:red)
295 296
        try_fixing_it(
          'sudo service gitlab stop',
297 298
          "sudo pkill -u #{gitlab_user} -f sidekiq",
          "sleep 10 && sudo pkill -9 -u #{gitlab_user} -f sidekiq",
299 300 301 302 303 304
          'sudo service gitlab start'
        )
        fix_and_rerun
      end
    end

305
    def sidekiq_process_count
306
      ps_ux, _ = Gitlab::Popen.popen(%w(ps uxww))
307
      ps_ux.scan(/sidekiq \d+\.\d+\.\d+/).count
308
    end
Riyad Preukschas's avatar
Riyad Preukschas committed
309 310
  end

311
  namespace :incoming_email do
312 313 314 315 316
    desc "GitLab | Check the configuration of Reply by email"
    task check: :environment  do
      warn_user_is_not_gitlab
      start_checking "Reply by email"

317
      if Gitlab.config.incoming_email.enabled
318
        check_imap_authentication
319

320 321 322 323 324 325
        if Rails.env.production?
          check_initd_configured_correctly
          check_mail_room_running
        else
          check_foreman_configured_correctly
        end
326 327 328 329 330 331 332 333 334 335 336
      else
        puts 'Reply by email is disabled in config/gitlab.yml'
      end

      finished_checking "Reply by email"
    end

    # Checks
    ########################

    def check_initd_configured_correctly
337
      return if omnibus_gitlab?
338

339
      print "Init.d configured correctly? ... "
340

341 342 343
      path = "/etc/default/gitlab"

      if File.exist?(path) && File.read(path).include?("mail_room_enabled=true")
344
        puts "yes".color(:green)
345
      else
346
        puts "no".color(:red)
347 348 349 350
        try_fixing_it(
          "Enable mail_room in the init.d configuration."
        )
        for_more_information(
351
          "doc/administration/reply_by_email.md"
352 353 354 355 356
        )
        fix_and_rerun
      end
    end

357 358 359 360 361
    def check_foreman_configured_correctly
      print "Foreman configured correctly? ... "

      path = Rails.root.join("Procfile")

362
      if File.exist?(path) && File.read(path) =~ /^mail_room:/
363
        puts "yes".color(:green)
364
      else
365
        puts "no".color(:red)
366 367 368 369
        try_fixing_it(
          "Enable mail_room in your Procfile."
        )
        for_more_information(
370
          "doc/administration/reply_by_email.md"
371 372 373 374 375
        )
        fix_and_rerun
      end
    end

376
    def check_mail_room_running
377 378
      return if omnibus_gitlab?

379 380 381 382 383
      print "MailRoom running? ... "

      path = "/etc/default/gitlab"

      unless File.exist?(path) && File.read(path).include?("mail_room_enabled=true")
384
        puts "can't check because of previous errors".color(:magenta)
385 386 387
        return
      end

Douwe Maan's avatar
Douwe Maan committed
388
      if mail_room_running?
389
        puts "yes".color(:green)
390
      else
391
        puts "no".color(:red)
392 393 394 395 396 397 398 399 400 401 402 403 404 405
        try_fixing_it(
          sudo_gitlab("RAILS_ENV=production bin/mail_room start")
        )
        for_more_information(
          see_installation_guide_section("Install Init Script"),
          "see log/mail_room.log for possible errors"
        )
        fix_and_rerun
      end
    end

    def check_imap_authentication
      print "IMAP server credentials are correct? ... "

406 407 408 409 410
      config_path = Rails.root.join('config', 'mail_room.yml').to_s
      erb = ERB.new(File.read(config_path))
      erb.filename = config_path
      config_file = YAML.load(erb.result)

411
      config = config_file[:mailboxes].first
412 413 414

      if config
        begin
415 416 417
          imap = Net::IMAP.new(config[:host], port: config[:port], ssl: config[:ssl])
          imap.starttls if config[:start_tls]
          imap.login(config[:email], config[:password])
418 419 420 421 422 423 424
          connected = true
        rescue
          connected = false
        end
      end

      if connected
425
        puts "yes".color(:green)
426
      else
427
        puts "no".color(:red)
428
        try_fixing_it(
429
          "Check that the information in config/gitlab.yml is correct"
430 431
        )
        for_more_information(
432
          "doc/administration/reply_by_email.md"
433 434 435 436 437
        )
        fix_and_rerun
      end
    end

Douwe Maan's avatar
Douwe Maan committed
438
    def mail_room_running?
439
      ps_ux, _ = Gitlab::Popen.popen(%w(ps uxww))
Douwe Maan's avatar
Douwe Maan committed
440
      ps_ux.include?("mail_room")
441 442 443
    end
  end

444
  namespace :ldap do
445
    task :check, [:limit] => :environment do |_, args|
446 447
      # Only show up to 100 results because LDAP directories can be very big.
      # This setting only affects the `rake gitlab:check` script.
448
      args.with_defaults(limit: 100)
449 450 451
      warn_user_is_not_gitlab
      start_checking "LDAP"

452
      if Gitlab::LDAP::Config.enabled?
453
        check_ldap(args.limit)
454 455 456
      else
        puts 'LDAP is disabled in config/gitlab.yml'
      end
457 458 459 460

      finished_checking "LDAP"
    end

461
    def check_ldap(limit)
462
      servers = Gitlab::LDAP::Config.providers
463

464 465
      servers.each do |server|
        puts "Server: #{server}"
466 467 468 469 470 471 472 473 474 475 476

        begin
          Gitlab::LDAP::Adapter.open(server) do |adapter|
            check_ldap_auth(adapter)

            puts "LDAP users with access to your GitLab server (only showing the first #{limit} results)"

            users = adapter.users(adapter.config.uid, '*', limit)
            users.each do |user|
              puts "\tDN: #{user.dn}\t #{adapter.config.uid}: #{user.uid}"
            end
477
          end
478 479
        rescue Net::LDAP::ConnectionRefusedError, Errno::ECONNREFUSED => e
          puts "Could not connect to the LDAP server: #{e.message}".color(:red)
480
        end
481
      end
482
    end
483 484 485 486

    def check_ldap_auth(adapter)
      auth = adapter.config.has_auth?

487 488 489 490 491 492 493
      message = if auth && adapter.ldap.bind
                  'Success'.color(:green)
                elsif auth
                  'Failed. Check `bind_dn` and `password` configuration values'.color(:red)
                else
                  'Anonymous. No `bind_dn` or `password` configured'.color(:yellow)
                end
494 495 496

      puts "LDAP authentication... #{message}"
    end
497
  end
Riyad Preukschas's avatar
Riyad Preukschas committed
498

Vinnie Okada's avatar
Vinnie Okada committed
499
  namespace :repo do
500
    desc "GitLab | Check the integrity of the repositories managed by GitLab"
Vinnie Okada's avatar
Vinnie Okada committed
501
    task check: :environment do
502 503
      Gitlab.config.repositories.storages.each do |name, repository_storage|
        namespace_dirs = Dir.glob(File.join(repository_storage['path'], '*'))
Vinnie Okada's avatar
Vinnie Okada committed
504

505 506 507 508
        namespace_dirs.each do |namespace_dir|
          repo_dirs = Dir.glob(File.join(namespace_dir, '*'))
          repo_dirs.each { |repo_dir| check_repo_integrity(repo_dir) }
        end
509 510 511 512 513 514 515
      end
    end
  end

  namespace :user do
    desc "GitLab | Check the integrity of a specific user's repositories"
    task :check_repos, [:username] => :environment do |t, args|
516
      username = args[:username] || prompt("Check repository integrity for fsername? ".color(:blue))
517 518 519
      user = User.find_by(username: username)
      if user
        repo_dirs = user.authorized_projects.map do |p|
520 521
          File.join(
            p.repository_storage_path,
522
            "#{p.disk_path}.git"
523 524
          )
        end
525 526 527

        repo_dirs.each { |repo_dir| check_repo_integrity(repo_dir) }
      else
528
        puts "\nUser '#{username}' not found".color(:red)
Vinnie Okada's avatar
Vinnie Okada committed
529 530 531 532
      end
    end
  end

Gabriel Mazetto's avatar
Gabriel Mazetto committed
533 534 535 536 537
  namespace :geo do
    desc 'GitLab | Check Geo configuration and dependencies'
    task check: :environment do
      warn_user_is_not_gitlab

538 539 540
      checks = [
        SystemCheck::Geo::LicenseCheck,
        SystemCheck::Geo::EnabledCheck,
541 542
        SystemCheck::Geo::GeoDatabaseConfiguredCheck,
        SystemCheck::Geo::DatabaseReplicationCheck,
543
        SystemCheck::Geo::HttpConnectionCheck,
544 545
        SystemCheck::Geo::ClocksSynchronizationCheck,
        SystemCheck::App::GitUserDefaultSSHConfigCheck
546
      ]
547

548
      SystemCheck.run('Geo', checks)
549
    end
Gabriel Mazetto's avatar
Gabriel Mazetto committed
550 551
  end

Riyad Preukschas's avatar
Riyad Preukschas committed
552 553 554
  # Helper methods
  ##########################

555 556 557 558
  def see_custom_certificate_doc
    'https://docs.gitlab.com/omnibus/common_installation_problems/README.html#using-self-signed-certificate-or-custom-certificate-authorities'
  end

559
  def check_gitlab_shell
560
    required_version = Gitlab::VersionInfo.new(gitlab_shell_major_version, gitlab_shell_minor_version, gitlab_shell_patch_version)
561
    current_version = Gitlab::VersionInfo.parse(gitlab_shell_version)
562

563
    print "GitLab Shell version >= #{required_version} ? ... "
Sato Hiroyuki's avatar
Sato Hiroyuki committed
564
    if current_version.valid? && required_version <= current_version
565
      puts "OK (#{current_version})".color(:green)
566
    else
567
      puts "FAIL. Please update gitlab-shell to #{required_version} from #{current_version}".color(:red)
568 569
    end
  end
570

571
  def check_repo_integrity(repo_dir)
572
    puts "\nChecking repo at #{repo_dir.color(:yellow)}"
573 574 575 576 577 578 579

    git_fsck(repo_dir)
    check_config_lock(repo_dir)
    check_ref_locks(repo_dir)
  end

  def git_fsck(repo_dir)
580
    puts "Running `git fsck`".color(:yellow)
581 582 583 584
    system(*%W(#{Gitlab.config.git.bin_path} fsck), chdir: repo_dir)
  end

  def check_config_lock(repo_dir)
585
    config_exists = File.exist?(File.join(repo_dir, 'config.lock'))
586 587
    config_output = config_exists ? 'yes'.color(:red) : 'no'.color(:green)
    puts "'config.lock' file exists?".color(:yellow) + " ... #{config_output}"
588 589 590
  end

  def check_ref_locks(repo_dir)
591
    lock_files = Dir.glob(File.join(repo_dir, 'refs/heads/*.lock'))
592
    if lock_files.present?
593
      puts "Ref lock files exist:".color(:red)
594 595 596 597
      lock_files.each do |lock_file|
        puts "  #{lock_file}"
      end
    else
598
      puts "No ref lock files exist".color(:green)
599 600
    end
  end
601
end