Skip to content

[Rails] Support OpenBao metadata CAS

Summary

Implement Compare-And-Set (CAS) support for both secret values and metadata in the Rails backend to prevent race conditions when multiple clients update the same secret concurrently. This extends the existing secret CAS functionality to include metadata versioning, utilizing OpenBao's new metadata CAS capabilities.

Prerequisites: This implementation requires OpenBao PR #1372 to be merged first.

Why this matters

Business Value:

  • Prevents data loss from concurrent secret updates in multi-user environments
  • Improves reliability of GitLab's secrets management for enterprise customers
  • Enables safe collaboration on secret configurations across teams

User Stories

  • As Priyanka (Platform Engineer), I want to update secret metadata without overwriting concurrent changes made by my teammate so that I can safely manage infrastructure configurations across teams.
  • As Amy (Application Security Engineer), I want version-controlled secret updates so that I can ensure no security configurations are accidentally overwritten during compliance reviews.
  • As Sasha (Software Developer), I want to receive clear error messages when my secret update conflicts with recent changes so that I can resolve conflicts and continue my development work.

Proposal

Extend the existing GraphQL mutations and types to support OpenBao's metadata CAS functionality by adding metadata version tracking fields and CAS parameters.

Important: Metadata CAS Only This implementation focuses exclusively on metadata CAS and does not include secret value CAS for the following reasons:

  • Current ProjectSecretUpdate mutation's secret value updates are temporary functionality
  • Secret value updates will be moved to a separate, dedicated mutation in future iterations
  • Metadata updates are the primary concern for the current mutation design
  • Adding secret CAS now would create technical debt when secret value functionality is separated

Key Changes:

  • Add metadata_version field to ProjectSecretType
  • Add required metadata_cas parameter to update mutation
  • Implement proper error handling for metadata CAS failures

Out of Scope

  • Automatic conflict resolution: Users must manually resolve version conflicts
  • Historical version browsing: This only tracks current versions, not full version history

Acceptance Criteria

  • ProjectSecretType returns metadata_version field from OpenBao
  • ProjectSecretUpdate mutation requires metadata_cas parameter
  • When metadata_cas matches current version, update succeeds
  • When metadata_cas doesn't match current version, update fails with clear error message

Implementation Table

Group Issue Link
backend 👈 You are here
frontend Frontend: Handle metadata CAS in Secret Manager UI

Technical Implementation Details

OpenBao Version Management

Files to Update:

  • GITLAB_OPENBAO_VERSION - Contains commit hash from openbao-internal repository
  • ee/lib/tasks/gitlab/secrets_management/openbao.rake - Contains SHA256 checksums for binary downloads

Update Process:

  1. Wait for openbao-internal to publish new release with CAS support
  2. Update GITLAB_OPENBAO_VERSION with new commit hash
  3. Update SHA256 checksums in openbao.rake for new binaries:
    • bao-darwin-arm64
    • bao-darwin-amd64
    • bao-linux-amd64

GraphQL Schema Changes

ProjectSecretType Extensions

field :metadata_version,
  type: GraphQL::Types::Int,
  null: false,
  description: 'Current version of the metadata.'

Mutation Argument Updates

# ProjectSecretUpdate  
argument :metadata_cas, GraphQL::Types::Int,
  required: true,
  description: 'Check-and-set parameter for metadata version. Metadata update will only succeed if current metadata version matches this value.'

Service Layer Updates

  • SecretsManagement::SecretsManagerClient - Add metadata_cas parameter to update_kv_secret_metadata method
  • SecretsManagement::ProjectSecret - Add metadata_version attribute
  • SecretsManagement::UpdateProjectSecretService - Handle required metadata_cas parameter and version extraction from responses
  • SecretsManagement::CreateProjectSecretService - Extract and return metadata_version from OpenBao create responses
  • SecretsManagement::ReadProjectSecretService - Extract and return metadata_version from secret metadata
  • SecretsManagement::ListProjectSecretsService - Extract and return metadata_version for each secret in list

Verification Steps

1. GraphQL Schema Verification

query {
  project(fullPath: "test-project") {
    projectSecrets {
      nodes {
        name
        metadataVersion
      }
    }
  }
}

Expected Response:

{
  "data": {
    "project": {
      "projectSecrets": {
        "nodes": [
          {
            "name": "database-password",
            "metadataVersion": 2
          }
        ]
      }
    }
  }
}

2. CAS Mutation Testing

These mutations should succeed and return updated metadata version.

mutation {
  projectSecretUpdate(
    input: {
      projectPath: "test-project"
      name: "test-secret"
      description: "updated description"
      metadataCas: 1
    }
  ) {
    projectSecret {
      metadataVersion
    }
    errors
  }
}
mutation {
  projectSecretCreate(
    input: {
      projectPath: "test-project"
      name: "new-secret"
      description: "new secret description"
      secret: "secret-value"
      environment: "production"
      branch: "main"
    }
  ) {
    projectSecret {
      name
      metadataVersion
    }
    errors
  }
}

3. Error Scenario Testing

# Should fail with CAS error when metadata version is outdated
mutation {
  projectSecretUpdate(
    input: {
      projectPath: "test-project"
      name: "test-secret"
      description: "updated description"
      metadataCas: 1  # Assuming current metadata version is 3
    }
  ) {
    errors # Should contain CAS failure details
  }
}
Edited by Erick Bajao