Skip to content

Postgres log readers: read csvlog/jsonlog when a target writes them, so a client can't forge a line through %u/%d (#4046 part 2) #4053

Description

@erikdarlingdata

Split from #4046. Parts 1, 1b and 1c stop a planted line from making a target's log read fail (#4049 is merged; #4051 is in review):

  • a line stamped in another zone is skipped and counted;
  • a byte that is not valid UTF-8 is read through pg_read_binary_file instead of throwing 22021.

They don't stop forgery. That is this issue.

The gap

In the stderr log format, a role or database name from the startup packet lands unescaped in the FATAL message. The server writes it before authentication. A newline in that name starts a new line of the client's choosing, and the line can carry a fake timestamp, pid and label under any log_line_prefix that puts %u/%d before %p. The RDS default, %t:%r:%u@%d:[%p]:, is one such prefix. No stderr parser can tell that line from a real one.

Fix

When a target's log_destination includes csvlog or jsonlog (PostgreSQL 15+), the three log-tail collectors read that file instead of the stderr one:

  • pg_log_events
  • pg_deadlocks
  • pg_plan_capture

Today the tail CTE excludes \.(csv|json)$. The collectors need a real CSV/JSON parser. In those formats every field is quoted or escaped, so a planted newline stays inside its field.

  • Self-hosted route: the tail CTE picks the newest .csv/.json file. A tail window can start mid-record, and a CSV record can span lines, so drop everything up to the first complete record.
  • Managed route (RDS/Aurora): the log download API serves the .csv files when csvlog is on. Use the same parser.
  • Keep the stderr path as the fallback. The logging audit (get_pg_logging_audit) should recommend csvlog or jsonlog and give this reason.
  • Keep Byte route (pg_read_binary_file) and managed-zone log_timezone (#4046 parts 1b, 1c) #4051's binary route: a CSV or JSON file can carry the same invalid byte.

Also open, carried over from #4051

Nobody knows yet whether AWS's log download re-encodes or rejects a byte that is not valid UTF-8. Test it on a throwaway RDS instance.

Done when

  • CSV and JSON parsing covers all three collectors, on both the self-hosted and managed routes.
  • A forged-newline test proves the planted line stays inside its field.
  • A live check has run on a rig with csvlog on.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    client-siteOwned by the client-site agents (other laptop). Local sessions never pick these up.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions