Skip to content

feat(garm): OWASP security event logging — Stage 1 producers (ISD-6341) - #408

Merged
florentianayuwono merged 25 commits into
mainfrom
feat/garm-owasp-security-logging-stage1-isd-6341
Oct 14, 2026
Merged

florentianayuwono merged 25 commits into
mainfrom
feat/garm-owasp-security-logging-stage1-isd-6341

Conversation

@florentianayuwono

@florentianayuwono florentianayuwono commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

What this PR does

Implements Stage-1 (§25.10) of OWASP-framework security event logging for the garm charm — the log producer side. Instruments the charm to emit structured OWASP security events (via the owasp-logger library, nested under an owasp_event key) for the priority event classes:

  • AUTHN — authn_login_success, authn_login_fail, authn_token_created, authn_token_revoked, authn_token_delete
  • AUTHZ — authz_fail (GARM 401, emitted from every privileged mutation path), authz_admin (privileged resource mutations: org/repo/scaleset/template CRUD, get-credentials action)
  • SYS — sys_startup, sys_shutdown
  • USER — user_created (GARM first-run)

Forwarding. Workload-stdout events are forwarded to Loki via the go-framework extension's built-in LogForwarder (no extra logging relation needed). Charm-hook events are written to juju-log, which Juju forwards to the Juju controller — their onward handling and retention is therefore the Juju controller operator's responsibility, not this charm's. (An earlier Stage-3 proposal to re-forward charm-hook events over OTLP from the charm has been dropped and its PR closed.)

Also adds a How-to guide for granting read-only, least-privilege access to the security feed (docs/how-to/access-security-logs.md).

Why we need it

Fulfils the ISD-6341 requirement to emit a standardized, auditable security-event trail for the portions of GARM that Canonical is responsible for, enabling independent security auditing with least-privilege log access.

Test plan

All run from a clean checkout of this branch:

  • cd charms/garm && tox -c tox.toml -e fmt,lint,complexity,static,unit → passed (435 unit tests, all gates green)
  • cd docs && make html (Sphinx --fail-on-warning) → build succeeded
  • make -C docs spellcheck → passed (0 errors, 16 files)

Review focus

  • src/garm_api.py carries the bulk of the instrumentation; authz_admin spans the privileged mutation paths, and authz_fail is now emitted on a GARM 401 from every privileged mutation (via the shared _raise_api_error / _log_authz_fail_on_401 helpers) rather than only the scaleset/instance paths.
  • The charm deliberately does not declare a second logging relation / LogForwarder — the go-framework extension already provides one (declaring it again errors on overlapping keys).
  • Event token formats follow owasp-logger's <name>:<comma-args> convention exactly. Note authn_token_delete's positional arg is the deletion target (forge-credential-<cred_id>), not the global appid.
  • Docs: the guide lives under the How-to Diátaxis section (not a bespoke "Security" toctree caption), per docs-review feedback.

Potential breaking changes

None. Purely additive logging; no schema or relation changes.

New dependencies

  • Adds owasp-logger to the charm's requirements.txt.

Checklist

  • Changes comply with the project's coding standards and guidelines (see CONTRIBUTING.md and STYLE.md)
  • CONTRIBUTING.md has been updated upon changes to the contribution/development process (e.g. changes to the way tests are run)
  • Technical author has been assigned to review the PR in case of documentation changes (usually *.md files)
  • I updated docs/changelog.md with user-relevant changes
  • I used AI to assist with preparing this PR
  • I added or updated tests as needed (unit and integration)
  • If integration test modules are used: I updated the workflow configuration
  • If this PR involves a Grafana dashboard: I added a screenshot of the dashboard
  • If this PR involves Terraform: terraform fmt passes and tflint reports no errors
  • If this PR involves Rockcraft: I updated the version
  • If this PR adds/removes a charm, or changes a charm's base class, conventions, tooling, or repo structure: I updated the relevant AGENTS.md
  • If this PR changes .copilot-collections.yaml or .github/instructions/: I re-checked whether the AGENTS.md "12-factor divergences" guidance still matches

