7 Commits
Author SHA1 Message Date
Lunny Xiao d39c6d0cd6 Merge branch 'main' into audit 2026-09-19 00:12:43 +00:00
Lunny Xiao 0c1071b3a6 Merge branch 'main' into audit 2026-09-19 00:04:45 +00:00
Lunny Xiao 2b1ddd1d44 Merge branch 'main' into audit 2026-09-18 03:37:34 +00:00
silverwindandClaude 1dd914792d Correct audit logging actors, warning and coverage
The system actor names did not match what Gitea records, the disabled
warning only shows on the admin page, and the recorded/not recorded lists
overstated login and issue edit coverage while missing user status changes.

Co-Authored-By: Claude (Opus 5) <[email protected]>
2026-09-18 03:14:54 +02:00
Lunny Xiao 240a2840a4 Merge branch 'main' into audit 2026-09-17 20:22:15 +00:00
bircni fa7c65e2dd Merge branch 'main' into audit 2026-09-13 07:30:18 +00:00
bircni a018c73100 docs: Document audit logging 2026-09-12 13:21:33 +02:00
5 changed files with 172 additions and 1 deletions

No files matched your search

+153
View File
@@ -0,0 +1,153 @@
---
date: "2026-09-12T00:00:00+00:00"
slug: "audit-logging"
sidebar_position: 41
aliases:
- /en-us/audit-logging
---
# Audit Logging
Gitea can record security-relevant events as structured audit records: each
event has an `action`, `actor`, `scope`, `origin`, `message` and a JSON
`metadata` blob. Recording is **off by default** and does not change request
behavior when it is disabled.
This is separate from the application, router and access logs described in
[Logging Configuration](logging-config.md). Those logs are for operations and
debugging. The audit log is a durable trail of who changed what.
## Enable recording
Set `[audit].RECORD_OUTPUT` to `database` in `app.ini` and restart Gitea:
```ini
[audit]
RECORD_OUTPUT = database
RETENTION_DAYS = 30
```
Invalid values of `RECORD_OUTPUT` fall back to `disabled`. See the
[configuration cheat sheet](config-cheat-sheet.md#audit-audit) for every option.
With Docker, the same settings can be applied as
`GITEA__audit__RECORD_OUTPUT=database` and `GITEA__audit__RETENTION_DAYS=30`
before Gitea writes them into `app.ini`.
## View events
The Audit Log pages are always available and still list any events that were
stored earlier. When recording is disabled, the site administration page also
shows a warning.
| Audience | Location | Events shown |
| --- | --- | --- |
| Site administrators | Site administration → Monitoring → Audit Log (`/-/admin/monitor/audit_logs`) | Every event |
| Signed-in users | User settings → Audit Log (`/user/settings/audit_logs`) | Events scoped to that user |
| Organization owners | Organization settings → Audit Log (`/<org>/settings/audit_logs`) | Events scoped to that organization |
| Repository administrators | Repository settings → Audit Log (`/<owner>/<repo>/settings/audit_logs`) | Events scoped to that repository |
Each listing can be filtered by actor (username), action and origin, and sorted
by timestamp. The actor filter does not match the
[system actors](#actors-and-origins). Action filters accept an exact action or
a family prefix such as `user:impersonation` or `repository:webhook`.
Site administrators can download the current filtered result as JSONL
(newline-delimited JSON) from **Export JSONL**. The file name is
`gitea-audit-log-<timestamp>.jsonl`.
## Event shape
A recorded event contains:
- **Action**: a stable, colon-separated identifier such as `repository:archive`
or `user:accesstoken:add`.
- **Actor**: the user who performed the action. Actions taken while impersonating
a user record both the impersonator and the impersonated user.
- **Actor credential**: when the request authenticated with a named credential,
the event stores its kind and id, never the secret. Typical values:
`access-token:<id>`, `oauth2-grant:<id>`, `gitea-actions:<task id>`,
`deploy-key:<key id>`.
- **Scope**: the affected unit — `user`, `organization`, `repository` or
`system` (instance-wide).
- **Origin**: how the action was initiated — `ui`, `api`, `cli` or `system`.
- **Message**: a human-readable sentence rendered from a per-action template.
- **Metadata**: extra fields that fill the message template (token name, new
visibility, and so on). Secrets and token values are not stored.
- **IP address**: the client address of the request, when one exists. Events
triggered by a git push record the SSH client address for SSH pushes and
`127.0.0.1` for HTTP pushes.
- **Timestamp**: when the event was recorded.
An exported JSONL line looks like:
```json
{"action":"repository:archive","actor":{"type":"user","id":1,"name":"alice"},"scope":{"type":"repository","id":42,"name":"org/repo"},"message":"Archived repository org/repo.","time":"2026-09-12T11:00:00Z","ip_address":"203.0.113.10","origin":"ui"}
```
The `system:startup` message is kept stable across Gitea versions so operators
can parse it: `System started [Gitea {version}]`.
## Actors and origins
Most web and API requests pick up the signed-in user automatically.
Other entry points record a system actor:
- `(gitea-cli)` for `gitea admin` commands and the `system:startup` and
`system:shutdown` events
- `(gitea-auth-source)` for authentication-source syncs and for users an
authentication source creates or updates at sign-in
- `gitea-actions` for Gitea Actions tasks
- `(deploy-key)` for pushes with a deploy key
- `(Cron)` for scheduled cron tasks
- `Unknown` when no actor can be resolved
Events of the `(gitea-cli)` actor use origin `cli`. Cron tasks, git hooks and
other background work use origin `system`.
## What is recorded
Events cover security-relevant changes, grouped by action family:
- **User**: create and delete, admin, restricted and active status, name,
password and password reset requests, visibility, emails, authentication
source, 2FA, WebAuthn, OpenID, external logins, access tokens, OAuth2
applications and grants, SSH/GPG/principal keys, secrets, webhooks,
impersonation, failed two-factor authentication
- **Organization**: create and delete, name, visibility, members, teams,
OAuth2 applications, secrets, webhooks
- **Repository**: create, fork, archive, delete, rename, visibility, transfer,
mirrors, signing verification, collaborators and teams, default branch,
branch and tag protection, deploy keys, webhooks, secrets
- **Issues and pull requests**: create, delete, comments and merges
- **Projects and wiki pages**: create, update and delete
- **Actions**: enable, disable and dispatch a workflow
- **System**: startup and shutdown, instance-wide webhooks, authentication
sources, instance-wide OAuth2 applications
Recording never fails the request that triggered it. A write error is logged
and the original operation continues.
## What is not recorded
- Read-only access such as browsing, clone, fetch or API GET requests
- Logins, successful or failed, other than failed two-factor authentication
(use the [access log](logging-config.md) and
[Fail2ban Setup](fail2ban-setup.md) for authentication traffic)
- Edits to issues and pull requests
- Token or secret values
- Which SSH key a real user pushed with (deploy keys are named; ordinary SSH
user pushes are not)
## Retention
`RETENTION_DAYS` (default **30**) is the age after which recorded events are
eligible for deletion. Set it to `0` to keep events forever.
Cleanup is performed by the `cron.delete_old_audit_events` task (`SCHEDULE`
defaults to `@every 24h`, `OLDER_THAN` defaults to `[audit].RETENTION_DAYS`).
The task is registered only while `RECORD_OUTPUT` records events **and**
`RETENTION_DAYS` is greater than `0`. Turning recording off therefore also
stops pruning; existing rows stay until you delete them or turn recording back
on.
+15
View File
@@ -656,6 +656,11 @@ And the following unique queues:
- CIDR list: `1.2.3.0/8` for IPv4 and `2001:db8::/32` for IPv6 - CIDR list: `1.2.3.0/8` for IPv4 and `2001:db8::/32` for IPv6
- Wildcard hosts: `*.mydomain.com`, `192.168.100.*` - Wildcard hosts: `*.mydomain.com`, `192.168.100.*`
## Audit (`audit`)
- `RECORD_OUTPUT`: **disabled**: Where security-relevant events are recorded. `disabled` records nothing. `database` writes events to the `audit_event` table and shows them in the admin, organization, repository and user settings. Invalid values fall back to `disabled`. See [Audit Logging](audit-logging.md).
- `RETENTION_DAYS`: **30**: Days to keep recorded events. `0` keeps them forever. Pruning is done by `cron.delete_old_audit_events`. That task is only registered while `RECORD_OUTPUT` records events and `RETENTION_DAYS` is greater than `0`.
## Camo (`camo`) ## Camo (`camo`)
- `ENABLED`: **false**: Enable media proxy, we support images only at the moment. - `ENABLED`: **false**: Enable media proxy, we support images only at the moment.
@@ -1201,6 +1206,16 @@ Synchronize external user data (only LDAP user synchronization is supported)
- `SCHEDULE`: **@every 168h**: Cron syntax to set how often to check. - `SCHEDULE`: **@every 168h**: Cron syntax to set how often to check.
- `OLDER_THAN`: **8760h**: any action older than this expression will be deleted from database, suggest using `8760h` (1 year) because that's the max length of heatmap. - `OLDER_THAN`: **8760h**: any action older than this expression will be deleted from database, suggest using `8760h` (1 year) because that's the max length of heatmap.
#### Cron - Delete old audit events (`cron.delete_old_audit_events`)
Deletes audit events older than the retention period. The task is only registered when `[audit].RECORD_OUTPUT` records events and `[audit].RETENTION_DAYS` is greater than `0`. See [Audit Logging](audit-logging.md).
- `ENABLED`: **true**: Enable service.
- `RUN_AT_START`: **false**: Run tasks at start up time (if ENABLED).
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `SCHEDULE`: **@every 24h**: Cron syntax to set how often to check.
- `OLDER_THAN`: **720h**: any audit event older than this expression will be deleted from the database. Defaults to `[audit].RETENTION_DAYS`.
#### Cron - Check for new Gitea versions (`cron.update_checker`) #### Cron - Check for new Gitea versions (`cron.update_checker`)
- `ENABLED`: **true**: Enable service. - `ENABLED`: **true**: Enable service.
+2
View File
@@ -16,6 +16,8 @@ The logging configuration of Gitea mainly consists of 3 types of components:
There is a fully functional log output by default, so it is not necessary to define one. There is a fully functional log output by default, so it is not necessary to define one.
Security-relevant events (repository transfers, token changes, impersonation, and similar) are recorded separately. See [Audit Logging](audit-logging.md).
## Collecting Logs for Help ## Collecting Logs for Help
To collect logs for help and issue report, see [Support Options](help/support.md). To collect logs for help and issue report, see [Support Options](help/support.md).
+1 -1
View File
@@ -43,7 +43,7 @@ You can try it out using [the online demo](https://demo.gitea.com).
- **Security** - **Security**
Gitea places a strong emphasis on security, offering features such as user permission management, access control lists, and more to ensure the security of code and data. Gitea places a strong emphasis on security, offering features such as user permission management, access control lists, [audit logging](administration/audit-logging.md), and more to ensure the security of code and data.
- **Code Review** - **Code Review**
+1
View File
@@ -34,6 +34,7 @@ _Symbols used in table:_
| Telemetry | **✘** | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ | | Telemetry | **✘** | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
| Third-party render tool support | ✓ | ✘ | ✘ | ✘ | ✓ | ✘ | ✘ | | Third-party render tool support | ✓ | ✘ | ✘ | ✘ | ✓ | ✘ | ✘ |
| WebAuthn (2FA) | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✓ | | WebAuthn (2FA) | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✓ |
| Audit log | ✓ | ✓ | ⁄ | ✓ | ✓ | ? | ? |
| Extensive API | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Extensive API | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Built-in Package/Container Registry | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ | ✘ | | Built-in Package/Container Registry | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ | ✘ |
| Sync commits to an external repo (push mirror) | ✓ | ✘ | ✓ | ✓ | ✘ | ✓ | ✓ | | Sync commits to an external repo (push mirror) | ✓ | ✘ | ✓ | ✓ | ✘ | ✓ | ✓ |