Skip to content

daemon: add security logging for admin actions (part 5.2)#17169

Open
ernestl wants to merge 1 commit into
canonical:masterfrom
ernestl:ernestl/seclog-part5.2-daemon-access-changes
Open

daemon: add security logging for admin actions (part 5.2)#17169
ernestl wants to merge 1 commit into
canonical:masterfrom
ernestl:ernestl/seclog-part5.2-daemon-access-changes

Conversation

@ernestl

@ernestl ernestl commented Jun 6, 2026

Copy link
Copy Markdown
Member

Add security logging for admin actions.

  1. Emit authz_admin and authz_fail — log successful and rejected admin API authorization over snapd.socket.
  2. Populate authz_checks during checkAccess — record pass/fail/not-applicable for each authorization stage evaluated.
  3. Return accessLevel from CheckAccess — carry the intended access level (authenticated, root, not-evaluated, etc.) through the access-check pipeline.
  4. Skip dispatch-only byActionAccess failures — use accessLevelNotEvaluated for pre-delegation errors (bad JSON, wrong content-type) so they do not produce authz audit events.
  5. Gate audit logging on access level — emit events only for authenticated or root endpoints via isAdministrativeAccess.
  6. Derive audit reasons from apiError.reason() — share structured error info between audit logging and API responses.
  7. Share JSON action body parsing — reuse requestAction / readBodyAndParseAction between audit logging and SNAPD_TRACE.
  8. Add spread coverage — verify authz_admin and authz_fail events in tests/main/security-logging.

Spec SD236


Part 5 adds security logging for administrative actions: Full reference implementation.

  • Part 5.2: Add daemon/access implementation & spread test <==== THIS
  • Part 5.1: Add seclog API

[Done] Part 4 adds security logging for adding, updating and removing a snapd user.

[Done] Part 1, 2, & 3 provides the logger implementation.

@github-actions github-actions Bot added the Needs Documentation -auto- Label automatically added which indicates the change needs documentation label Jun 6, 2026
@ernestl ernestl changed the title Ernestl/seclog part5.2 daemon access changes daemon: add security logging for admin actions Jun 6, 2026
@ernestl ernestl closed this Jun 6, 2026
@ernestl ernestl reopened this Jun 6, 2026
@ernestl ernestl requested a review from Copilot June 6, 2026 18:50
@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown

Mon Jun 15 14:53:45 UTC 2026
The following results are from: https://github.com/canonical/snapd/actions/runs/27550583642

Failures:

Executing:

  • openstack:ubuntu-core-18-64:tests/main/security-logging
  • openstack:ubuntu-core-20-64:tests/main/security-logging
  • openstack:ubuntu-core-24-64:tests/main/security-logging

Skipped tests from snapd-testing-skip

If you wish to have any of the below tests run in your PR, in your PR description, add 'unskip:' followed by a copy-and-pasted list (without variants) of the below tests you wish to run (unskip plus test list must be valid yaml)

  • openstack:ubuntu-24.04-64:tests/main/i18n
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-flag-restart
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:audio_record_single
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:audio_record_timespan_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:audio_record_timespan_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_multiple_actioned_by_other_pid_always_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_multiple_actioned_by_other_pid_always_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_multiple_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_multiple_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_multiple_not_actioned_by_other_pid_single_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_multiple_not_actioned_by_other_pid_single_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_write_chmod_same_fd_single_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_write_chmod_same_path_single_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_write_write_same_path_single_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:download_file_conflict
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:download_file_defaults
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:download_file_safer
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:read_single_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:read_single_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:special_characters
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:timespan_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:timespan_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:write_read_multiple_actioned_by_other_pid_allow_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:write_read_multiple_actioned_by_other_pid_deny_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:write_single_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:write_single_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-prompt-restoration
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:audiorecord_allow_forever
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:audiorecord_allow_session
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:audiorecord_allow_single
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:audiorecord_allow_timespan
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:audiorecord_deny_forever
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:audiorecord_deny_session
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:audiorecord_deny_single
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:audiorecord_deny_timespan
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:camera_allow_forever
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:camera_allow_session
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:camera_allow_single
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:camera_allow_timespan
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:camera_deny_forever
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:camera_deny_session
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:camera_deny_single
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:camera_deny_timespan
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:home_allow_forever
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:home_allow_session
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:home_allow_single
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:home_allow_timespan
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:home_deny_forever
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:home_deny_session
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:home_deny_single
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:home_deny_timespan
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-snapd-startup
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-support
  • openstack:ubuntu-26.04-64:tests/main/i18n
  • openstack:ubuntu-26.04-64:tests/main/interfaces-requests-activates-handlers

@ernestl ernestl changed the title daemon: add security logging for admin actions daemon: add security logging for admin actions (part 5.2) Jun 6, 2026
@ernestl ernestl added the Auto rerun spread Auto reruns spread up to 4 times in non-draft PRs w/ >=1 approval and <20 fails in any fund. system label Jun 6, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends snapd’s security/audit logging to cover administrative API authorization decisions, emitting structured authz_admin (success) and authz_fail (rejection) events and recording per-stage authorization check outcomes for requests over snapd.socket.

