Skip to main content

Implementing Security Best Practices

1. Using Protected Branches

Property Description Default Notes
protected Protects the branch from being deleted or force-pushed false Enables branch protection rules
developers_can_push Allows developers to push to the branch false Requires approval for pushes
maintainers_can_push Allows maintainers to push to the branch false Enables direct pushes for maintainers
developers_can_merge Allows developers to merge into the branch false Requires approval for merges
maintainers_can_merge Allows maintainers to merge into the branch false Enables direct merges for maintainers

Example: Protecting a Branch in GitLab CI/CD

branch_protection:
  rules:
    - if: "($CI_COMMIT_BRANCH == 'main')"
      protected: true
      developers_can_push: false
      maintainers_can_push: true
      developers_can_merge: false
      maintainers_can_merge: true
Note: Protected branches are essential for enforcing security policies and preventing unauthorized changes to critical code.

2. Configuring Protected Tags

Property Value Description
protected true Indicates if the tag is protected
create_access_level 40 Access level required to create the tag (Maintainer: 40, Developer: 30)
push_access_level 40 Access level required to push to the tag (Maintainer: 40, Developer: 30)

Example: Protecting a Tag in .gitlab-ci.yml

release:
  stage: release
  script:
    - echo "Releasing version $CI_COMMIT_TAG"
  only:
    - tags
  tags:
    - release
Note: Protected tags can only be created or modified by users with Maintainer or Owner permissions.
Warning: Ensure that only trusted users have the ability to push to protected tags.

3. Implementing Least Privilege for Runners

Runner Type Permissions Security Considerations
Shared Runner read, write to project repo High risk if compromised; use DEPRECATED in favor of group/project runners
Group Runner read, write to group projects Better isolation; restrict access to specific groups
Project Runner read, write to project repo only Best practice for least privilege; highly secure

Example: Project Runner Configuration

variables:
  GIT_STRATEGY: clone
runner:
  name: project-runner
  tags:
    - project-specific
  access_level: ref_protected
Note: Runner access levels can be set to not_protected, ref_protected, or protected. Use protected for sensitive projects.
Warning: Never grant write permissions to runners in public projects without strict access controls.
Permission Level Description Use Case
read Can pull code but not push changes Build and test environments
write Can pull and push changes to repo Deployment or release pipelines
admin Full access to repo and CI/CD settings Highly sensitive or internal tools only

4. Using Masked Variables (masked: true)

Property Value Description
masked true When set to true, the variable value is masked in the job logs and UI.

Example: Masked Variable in GitLab CI/CD

variables:
  API_KEY: "secret123"
  masked: true

job_name:
  script:
    - echo "Using API key: $API_KEY"
Note: The masked property is not a standard GitLab CI/CD variable attribute. Instead, use the masked keyword in the variables section to mask sensitive values in logs.
Warning: Even with masking, sensitive data should not be stored in plain text within CI/CD configuration files. Use GitLab's protected variables or external secret management tools.

5. Rotating CI/CD Tokens Regularly

Token Type Rotation Frequency Notes
Runner Registration Token Every 90 days Used for registering new runners. Rotate to prevent unauthorized access.
Job Token Every 30 days Used for job execution and access to artifacts. Rotate to reduce exposure.
Personal Access Token (PAT) Every 90 days Used for API access and project operations. Rotate to maintain security.
Deploy Token Every 180 days Used for deployment to environments. Rotate to limit access scope.

Example: Rotating Job Token in CI/CD Pipeline

stages:
  - build
  - test
  - deploy

variables:
  GITLAB_TOKEN: ${{CI_JOB_TOKEN}}

job_build:
  stage: build
  script:
    - echo "Building project..."
    - curl --header "JOB-TOKEN: $GITLAB_TOKEN" https://gitlab.example.com/api/v4/projects/12345678

job_deploy:
  stage: deploy
  script:
    - echo "Deploying to production..."
    - curl --header "JOB-TOKEN: $GITLAB_TOKEN" https://gitlab.example.com/api/v4/projects/12345678/deployments
Note: Ensure that all pipelines using the token are updated to use the new token after rotation.
Warning: Tokens that are not rotated may lead to unauthorized access if compromised.

6. Implementing Secrets Management

Property Description Example
CI_REGISTRY_PASSWORD Registry password for Docker image pushing CI_REGISTRY_PASSWORD=$CI_REGISTRY_USER
GITLAB_TOKEN Personal access token for GitLab API access GITLAB_TOKEN=$CI_JOB_TOKEN
AWS_ACCESS_KEY_ID Access key for AWS services AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
DATABASE_PASSWORD Password for database connections DATABASE_PASSWORD=$DB_PASS

Example: Using GitLab CI Variables

variables:
  DATABASE_PASSWORD: $DB_PASS
  GITLAB_TOKEN: $CI_JOB_TOKEN

script:
  - echo "Using database password: $DATABASE_PASSWORD"
  - curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" https://gitlab.example.com/api/v4/projects
Note: Never expose secrets in logs or console output. Use masked variables where possible.
Warning: Avoid hardcoding secrets in .gitlab-ci.yml files. Use GitLab's UI or API to set variables securely.
Secret Type Storage Method Security Level Best Practice
CI_REGISTRY_PASSWORD GitLab CI Variables High Use masked variables to prevent exposure in logs
GITLAB_TOKEN GitLab CI Variables High Limit scope to specific project or group permissions
AWS_ACCESS_KEY_ID GitLab CI Variables Medium Rotate keys regularly and use least privilege IAM roles
DATABASE_PASSWORD GitLab CI Variables High Store in encrypted format and restrict access to authorized users only

7. Using Signed Commits Verification

Feature Description Configuration
commit signing Ensures commits are cryptographically signed by the author Enabled via GPG key setup in GitLab CI/CD
commit verification Verifies that commits were signed with a valid key Configurable in project settings or pipeline rules

Example: Enabling commit signing in GitLab CI

variables:
  GIT_AUTHOR_NAME: "CI Pipeline"
  GIT_AUTHOR_EMAIL: "ci@example.com"

script:
  - git config --global user.name "$GIT_AUTHOR_NAME"
  - git config --global user.email "$GIT_AUTHOR_EMAIL"
  - git commit --signoff -m "Signed commit from CI"
Note: Commit signing requires GPG key setup in GitLab for verification to work properly.

Verification Process

  • GitLab verifies GPG signature on each commit
  • Requires valid GPG key associated with user
  • Can be enforced via branch protection rules

8. Configuring RBAC for Runners

Permission Description Resource Effect
Runner: Read Allows reading runner information and status runner View runners in project
Runner: Manage Allows managing runners including enabling/disabling and assigning to jobs runner Enable/disable runners, assign to jobs
Runner: Admin Allows full control over runners including deletion and configuration runner Delete runners, modify configurations

Example: Setting up RBAC for runners in GitLab

gitlab-runner register \
  --url "https://gitlab.example.com/" \
  --registration-token "PROJECT_REGISTRATION_TOKEN" \
  --executor "shell" \
  --name "secure-runner" \
  --tag-list "secure,production" \
  --run-untagged="false" \
  --locked="true"
Note: RBAC policies should be configured at the project or group level to restrict runner access based on roles.

9. Implementing Audit Logging

Aspect Description Configuration
audit_log_enabled Enables audit logging for GitLab CI/CD jobs true or false
audit_log_level Log level for audit events info, warn, error
audit_log_output Destination for audit logs file, syslog, http

Example: Enabling Audit Logging in GitLab CI/CD

variables:
  GITLAB_AUDIT_LOG_ENABLED: "true"
  GITLAB_AUDIT_LOG_LEVEL: "info"
  GITLAB_AUDIT_LOG_OUTPUT: "file"

script:
  - echo "Audit logging enabled"
Note: Audit logs capture actions like job creation, pipeline triggers, and environment changes. Ensure logs are stored securely and rotated regularly.
Warning: Enabling audit logging can increase disk I/O and storage usage. Monitor system performance after enabling.

10. Using Security Policies

Policy Type Description Implementation Notes
Code Review Policy Enforces mandatory code reviews before merging to main branches
rules:
  - if: $CI_MERGE_REQUEST_ID
    changes:
      - src/**
    actions:
      - require_code_review
Ensures peer review for all changes
Vulnerability Scanning Automatically scans code for known vulnerabilities
security:
  - name: SAST
    image: gitlab/gitlab-sast:latest
    script:
      - sast
Integrates with GitLab's built-in SAST scanner
Dependency Scanning Scans dependencies for security issues
security:
  - name: DAST
    image: gitlab/gitlab-dast:latest
    script:
      - dast
Scans for vulnerabilities in deployed applications

Example: Enforcing Code Review Policy

workflow:
  rules:
    - if: $CI_MERGE_REQUEST_ID
      changes:
        - src/**
      actions:
        - require_code_review
Note: Code review policies can be enforced at the project or group level using GitLab's built-in policy management features.

Was this article helpful?