Part 1 of 2 (stacked). Stage-2 (Sigma detection rules, #409) is stacked on top of this branch. Tracking: ISD-6341.

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.

Copilot review overview

🟡 Changes recommended

Unresolved issues affect authentication auditing, actor identity, Loki forwarding, documented queries, and the required changelog entry.

Review effort: Lite
Findings: 3 Medium severity · 1 Low severity

Open (4)
What changed in this PR

Adds Stage 1 OWASP security-event logging to the garm charm, with API instrumentation, tests, dependency updates, and security-log documentation.

Changes:

  • Instruments authentication, authorization, lifecycle, and administrative events.
  • Adds the owasp-logger dependency and unit coverage.
  • Documents Security Logs Viewer access and Loki filtering.
File Summary
docs/​security/​README-security-logs-viewer.md Documents security-log access and filtering.
docs/​.custom_wordlist.txt Adds documentation vocabulary.
charms/​garm/​tests/​unit/​test_security_log.py Tests structured event output.
charms/​garm/​tests/​unit/​test_garm_api.py Tests API event emission.
charms/​garm/​tests/​unit/​test_charm.py Tests lifecycle and action events.
charms/​garm/​src/​security_log.py Defines the OWASP logger configuration.
charms/​garm/​src/​garm_api.py Emits API security events.
charms/​garm/​src/​charm.py Emits lifecycle and user events.
charms/​garm/​requirements.txt Adds owasp-logger.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread charms/garm/src/garm_api.py
Comment thread charms/garm/src/security_log.py Outdated
Comment thread docs/security/README-security-logs-viewer.md Outdated
Comment thread docs/security/README-security-logs-viewer.md Outdated
Comment thread docs/security/README-security-logs-viewer.md Outdated
Comment thread charms/garm/requirements.txt
Comment thread docs/security/README-security-logs-viewer.md Outdated
Comment thread charms/garm/src/garm_api.py Outdated
Comment thread charms/garm/src/garm_api.py Outdated
Comment thread charms/garm/tests/unit/test_charm.py Outdated
Comment thread docs/security/README-security-logs-viewer.md Outdated

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.

🟡 Changes recommended

Unresolved moderate issues remain in 401 event coverage and HTTPS OTLP forwarding.

2 open findings
4 resolved since last review

🧠 Review effort: Lite


Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.

Comment thread charms/garm/src/garm_api.py Outdated
Comment thread charms/garm/src/security_log.py Outdated
florentianayuwono and others added 14 commits October 8, 2026 22:46
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…admin

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…o Loki

The go-framework charmcraft extension already provides the 'logging'
relation and instantiates a loki_push_api LogForwarder, so the charm
relies on that rather than declaring a conflicting second forwarder.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…titlement typing

- Guard forge-credential name against None for authn_token_created entitlements.
- Add a parametrized test asserting every privileged GARM mutation emits authz_admin.
- Apply ruff formatting to the new instrumentation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add the RBAC Security Logs Viewer role documentation and the Vale
wordlist terms it relies on.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Point the Security Logs Viewer forwarding section at the concrete
mechanisms already in the repo: the go-framework LogForwarder plus the
OTEL_LOGS_EXPORTER=console stdout path the sibling 12-factor charms use,
and the enable-log-forwarding action's filelog-receiver-to-OTLP pattern
as a reference for capturing charm-hook events.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The upstream owasp-logger renders the event timestamp in the host's
local UTC offset (via .astimezone()). OWASP requires UTC-aligned
timestamps, so subclass the logger to emit RFC3339 UTC ('...Z')
regardless of host timezone.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Emit authn_login_fail before raising on an empty login token, so that path
  leaves an audit record.
- Attribute forge-credential deletions to the credential id subject.
- Replace the literal garm-admin actor with a documented _GARM_ADMIN_ACTOR
  constant explaining the single built-in admin account.
- Fix the Loki queries to parse the nested owasp_event JSON, drop the internal
  SSDLC reference, condense the forwarding section, and add a changelog entry.
- Remove the divider comment in the charm tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
authz_fail was only emitted from _raise_resource_api_error, which the
scaleset and instance paths use; the organization, repository, template,
credential, scaleset-create, controller, and agent-tool mutations catch
ApiException directly, so a GARM 401 there raised without an AUTHZ event.

Add _raise_api_error, which logs authz_fail on a 401 and then raises the
flat GarmApiError (preserving the existing exception type so callers that
special-case GarmNotFoundError/GarmUnauthorizedError are unaffected), and
route every direct-catch privileged mutation through it. Centralize the
single authz_fail emission point via _log_authz_fail_on_401, shared with
_raise_resource_api_error.

Also add the Security Logs Viewer guide to the docs toctree so the
Sphinx (RTD) build no longer fails on the "not included in any toctree"
warning.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@florentianayuwono
florentianayuwono force-pushed the feat/garm-owasp-security-logging-stage1-isd-6341 branch from 86e7fac to c1ea46a Compare October 8, 2026 16:18
Comment thread docs/index.rst Outdated
Comment thread docs/how-to/access-security-logs.md Outdated
florentianayuwono and others added 2 commits October 9, 2026 22:41
…oller

Charm-hook security events are written to juju-log and forwarded to the Juju
controller, so their onward handling and retention is the Juju controller
operator's responsibility rather than this charm's. Drop the OTLP-collector
forwarding recommendation and document controller-side inspection via
`juju debug-log`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The dedicated "Security" toctree caption was only a workaround for the
"document not included in any toctree" build error. Per Diátaxis, this is a
how-to guide, so relocate it to docs/how-to/ (anchor how_to_access_security_logs),
list it in the How-to toctree and the Operations overview row, and drop the
Security caption. Addresses docs review feedback on #408.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@erinecon erinecon 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.

Thanks for clarifying the role of the documentation in this PR! The page currently has some tension between how it's written (Reference-like) and where it's placed (How-to guide). I agree the information is useful, but either the page needs a major refactor, or the page needs to be reorganized under Reference

Comment thread docs/.custom_wordlist.txt
Comment thread docs/index.rst Outdated
Comment thread docs/how-to/access-security-logs.md Outdated
Comment thread docs/how-to/access-security-logs.md Outdated
Comment thread docs/how-to/access-security-logs.md Outdated
Comment thread docs/how-to/access-security-logs.md Outdated
Comment thread docs/how-to/access-security-logs.md Outdated
Comment thread docs/how-to/access-security-logs.md Outdated
florentianayuwono and others added 6 commits October 13, 2026 22:16
Co-authored-by: Erin Conley <erin.conley@canonical.com>
Co-authored-by: Erin Conley <erin.conley@canonical.com>
…ity-logging-stage1-isd-6341

# Conflicts:
#	docs/how-to/index.rst
Address erinecon's review: the page described the security event feed rather
than instructing an operator task, so relocate it from How-to to Reference.

- New docs/reference/security-logs.md with descriptive (reference-tone) headings
- Link it from the home-page Security row, the Reference toctree, and
  reference/security.md "Related information"
- Remove docs/how-to/access-security-logs.md and its home-page Operations link
- Apply grammar suggestions (Charmcraft capitalization, relation wording)
- Alphabetize this PR's additions to docs/.custom_wordlist.txt
- Update changelog to point at the new reference path

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…-stage1-isd-6341' into feat/garm-owasp-security-logging-stage1-isd-6341

# Conflicts:
#	docs/how-to/access-security-logs.md
Sort the whole wordlist case-insensitively (regex-form entries sort last)
so new terms have an obvious insertion point, per review feedback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The newer unpinned pyright (garm) and mypy 2.4.0 (garm-configurator)
no longer narrow the IPv4/IPv6 union on a `.version` guard, so the
subsequent `start <= end` comparison is flagged as an unsupported
operand across address versions. Compare the integer forms instead;
for same-version addresses the integer ordering is identical, so
behaviour is unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@erinecon erinecon 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.

Thanks for the quick turnaround! Nits only on the docs side

Comment thread docs/reference/security-logs.md Outdated
Comment thread docs/reference/security-logs.md Outdated
Comment thread docs/how-to/access-security-logs.md Outdated
florentianayuwono and others added 2 commits October 14, 2026 09:57
Co-authored-by: Erin Conley <erin.conley@canonical.com>
Co-authored-by: Erin Conley <erin.conley@canonical.com>
@florentianayuwono
florentianayuwono merged commit 81b483e into main Oct 14, 2026
62 checks passed
@florentianayuwono
florentianayuwono deleted the feat/garm-owasp-security-logging-stage1-isd-6341 branch October 14, 2026 04:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants