From c8305352686e74a65d350a4e27d56fb3e1d989e3 Mon Sep 17 00:00:00 2001 From: Baodong Date: Fri, 18 Feb 2022 17:51:21 +0800 Subject: [PATCH 01/14] Add shared_examples for audit streaming event --- ...t_audit_streaming_event_shared_examples.rb | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb diff --git a/spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb b/spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb new file mode 100644 index 00000000000000..8d3ce79ee58e45 --- /dev/null +++ b/spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'sends git audit streaming event' do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project) { create(:project, :private, :repository, namespace: group) } + + before do + stub_licensed_features(external_audit_events: true) + group.external_audit_event_destinations.create!(destination_url: 'http://example.com') + + project.add_developer(user) + sign_in(user) + end + + subject {} + + it 'sends the audit streaming event' do + expect(AuditEvents::AuditEventStreamingWorker).to receive(:perform_async).once + subject + end +end -- GitLab From 5b0d409ced70c59b601cc17e4efc9ed3ec634f16 Mon Sep 17 00:00:00 2001 From: Baodong Date: Fri, 18 Feb 2022 17:55:25 +0800 Subject: [PATCH 02/14] Add git audit streaming event unit test When user download source code on GUI. --- .../projects/repositories_controller_spec.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ee/spec/controllers/projects/repositories_controller_spec.rb b/ee/spec/controllers/projects/repositories_controller_spec.rb index 8d9290c1063c60..cc6860457f97d4 100644 --- a/ee/spec/controllers/projects/repositories_controller_spec.rb +++ b/ee/spec/controllers/projects/repositories_controller_spec.rb @@ -31,4 +31,14 @@ end end end + + describe 'git audit streaming event' do + it_behaves_like 'sends git audit streaming event' do + subject do + get :archive, + params: { namespace_id: project.namespace, project_id: project, id: "master" }, + format: "zip" + end + end + end end -- GitLab From 56d19aeaa672a97c47a3208c2cdeb10e173efffa Mon Sep 17 00:00:00 2001 From: Baodong Date: Fri, 18 Feb 2022 17:59:45 +0800 Subject: [PATCH 03/14] Add streaming event for allowed check --- ee/lib/ee/api/internal/base.rb | 10 ++++++++++ ee/spec/requests/api/internal/base_spec.rb | 7 +++++++ lib/api/internal/base.rb | 6 ++++++ 3 files changed, 23 insertions(+) diff --git a/ee/lib/ee/api/internal/base.rb b/ee/lib/ee/api/internal/base.rb index d70c6edb3a8ad3..29b34357a598a6 100644 --- a/ee/lib/ee/api/internal/base.rb +++ b/ee/lib/ee/api/internal/base.rb @@ -23,6 +23,16 @@ def check_allowed(params) end end + override :send_git_audit_streaming_event + def send_git_audit_streaming_event(msg) + ::AuditEvents::BuildService.new( + author: actor.user, + scope: @project, + target: @project, + message: msg + ).execute.stream_to_external_destinations + end + override :two_factor_otp_check def two_factor_otp_check return { success: false, message: 'Feature is not available' } unless ::License.feature_available?(:git_two_factor_enforcement) diff --git a/ee/spec/requests/api/internal/base_spec.rb b/ee/spec/requests/api/internal/base_spec.rb index 48a47715952cf4..740e7f4d64bdfa 100644 --- a/ee/spec/requests/api/internal/base_spec.rb +++ b/ee/spec/requests/api/internal/base_spec.rb @@ -277,6 +277,13 @@ def check_access_by_alias(alias_name) end end end + + context 'git audit streaming event' do + it_behaves_like 'sends git audit streaming event' do + let(:key) { create(:key, user: user) } + subject { pull(key, project) } + end + end end describe "POST /internal/lfs_authenticate", :geo do diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb index 48157a91477d2c..9c527f28d445df 100644 --- a/lib/api/internal/base.rb +++ b/lib/api/internal/base.rb @@ -92,6 +92,8 @@ def check_allowed(params) payload[:git_config_options] << "receive.maxInputSize=#{receive_max_input_size.megabytes}" end + send_git_audit_streaming_event(protocol: params[:protocol], action: params[:action]) + response_with_status(**payload) when ::Gitlab::GitAccessResult::CustomAction response_with_status(code: 300, payload: check_result.payload, gl_console_messages: check_result.console_messages) @@ -100,6 +102,10 @@ def check_allowed(params) end end + def send_git_audit_streaming_event(msg) + # Defined in EE + end + def access_check!(actor, params) access_checker = access_checker_for(actor, params[:protocol]) access_checker.check(params[:action], params[:changes]).tap do |result| -- GitLab From 42fc1c8957f1527afdc513e3853979405e82fd2d Mon Sep 17 00:00:00 2001 From: Baodong Date: Fri, 18 Feb 2022 18:01:11 +0800 Subject: [PATCH 04/14] Add streaming event for pulling repo by http --- .../ee/repositories/git_http_controller.rb | 15 +++++++++++++++ .../repositories/git_http_controller_spec.rb | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/ee/app/controllers/ee/repositories/git_http_controller.rb b/ee/app/controllers/ee/repositories/git_http_controller.rb index 903fec8f0c74c9..fa8959070b3d9d 100644 --- a/ee/app/controllers/ee/repositories/git_http_controller.rb +++ b/ee/app/controllers/ee/repositories/git_http_controller.rb @@ -81,6 +81,12 @@ def authenticate_user render_bad_geo_jwt("Invalid signature time ") end + override :update_fetch_statistics + def update_fetch_statistics + send_git_audit_streaming_event + super + end + def jwt_scope_valid? decoded_authorization[:scope] == repository_path.delete_suffix('.git') end @@ -102,6 +108,15 @@ def render_bad_geo_response(message) def ip_allowed? ::Gitlab::Geo.allowed_ip?(request.ip) end + + def send_git_audit_streaming_event + AuditEvents::BuildService.new( + author: user, + scope: project, + target: project, + message: { protocol: 'http', action: 'git-upload-pack' } + ).execute.stream_to_external_destinations + end end end end diff --git a/ee/spec/controllers/repositories/git_http_controller_spec.rb b/ee/spec/controllers/repositories/git_http_controller_spec.rb index 2f01da29b719db..0cf5d0c5277818 100644 --- a/ee/spec/controllers/repositories/git_http_controller_spec.rb +++ b/ee/spec/controllers/repositories/git_http_controller_spec.rb @@ -22,4 +22,19 @@ let(:access_checker_class) { Gitlab::GitAccessWiki } end end + + context 'git audit streaming event' do + include GitHttpHelpers + + it_behaves_like 'sends git audit streaming event' do + before do + password = user.try(:password) || user.try(:token) + request.headers.merge! auth_env(user.username, password, nil) + end + + subject do + post :git_upload_pack, params: { repository_path: "#{project.full_path}.git" } + end + end + end end -- GitLab From 6f54aaba31b130ff5a90ed00b6db2abd4f52aa24 Mon Sep 17 00:00:00 2001 From: Baodong Date: Fri, 18 Feb 2022 18:01:32 +0800 Subject: [PATCH 05/14] Update AuditEventStreamingWorker params --- ee/app/models/ee/audit_event.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/app/models/ee/audit_event.rb b/ee/app/models/ee/audit_event.rb index 9aa269f9a4884e..5c6ffdcce6c248 100644 --- a/ee/app/models/ee/audit_event.rb +++ b/ee/app/models/ee/audit_event.rb @@ -58,7 +58,7 @@ def stream_to_external_destinations return if entity.nil? return unless group_entity&.licensed_feature_available?(:external_audit_events) - AuditEvents::AuditEventStreamingWorker.perform_async(id) + AuditEvents::AuditEventStreamingWorker.perform_async(nil, self.to_json) end def entity_is_group_or_project? -- GitLab From 09bafeb75700c3949b1580e571be59133f9f4316 Mon Sep 17 00:00:00 2001 From: Baodong Date: Fri, 18 Feb 2022 19:06:15 +0800 Subject: [PATCH 06/14] Send git audit streaming event only for logged --- ee/app/controllers/ee/repositories/git_http_controller.rb | 2 ++ ee/lib/ee/api/internal/base.rb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/ee/app/controllers/ee/repositories/git_http_controller.rb b/ee/app/controllers/ee/repositories/git_http_controller.rb index fa8959070b3d9d..299b39ec2612b6 100644 --- a/ee/app/controllers/ee/repositories/git_http_controller.rb +++ b/ee/app/controllers/ee/repositories/git_http_controller.rb @@ -110,6 +110,8 @@ def ip_allowed? end def send_git_audit_streaming_event + return if user.blank? || project.blank? + AuditEvents::BuildService.new( author: user, scope: project, diff --git a/ee/lib/ee/api/internal/base.rb b/ee/lib/ee/api/internal/base.rb index 29b34357a598a6..f1ecd674ceab0b 100644 --- a/ee/lib/ee/api/internal/base.rb +++ b/ee/lib/ee/api/internal/base.rb @@ -25,6 +25,8 @@ def check_allowed(params) override :send_git_audit_streaming_event def send_git_audit_streaming_event(msg) + return if actor.user.blank? || @project.blank? + ::AuditEvents::BuildService.new( author: actor.user, scope: @project, -- GitLab From f0136eb6d2ced5d55abb9e30a30d508dab03119a Mon Sep 17 00:00:00 2001 From: Baodong Date: Tue, 22 Feb 2022 12:52:01 +0800 Subject: [PATCH 07/14] Refactor stream_to_external_destinations --- ee/app/controllers/ee/repositories/git_http_controller.rb | 2 +- ee/app/models/ee/audit_event.rb | 5 +++-- ee/lib/ee/api/internal/base.rb | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ee/app/controllers/ee/repositories/git_http_controller.rb b/ee/app/controllers/ee/repositories/git_http_controller.rb index 299b39ec2612b6..274c031e74964e 100644 --- a/ee/app/controllers/ee/repositories/git_http_controller.rb +++ b/ee/app/controllers/ee/repositories/git_http_controller.rb @@ -117,7 +117,7 @@ def send_git_audit_streaming_event scope: project, target: project, message: { protocol: 'http', action: 'git-upload-pack' } - ).execute.stream_to_external_destinations + ).execute.stream_to_external_destinations(use_json: true) end end end diff --git a/ee/app/models/ee/audit_event.rb b/ee/app/models/ee/audit_event.rb index 5c6ffdcce6c248..b3d76cc6535822 100644 --- a/ee/app/models/ee/audit_event.rb +++ b/ee/app/models/ee/audit_event.rb @@ -54,11 +54,12 @@ def lazy_entity end end - def stream_to_external_destinations + def stream_to_external_destinations(use_json: false) return if entity.nil? return unless group_entity&.licensed_feature_available?(:external_audit_events) - AuditEvents::AuditEventStreamingWorker.perform_async(nil, self.to_json) + perform_params = use_json ? [nil, self.to_json] : [id, nil] + AuditEvents::AuditEventStreamingWorker.perform_async(*perform_params) end def entity_is_group_or_project? diff --git a/ee/lib/ee/api/internal/base.rb b/ee/lib/ee/api/internal/base.rb index f1ecd674ceab0b..d52301d5b05072 100644 --- a/ee/lib/ee/api/internal/base.rb +++ b/ee/lib/ee/api/internal/base.rb @@ -32,7 +32,7 @@ def send_git_audit_streaming_event(msg) scope: @project, target: @project, message: msg - ).execute.stream_to_external_destinations + ).execute.stream_to_external_destinations(use_json: true) end override :two_factor_otp_check -- GitLab From 799417b42a0fa0c1433c95f3a62f6fb729ccc765 Mon Sep 17 00:00:00 2001 From: Baodong Date: Wed, 23 Feb 2022 11:33:29 +0800 Subject: [PATCH 08/14] Add with_indifferent_access when parsing json --- ee/app/workers/audit_events/audit_event_streaming_worker.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/app/workers/audit_events/audit_event_streaming_worker.rb b/ee/app/workers/audit_events/audit_event_streaming_worker.rb index 6eaee43bd02af5..151b4530abb521 100644 --- a/ee/app/workers/audit_events/audit_event_streaming_worker.rb +++ b/ee/app/workers/audit_events/audit_event_streaming_worker.rb @@ -46,7 +46,7 @@ def audit_event(audit_event_id, audit_event_json) end def parse_audit_event_json(audit_event_json) - audit_event_json = Gitlab::Json.parse(audit_event_json) + audit_event_json = Gitlab::Json.parse(audit_event_json).with_indifferent_access audit_event = AuditEvent.new(audit_event_json) # We want to have created_at as unique id for deduplication if audit_event id is not present audit_event.id = audit_event.created_at.to_i if audit_event.id.blank? -- GitLab From 99ee6c9710f08e6292e389ad04a83d2f187348aa Mon Sep 17 00:00:00 2001 From: Baodong Date: Wed, 23 Feb 2022 14:49:03 +0800 Subject: [PATCH 09/14] Add audit event streaming with git operations Add audit event streaming with git operations When users push or pull the repository through git (through ssh/http/https/GUI download button), streaming audit events are sent. Changelog: added EE: true MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76719 --- .../audit_event_streaming_git_operations.md | 187 ++++++++++++++++++ .../ee/repositories/git_http_controller.rb | 1 + .../audit_event_streaming_git_operations.yml | 8 + ee/lib/ee/api/internal/base.rb | 1 + 4 files changed, 197 insertions(+) create mode 100644 doc/administration/audit_event_streaming_git_operations.md create mode 100644 ee/config/feature_flags/development/audit_event_streaming_git_operations.yml diff --git a/doc/administration/audit_event_streaming_git_operations.md b/doc/administration/audit_event_streaming_git_operations.md new file mode 100644 index 00000000000000..b00e8427444066 --- /dev/null +++ b/doc/administration/audit_event_streaming_git_operations.md @@ -0,0 +1,187 @@ +--- +stage: Manage +group: Compliance +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +--- + +# Audit event streaming on Git operations **(ULTIMATE)** + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/332747) in GitLab 14.9 [with a flag](../administration/feature_flags.md) named `audit_event_streaming_git_operations`. Disabled by default. + +FLAG: +On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](feature_flags.md) named `audit_event_streaming_git_operations`. On GitLab.com, this feature is not available. + +Streaming audit events can be sent when users push or pull repositories with Git. For more information, see +[Add a new event streaming destination](../administration/audit_event_streaming.md#add-a-new-event-streaming-destination). + +For specific configuration, refer to [Add a new event streaming destination](../administration/audit_event_streaming.md#add-a-new-event-streaming-destination) . + +Users can interact with remote repositories in three ways: + +- [Using SSH](../ssh/index.md). +- Using HTTP or HTTPS. +- A **download button** (**{download}**) in the GitLab UI. + +## Request headers + +Request headers are formatted as follows: + +```plaintext +POST /logs HTTP/1.1 +Host: +Content-Type: application/x-www-form-urlencoded +X-Gitlab-Event-Streaming-Token: +``` + +## Example responses for SSH events + +Fetch: + +```json +{ + "id": 1645597265, + "author_id": 1, + "entity_id": 29, + "entity_type": "Project", + "details": { + "author_name": "Administrator", + "target_id": 29, + "target_type": "Project", + "target_details": "notice", + "custom_message": { + "protocol": "ssh", + "action": "git-upload-pack" + }, + "ip_address": "127.0.0.1", + "entity_path": "icbd-private-group/notice" + }, + "ip_address": "127.0.0.1", + "author_name": "Administrator", + "entity_path": "icbd-private-group/notice", + "target_details": "notice", + "created_at": "2022-02-23T06:21:05.283Z", + "target_type": "Project", + "target_id": 29 +} +``` + +Push: + +```json +{ + "id": 1645597388, + "author_id": 1, + "entity_id": 29, + "entity_type": "Project", + "details": { + "author_name": "Administrator", + "target_id": 29, + "target_type": "Project", + "target_details": "notice", + "custom_message": { + "protocol": "ssh", + "action": "git-receive-pack" + }, + "ip_address": "127.0.0.1", + "entity_path": "icbd-private-group/notice" + }, + "ip_address": "127.0.0.1", + "author_name": "Administrator", + "entity_path": "icbd-private-group/notice", + "target_details": "notice", + "created_at": "2022-02-23T06:23:08.746Z", + "target_type": "Project", + "target_id": 29 +} +``` + +## Example responses for HTTP and HTTPS events + +Fetch: + +```json +{ + "id": 1645597543, + "author_id": 1, + "entity_id": 29, + "entity_type": "Project", + "details": { + "author_name": "Administrator", + "target_id": 29, + "target_type": "Project", + "target_details": "notice", + "custom_message": { + "protocol": "http", + "action": "git-upload-pack" + }, + "ip_address": "127.0.0.1", + "entity_path": "icbd-private-group/notice" + }, + "ip_address": "127.0.0.1", + "author_name": "Administrator", + "entity_path": "icbd-private-group/notice", + "target_details": "notice", + "created_at": "2022-02-23T06:25:43.938Z", + "target_type": "Project", + "target_id": 29 +} +``` + +Push: + +```json +{ + "id": 1645597589, + "author_id": 1, + "entity_id": 29, + "entity_type": "Project", + "details": { + "author_name": "Administrator", + "target_id": 29, + "target_type": "Project", + "target_details": "notice", + "custom_message": { + "protocol": "http", + "action": "git-receive-pack" + }, + "ip_address": "127.0.0.1", + "entity_path": "icbd-private-group/notice" + }, + "ip_address": "127.0.0.1", + "author_name": "Administrator", + "entity_path": "icbd-private-group/notice", + "target_details": "notice", + "created_at": "2022-02-23T06:26:29.294Z", + "target_type": "Project", + "target_id": 29 +} +``` + +## Example responses for events from GitLab UI **download button** (**{download}**) + +Fetch: + +```json +{ + "id": 185, + "author_id": 99, + "entity_id": 29, + "entity_type": "Project", + "details": { + "custom_message": "Repository Download Started", + "author_name": "Baodong Cao", + "target_id": 29, + "target_type": "Project", + "target_details": "icbd-private-group/notice", + "ip_address": "127.0.0.1", + "entity_path": "icbd-private-group/notice" + }, + "ip_address": "127.0.0.1", + "author_name": "Baodong Cao", + "entity_path": "icbd-private-group/notice", + "target_details": "icbd-private-group/notice", + "created_at": "2022-02-23T06:27:17.873Z", + "target_type": "Project", + "target_id": 29 +} +``` diff --git a/ee/app/controllers/ee/repositories/git_http_controller.rb b/ee/app/controllers/ee/repositories/git_http_controller.rb index 274c031e74964e..e7b940324183b4 100644 --- a/ee/app/controllers/ee/repositories/git_http_controller.rb +++ b/ee/app/controllers/ee/repositories/git_http_controller.rb @@ -111,6 +111,7 @@ def ip_allowed? def send_git_audit_streaming_event return if user.blank? || project.blank? + return unless ::Feature.enabled?(:audit_event_streaming_git_operations, project.group) AuditEvents::BuildService.new( author: user, diff --git a/ee/config/feature_flags/development/audit_event_streaming_git_operations.yml b/ee/config/feature_flags/development/audit_event_streaming_git_operations.yml new file mode 100644 index 00000000000000..c12dad37bf44df --- /dev/null +++ b/ee/config/feature_flags/development/audit_event_streaming_git_operations.yml @@ -0,0 +1,8 @@ +--- +name: audit_event_streaming_git_operations +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76719 +rollout_issue_url: +milestone: '14.9' +type: development +group: group::compliance +default_enabled: false diff --git a/ee/lib/ee/api/internal/base.rb b/ee/lib/ee/api/internal/base.rb index d52301d5b05072..4cebd645c77d25 100644 --- a/ee/lib/ee/api/internal/base.rb +++ b/ee/lib/ee/api/internal/base.rb @@ -26,6 +26,7 @@ def check_allowed(params) override :send_git_audit_streaming_event def send_git_audit_streaming_event(msg) return if actor.user.blank? || @project.blank? + return unless ::Feature.enabled?(:audit_event_streaming_git_operations, @project.group) ::AuditEvents::BuildService.new( author: actor.user, -- GitLab From fd265fd8443fdf49f4a6b7321bd3d4253c474562 Mon Sep 17 00:00:00 2001 From: Baodong Date: Fri, 25 Feb 2022 09:18:54 +0800 Subject: [PATCH 10/14] Update doc audit_event_streaming --- doc/administration/audit_event_streaming.md | 181 +++++++++++++++++ .../audit_event_streaming_git_operations.md | 187 ------------------ ...t_audit_streaming_event_shared_examples.rb | 44 ++++- 3 files changed, 215 insertions(+), 197 deletions(-) delete mode 100644 doc/administration/audit_event_streaming_git_operations.md diff --git a/doc/administration/audit_event_streaming.md b/doc/administration/audit_event_streaming.md index 3bdc67e5a69cd6..fcfbb39c4ab7af 100644 --- a/doc/administration/audit_event_streaming.md +++ b/doc/administration/audit_event_streaming.md @@ -78,3 +78,184 @@ token is generated when the event destination is created and cannot be changed. Each streamed event contains a random alphanumeric identifier for the `X-Gitlab-Event-Streaming-Token` HTTP header that can be verified against the destination's value when [listing streaming destinations](#list-currently-enabled-streaming-destinations). + +## Audit event streaming on Git operations + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/332747) in GitLab 14.9 [with a flag](../administration/feature_flags.md) named `audit_event_streaming_git_operations`. Disabled by default. + +FLAG: +On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](feature_flags.md) named `audit_event_streaming_git_operations`. On GitLab.com, this feature is not available. + +Streaming audit events can be sent when signed-in users push or pull a project's remote Git repositories: + +- [Using SSH](../ssh/index.md). +- Using HTTP or HTTPS. +- Using the **Download** button (**{download}**) in GitLab UI. + +Audit events are not captured for users that are not signed in. For example, when downloading a public project. + +To configure streaming audit events for Git operations, see [Add a new event streaming destination](#add-a-new-event-streaming-destination). + +### Request headers + +Request headers are formatted as follows: + +```plaintext +POST /logs HTTP/1.1 +Host: +Content-Type: application/x-www-form-urlencoded +X-Gitlab-Event-Streaming-Token: +``` + +### Example responses for SSH events + +Fetch: + +```json +{ + "id": 1645597265, + "author_id": 1, + "entity_id": 29, + "entity_type": "Project", + "details": { + "author_name": "Administrator", + "target_id": 29, + "target_type": "Project", + "target_details": "example-project", + "custom_message": { + "protocol": "ssh", + "action": "git-upload-pack" + }, + "ip_address": "127.0.0.1", + "entity_path": "example-group/example-project" + }, + "ip_address": "127.0.0.1", + "author_name": "Administrator", + "entity_path": "example-group/example-project", + "target_details": "example-project", + "created_at": "2022-02-23T06:21:05.283Z", + "target_type": "Project", + "target_id": 29 +} +``` + +Push: + +```json +{ + "id": 1645597388, + "author_id": 1, + "entity_id": 29, + "entity_type": "Project", + "details": { + "author_name": "Administrator", + "target_id": 29, + "target_type": "Project", + "target_details": "example-project", + "custom_message": { + "protocol": "ssh", + "action": "git-receive-pack" + }, + "ip_address": "127.0.0.1", + "entity_path": "example-group/example-project" + }, + "ip_address": "127.0.0.1", + "author_name": "Administrator", + "entity_path": "example-group/example-project", + "target_details": "example-project", + "created_at": "2022-02-23T06:23:08.746Z", + "target_type": "Project", + "target_id": 29 +} +``` + +### Example responses for HTTP and HTTPS events + +Fetch: + +```json +{ + "id": 1645597543, + "author_id": 1, + "entity_id": 29, + "entity_type": "Project", + "details": { + "author_name": "Administrator", + "target_id": 29, + "target_type": "Project", + "target_details": "example-project", + "custom_message": { + "protocol": "http", + "action": "git-upload-pack" + }, + "ip_address": "127.0.0.1", + "entity_path": "example-group/example-project" + }, + "ip_address": "127.0.0.1", + "author_name": "Administrator", + "entity_path": "example-group/example-project", + "target_details": "example-project", + "created_at": "2022-02-23T06:25:43.938Z", + "target_type": "Project", + "target_id": 29 +} +``` + +Push: + +```json +{ + "id": 1645597589, + "author_id": 1, + "entity_id": 29, + "entity_type": "Project", + "details": { + "author_name": "Administrator", + "target_id": 29, + "target_type": "Project", + "target_details": "example-project", + "custom_message": { + "protocol": "http", + "action": "git-receive-pack" + }, + "ip_address": "127.0.0.1", + "entity_path": "example-group/example-project" + }, + "ip_address": "127.0.0.1", + "author_name": "Administrator", + "entity_path": "example-group/example-project", + "target_details": "example-project", + "created_at": "2022-02-23T06:26:29.294Z", + "target_type": "Project", + "target_id": 29 +} +``` + +### Example responses for events from GitLab UI **download button** (**{download}**) + +Fetch: + +```json +{ + "id": 185, + "author_id": 99, + "entity_id": 29, + "entity_type": "Project", + "details": { + "custom_message": "Repository Download Started", + "author_name": "example_username", + "target_id": 29, + "target_type": "Project", + "target_details": "example-group/example-project", + "ip_address": "127.0.0.1", + "entity_path": "example-group/example-project" + }, + "ip_address": "127.0.0.1", + "author_name": "example_username", + "entity_path": "example-group/example-project", + "target_details": "example-group/example-project", + "created_at": "2022-02-23T06:27:17.873Z", + "target_type": "Project", + "target_id": 29 +} +``` diff --git a/doc/administration/audit_event_streaming_git_operations.md b/doc/administration/audit_event_streaming_git_operations.md deleted file mode 100644 index b00e8427444066..00000000000000 --- a/doc/administration/audit_event_streaming_git_operations.md +++ /dev/null @@ -1,187 +0,0 @@ ---- -stage: Manage -group: Compliance -info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments ---- - -# Audit event streaming on Git operations **(ULTIMATE)** - -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/332747) in GitLab 14.9 [with a flag](../administration/feature_flags.md) named `audit_event_streaming_git_operations`. Disabled by default. - -FLAG: -On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](feature_flags.md) named `audit_event_streaming_git_operations`. On GitLab.com, this feature is not available. - -Streaming audit events can be sent when users push or pull repositories with Git. For more information, see -[Add a new event streaming destination](../administration/audit_event_streaming.md#add-a-new-event-streaming-destination). - -For specific configuration, refer to [Add a new event streaming destination](../administration/audit_event_streaming.md#add-a-new-event-streaming-destination) . - -Users can interact with remote repositories in three ways: - -- [Using SSH](../ssh/index.md). -- Using HTTP or HTTPS. -- A **download button** (**{download}**) in the GitLab UI. - -## Request headers - -Request headers are formatted as follows: - -```plaintext -POST /logs HTTP/1.1 -Host: -Content-Type: application/x-www-form-urlencoded -X-Gitlab-Event-Streaming-Token: -``` - -## Example responses for SSH events - -Fetch: - -```json -{ - "id": 1645597265, - "author_id": 1, - "entity_id": 29, - "entity_type": "Project", - "details": { - "author_name": "Administrator", - "target_id": 29, - "target_type": "Project", - "target_details": "notice", - "custom_message": { - "protocol": "ssh", - "action": "git-upload-pack" - }, - "ip_address": "127.0.0.1", - "entity_path": "icbd-private-group/notice" - }, - "ip_address": "127.0.0.1", - "author_name": "Administrator", - "entity_path": "icbd-private-group/notice", - "target_details": "notice", - "created_at": "2022-02-23T06:21:05.283Z", - "target_type": "Project", - "target_id": 29 -} -``` - -Push: - -```json -{ - "id": 1645597388, - "author_id": 1, - "entity_id": 29, - "entity_type": "Project", - "details": { - "author_name": "Administrator", - "target_id": 29, - "target_type": "Project", - "target_details": "notice", - "custom_message": { - "protocol": "ssh", - "action": "git-receive-pack" - }, - "ip_address": "127.0.0.1", - "entity_path": "icbd-private-group/notice" - }, - "ip_address": "127.0.0.1", - "author_name": "Administrator", - "entity_path": "icbd-private-group/notice", - "target_details": "notice", - "created_at": "2022-02-23T06:23:08.746Z", - "target_type": "Project", - "target_id": 29 -} -``` - -## Example responses for HTTP and HTTPS events - -Fetch: - -```json -{ - "id": 1645597543, - "author_id": 1, - "entity_id": 29, - "entity_type": "Project", - "details": { - "author_name": "Administrator", - "target_id": 29, - "target_type": "Project", - "target_details": "notice", - "custom_message": { - "protocol": "http", - "action": "git-upload-pack" - }, - "ip_address": "127.0.0.1", - "entity_path": "icbd-private-group/notice" - }, - "ip_address": "127.0.0.1", - "author_name": "Administrator", - "entity_path": "icbd-private-group/notice", - "target_details": "notice", - "created_at": "2022-02-23T06:25:43.938Z", - "target_type": "Project", - "target_id": 29 -} -``` - -Push: - -```json -{ - "id": 1645597589, - "author_id": 1, - "entity_id": 29, - "entity_type": "Project", - "details": { - "author_name": "Administrator", - "target_id": 29, - "target_type": "Project", - "target_details": "notice", - "custom_message": { - "protocol": "http", - "action": "git-receive-pack" - }, - "ip_address": "127.0.0.1", - "entity_path": "icbd-private-group/notice" - }, - "ip_address": "127.0.0.1", - "author_name": "Administrator", - "entity_path": "icbd-private-group/notice", - "target_details": "notice", - "created_at": "2022-02-23T06:26:29.294Z", - "target_type": "Project", - "target_id": 29 -} -``` - -## Example responses for events from GitLab UI **download button** (**{download}**) - -Fetch: - -```json -{ - "id": 185, - "author_id": 99, - "entity_id": 29, - "entity_type": "Project", - "details": { - "custom_message": "Repository Download Started", - "author_name": "Baodong Cao", - "target_id": 29, - "target_type": "Project", - "target_details": "icbd-private-group/notice", - "ip_address": "127.0.0.1", - "entity_path": "icbd-private-group/notice" - }, - "ip_address": "127.0.0.1", - "author_name": "Baodong Cao", - "entity_path": "icbd-private-group/notice", - "target_details": "icbd-private-group/notice", - "created_at": "2022-02-23T06:27:17.873Z", - "target_type": "Project", - "target_id": 29 -} -``` diff --git a/spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb b/spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb index 8d3ce79ee58e45..e2437be503f609 100644 --- a/spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb +++ b/spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb @@ -1,22 +1,46 @@ # frozen_string_literal: true RSpec.shared_examples 'sends git audit streaming event' do - let(:user) { create(:user) } - let(:group) { create(:group) } - let(:project) { create(:project, :private, :repository, namespace: group) } + let_it_be(:user) { create(:user) } before do stub_licensed_features(external_audit_events: true) - group.external_audit_event_destinations.create!(destination_url: 'http://example.com') - - project.add_developer(user) - sign_in(user) end subject {} - it 'sends the audit streaming event' do - expect(AuditEvents::AuditEventStreamingWorker).to receive(:perform_async).once - subject + content 'for public groups and projects' do + let(:group) { create(:group, :public) } + let(:project) { create(:project, :public, :repository, namespace: group) } + + before do + group.external_audit_event_destinations.create!(destination_url: 'http://example.com') + project.add_developer(user) + end + + context 'when user not logged in' do + it 'sends the audit streaming event' do + expect(AuditEvents::AuditEventStreamingWorker).not_to receive(:perform_async) + subject + end + end + end + + content 'for private groups and projects' do + let(:group) { create(:group, :private) } + let(:project) { create(:project, :private, :repository, namespace: group) } + + before do + group.external_audit_event_destinations.create!(destination_url: 'http://example.com') + project.add_developer(user) + sign_in(user) + end + + context 'when user logged in' do + it 'sends the audit streaming event' do + expect(AuditEvents::AuditEventStreamingWorker).to receive(:perform_async).once + subject + end + end end end -- GitLab From 58a185b2b97292347bbbdc4ab6aa78c4d349ca69 Mon Sep 17 00:00:00 2001 From: Evan Read Date: Mon, 7 Mar 2022 02:42:56 +0000 Subject: [PATCH 11/14] Apply 1 suggestion(s) to 1 file(s) --- doc/administration/audit_event_streaming.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/administration/audit_event_streaming.md b/doc/administration/audit_event_streaming.md index fcfbb39c4ab7af..fcd14adb46e8f6 100644 --- a/doc/administration/audit_event_streaming.md +++ b/doc/administration/audit_event_streaming.md @@ -231,7 +231,7 @@ Push: } ``` -### Example responses for events from GitLab UI **download button** (**{download}**) +### Example responses for events from GitLab UI download button Fetch: -- GitLab From 2d6014e97c4953aed92067d08bae207237db2cdf Mon Sep 17 00:00:00 2001 From: Baodong Date: Mon, 7 Mar 2022 10:54:15 +0800 Subject: [PATCH 12/14] Update audit event streaming example id --- doc/administration/audit_event_streaming.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/administration/audit_event_streaming.md b/doc/administration/audit_event_streaming.md index fcd14adb46e8f6..70e175c8899a94 100644 --- a/doc/administration/audit_event_streaming.md +++ b/doc/administration/audit_event_streaming.md @@ -113,7 +113,7 @@ Fetch: ```json { - "id": 1645597265, + "id": 1, "author_id": 1, "entity_id": 29, "entity_type": "Project", @@ -143,7 +143,7 @@ Push: ```json { - "id": 1645597388, + "id": 1, "author_id": 1, "entity_id": 29, "entity_type": "Project", @@ -175,7 +175,7 @@ Fetch: ```json { - "id": 1645597543, + "id": 1, "author_id": 1, "entity_id": 29, "entity_type": "Project", @@ -205,7 +205,7 @@ Push: ```json { - "id": 1645597589, + "id": 1, "author_id": 1, "entity_id": 29, "entity_type": "Project", @@ -237,7 +237,7 @@ Fetch: ```json { - "id": 185, + "id": 1, "author_id": 99, "entity_id": 29, "entity_type": "Project", -- GitLab From 90dc027d4250d7edd64a6a5a605c77b8be09694a Mon Sep 17 00:00:00 2001 From: Baodong Date: Mon, 7 Mar 2022 16:28:53 +0800 Subject: [PATCH 13/14] Fix streaming audit event UT --- .../projects/repositories_controller_spec.rb | 44 ++++++++++++++----- .../repositories/git_http_controller_spec.rb | 5 --- ee/spec/requests/api/internal/base_spec.rb | 1 - ...t_audit_streaming_event_shared_examples.rb | 19 +++++++- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/ee/spec/controllers/projects/repositories_controller_spec.rb b/ee/spec/controllers/projects/repositories_controller_spec.rb index cc6860457f97d4..7325e8d699b5e7 100644 --- a/ee/spec/controllers/projects/repositories_controller_spec.rb +++ b/ee/spec/controllers/projects/repositories_controller_spec.rb @@ -3,14 +3,22 @@ require "spec_helper" RSpec.describe Projects::RepositoriesController do - let(:project) { create(:project, :repository) } + let(:group) { create(:group) } + let(:project) { create(:project, :repository, namespace: group) } describe "GET archive" do + subject(:get_archive) do + get :archive, params: { namespace_id: project.namespace, project_id: project, id: "master" }, format: "zip" + end + + def set_group_destination + group.external_audit_event_destinations.create!(destination_url: 'http://example.com') + stub_licensed_features(external_audit_events: true) + end + shared_examples 'logs the audit event' do it 'logs the audit event' do - expect do - get :archive, params: { namespace_id: project.namespace, project_id: project, id: "master" }, format: "zip" - end.to change { AuditEvent.count }.by(1) + expect { get_archive }.to change { AuditEvent.count }.by(1) end end @@ -18,6 +26,16 @@ it_behaves_like 'logs the audit event' do let(:project) { create(:project, :repository, :public) } end + + context 'when group sets event destination' do + before do + set_group_destination + end + it "doesn't send the streaming audit event" do + expect(AuditEvents::AuditEventStreamingWorker).not_to receive(:perform_async) + get_archive + end + end end context 'when authenticated', 'as a developer' do @@ -29,15 +47,17 @@ it_behaves_like 'logs the audit event' do let(:user) { create(:user) } end - end - end - describe 'git audit streaming event' do - it_behaves_like 'sends git audit streaming event' do - subject do - get :archive, - params: { namespace_id: project.namespace, project_id: project, id: "master" }, - format: "zip" + context 'when group sets event destination' do + let(:user) { create(:user) } + + before do + set_group_destination + end + it "sends the streaming audit event" do + expect(AuditEvents::AuditEventStreamingWorker).to receive(:perform_async) + get_archive + end end end end diff --git a/ee/spec/controllers/repositories/git_http_controller_spec.rb b/ee/spec/controllers/repositories/git_http_controller_spec.rb index 0cf5d0c5277818..8379b2b2307b34 100644 --- a/ee/spec/controllers/repositories/git_http_controller_spec.rb +++ b/ee/spec/controllers/repositories/git_http_controller_spec.rb @@ -27,11 +27,6 @@ include GitHttpHelpers it_behaves_like 'sends git audit streaming event' do - before do - password = user.try(:password) || user.try(:token) - request.headers.merge! auth_env(user.username, password, nil) - end - subject do post :git_upload_pack, params: { repository_path: "#{project.full_path}.git" } end diff --git a/ee/spec/requests/api/internal/base_spec.rb b/ee/spec/requests/api/internal/base_spec.rb index 740e7f4d64bdfa..c73d149d34f344 100644 --- a/ee/spec/requests/api/internal/base_spec.rb +++ b/ee/spec/requests/api/internal/base_spec.rb @@ -280,7 +280,6 @@ def check_access_by_alias(alias_name) context 'git audit streaming event' do it_behaves_like 'sends git audit streaming event' do - let(:key) { create(:key, user: user) } subject { pull(key, project) } end end diff --git a/spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb b/spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb index e2437be503f609..2c2be0152a07b5 100644 --- a/spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb +++ b/spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb @@ -9,7 +9,7 @@ subject {} - content 'for public groups and projects' do + context 'for public groups and projects' do let(:group) { create(:group, :public) } let(:project) { create(:project, :public, :repository, namespace: group) } @@ -19,6 +19,13 @@ end context 'when user not logged in' do + let(:key) { create(:key) } + + before do + if request + request.headers.merge! auth_env(user.username, nil, nil) + end + end it 'sends the audit streaming event' do expect(AuditEvents::AuditEventStreamingWorker).not_to receive(:perform_async) subject @@ -26,7 +33,7 @@ end end - content 'for private groups and projects' do + context 'for private groups and projects' do let(:group) { create(:group, :private) } let(:project) { create(:project, :private, :repository, namespace: group) } @@ -37,6 +44,14 @@ end context 'when user logged in' do + let(:key) { create(:key, user: user) } + + before do + if request + password = user.try(:password) || user.try(:token) + request.headers.merge! auth_env(user.username, password, nil) + end + end it 'sends the audit streaming event' do expect(AuditEvents::AuditEventStreamingWorker).to receive(:perform_async).once subject -- GitLab From 2c68af3fa0f661984599198e89b0a20d99d70367 Mon Sep 17 00:00:00 2001 From: Evan Read Date: Tue, 8 Mar 2022 01:34:58 +0000 Subject: [PATCH 14/14] Apply 1 suggestion(s) to 1 file(s) --- doc/administration/audit_event_streaming.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/administration/audit_event_streaming.md b/doc/administration/audit_event_streaming.md index 70e175c8899a94..4f2fc8c26e25e2 100644 --- a/doc/administration/audit_event_streaming.md +++ b/doc/administration/audit_event_streaming.md @@ -81,7 +81,7 @@ the destination's value when [listing streaming destinations](#list-currently-en ## Audit event streaming on Git operations -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/332747) in GitLab 14.9 [with a flag](../administration/feature_flags.md) named `audit_event_streaming_git_operations`. Disabled by default. +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/332747) in GitLab 14.9 [with a flag](../administration/feature_flags.md) named `audit_event_streaming_git_operations`. Disabled by default. FLAG: On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](feature_flags.md) named `audit_event_streaming_git_operations`. On GitLab.com, this feature is not available. -- GitLab