Skip to content

[Backend] Add audit events on change in instance level external audit event destinations

With APIs added in !115157 (merged) instance level external audit events destinations can be added, updated and destroyed. We need to log audit events for such operations.

Implementation plan

  1. Currently the only scopes supported for audit events are user, group and project refer https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/audit/auditor.rb#L17.
  2. For audit events for changes made on instance level, we can consider creating a new abstract scope instance_scope, it will be similar to https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/audit/unauthenticated_author.rb.
  3. For this we can create a new class InstanceScope, which will have some required attributes and methods for a scope such as id, name, full_path and licensed_feature_available?().
  4. We can provide dummy values to attributes id, name and full_path.
  5. For licensed_feature_available?, we can simply consider whether the license is supported for instance or not.
  6. This method handles the instance scope without involving any database queries.

Alternate Implementation plan

  1. Currently the only scopes supported for audit events are user, group and project refer https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/audit/auditor.rb#L17.
  2. For supporting changes made on instance level, which means to application settings. We can consider a new scope application_settings which is unique to an instance.
  3. In AuditEvent, the scope is stored in polymorphic form under parameter entity for which the attributes entity_id and entity_type are stored in the audit_events table. Since we have an entity for application_settings, it would fit perfectly under this entity reference.
  4. Even though all instance level changes need not to be in application_settings table but they will be in other tables representing instance/application, so I think using the application_settings as scope is justified. Similar thing is done for group and project settings, where changes are actually made in some other tables but scope is group or project.
  5. The audit event context could be something like as created in !123335 (diffs).
module Audit
  class InstanceAuditEventsHelper
    def self.log_audit_event(user, event_type, message, target = nil)
      application_setting = ApplicationSetting.current

      target = application_setting if target.nil?

      audit_context = {
        name: event_type,
        author: user,
        scope: application_setting,
        target: target,
        message: message
      }

      ::Gitlab::Audit::Auditor.audit(audit_context)
    end
  end
end
  1. Regarding the method audit_enabled? in https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/ee/gitlab/audit/auditor.rb#L35, we can add a check for ::License.feature_available?(:audit_events) if scope is ApplicationSetting. This was not necessary as we already have a check ::License.feature_available?(:extended_audit_events) which will be true for premium and ultimate customer, but to maintain consistency I am adding this check.

Verification steps:

  1. For this you need to have Ultimate license for the gitlab instance and instance admin access.
  2. Since this requires instance admin access, I am using staging-ref environment here.
  3. Login as instance admin on https://staging-ref.gitlab.com/.
  4. Visit https://staging-ref.gitlab.com/admin/audit_logs?tab=streams.
  5. Add a new streaming destination by clicking on "Add streaming destination" button and then select "HTTP endpoint".
  6. Add a streaming destination url, for example "https://www.example.com", then click on "Add" button.
  7. Visit https://staging-ref.gitlab.com/admin/audit_logs?tab=log and refresh the page and you will see a new audit event for this destination being added in the logs. image
  8. Now for update, we can't do it from UI, so lets do it via API.
  9. Open https://staging-ref.gitlab.com/-/graphql-explorer for running graphql queries and mutations.
  10. Run following query in graphql explorer for listing down all the destinations and note down the gid of the destination you want to update:
query {
  instanceExternalAuditEventDestinations {
    nodes {
      id
      destinationUrl
      verificationToken      
      headers {
        nodes {
          id
          key
          value
        }
      }
    }
  }
}
  1. Run following mutation to update the destination, replace the gid with one obtained in step 5
mutation {
  instanceExternalAuditEventDestinationUpdate(input: { 
    id: "gid://gitlab/AuditEvents::InstanceExternalAuditEventDestination/<id>",
    destinationUrl: "https://www.newexample.com"
  }) {
    errors
    instanceExternalAuditEventDestination {
      destinationUrl
      id
      verificationToken
    }
  }
}
  1. Visit https://staging-ref.gitlab.com/admin/audit_logs?tab=log and refresh the page, there will be a new audit event related to destination update.
  2. Visit https://staging-ref.gitlab.com/admin/audit_logs?tab=streams again for deleting the destination.
  3. Click on the destination and then click the Delete destination button.
  4. Again visit https://staging-ref.gitlab.com/admin/audit_logs?tab=log and refresh the page, there should be an audit event for deletion of the destination.
Edited by Hitesh Raghuvanshi
OSZAR »