Changes:

  • Add new security-log event data structures (Peer, Endpoint, AuthzChecks) and structured Reason fields (code, optional kind, message) with slog LogValuer support.
  • Rework daemon access checking to return (apiError, authz_checks, accessLevel) and gate authz audit emission on administrative access levels.
  • Add unit + spread coverage verifying emitted authz audit events and structured payloads.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/main/security-logging/task.yaml Extends spread test to assert authz_admin/authz_fail events for admin API calls and adjusts login-failure expectations.
seclog/slog.go Updates Reason logging shape and adds LogValue for Endpoint, Peer, and AuthzChecks.
seclog/slog_test.go Updates tests for new structured Reason format and adds coverage for authz-related structured attrs.
seclog/seclog.go Adds LogAdminActivity and LogUnauthorizedAccess helpers emitting AUTHZ category events.
seclog/seclog_test.go Adds tests for new authz logging helpers and updates login-failure expectations.
seclog/eventdata.go Redefines Reason schema and introduces Peer, Endpoint, AuthzChecks (+ constructors/stringers).
seclog/eventdata_test.go Adds tests for new Peer/Endpoint string formatting and updates Reason.String() expectations.
daemon/export_test.go Exposes APIError.reason() as APIErrorReason for tests.
daemon/export_access_test.go Exposes new access-check helpers/types for tests (AccessLevel, prerequisites/authz funcs, etc.).
daemon/errors.go Adds apiError.reason() to convert API errors into structured seclog.Reason.
daemon/errors_test.go Adds unit coverage for APIErrorReason.
daemon/daemon.go Integrates authz audit logging into request dispatch and adds shared action-body parsing helpers.
daemon/daemon_test.go Adds tests verifying authz audit emission/gating and requestAction parsing behavior.
daemon/api_users.go Switches login-failure logging to use structured apiError.reason().
daemon/api_users_test.go Updates login-failure assertions to match structured reasons (HTTP code + kind).
daemon/access.go Refactors access checking to return authz stage outcomes + intended access level; adds admin gating helper.
daemon/access_test.go Updates existing access tests for new signature and adds coverage for prerequisites/authz-stage results.

Comment thread daemon/daemon.go
Comment on lines +264 to +278
func readBodyAndParseAction(r *http.Request, limit int64) (string, error) {
bodyBytes, err := io.ReadAll(io.LimitReader(r.Body, limit))
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
if err != nil {
return "", err
}

var data struct {
Action string `json:"action"`
}
if err := json.Unmarshal(bodyBytes, &data); err != nil {
return "", nil
}
return data.Action, nil
}
Comment thread daemon/daemon.go Outdated
Comment on lines 163 to 171
rspe, checks, level := access.CheckAccess(c.d, r, ucred, user)
admin := isAdministrativeAccess(level)

if rspe != nil {
if admin {
logUnauthorizedAccess(c, r, ucred, user, access, level, rspe, checks)
}
rspe.ServeHTTP(w, r)
return
Comment thread daemon/daemon.go Outdated
Comment on lines +304 to +306
func logAdminActivity(c *Command, r *http.Request, ucred *ucrednet, user *auth.UserState, ac accessChecker, level accessLevel, checks seclog.AuthzChecks) {
seclog.LogAdminActivity(snapdUser(user), peerFromUcred(ucred), endpointFromRequest(c, r, ac, level, requestAction(r)), checks)
}
Comment thread daemon/daemon.go Outdated
Comment on lines +308 to +310
func logUnauthorizedAccess(c *Command, r *http.Request, ucred *ucrednet, user *auth.UserState, ac accessChecker, level accessLevel, rspe *apiError, checks seclog.AuthzChecks) {
seclog.LogUnauthorizedAccess(snapdUser(user), peerFromUcred(ucred), endpointFromRequest(c, r, ac, level, requestAction(r)), checks, rspe.reason())
}
Comment thread daemon/daemon.go Outdated
Comment on lines +197 to +199
if admin {
logAdminActivity(c, r, ucred, user, access, level, checks)
}
@ernestl ernestl force-pushed the ernestl/seclog-part5.2-daemon-access-changes branch 2 times, most recently from c70af8e to 7f6bcdd Compare June 8, 2026 10:15
@codecov

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.48837% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.15%. Comparing base (f0c7df2) to head (db7b62b).
⚠️ Report is 3 commits behind head on master.

Files with missing lines Patch % Lines
daemon/daemon.go 84.44% 13 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #17169      +/-   ##
==========================================
+ Coverage   79.12%   79.15%   +0.02%     
==========================================
  Files        1390     1386       -4     
  Lines      193561   193566       +5     
  Branches     2466     2466              
==========================================
+ Hits       153156   153212      +56     
+ Misses      31209    31169      -40     
+ Partials     9196     9185      -11     
Flag Coverage Δ
unittests 79.15% <93.48%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ernestl ernestl force-pushed the ernestl/seclog-part5.2-daemon-access-changes branch 5 times, most recently from ba37cb3 to a9cb70e Compare June 15, 2026 13:43
 Emit authz_admin and authz_fail for admin API calls, populate
 authz_checks during checkAccess, skip dispatch-only byActionAccess
 failures via accessLevelNotEvaluated, and add spread coverage.
@ernestl ernestl force-pushed the ernestl/seclog-part5.2-daemon-access-changes branch from a9cb70e to db7b62b Compare June 15, 2026 14:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Auto rerun spread Auto reruns spread up to 4 times in non-draft PRs w/ >=1 approval and <20 fails in any fund. system Needs Documentation -auto- Label automatically added which indicates the change needs documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants