CIS GitLab Benchmark

Security configuration recommendations for self-managed GitLab instances

v1.0.0 01-2025

Overview

▶

This benchmark provides prescriptive guidance for establishing a secure configuration posture for self-managed GitLab instances. It covers installation, authentication, token management, network transport, repository security, CI/CD pipeline hardening, logging, backups, and container registry security using gitlab-ctl, gitlab-rails runner, and gitlab.rb configuration.

20Recommendations
7Sections
2Profile Levels
SectionAreaFocus
1Installation & ServicesVersion management and disabling unnecessary bundled components
2Authentication & Token Security2FA, password policy, session timeout, sign-up, token lifetime, and SSH keys
3Network & Transport SecurityHTTPS enforcement and TLS cipher/protocol configuration
4Repository & CI/CD SecurityBranch protection, push rules, runner restrictions, and variable protection
5Logging & AuditingAudit event logging and log rotation configuration
6Backup & RecoveryScheduled backups with gitlab-backup
7Container RegistryTLS-secured container registry configuration

Profile Definitions

▶
ProfileDescriptionIntended Use
L1Level 1 — StandardEssential security for all GitLab deployments; minimal performance impact.
L2Level 2 — HardenedAdvanced hardening for PCI-DSS, HIPAA, or high-security environments.

1 — Installation & Services

▶

1.1 Version & Components

▶
1.1.1 Ensure GitLab is running the latest stable version (Manual)
L1 Manual
Description

This recommendation verifies that GitLab is running the latest stable version on the GitLab DevOps platform. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.

Rationale

Failure to implement this control may leave the GitLab DevOps platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
# Check GitLab version:
gitlab-rake gitlab:env:info

# Or:
cat /opt/gitlab/version-manifest.txt | head -3

# Via API:
curl -s --header 'PRIVATE-TOKEN: <token>' https://gitlab.example.com/api/v4/version
Remediation
# Update GitLab to latest:
apt update && apt install -y gitlab-ee

# Or RHEL:
dnf update -y gitlab-ee

# Reconfigure after update:
gitlab-ctl reconfigure
1.1.2 Ensure unnecessary services are disabled (Automated)
L1 Auto
Description

This recommendation verifies that unnecessary services are disabled on the GitLab DevOps platform. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.

Rationale

Failure to implement this control may leave the GitLab DevOps platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
# Verify GitLab services status:
gitlab-ctl status

# Check for unnecessary services:
gitlab-ctl service-list | grep enabled
Remediation
# Disable unnecessary services in /etc/gitlab/gitlab.rb:
# mattermost['enable'] = false
# registry['enable'] = false  # if not using container registry
# pages_external_url nil       # if not using GitLab Pages

gitlab-ctl reconfigure

2 — Authentication & Token Security

▶

2.1 Authentication

▶
2.1.1 Ensure two-factor authentication is enforced (Automated)
L1 Auto
Description

This recommendation ensures that two-factor authentication is enforced on the GitLab DevOps platform. Enforcing this requirement establishes a minimum security standard and prevents insecure configurations.

Rationale

Without this enforcement, the GitLab DevOps platform may accept insecure configurations that weaken overall security posture. Mandating this control ensures consistent protection against common attack vectors.

Audit
# Check if two-factor authentication is enforced:
gitlab-rails runner "puts Gitlab::CurrentSettings.require_two_factor_authentication"

# Check 2FA grace period:
gitlab-rails runner "puts Gitlab::CurrentSettings.two_factor_grace_period"
Remediation
# Enforce 2FA in /etc/gitlab/gitlab.rb or Admin UI:
gitlab-rails runner <<'RUBY'
ApplicationSetting.last.update!(
  require_two_factor_authentication: true,
  two_factor_grace_period: 48
)
RUBY

# Or via Admin Area > Settings > General > Sign-in restrictions
2.1.2 Ensure password policy meets complexity requirements (Automated)
L1 Auto
Description

This recommendation ensures that password policy meets complexity requirements on the GitLab DevOps platform. Enforcing this requirement establishes a minimum security standard and prevents insecure configurations.

Rationale

Without this enforcement, the GitLab DevOps platform may accept insecure configurations that weaken overall security posture. Mandating this control ensures consistent protection against common attack vectors.

Audit
# Check password requirements:
gitlab-rails runner <<'RUBY'
s = Gitlab::CurrentSettings
puts "min_length: #{s.minimum_password_length}"
puts "require_uppercase: #{s.password_uppercase_required}"
puts "require_number: #{s.password_number_required}"
puts "require_lowercase: #{s.password_lowercase_required}"
RUBY
Remediation
# Configure password policy:
gitlab-rails runner <<'RUBY'
ApplicationSetting.last.update!(
  minimum_password_length: 14,
  password_uppercase_required: true,
  password_lowercase_required: true,
  password_number_required: true,
  password_symbol_required: true
)
RUBY
2.1.3 Ensure session timeout is configured (Automated)
L1 Auto
Description

This recommendation verifies that session timeout is configured on the GitLab DevOps platform. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.

Rationale

Failure to implement this control may leave the GitLab DevOps platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
# Check session timeout:
gitlab-rails runner "puts Gitlab::CurrentSettings.session_expire_delay"

# Check in gitlab.rb:
grep 'session_expire_delay' /etc/gitlab/gitlab.rb
Remediation
# Set session timeout in /etc/gitlab/gitlab.rb:
gitlab_rails['session_expire_delay'] = 60

gitlab-ctl reconfigure
2.1.4 Ensure public sign-up is disabled (Automated)
L1 Auto
Description

This recommendation verifies that public sign-up is disabled on the GitLab DevOps platform. Disabling or removing unnecessary components reduces the attack surface and limits potential vectors for exploitation.

Rationale

Running unnecessary components on the GitLab DevOps platform increases the attack surface and the risk of exploitation. Disabling or removing them follows the principle of least functionality and reduces exposure to known vulnerabilities.

Audit
# Check if sign-up is restricted:
gitlab-rails runner <<'RUBY'
s = Gitlab::CurrentSettings
puts "signup_enabled: #{s.signup_enabled}"
puts "domain_allowlist: #{s.domain_allowlist}"
puts "require_admin_approval: #{s.require_admin_approval_after_user_signup}"
RUBY
Remediation
# Disable public sign-up:
gitlab-rails runner <<'RUBY'
ApplicationSetting.last.update!(
  signup_enabled: false,
  require_admin_approval_after_user_signup: true,
  domain_allowlist: ['company.com']
)
RUBY

2.2 Token Management

▶
2.2.1 Ensure personal access token lifetime is limited (Automated)
L1 Auto
Description

This setting ensures that personal access token lifetime is limited on the GitLab DevOps platform. Restricting this capability limits potential abuse and enforces the principle of least privilege across the environment.

Rationale

Unrestricted access to this capability could allow unauthorized users or processes to perform actions beyond their intended scope. Applying least-privilege principles to the GitLab DevOps platform is essential for defense in depth.

Audit
# Check personal access token expiration policy:
gitlab-rails runner "puts Gitlab::CurrentSettings.max_personal_access_token_lifetime"

# List tokens without expiry:
gitlab-rails runner "PersonalAccessToken.active.where(expires_at: nil).each { |t| puts \"#{t.user.username}: #{t.name}\" }"
Remediation
# Set maximum token lifetime:
gitlab-rails runner <<'RUBY'
ApplicationSetting.last.update!(
  max_personal_access_token_lifetime: 90
)
RUBY
2.2.2 Ensure SSH key restrictions are configured (Automated)
L1 Auto
Description

This recommendation verifies that SSH key restrictions are configured on the GitLab DevOps platform. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.

Rationale

Failure to implement this control may leave the GitLab DevOps platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
# Check SSH key restrictions:
gitlab-rails runner <<'RUBY'
s = Gitlab::CurrentSettings
puts "rsa_min: #{s.rsa_key_restriction}"
puts "ecdsa_min: #{s.ecdsa_key_restriction}"
puts "ed25519_min: #{s.ed25519_key_restriction}"
RUBY
Remediation
# Set minimum SSH key sizes:
gitlab-rails runner <<'RUBY'
ApplicationSetting.last.update!(
  rsa_key_restriction: 3072,
  dsa_key_restriction: -1,
  ecdsa_key_restriction: 256,
  ed25519_key_restriction: 0
)
RUBY

3 — Network & Transport Security

▶

3.1 TLS Configuration

▶
3.1.1 Ensure HTTPS is enforced (Automated)
L1 Auto
Description

This recommendation ensures that HTTPS is enforced on the GitLab DevOps platform. Enforcing this requirement establishes a minimum security standard and prevents insecure configurations.

Rationale

Without this enforcement, the GitLab DevOps platform may accept insecure configurations that weaken overall security posture. Mandating this control ensures consistent protection against common attack vectors.

Audit
# Verify HTTPS is configured:
grep "external_url" /etc/gitlab/gitlab.rb | head -1
# Should start with https://

# Check SSL certificate:
openssl s_client -connect gitlab.example.com:443 -brief 2>/dev/null | head -5
Remediation
# Configure HTTPS in /etc/gitlab/gitlab.rb:
external_url 'https://gitlab.example.com'

# Let's Encrypt auto:
letsencrypt['enable'] = true
letsencrypt['contact_emails'] = ['admin@company.com']

# Or custom cert:
nginx['ssl_certificate'] = '/etc/gitlab/ssl/gitlab.crt'
nginx['ssl_certificate_key'] = '/etc/gitlab/ssl/gitlab.key'

gitlab-ctl reconfigure
3.1.2 Ensure strong TLS ciphers and protocols are configured (Automated)
L1 Auto
Description

This recommendation verifies that strong TLS ciphers and protocols are configured on the GitLab DevOps platform. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.

Rationale

Failure to implement this control may leave the GitLab DevOps platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
# Check NGINX SSL configuration:
grep 'ssl_protocols' /var/opt/gitlab/nginx/conf/gitlab-http.conf
grep 'ssl_ciphers' /var/opt/gitlab/nginx/conf/gitlab-http.conf

# Or in gitlab.rb:
grep 'ssl_protocols\|ssl_ciphers' /etc/gitlab/gitlab.rb
Remediation
# Configure strong TLS in /etc/gitlab/gitlab.rb:
nginx['ssl_protocols'] = 'TLSv1.2 TLSv1.3'
nginx['ssl_ciphers'] = 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384'
nginx['hsts_max_age'] = 31536000
nginx['hsts_include_subdomains'] = true

gitlab-ctl reconfigure

4 — Repository & CI/CD Security

▶

4.1 Repository Protection

▶
4.1.1 Ensure default branch protection is enabled (Automated)
L1 Auto
Description

This recommendation verifies that default branch protection is enabled on the GitLab DevOps platform. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.

Rationale

Failure to implement this control may leave the GitLab DevOps platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
# Check default branch protection:
gitlab-rails runner <<'RUBY'
puts Gitlab::CurrentSettings.default_branch_protection
# 0=not protected, 1=partial, 2=full
RUBY

# Check push rules:
gitlab-rails runner "PushRule.global&.attributes&.each { |k,v| puts \"#{k}: #{v}\" }"
Remediation
# Set default branch protection to 'fully protected':
gitlab-rails runner <<'RUBY'
ApplicationSetting.last.update!(
  default_branch_protection: 2
)
RUBY
4.1.2 Ensure push rules prevent secrets in commits (Automated)
L1 Auto
Description

This recommendation verifies that push rules prevent secrets in commits on the GitLab DevOps platform. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.

Rationale

Failure to implement this control may leave the GitLab DevOps platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
# Check global push rules:
gitlab-rails runner <<'RUBY'
rule = PushRule.global
if rule
  puts "deny_delete_tag: #{rule.deny_delete_tag}"
  puts "prevent_secrets: #{rule.prevent_secrets}"
  puts "commit_message_regex: #{rule.commit_message_regex}"
end
RUBY
Remediation
# Configure push rules to prevent secrets:
gitlab-rails runner <<'RUBY'
PushRule.find_or_create_global.update!(
  deny_delete_tag: true,
  prevent_secrets: true,
  commit_message_regex: '\\A(?!fixup!).+',
  file_name_regex: '(\\.(pem|key|pkcs12|pfx|p12|jks)$|id_rsa)',
  max_file_size: 100
)
RUBY

4.2 CI/CD Hardening

▶
4.2.1 Ensure shared runners are restricted (Automated)
L1 Auto
Description

This setting ensures that shared runners are restricted on the GitLab DevOps platform. Restricting this capability limits potential abuse and enforces the principle of least privilege across the environment.

Rationale

Unrestricted access to this capability could allow unauthorized users or processes to perform actions beyond their intended scope. Applying least-privilege principles to the GitLab DevOps platform is essential for defense in depth.

Audit
# Check shared runner registration:
gitlab-rails runner "puts Gitlab::CurrentSettings.shared_runners_enabled"

# List runners:
gitlab-rails runner "Ci::Runner.all.each { |r| puts \"#{r.description}: #{r.runner_type}, active=#{r.active}\" }"
Remediation
# Disable shared runners by default:
gitlab-rails runner <<'RUBY'
ApplicationSetting.last.update!(
  shared_runners_enabled: false
)
RUBY
4.2.2 Ensure CI/CD variables are protected and masked (Automated)
L1 Auto
Description

