Add native GitLab Secrets Manager support to GitLab Runner

What does this MR do?

Resolves gitlab#540909 (closed)

This MR implements native GitLab Secrets Manager support in GitLab Runner using OpenBao's inline authentication feature. Given OpenBao is a fork of Vault, we also decided to fully replace the Vault module with OpenBao so we can reuse the existing Vault client and just add the inline-auth support to it.

This is the first part of a two-phase implementation that will eventually replace the current "masquerading" approach where Rails converts gitlab_secrets_manager secrets to vault format.

Phase 1 (this MR): Add native gitlab_secrets_manager support to Runner Phase 2 (future MR): Rails stops masquerading and sends native format

Implementation overview

Architecture

This implementation follows the same patterns as the existing Vault resolver:

helpers/secrets/resolvers/gitlab_secrets_manager/
├── resolver.go              # Main resolver (follows vault resolver pattern)
└── resolver_test.go         # Comprehensive resolver tests

helpers/gitlab_secrets_manager/service/
├── gitlab_secrets_manager.go      # Service layer implementation
├── gitlab_secrets_manager_test.go # Service tests
└── mocks.go            # Service mocks for testing

Key Design Decisions

1. OpenBao Inline Authentication

Why inline auth?

  • Performance: No separate authentication step required
  • Efficiency: Credentials sent with each request, avoiding token storage/refresh cycles
  • CI/CD Optimized: Better suited for ephemeral CI/CD workloads
  • Stateless: No need to manage authentication state between requests

How it works:

GET /v1/project_123/ci/data/database_password
X-Vault-Inline-Auth-Path: auth/gitlab_rails_jwt/login
X-Vault-Inline-Auth-Parameter-token: <base64-url-encoded-jwt>
X-Vault-Inline-Auth-Parameter-role: <base64-url-encoded-role>

2. No Variable Expansion

Unlike other secret resolvers, GitLab Secrets Manager deliberately does not expand variables.

Rationale:

  • Only user input is the secret name from .gitlab-ci.yml
  • All server URLs, auth paths, JWT tokens, and roles are generated internally by Rails
  • This prevents users from tampering with authentication parameters
  • Maintains security by keeping all sensitive configuration server-side

3. Reuse existing Vault Client

Decision: Replace HashiCorp Vault SDK with OpenBao SDK and extend the existing vault client rather than creating a new one.

Benefits:

  • Code Reuse: Leverages battle-tested vault client code for connection management, error handling, and API interactions
  • Minimal Changes: Only adds inline auth support to existing client - all other functionality remains unchanged
  • Reduced Maintenance: Single client implementation to maintain instead of two separate ones
  • API Compatibility: OpenBao maintains full API compatibility with Vault, making this a drop-in replacement

Configuration Format

User configuration (in .gitlab-ci.yml):

test_job:
  secrets:
    DATABASE_PASSWORD:
      gitlab_secrets_manager:
        name: database_credentials

What Rails sends to Runner (this MR supports this format):

{
  "secrets": {
    "DATABASE_PASSWORD": {
      "gitlab_secrets_manager": {
        "server": {
          "url": "https://openbao.gitlab.com",
          "namespace": <for future use, just in case>
          "inline_auth": {
            "path": "gitlab_rails_jwt",
            "data": {
              "jwt": "eyJ...",
              "role": "project_123_ci"
            }
          }
        },
        "engine": {
          "name": "kv-v2",
          "path": "project_123/ci"
        },
        "path": "database_credentials",
        "field": "value"
      }
    }
  }
}

Backward Compatibility

  • No breaking changes: Existing vault, AWS, GCP, and Azure secret resolvers unchanged
  • Additive feature: New resolver only activates for gitlab_secrets_manager secrets
  • Rails compatibility: Supports the format Rails will send in the future
Edited by Erick Bajao

Merge request reports

Loading