Add authentication to merge request external status checks
Release Post
External status checks can now be configured with HMAC (Hash-based Message Authentication Code) authentication. This will provide a more secure way to verify the authenticity of requests from GitLab to external services.
When enabled for your status check, a shared secret will be used to generate a unique signature for each request. The signature will be sent in the X-Gitlab-Signature
header, using SHA256 as the hash algorithm.
- Improved Security: HMAC authentication prevents tampering with requests and ensures they come from a legitimate source.
- Compliance: This feature is particularly valuable for regulated industries, such as banking, where security is paramount.
- Backwards Compatibility: The feature will be optional and backwards compatible. Users can choose to enable HMAC authentication for new or existing checks, but existing external status checks will continue to function without changes.
In a future iteration, GitLab plans to add options for verifying HTTPS and potentially blocking HTTP requests when HMAC is enabled.
Why are we doing this work
Problem
Merge request external status checks currently have no way of checking the authenticity of the request from GitLab in the external service. This could lead to spoofing of the GitLab request to the external status check.
Proposals
💡 Basic auth (we decided to go with HMAC (see below))
Add header parameter that is defined on creation of the external status check and can not be changed. Similar to . The external service can then verify the header parameter to ensure authenticity.audit event streaming authenticity
💡 HMAC implementation (thread)
- Implement with HMAC.
- Ensure that it's backwards compatible such that no existing un-authenticated checks are broken (and that this is very well tested and monitored during any rollout).
- Allow users to configure with HMAC, at which point we conditionally block HTTP.
Relevant links
Non-functional requirements
-
Documentation: -
Feature flag: -
Performance: -
Testing:
Implementation plan
-
database MR1: extend external_status_check
with new column to store the shared secret key with encrypted value (example: https://gitlab.com/gitlab-org/gitlab/-/blob/master/db/migrate/20240212223930_add_arkose_client_api_settings.rb):
encrypted_shared_secret character varying,
encrypted_shared_secret_iv character varying,
-
backend MR2: extend ee/app/models/merge_requests/external_status_check.rb
to withattr_encrypted
attr_encrypted :shared_secret,
mode: :per_attribute_iv,
algorithm: 'aes-256-gcm',
key: Settings.attr_encrypted_db_key_base_32
-
backend MR3: extend ee/app/graphql/mutations/branch_rules/external_status_checks
mutations (create only) to include argument with shared_secret, extend related services to store these values, extendee/app/graphql/types/branch_rules/external_status_check_type.rb
to return information that HMAC is enabled or not, -
backend MR4: modify ee/app/services/external_status_checks/dispatch_service.rb
to send webhook with HMAC (if shared secret is set for external status check, if it is not provided send it without any updates), -
frontend MR5: add to the modal ability to configure shared secret for external status check:
Verification steps
- Create new project in Ultimate group
- Go to Settings -> Merge Requests -> Status checks
- Click
Add status check
, provide URL, Service name, Target branch and specify shared secret. Save the check. - Create new MR with same target branch as defined in the external status check.
- Verify that request sent to the server included HMAC.