This recommendation verifies that CI/CD variables are protected and masked on the GitLab DevOps platform. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.

Rationale

Failure to implement this control may leave the GitLab DevOps platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
# Check CI/CD variable protection:
gitlab-rails runner <<'RUBY'
Ci::Variable.where(protected: false).each do |v|
  puts "#{v.project&.full_path}: #{v.key} (unprotected)"
end
RUBY
Remediation
# Protect CI/CD variables:
gitlab-rails runner <<'RUBY'
Ci::Variable.where(protected: false, masked: false).each do |v|
  v.update!(protected: true, masked: true)
end
RUBY

5 — Logging & Auditing

▶

5.1 Audit Configuration

▶
5.1.1 Ensure audit logging is enabled (Automated)
L1 Auto
Description

This recommendation verifies that audit logging is enabled on the GitLab DevOps platform. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.

Rationale

Failure to implement this control may leave the GitLab DevOps platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
# Check audit logging:
ls -la /var/log/gitlab/gitlab-rails/audit_json.log

# Check log level:
grep 'log_level' /etc/gitlab/gitlab.rb

# Verify structured logging:
head -5 /var/log/gitlab/gitlab-rails/production_json.log
Remediation
# Configure audit logging in /etc/gitlab/gitlab.rb:
gitlab_rails['audit_events_enabled'] = true

# Set log level:
gitlab_rails['env'] = { 'GITLAB_LOG_LEVEL' => 'info' }

# Enable audit event streaming (Premium+):
# Admin Area > Settings > Audit Events

gitlab-ctl reconfigure
5.1.2 Ensure log rotation is configured (Automated)
L1 Auto
Description

This recommendation verifies that log rotation is configured on the GitLab DevOps platform. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.

Rationale

Failure to implement this control may leave the GitLab DevOps platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
# Verify log retention/rotation:
cat /var/opt/gitlab/logrotate/logrotate.d/gitlab

# Check current log sizes:
du -sh /var/log/gitlab/*/
Remediation
# Configure log rotation in /etc/gitlab/gitlab.rb:
logging['logrotate_frequency'] = 'daily'
logging['logrotate_maxsize'] = nil
logging['logrotate_size'] = '100M'
logging['logrotate_rotate'] = 30
logging['logrotate_compress'] = 'compress'
logging['logrotate_delaycompress'] = 'delaycompress'

gitlab-ctl reconfigure

6 — Backup & Recovery

▶

6.1 Backup Configuration

▶
6.1.1 Ensure regular backups are scheduled (Manual)
L1 Manual
Description

This recommendation verifies that regular backups are scheduled on the GitLab DevOps platform. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.

Rationale

Failure to implement this control may leave the GitLab DevOps platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
# Check backup configuration:
grep 'backup' /etc/gitlab/gitlab.rb | grep -v '#'

# List existing backups:
ls -la /var/opt/gitlab/backups/

# Check cron:
crontab -l -u root | grep backup
Remediation
# Configure backups in /etc/gitlab/gitlab.rb:
gitlab_rails['backup_path'] = '/var/opt/gitlab/backups'
gitlab_rails['backup_keep_time'] = 604800

# Run manual backup:
gitlab-backup create

# Schedule via cron:
# 0 2 * * * /opt/gitlab/bin/gitlab-backup create CRON=1

7 — Container Registry

▶

7.1 Registry Security

▶
7.1.1 Ensure container registry uses TLS (Automated)
L1 Auto
Description

This recommendation verifies that container registry uses TLS on the GitLab DevOps platform. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.

Rationale

Failure to implement this control may leave the GitLab DevOps platform vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.

Audit
# Check container registry status:
gitlab-ctl status registry

# Verify registry HTTPS:
grep 'registry_external_url' /etc/gitlab/gitlab.rb

# Check auth:
grep 'registry\[' /etc/gitlab/gitlab.rb | grep -v '#'
Remediation
# Configure container registry with TLS in /etc/gitlab/gitlab.rb:
registry_external_url 'https://registry.gitlab.example.com'
registry_nginx['ssl_certificate'] = '/etc/gitlab/ssl/registry.crt'
registry_nginx['ssl_certificate_key'] = '/etc/gitlab/ssl/registry.key'

gitlab-ctl reconfigure