Skip to content

sysklogd-next (v3.0)#109

Draft
troglobit wants to merge 26 commits intomasterfrom
next
Draft

sysklogd-next (v3.0)#109
troglobit wants to merge 26 commits intomasterfrom
next

Conversation

@troglobit
Copy link
Copy Markdown
Owner

Testing of next

troglobit added 25 commits April 2, 2026 14:28
Implement TCP as an alternative to UDP for remote syslog forwarding,
providing reliable delivery for environments where message loss is
unacceptable.

Forwarding syntax:
 - @@host:port  (double-at prefix)
 - tcp://host:port

Receiving syntax:
 - listen tcp://addr:port

Uses octet counting framing for sending per RFC 6587. Supports both
octet counting and LF-delimited framing for receiving to interoperate
with various syslog implementations.

Also fixes a use-after-free bug in socket_poll() where LIST_FOREACH
was used while callbacks could remove entries from the socket list.
Changed to LIST_FOREACH_SAFE to handle this correctly.

Fixes #91

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Verify TCP forwarding between two syslogd instances using both
the @@ and tcp:// configuration syntax. Tests that messages are
correctly forwarded after config reload.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Update syslog.conf(5) with TCP forwarding syntax (@@, tcp://) and
TCP listener configuration (listen tcp://).  Add examples showing
typical usage patterns.

Update syslogd(8) STANDARDS section to reference RFC 6587 for
syslog over TCP.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Implements cryptographic signing of syslog messages per RFC 5848,
providing origin authentication, message integrity, replay resistance,
and detection of missing messages.

Requires OpenSSL >= 1.1.0 and ./configure --with-openssl to enable.
New configuration options: sign_sg, sign_delim_sg2, sign_keyfile,
sign_certfile.  Follows NetBSD's configuration syntax for compatibility.

Features:
- Signature blocks (ssign) with SHA-256 hashes and digital signatures
- Certificate blocks (ssign-cert) for public key distribution
- Four signature group modes (SG=0,1,2,3) per RFC 5848 Section 4.2.5
- Compiles cleanly with stub macros when OpenSSL is not available

Fixes #21

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Implements TLS-encrypted syslog forwarding and receiving per RFC 5425,
building on the existing TCP infrastructure (RFC 6587) and reusing
OpenSSL from RFC 5848 signing support.

Configuration syntax:
 - Forwarding: tls://host:port or @@@host:port
 - Listening:  listen tls://addr:port
 - Global:     tls_keyfile, tls_certfile, tls_cafile, tls_capath
 - Per-action: verify=off|optional|required|hostname, fingerprint=SHA256:...

Key implementation details:
 - TLS 1.2 minimum per RFC 5425 requirements
 - Default port 6514 for TLS syslog
 - Certificate verification: chain, fingerprint, or hostname
 - Optional mutual TLS authentication with client certificates
 - Deferred TLS connection until after tls_init() completes
 - Reuses F_FORW_TLS/F_FORW_TLS_SUSP/F_FORW_TLS_UNKN state machine

Fixes #22

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Verifies TLS forwarding between two syslogd instances using both @@@ and
tls:// configuration syntax. Generates self-signed test certificates and
validates message delivery through encrypted channel.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Add documentation for TLS configuration options including new global
settings: tls_keyfile, tls_certfile, tls_cafile, tls_capath, listener
syntax: listen tls://, forwarding syntax: tls:// and @@@, and per-action
options: verify=, fingerprint=.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Inspired by OpenBSD syslogd, introduce a double-prefix syntax for block
headers that stops rule evaluation once a matching rule fires,
preventing a message from being logged twice.

  !!prog      -- like !prog  but stops on match
  ++host      -- like +host  but stops on match
  ::filter    -- like :filter but stops on match (sysklogd extension)

Use !*, +*, or :* as usual to reset and resume normal evaluation for
subsequent blocks.

The STOP_FLAG (0x040) is set on all filed entries created within a
double-prefix block.  The logmsg() dispatch loop breaks out of the
SIMPLEQ_FOREACH as soon as it processes a rule carrying that flag,
so no further rules see the message.

Example -- route nftables firewall logs exclusively to their own file:

  ::msg, contains, "[nftables"
  kern.*                    -/var/log/nftables.log
  :*
  *.warn;authpriv.none      -/var/log/syslog

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Stop-processing block prefixes (!!, ++, ::) can only suppress rules
that follow in the parsed order.  A snippet in /etc/syslog.d/ cannot
gate rules that were already parsed from the main config file above
the include line.

Fix this by moving the include directive in the shipped syslog.conf to
sit before the general catch-all rules, so snippets are evaluated first
and their stop-blocks take effect.

Document the ordering constraint in two places in syslog.conf(5):
in the include description, where the interaction is explained and
the shipped file's structure is called out; and in the Stop Processing
example section, where a direct note warns users who place stop-blocks
in syslog.d/ snippets.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
- Fix .Sh DESCRIPTIOMN typo in man page
- Fix chomp() indentation, spaces -> tabs
- Fix nslookup() EAI_SERVICE warning, drop hardcoded "/udp" suffix
- Bump message buffer from 512 to 2048 bytes (MAXLINE), matching
  syslogd's own limit to avoid silent truncation of long messages

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The -h option now accepts a transport prefix, matching the forwarding
action syntax used in syslog.conf:

  logger -h tcp://logserver:514 "test"
  logger -h udp://logserver "test"    # explicit UDP, same as before
  logger -h logserver "test"          # plain host, UDP (backward compat)

IPv6 addresses require bracket notation: tcp://[::1]:514
An optional port embedded in the URL overrides -P.

The TCP path bypasses the syslog library entirely and handles DNS
resolution (SOCK_STREAM), connection, message formatting (RFC5424 by
default, RFC3164 with -b), and RFC 6587 octet-count framing directly,
matching the framing used by syslogd's own TCP forwarding path.

A tls:// prefix is recognised and rejected with a clear error for
forward compatibility.

Also change struct sockaddr to sockaddr_storage throughout to correctly
handle IPv6 addresses in the UDP path as well.

Note: bare IPv6 address literals (e.g. ::1) passed to -h are not
supported; bracket notation must be used.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
When -V is given, diagnostic output is written to stderr showing
what logger is actually doing, making it easy to verify a remote
syslogd configuration:

  TCP:
    connecting to 192.0.2.1 port 514 (tcp) ... connected
    sending (82 bytes): <28>1 2026-03-20T14:30:00.123456+01:00 ...

  UDP:
    sending to 192.0.2.1 port 514 (udp)

For TCP, the exact RFC 6587-framed syslog message is shown, allowing
the user to confirm the message content matches the filter rules in
syslog.conf.  Connection failures are reported on the same line as
the connect attempt, giving a clear pass/fail result.

For UDP only the resolved peer is shown; delivery cannot be confirmed
since the transport is fire-and-forget.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Add two steps to the existing TCP forwarding test:

 - verify_direct_tcp: send directly from logger to the receiver's
   TCP listener using logger -h tcp://[::1]:PORT, bypassing the
   forwarding syslogd entirely.  Confirms the RFC 6587 framing and
   message delivery end-to-end.

 - verify_verbose_tcp: invoke logger -V and check that "connected"
   appears on stderr, exercising the new verbose mode added alongside
   the TCP transport feature.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Verify that the :: (propfilter), !! (program), and ++ (hostname)
stop-block prefixes prevent a matched message from reaching subsequent
rules, while non-matching messages continue to fall through normally.

Eight steps cover both positive (message lands in the right log) and
negative (message absent from the wrong log) assertions for the
propfilter and program-filter variants.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
When a TCP receiver goes down, the sender previously dropped all messages
for the full suspension window (default 180 s).  This change retrofits a
per-destination FIFO queue so that messages accumulate during outages and
are flushed automatically on reconnect (inspired by the NetBSD GSoC 2008
work).

New data structures (syslogd.h):
 - struct fwd_qentry / fwd_qhead -- RFC 6587 framed queue entries
 - FORW_QUEUE_MAX_LEN (1000) and FORW_QUEUE_MAX_SIZE (1 MiB) limits
 - Four new fields on struct filed: f_queue, f_qlen, f_qsize, f_qoverflow

New helpers (syslogd.c):
 - tcp_build_frame()       flatten iovec into a single "LEN SP MSG" frame
 - forw_queue_enqueue()    add a frame; evict oldest on overflow
 - forw_queue_flush()      drain the queue over a live socket
 - forw_queue_clear()      discard all entries (SIGHUP / shutdown)

Behaviour changes:
 - F_FORW_TCP_SUSP:        enqueue instead of drop during the backoff window
 - F_FORW_TCP_UNKN:        enqueue when DNS is still unresolved
 - F_FORW_TCP:             flush any backlog before/after a successful send
 - close_open_log_files(): always call forw_queue_clear() to prevent leaks

New syslog.conf global directive:
  tcp_suspend_time N   override the per-TCP-destination backoff (default
                       INET_SUSPEND_TIME = 180 s); useful in tests and for
                       operators who want faster failover.

Two bugs found and fixed during testing:
 1. fprintlog_first() was resetting f_time for all filed types except the
    two original UDP suspended/unknown states (F_FORW_SUSP, F_FORW_UNKN).
    The TCP and TLS equivalents were missing from the guard, so the
    suspension start timestamp was clobbered on every arriving message,
    making fwd_suspend always 0 and the backoff window never expire.
    Fixed by extending the exclusion to F_FORW_TCP_SUSP/UNKN and
    F_FORW_TLS_SUSP/UNKN.

 2. The SUSP → reconnect path transitioned through F_FORW_TCP_UNKN /
    forw_lookup(), which silently skips re-resolution (and thus
    tcp_connect()) when timer_now() - f_time < INET_DNS_DELAY (60 s).
    With a tcp_suspend_time shorter than 60 s the reconnect never fired.
    Fixed by jumping directly to f_forw_tcp (bypassing forw_lookup) when
    the suspension window expires; the stored f_addr is still valid.

New regression test: test/tcp-queue.sh
 Kills the receiver, injects three messages during the outage, restarts
 the receiver, sends a trigger after tcp_suspend_time expires, and verifies
 that all four messages arrive in order.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
syslog.conf(5):
 - Add tcp_suspend_time to the synopsis block
 - Document the directive in the global options section: default value,
   suspension/queue/flush cycle, queue bounds (1000 msgs / 1 MiB),
   overflow eviction policy, and the SIGHUP/exit discard caveat
 - Add a paragraph in the Remote Machine action section explaining the
   queue from the operator's perspective when configuring @@ / tcp://
   forwarding targets

syslogd(8):
 - Note in the HUP signal entry that queued TCP messages are discarded
   on reload
 - Extend the RFC 6587 TCP sentence in STANDARDS to mention the
   per-destination send queue and cross-reference tcp_suspend_time

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
 - bump GitHub Actions to Node.js 24 compatible versions
 - simplify, skip apt update
 - prevent duplicate jobs

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
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.

1 participant