CIS GitLab Benchmark
Security configuration recommendations for self-managed GitLab instances
v1.0.0 01-2025Overview
▶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.
| Section | Area | Focus |
|---|---|---|
| 1 | Installation & Services | Version management and disabling unnecessary bundled components |
| 2 | Authentication & Token Security | 2FA, password policy, session timeout, sign-up, token lifetime, and SSH keys |
| 3 | Network & Transport Security | HTTPS enforcement and TLS cipher/protocol configuration |
| 4 | Repository & CI/CD Security | Branch protection, push rules, runner restrictions, and variable protection |
| 5 | Logging & Auditing | Audit event logging and log rotation configuration |
| 6 | Backup & Recovery | Scheduled backups with gitlab-backup |
| 7 | Container Registry | TLS-secured container registry configuration |
Profile Definitions
▶| Profile | Description | Intended Use |
|---|---|---|
| L1 | Level 1 — Standard | Essential security for all GitLab deployments; minimal performance impact. |
| L2 | Level 2 — Hardened | Advanced hardening for PCI-DSS, HIPAA, or high-security environments. |
1 — Installation & Services
▶1.1 Version & Components
▶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.
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.
# 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
# Update GitLab to latest: apt update && apt install -y gitlab-ee # Or RHEL: dnf update -y gitlab-ee # Reconfigure after update: gitlab-ctl reconfigure
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.
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.
# Verify GitLab services status: gitlab-ctl status # Check for unnecessary services: gitlab-ctl service-list | grep enabled
# 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
▶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.
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.
# 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"
# 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
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.
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.
# 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# 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
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.
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.
# Check session timeout: gitlab-rails runner "puts Gitlab::CurrentSettings.session_expire_delay" # Check in gitlab.rb: grep 'session_expire_delay' /etc/gitlab/gitlab.rb
# Set session timeout in /etc/gitlab/gitlab.rb: gitlab_rails['session_expire_delay'] = 60 gitlab-ctl reconfigure
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.
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.
# 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# 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
▶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.
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.
# 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}\" }"# Set maximum token lifetime: gitlab-rails runner <<'RUBY' ApplicationSetting.last.update!( max_personal_access_token_lifetime: 90 ) RUBY
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.
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.
# 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# 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
▶This recommendation ensures that HTTPS is enforced on the GitLab DevOps platform. Enforcing this requirement establishes a minimum security standard and prevents insecure configurations.
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.
# 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
# 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
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.
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.
# 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
# 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
▶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.
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.
# 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}\" }"# Set default branch protection to 'fully protected': gitlab-rails runner <<'RUBY' ApplicationSetting.last.update!( default_branch_protection: 2 ) RUBY
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.
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.
# 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# 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
▶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.
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.
# 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}\" }"# Disable shared runners by default: gitlab-rails runner <<'RUBY' ApplicationSetting.last.update!( shared_runners_enabled: false ) RUBY
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.
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.
# 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# 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
▶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.
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.
# 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
# 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 reconfigureThis 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.
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.
# Verify log retention/rotation: cat /var/opt/gitlab/logrotate/logrotate.d/gitlab # Check current log sizes: du -sh /var/log/gitlab/*/
# 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
▶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.
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.
# 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
# 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
▶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.
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.
# 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 '#'
# 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