Audit Log
Philosophy
The audit log will capture all critical events that affect entities of interest within Sourcegraph services. The audit log is built on top of our logging standard, using structured logs as the base building block. Every captured entry is aligned with the following design mantra:
Actor takes action on an entity within a context
Here's a sample audit log entry:
SHELL{ "SeverityText": "INFO", "Timestamp": 1667210919544146000, "InstrumentationScope": "server.SecurityEvents", "Caller": "audit/audit.go:43", "Function": "github.com/sourcegraph/sourcegraph/internal/audit.Log", "Body": "AccessGranted (sampling immunity token: 7aacf0e8-d001-4aec-8b7d-20e46d34c8db)", "Resource": { "service.name": "frontend", "service.version": "0.0.0+dev", "service.instance.id": "Michals-MacBook-Pro-2.local" }, "Attributes": { "audit": { "auditId": "7aacf0e8-d001-4aec-8b7d-20e46d34c8db", "entity": "security events", "actor": { "actorUID": "1", "ip": "127.0.0.1", "X-Forwarded-For": "127.0.0.1, 127.0.0.1" } }, "event": { "URL": "", "source": "BACKEND", "argument": "{\"resource\":\"db.repo\",\"service\":\"frontend\",\"repo_ids\":[9]}", "version": "0.0.0+dev", "timestamp": "2022-10-31 10:08:39.542876 +0000 UTC" } } }
Here's a word-by-word breakout to demonstrate how the captured entry aligns with the design mantra:
- Actor -
Attributes.audit.actorfield carries essential information about the actor who performed the action. - Action -
Bodyfield carries the action description. This action is suffixed with a "sampling immunity token," which carries the unique audit log entry ID. The audit entry ID must be present in theBodyso that the message is always unique and never gets dropped by the sampling mechanism (hence the sampling immunity token string). - Entity -
Attributes.audit.entitydescribes the audited entity.Resourcefield contains additional information about the audited resource as well. - Context - Any non-
auditchild node ofAttributes. This is represented by theeventnode in the example above.
What is audited?
- Security Events - A list of events such as logins, site configuration updates, code host configuration updates (These logs can be sent to the database for easier collection as well as to the standard output).
- Gitserver Access - Requests to the
gitservercomponent that is responsible for brokering transactions between the git repos on disk and all other Sourcegraph components. - GraphQL Requests - Requests To The Sourcegraph
frontendcomponent that is usually namedsourcegraph-frontend-{DYNAMICALLY CREATED ALPHANUMERIC}
This list is expected to grow in the future.
Target audience
Security specialists. We expect these to ingest the logs into their SIEM tools and define alert policies as they see fit. Site admins are currently not the target audience, but we'll likely offer an easy-to-use in-app audit log.
Configuring
The audit log is currently configured using the site config. Here's the corresponding entry:
SHELL"log": { "auditLog": { "internalTraffic": false, "graphQL": false, "gitserverAccess": false, "severityLevel": "INFO" // DEPRECATED, defaults to SRC_LOG_LEVEL } "securityEventLog": { "location": "auditlog" // option to set "database" or "all" as well, default to outputing as an audit log }
We believe the individual settings are self-explanatory, but here are a couple of notes:
securityEventLogconfigures the destination of security events, logging to the database may result in performance issuesinternalTrafficis disabled by default and will result in security events from internal traffic not being logged
Using
Audit logs are structured logs. As long as one can ingest logs, we assume one can also ingest audit logs.
On Premises
All logs mentioned are delivered to the standard output (stdout) for each individual component.
There are two easy approaches to filtering the audit logs:
- JSON-based: look for the presence of the
Attributes.auditnode. Do not depend on the log level, as it can change based onSRC_LOG_LEVEL. - Message-based: we recommend going the JSON route, but if there's no easy way of parsing JSON using your SIEM or data processing stack, you can filter based on the following string:
auditId.
Cloud
For Sourcegraph Cloud customers, please refer to Cloud documentations.
Developing
The single entry point to the audit logging API is made via the audit.Log function. This internal function can be used from any place in the app, and nothing else needs to be done for the logged entry to appear in the audit log.
Example call:
SHELLaudit.Log(ctx, logger, audit.Record{ Entity: "security events", Action: string(event.Name), Fields: []log.Field{ log.Object("event", log.String("URL", event.URL), ), }, })
- audit log checks the current settings via the cached
schema.SiteConfiguration ctxparameter is required for acquiringactor.Actorandrequestclient.Clientloggerparameter is used for performing the actual log callaudit.Recordcarries all the information required for constructing a valid audit log entry
FAQ
How do I map actor ID to the Sourcegraph user?
The audit.actor node carries ID of the user who performed the action (actorUID), but it’s not mapped into a full Sourcegraph user right now. You can, however, obtain the user details by following these steps:
- Grab the user ID from the audit log
- Base64 encode the ID with a "User:" prefix. For example, for Actor with ID 71 use
User:71, which encodes toVXNlcjo3MQ== - Navigate to Site Admin -> API Console and run the query below
- Find the corresponding user by searching the query results for the encoded ID from above
GraphQL query:
SHELL{ users { nodes { id username } } }
Excessive audit logging
If you are seeing a large number of logs in the format frontend.SecurityEvents or similar, these are securityEventLogs.
To disable them, in the site config set log.securityEventLog.location to none.
JSON"log": { "securityEventLog": { "location": "none" } }