For an organization that builds software, GitLab can be one of its crown jewels. It may hold proprietary source code, internal architecture, deployment configuration, and customer information in issues, attachments, or repository files. Depending on permissions and how delivery is configured, access to GitLab can also provide a way to change what the organization builds and deploys.
That makes GitLab a security monitoring priority. The questions extend from who signed in to who changed access, read sensitive repositories, pushed code, or initiated a destructive action. The answers matter to the confidentiality of company and customer data, the integrity of software, and the availability of the development environment.
This article walks through understanding GitLab’s security-relevant logs, sending them to a central SIEM, and using them for dashboards, alerts, and investigation. Wazuh is the implementation example. The approach starts with the information an analyst needs and follows it from a real GitLab event to a usable security signal.
Central collection gives the team a place to search and connect events. Dashboards make activity and changes visible. Alerts bring selected patterns to an analyst’s attention. Each needs its own validation: a file being forwarded does not establish that an account field is searchable, a chart counts the right thing, or a detection reaches someone who can investigate.
The examples draw on GitLab logs and Wazuh configuration, with identifying values replaced by placeholders. Authentication is the detailed walkthrough; access changes and repository activity extend monitoring to what happens after sign-in. Validate the configuration against your GitLab version and installation.
Start with the security questions#
Before choosing log files or writing XML, identify the actions that could affect the organization’s data and software.
| Security question | Evidence to look for |
|---|---|
| Is someone trying to gain access? | Rejected sign-ins, repeated failures, attempted accounts, source addresses, and lockouts where emitted |
| Who successfully authenticated, and how? | Password, OAuth, and MFA audit events, with account, address, time, and request correlation |
| Was access extended or weakened? | Token and SSH-key activity, MFA changes, membership changes, and repository access settings |
| Was sensitive code read or changed? | HTTP and SSH Git activity, repository/account context, exports, and transfer volume where available |
| Was a destructive or administrative action attempted? | Project lifecycle requests, related background jobs, and administrative changes, followed by outcome verification |
These questions define a monitoring scope. The example rules address parts of it; they do not establish complete coverage of customer-data access, pipeline changes, or every action GitLab supports.
Know which GitLab logs answer which questions#
GitLab separates application, audit, and component activity across several logs. Start with its log system documentation and inspect the files your installation actually writes. Paths below follow the Linux package layout used by the example collection configuration, under /var/log/gitlab/.
| Log source | Security use in this integration | Evidence or coverage boundary |
|---|---|---|
gitlab-rails/production_json.log | Web sign-ins, security-setting requests, and Git-over-HTTP activity | Check controller, action, method, and status together; a request does not always prove a completed change |
gitlab-rails/audit_json.log | Explicit audit events and actor context | Check which password, OAuth, MFA, and lockout events your version and edition emit |
gitlab-rails/application.log / application_json.log | Application events; the example custom failure decoder expects the plain-text file | Confirm file availability and the failure-message format before enabling account-based correlation |
gitlab-rails/api_json.log | API activity, including the routes targeted by project lifecycle rules | Verify API event shapes and outcomes on your instance |
gitlab-rails/auth.log | Authentication/rate-limit context, depending on emitted events | Inspect the format and event content; allowlist messages do not establish failed-login coverage |
nginx/gitlab_access.log / gitlab_error.log | HTTP access and error context for investigating requests | Collection is configured; these do not replace application-level account and outcome evidence |
gitlab-workhorse/current | Transfer context; the repository rules inspect route_id, written_bytes, and source address | Large-transfer rules are heuristics requiring validation and an automation baseline |
gitlab-shell/gitlab-shell.log | The intended source for Git-over-SSH push/fetch rules | Expected SSH fields need validation against an actual event on your instance |
sidekiq/current | Background-job context for asynchronous actions | Deletion rules inspect job starts; a start does not prove completed deletion |
gitaly/current, Rails git_json.log and graphql_json.log | Additional repository-service, Git-error, and GraphQL context to investigate | Collection entries exist; their presence alone does not establish detection coverage |
The collection file also includes webhook, integration, exception, and Gitaly-hook logs. Choose and validate sources against the security questions above. For each one, record whether it exists, which events it emits, whether the agent collects it, and whether any rule or dashboard actually uses it.
Send the logs to a central SIEM#
In this example, the Wazuh agent reads the GitLab files and forwards events to the manager for decoding and rule evaluation. Wazuh describes the collection flow in its log collection documentation. Centralizing that evidence supports investigation across accounts, repositories, hosts, and identity systems without relying on a local log search during an incident.
These two blocks from the example agent configuration collect the main sources used in the authentication walkthrough. Add them inside the agent’s existing ossec_config element after verifying paths and file access:
<localfile>
<location>/var/log/gitlab/gitlab-rails/production_json.log</location>
<log_format>json</log_format>
<only-future-events>yes</only-future-events>
<label key="integration.source">gitlab</label>
<label key="integration.log_type">production_json</label>
</localfile>
<localfile>
<location>/var/log/gitlab/gitlab-rails/audit_json.log</location>
<log_format>json</log_format>
<only-future-events>yes</only-future-events>
<label key="integration.source">gitlab</label>
<label key="integration.log_type">audit_json</label>
</localfile>The labels identify the integration and log family; several example rules explicitly require integration.log_type. With only-future-events=yes, generate new test activity after collection starts. Check the source line, received event, decoded fields, and final indexed alert. Repeat the check after rotation or a logging-format change.
flowchart LR
A["GitLab application, audit, and component logs"] --> B["Wazuh agent collection and labels"]
B --> C["Manager decoding and rules"]
C --> D["Indexed alerts"]
D --> E["Security dashboards"]
D --> F["Configured notification or triage queue"]
E --> G["Investigation and response"]
F --> GThe bundled dashboards query wazuh-alerts-*. Do not assume that this view contains every forwarded source event. Decide separately how much raw evidence to retain for reconstruction, who can access it, and how long it is needed. GitLab logs can themselves contain account details, repository paths, request parameters, and sensitive context. Central retention needs access controls appropriate to that data.
Give someone ownership of collection health as well as security alerts. An unexpectedly silent source should lead to a check of file activity, agent access, forwarding, and indexing before its empty dashboard is interpreted as an absence of suspicious activity.
Extract the fields needed for authentication monitoring#
There are two request/failure paths in the example configuration, supplemented by audit events.
| Source | Decoder and useful fields | Intended use |
|---|---|---|
production_json.log | json; remote_ip, ua, controller, path, action, status, correlation_id | Failed web sign-ins and repeated failures from one IP |
Plain-text application.log | gitlab-failed-login; dstuser, srcip | Repeated failures against an account and attempts across accounts |
audit_json.log | json; event_name, meta.user, meta.remote_ip, correlation_id, where present | Password, OAuth and two-factor authentication events |
GitLab documents several application and component logs, with different purposes and formats. The useful starting point is its log system documentation, followed by checking what the installed instance actually writes.
Check which decoder handles each event before writing rules against its fields. The JSON request rules here target json; the plain-text failure rule targets gitlab-failed-login. A rule tied to a decoder that never handles its input will not provide the intended coverage.
A failed sign-in can carry the submitted login inside the user entry of params, an array of key/value objects, without a populated top-level username or meta.user. Account correlation requires extracting that login into a field the rule can compare across events. A submitted login is an attempted identity, not proof that the account exists.
Wazuh documents JSON extraction and plugin-based decoder arrangements, and explicitly lists arrays of objects as unsupported, in its JSON decoder guide. Replay the full event, including params, when checking extraction. After any decoder change, confirm that the account field and the request fields needed by other rules are still available.
The configuration separates request-level and account-level detection:
flowchart TD
A["GitLab authentication activity"] --> B["JSON request logs"]
A --> C["Plain-text failure log, if available"]
B --> D["Built-in JSON decoder"]
C --> E["Custom failure decoder"]
D --> F["Per-IP failure correlation"]
E --> G["Account correlation: review required"]
F --> H["Alerts and investigation"]
G --> HThe second path uses a small decoder:
<decoder name="gitlab-failed-login">
<prematch>Failed Login: </prematch>
<regex offset="after_prematch">^username=(\S+) ip=(\S+)</regex>
<order>dstuser, srcip</order>
</decoder>For an illustrative message such as Failed Login: username=developer1 ip=192.0.2.10, the intended output is dstuser=developer1 and srcip=192.0.2.10. The prematch locates the failure message; the regex reads the account and address after it.
The account-based path depends on a collected application.log that emits the expected failure message. Confirm file availability and message format on your instance before enabling the decoder. A collection entry alone does not establish that the source exists or produces useful events.
Inspect auth.log before choosing a parser or treating it as a failed-login source. It can contain JSON messages with fields such as gitlab_throttle_user_allowlist; the filename alone does not establish which authentication events it provides.
An empty account-failure panel needs a collection and decoding check. Confirm that application.log receives failed-login messages and is readable by the agent, then check field extraction and correlation. An empty chart can mean missing telemetry as well as no matching activity.
Alert on authentication behaviour, starting with the actual outcome#
The request-level failure rule is deliberately specific:
<rule id="110100" level="5">
<decoded_as>json</decoded_as>
<field name="controller">^SessionsController$</field>
<field name="path">^/users/sign_in$</field>
<field name="method">^POST$</field>
<action type="pcre2">^new$</action>
<status type="pcre2">^200$</status>
<description>GitLab failed web sign-in from $(remote_ip), UA $(ua), correlation $(correlation_id)</description>
<mitre><id>T1110</id></mitre>
<group>authentication_failed,credential_access,</group>
</rule>In the example logs, rejected sign-ins render the form again with action=new, status=200. HTTP 200 describes the response, not successful authentication.
A reduced, sanitized example from production_json.log looks like this:
{"time":"2026-01-01T12:00:00.000Z","controller":"SessionsController","path":"/users/sign_in","method":"POST","action":"new","status":200,"meta.caller_id":"SessionsController#create","remote_ip":"192.0.2.10","meta.remote_ip":"192.0.2.10","ua":"ExampleBrowser/1.0","correlation_id":"example-request-001"}The example keeps the request-field structure while replacing the timestamp and identifying values. Other fields, including params, are omitted for readability. Use a complete event when testing decoding, including any arrays. Notice that meta.caller_id says SessionsController#create while the final action is new; rule 110100 checks action.
A sign-in request can produce multiple records with the same correlation_id, including a create/0 record and a final new/200 record. Counting both as attempts can inflate failure totals. Rule 110100 selects the final failure fingerprint. Other outcomes, such as 422 or create/200, need separate interpretation; these rules do not classify every possible sign-in response.
Rule 110105 matches the same controller, path and POST method with status 302, and labels that a successful sign-in at level 3. It does not require action=create, despite that combination appearing in the explanatory comments. A redirect is therefore the implemented signal; completed authentication should be cross-checked against audit events, including MFA and unsuccessful redirect flows.
Join the request and audit records using correlation_id to check whether a redirect corresponds to password or MFA authentication. Treat linked records as evidence about the same request when calculating login totals, rather than adding each record as a separate login.
The audit rules add useful context: 110010 matches authenticated_with_password, 110011 matches authenticated_with_oauth, and 110012 matches authenticated_with_two_factor. All three use level 3. Use meta.user, meta.remote_ip and correlation_id, where populated, to connect the authentication event to its actor and request. Check event availability on your GitLab version and edition.
These audit rules also require the agent’s integration.log_type=audit_json label. Pasting a raw line into a test session without reproducing that field will not exercise the complete predicate. The request-level replacement rules deliberately avoid that dependency.
Distinguish repeated guessing from possible password spraying#
A single rejected sign-in can be a typing mistake. Repeated attempts against one account and one source trying several accounts deserve different investigation paths. The latter can fit password spraying: trying a small set of passwords across many users. These logs do not expose attempted passwords, so they cannot prove password reuse. That distinction follows MITRE’s definition of password spraying and should constrain the alert’s claim.
The per-IP rule builds on the JSON failure event:
<rule id="110101" level="10" frequency="5" timeframe="600" ignore="300">
<if_matched_sid>110100</if_matched_sid>
<same_field>remote_ip</same_field>
<description>GitLab: repeated failed web sign-ins from $(remote_ip)</description>
<mitre><id>T1110.001</id></mitre>
<group>brute_force,authentication_failures,</group>
</rule>The XML sets frequency=5, timeframe=600, grouping by remote_ip, and ignore=300. The exact trigger event and suppression behaviour still need a sequence replay on the installed release. The intended signal is a concentration of failures from an address; it does not say how many accounts were targeted.
The account-aware rules depend on 110106, the level-5 event matching gitlab-failed-login.
| Rule | Configured condition | Intended interpretation |
|---|---|---|
110101 | 110100, frequency 5, 600 seconds, same remote_ip | Repeated web failures from one source; level 10 |
110102 | 110106, frequency 5, 600 seconds, same_field on dstuser | Repeated attempts against one account; level 10 |
110103 | 110106, frequency 5, 600 seconds, same source IP and different_field on dstuser | Attempts against multiple accounts from one source; level 12 |
The last two need a technical correction before I would present them as dependable coverage. The decoder puts the username in Wazuh’s static dstuser field, while those rules use dynamic-field correlation operators. Wazuh distinguishes these from its dedicated user operators. Review same_user and different_user, then replay the full sequences. See the rule syntax reference.
The downloadable spraying rule illustrates the correlation intent, but still contains the dstuser operator issue described above:
<rule id="110103" level="12" frequency="5" timeframe="600" ignore="300">
<if_matched_sid>110106</if_matched_sid>
<same_srcip />
<different_field>dstuser</different_field>
<description>GitLab: one source IP attempted multiple accounts: $(srcip)</description>
<mitre><id>T1110.003</id></mitre>
<group>password_spraying,</group>
</rule>Even after fixing the operator, “different users” needs testing. Five events are not automatically five distinct accounts. A sequence alternating developer1 and developer2 should be tested separately from one touching five different users. Only describe a distinct-account threshold once the test sequence demonstrates it.
The configuration does not automatically link a later successful login to earlier failures; the analyst must make that connection. Correlation by user agent across accounts would also require both values to be available as usable fields on the failure event.
The ATT&CK mappings express detection intent. T1110 accompanies individual failures, T1110.001 repeated guessing, and T1110.003 the proposed cross-account pattern. An ordinary mistyped password is not an attack merely because the alert includes a technique ID.
Monitor OAuth with a clear identity boundary#
Rule 110041 handles Google OAuth callbacks. It matches OmniauthCallbacksController, action google_oauth2 and status 302, then searches the raw event for an approved hd parameter. It is configured to emit a level-12 alert when that raw-text pattern is absent.
Test approved, unapproved, and missing hd values. Keep callback records and successful OAuth audit events distinct: they describe different parts of the authentication flow and should not be counted as interchangeable sessions.
The rule uses a negated match against the raw JSON, with alternatives for the two key/value orders. A regex written for raw JSON may not match the representation of a decoded field. Inspect the actual input to the matcher when testing this rule.
There is a more important boundary: a request parameter is not a verified identity claim. Google says domain restrictions should be checked using the hd claim in the validated ID token; the request parameter is a flow hint. See Google’s OpenID Connect documentation.
So this rule is a callback-parameter anomaly check. It does not prove that an unauthorized domain successfully authenticated, and its T1078 mapping does not establish compromised valid-account use. Missing parameters, legitimate callback variations and redirects after errors need negative tests too.
The downloadable copy replaces the original domain expression with example\.(?:org|net). That is a documented sanitization change, not a production allowlist recommendation.
Extend monitoring to access changes and repository activity#
Successful authentication is the start of an investigation into what an account did. For an organization protecting proprietary code or customer information, changes to access and movement of repository data need attention alongside failed sign-ins.
| Monitoring area | Example rules | Security interpretation |
|---|---|---|
| Tokens, SSH keys, and MFA settings | 110050–110053, with selected audit names in 110030 | Review whether access was created or account security changed; confirm request outcomes and authorization |
| Project settings, membership, hooks, and related access controls | 110061, 110062 | Investigate changes that could alter who can reach or modify a repository |
| HTTP pushes and repeated push activity | 110064, 110065 | Identify account/repository activity and bursts; relate them to expected developer or automation work |
| SSH pushes and fetches | 110080–110082 | Intended coverage for another repository access path; still needs a real SSH sample |
| Exports and large transfers | 110066, 110083–110085 | Review potential data movement; volume alone does not establish exfiltration |
| Project/group lifecycle and administrative activity | 110067–110074 | Investigate destructive or privileged actions; distinguish requests and job starts from completed outcomes |
These rules provide starting points for monitoring. Validate SSH fields and lifecycle outcomes on your instance, and check for gaps in pipeline configuration and customer-data access separately. A rule covering repository activity does not establish visibility into every sensitive action.
For triage, preserve the actor, source address, repository, action, timestamp, and correlation ID where available. Compare the activity with that account’s role and expected workflow. A service account performing scheduled fetches needs different context from an unfamiliar account accessing several sensitive repositories.
Validate the path from source event to security alert#
Validate each detection through the complete collection and analysis path before depending on it. The examples above establish source-event shapes; they do not establish that every downloadable rule will work unchanged on your manager.
Use positive, negative, and boundary cases to check the following behaviours:
| Test | Evidence to retain |
|---|---|
| One rejected web sign-in | Raw request, decoded fields and match for 110100 |
| Ordinary GET of the sign-in form | Confirmation that the failed-POST rule does not match |
| Repeated rejected POSTs from one IP | Exact trigger event for 110101, timeframe boundary and suppression behaviour |
| Plain-text failure with an account | File availability, collection and dstuser/srcip extraction for 110106 |
| One account targeted repeatedly | Result for 110102 after reviewing the static-field operator |
| One IP, several accounts | Result for reviewed 110103; compare distinct users with alternating repeated users |
| Different IPs targeting one account | Whether the intended account grouping works without source grouping |
| A successful login after failures | Success evidence and manual timeline review; no automatic sequence rule exists |
| Password, OAuth and MFA success | Audit matches, required labels and actor field accuracy |
Approved, unapproved and missing OAuth hd | Positive and negative matches for 110041, with redirect outcomes checked |
| Account lockout | A real user_access_locked event before trusting 110013 |
Start on a test manager with the configuration loaded:
/var/ossec/bin/wazuh-logtest -vKeep related events in the same test session when evaluating correlation. Inspect decoding before the final rule match, and retain negative cases as well as positive ones. Wazuh explains this workflow in its decoder and rule testing guide.
Then test through the agent. The example collection blocks use only-future-events=yes, so generate new activity after collection starts. Check file access, labels and the resulting indexed alert. A successful manual replay does not establish that the agent is reading the source file.
There is also a counting problem if both authentication paths work: one failed login may appear in both request and application logs. Summing every authentication_failed alert can overcount attempts. Success events can likewise represent different stages of one sign-in. Decide what a dashboard counter measures before naming it “logins.”
Build dashboards around security decisions#
Use dashboards to answer the same questions that drove collection: who is attempting access, who authenticated, what access changed, and what happened to repositories. Keep an overview for prioritization and event ledgers for reconstruction. A useful panel lets an analyst move from an unusual count to the underlying account, source, time window, and events.
The downloads include GitLab Authentication and Account Security and GitLab Repository Activity and Anomalies. Both use the wazuh-alerts-* data view. Set the time window, refresh interval, and GitLab scope to match your operational needs.
The authentication dashboard is mostly counters, tables and event ledgers. It does not include an authentication time-series chart or an automatic failure-to-success join. The saved ledgers sort events newest first, which helps with manual investigation, but that is a different capability.
| Authentication panel | What its query and aggregation actually measure |
|---|---|
| Failed sign-ins / distinct source IPs | Count of 110100 alert documents and cardinality of data.remote_ip.keyword |
| Failed sign-ins by source IP | Top 25 addresses for 110100, with document count and distinct correlation IDs |
| Repeated-failure bursts | Count of 110101 alerts by data.remote_ip.keyword, rather than the number of underlying attempts |
| One IP, many accounts | 110103 alerts grouped by data.srcip.keyword, with cardinality of data.dstuser.keyword |
| Account lockouts / lockouts by account | 110013 documents, grouped by data.meta.user.keyword in the table |
| Successful sign-ins | Combined document count for 110010, 110011, 110012 and 110105 |
| Successful OAuth by account | 110011 grouped by data.meta.user.keyword, with distinct data.meta.remote_ip.keyword values |
| OAuth domain-violation ledger | 110041, displaying account, request IP, parameters and correlation ID |
The success counter is therefore a count of matching records, not deduplicated sessions. The failed-sign-in counter also deserves a replay check: it selects only 110100, while the failure ledger includes both 110100 and 110101. Check how the final indexed rule changes when correlation fires before describing that counter as every rejected attempt.
The spraying panel has a subtler limitation. Its “Distinct accounts” metric reads the account field on correlation-alert documents. It does not expand every contributing failure into an account list. Even with a working rule, that number is not automatically the number of users targeted in the whole burst. The separate per-account ledger queries 110102, 110103 and 110106; underlying failure events remain necessary for reconstructing the sequence.
Its field paths also need validation: the export queries data.srcip.keyword and data.dstuser.keyword, while the custom decoder declares static srcip and dstuser. Inspect an actual indexed alert and adjust the panel to the field paths your deployment produces.
Check panel descriptions against their queries and the deployed rules. The downloadable OAuth caption refers to an older implementation using flattened params; the XML now matches raw log text. Update that caption when adapting the dashboard.
The repository dashboard provides the next part of an investigation: pushes by account and repository, read/write trends, transport split, lifecycle activity, source addresses, anonymous pressure and a level-7-and-above anomaly queue. Its push counter selects 110064 and 110080. As its own caption correctly points out, those are transport events, not commit counts.
Normalize account, repository, and address fields before combining HTTP and SSH activity in a panel. The HTTP panels use data.meta.user.keyword and data.meta.project.keyword, while the SSH rules expect gl_username and gl_project_path. HTTP request events can contain both remote_ip and meta.remote_ip; verify the indexed fields instead of inferring a mismatch from rule descriptions. Including both transport rule IDs in a query does not populate missing aggregation fields.
The lifecycle panels also measure different things. “Projects created” counts web/API create and API fork records. “Projects deleted” uses distinct project paths from 110067 and 110073, which reduces repeated-job counting but still does not prove completed deletion. “Projects pending deletion” counts paths seen by 110068 during the selected window, not a current inventory of all pending projects. The lifecycle-by-account table includes group deletion rule 110069 but deliberately excludes pending-project browsing.
One more mismatch: the repository anomaly table and raw anomaly feed use different group lists, despite captions suggesting the same queue. Neither includes an explicit rule.groups:gitlab condition, and there is no global GitLab filter. Shared groups such as data_access could bring unrelated alerts into those views. That is worth fixing before comparing their totals.
For investigation, I would start with the failed-sign-in ledger, isolate a source and time window, then inspect the success ledger and validated repository activity. The large-clone ledger includes data.correlation_id for looking up a related Rails event, but that lookup is manual; an existing restrictive panel query may need clearing to find the other event. A level-12 label still needs the underlying evidence before containment.
After importing the dashboards, generate known test activity and verify the resulting panels against indexed alerts. Confirm counts, field mappings, and drill-downs before using the views for operational decisions.
Make alerts actionable and keep the limits visible#
Choose which validated detections enter the security team’s triage queue and which remain searchable context. Define an owner, severity, and investigation steps for each promoted alert. A Wazuh rule level or an imported dashboard does not configure an email, ticket, or on-call notification; notification delivery must be configured separately. Test it separately so a relevant event reaches the person expected to act.
For a suspicious authentication burst, establish the source and attempted accounts, look for subsequent successful authentication, then review access changes and repository activity in the same period. Use correlation IDs where they connect records and a wider timeline where they do not. Escalate based on the affected account’s privileges, repository sensitivity, and corroborating activity. Record the evidence and outcome so the team can tune the detection without losing its security purpose.
A source-IP threshold can miss attempts spread over many addresses or paced outside the configured window. Shared office egress, proxies and mobile address changes can also complicate attribution. Verify the address semantics before applying an IP-based response.
Similarly, high-volume Git activity can belong to automation. The wider ruleset matches automation user agents on pushes, but that alone does not identify a compromised token. A change to the application’s log format can break extraction without changing the XML at all.
The next useful work is to verify the account-aware path, retain repeatable tests and cross-check the dashboard against indexed events. Explicit failure-to-success correlation, identity-provider evidence and distributed spraying detection would be separate enhancements. Geographic enrichment is not implemented by a rule merely carrying an impossible_travel_candidate group.
Protecting GitLab means maintaining visibility over access to the organization’s code, data, and delivery environment. Start with the logs the instance actually produces, centralize the evidence, and build dashboards and alerts around meaningful security questions. Keep testing both the detection and the investigation path as GitLab and the organization’s workflows change.
Download the configuration and dashboards#
The downloads include decoder, rule, collection, and dashboard examples. Identifying details have been removed and the OAuth allowlist uses example domains. The account-correlation and dashboard limitations discussed above remain in these reference files; validate and adapt them before deployment.
| Resource | Download |
|---|---|
| Decoder | gitlab-decoders.xml |
| Authentication rules | gitlab-auth-rules.xml |
| OAuth rule | gitlab-oauth-rules.xml |
| JSON and audit rules | gitlab_rules_10_json.xml |
| Repository activity rules | gitlab_rules_30_git_activity.xml |
| Agent collection fragments | gitlab-agent-localfile.xml |
| Authentication dashboard export | wazuh-dashboard-gitlab-authentication-security.ndjson |
| Repository activity dashboard export | wazuh-dashboard-gitlab-activity.ndjson |
| Sanitization and usage notes | README.md |
The dashboard copies preserve the example queries, aggregations, object IDs and captions, with identifying domains and a source subnet replaced by examples. Their known issues are documented rather than silently repaired. Both require an existing or remapped wazuh-alerts-* data view; its definition is not included in either export.







