Skip to content

Conversation

@blackboxsw
Copy link
Collaborator

Provide an initial spike for logging OWASP formatted events in cloud-init for discussion. Integration tests will be added upon agreement for security logging procedures.

Proposed Commit Message

    feat: add OSWAP security event logging for user creation and system restart
    
    Implement OWASP structured logs in /var/log/cloud-init-security.log.
    
    Add security-related operations performed by cloud-init on behalf
    of user-data or platform meta-data:
    - user creation
    - user password change
    - system restart
    - system shutdown
    
    Default security log file can be changed by setting an alternative value
    for security_log_file in /etc/cloud/cloud.cfg(.d/*.cfg).

Additional Context

Test Steps

TODO: extend integration tests once initial discussion conclused

Merge type

  • Squash merge using "Proposed Commit Message"
  • Rebase and merge unique commits. Requires commit messages per-commit each referencing the pull request number (#<PR_NUM>)

…estart

Implement OWASP structured logs in /var/log/cloud-init-security.log.

Add security-related operations performed by cloud-init on behalf
of user-data or platform meta-data:
- user creation
- user password change
- system restart
- system shutdown

Default security log file can be changed by setting an alternative value
for security_log_file in /etc/cloud/cloud.cfg(.d/*.cfg).
@github-actions github-actions bot added the documentation This Pull Request changes documentation label Jan 26, 2026
@blackboxsw blackboxsw requested a review from holmanb January 26, 2026 21:48
@blackboxsw blackboxsw assigned blackboxsw and holmanb and unassigned blackboxsw Jan 26, 2026
@blackboxsw
Copy link
Collaborator Author

cc: @dbungert

Copy link
Member

@holmanb holmanb left a comment

Choose a reason for hiding this comment

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

Thanks for this @blackboxsw. First pass.

If the goal is for this to be cross-distro, then implementing this in the distro class doesn't seem optimal because distro methods are expected to be (and in this case are) overridden.

Also, is there a reason that the logger package cannot be used? Manually writing to files seems undesirable - I would have expected this to define a custom logger for this purpose.

Thank you for the type annotations. Besides just annotating the types, I'd like to see if we can also avoid unnecessary complexity - something that annotations can assist with. For example rather than checking an Optional parameter for trutheyness, we could instead avoid the possibility of None and pass a falsey value instead.

Comment on lines +146 to +150
# Create file with restricted permissions if it doesn't exist
if not os.path.exists(log_file):
util.ensure_file(log_file, mode=0o600, preserve_mode=False)

util.append_file(log_file, json_line, disable_logging=True)
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't seem ideal. Was Python's builtin logger considered?

This complicates code in util and makes for a bunch of otherwise unnecessary logger file parameters.

Comment on lines +825 to +829
sec_log_user_created(
userid="cloud-init",
new_userid=name,
attributes={"snapuser": True, "sudo": True},
)
Copy link
Member

Choose a reason for hiding this comment

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

Intermixing code of different different purposes like this will lead to difficulty maintaining and auditing the code. I would prefer if we could find a cleaner design.

LOG = logging.getLogger(__name__)

# Hard-coded application identifier per spec
APP_ID = "canonical.cloud_init"
Copy link
Member

Choose a reason for hiding this comment

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

Why the underscore? If no hyphen is allowed then I would prefer "canonical.cloudinit" over this.

rotated_logs = []
if not cfg or not isinstance(cfg, dict):
return logs
default_log = cfg.get("def_log_file")
Copy link
Member

Choose a reason for hiding this comment

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

The changes in this file seem unnecessary.

Comment on lines +244 to +249
def sec_log_system_shutdown(
userid: Optional[str] = None,
mode: Optional[str] = None,
delay: Optional[str] = None,
log_file: Optional[str] = DEFAULT_SECURITY_LOG,
) -> None:
Copy link
Member

Choose a reason for hiding this comment

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

Why the unused parameters? Possibly same question elsewhere.

userid: str,
new_userid: str,
attributes: Optional[Dict[str, Any]] = None,
log_file: Optional[str] = DEFAULT_SECURITY_LOG,
Copy link
Member

Choose a reason for hiding this comment

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

Why the log file parameters? This seems unnecessary.

def sec_log_user_created(
userid: str,
new_userid: str,
attributes: Optional[Dict[str, Any]] = None,
Copy link
Member

Choose a reason for hiding this comment

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

Optional parameters often complicate type annotations unnecessarily. Can we restructure this to be cleaner? Same comment repeated throughout this code.

util.logexc(LOG, "Failed to set password for %s", user)
raise e

# Log security event for password change
Copy link
Member

Choose a reason for hiding this comment

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

This comment seems unnecessary.

cmd = ["chpasswd"] + (["-e"] if hashed else [])
subp.subp(cmd, data=payload)

# Log security event for each password change
Copy link
Member

Choose a reason for hiding this comment

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

This comment seems unnecessary.

if params:
# Filter out None values and convert to strings
filtered_params = [str(p) for p in params if p is not None]
if filtered_params:
Copy link
Member

Choose a reason for hiding this comment

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

This conditional is unnecessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation This Pull Request changes documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants