Skip to content

feat: present a client certificate for mutual TLS - #46

Merged
DavidCozens merged 1 commit into
mainfrom
stage/mtls
Jul 30, 2026
Merged

feat: present a client certificate for mutual TLS#46
DavidCozens merged 1 commit into
mainfrom
stage/mtls

Conversation

@DavidCozens

@DavidCozens DavidCozens commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Pull request

What this stage adds

ClientCertChain and ClientKey join the stream config, and the collector moves to
6515. The device now authenticates itself to the collector as well as verifying it.

Flash +13,624 B (+76 on the previous stage)
RAM +37,740 B (+2,052)
mbedTLS peak +15,912 B (37,244 absolute)
Log stack +712 B (unchanged)
Service +3,800 B (unchanged)

Both handles come from the cert store, which has parsed them since boot. This
figure is therefore the cost of using credentials the device already holds — a
device doing server-authenticated TLS only would also have to provision, store and
parse a client certificate and key to reach the same place.

Neither stack moves: client authentication is another leg of a handshake the
service task already had the depth for. The RAM is 2,048 of mbedTLS pool and 4
bytes of element, and the pool is the whole story: proving the device's identity
raised the measured peak from 36,092 to 37,228, and peak x 1.5 rounded up to the
next KiB takes the pool from 53 to 55 KiB.

That resize is the rule, not a repair. The run passes at 53 KiB with 17 KB of the
pool still free, so nothing forced it — but the margin exists for fragmentation
rather than capacity, and a pool sized to yesterday's peak is how a later stage
inherits someone else's headroom.

Both credentials must be set: either one NULL disables mTLS and the connection
falls back to server authentication only, without failing. That is why the pipeline
element reports what was configured rather than what was intended — an element
claiming protection the device does not have would be worse than not reporting it,
since it is exactly the weakening a collector is watching for.

Delivery is the proof. 6515 requires a client certificate, and scripts/smoke-oracle.sh
shows it refusing a client that presents none, so a record arriving there is a
record whose sender authenticated.

Files

 README.md                     | 18 ++++++++----------
 app/AppConfig.h               |  2 +-
 app/syslog/Syslog.c           | 11 +++++++++--
 app/syslog/SyslogPipelineSd.c |  7 +++++--
 app/syslog/SyslogPipelineSd.h |  8 ++++++--
 measurements/mtls.csv         | 13 +++++++++++++
 measurements/stages.tsv       |  1 +
 run-report.md                 | 34 +++++++++++++++++-----------------
 8 files changed, 60 insertions(+), 34 deletions(-)

Checklist

  • The diff is application-only — no change to board bring-up, config headers, or build infra.
  • measurements/mtls.csv committed.
  • Row added to measurements/stages.tsv.
  • README regenerated: python3 scripts/gen-cost-table.py.
  • ./run.sh green (build + QEMU + baseline self-check) — run-report.md committed.

Context for review: 19 of 21 sequential single-commit PRs replaying the integration on top of the Baseline root. Each lands green and reviewed before the next is built on it, because a change to an early commit would force every commit above it to be re-run and re-measured.

Summary by CodeRabbit

  • New Features

    • Added Mutual TLS support for secure syslog collector connections.
    • Client certificates and keys are used to authenticate devices and associate records with their source.
    • Syslog transport now identifies Mutual TLS connections separately from standard TLS.
  • Documentation

    • Updated setup guidance, resource-cost figures, and footprint tables for Mutual TLS.
    • Refreshed validation results and performance metrics to reflect the new configuration.
  • Configuration

    • Increased the simulated TLS memory allocation to support Mutual TLS operation.

ClientCertChain and ClientKey join the stream config, and the collector moves to
6515. The device now authenticates itself to the collector as well as verifying it.

  Flash        +13,624 B     (+76 on the previous stage)
  RAM          +37,740 B        (+2,052)
  mbedTLS peak +15,912 B  (37,244 absolute)
  Log stack       +712 B     (unchanged)
  Service       +3,800 B     (unchanged)

Both handles come from the cert store, which has parsed them since boot. This
figure is therefore the cost of *using* credentials the device already holds — a
device doing server-authenticated TLS only would also have to provision, store and
parse a client certificate and key to reach the same place.

Neither stack moves: client authentication is another leg of a handshake the
service task already had the depth for. The RAM is 2,048 of mbedTLS pool and 4
bytes of element, and the pool is the whole story: proving the device's identity
raised the measured peak from 36,092 to 37,228, and peak x 1.5 rounded up to the
next KiB takes the pool from 53 to 55 KiB.

That resize is the rule, not a repair. The run passes at 53 KiB with 17 KB of the
pool still free, so nothing forced it — but the margin exists for fragmentation
rather than capacity, and a pool sized to yesterday's peak is how a later stage
inherits someone else's headroom.

Both credentials must be set: either one NULL disables mTLS and the connection
falls back to server authentication only, without failing. That is why the pipeline
element reports what was configured rather than what was intended — an element
claiming protection the device does not have would be worse than not reporting it,
since it is exactly the weakening a collector is watching for.

Delivery is the proof. 6515 requires a client certificate, and scripts/smoke-oracle.sh
shows it refusing a client that presents none, so a record arriving there is a
record whose sender authenticated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Mutual TLS is wired into syslog startup with client credentials from DeviceCertStore, port 6515, and pipeline metadata reporting transport="mtls". The simulated mbedTLS heap, README cost tables, and mtls run report are updated.

Mutual TLS syslog flow

Layer / File(s) Summary
Pipeline transport API
app/syslog/SyslogPipelineSd.[ch]
SyslogPipelineSd_Init(bool) selects tls or mtls, and formatting emits the selected transport.
Syslog mutual TLS wiring
app/syslog/Syslog.c, app/AppConfig.h
Syslog_Start loads client certificate material, configures the TLS stream, uses port 6515, initializes pipeline metadata from credential availability, and increases the simulated mbedTLS heap to 55 KiB.
Mutual TLS documentation and measurements
README.md, run-report.md
Stage descriptions, cost tables, run labels, resource metrics, wire output, and self-check values are updated for mutual TLS.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SyslogStart
  participant DeviceCertStore
  participant MbedTlsStream
  participant SyslogPipelineSd
  SyslogStart->>DeviceCertStore: fetch client certificate chain and key
  SyslogStart->>MbedTlsStream: configure mutual TLS stream
  SyslogStart->>SyslogPipelineSd: initialize transport metadata
  SyslogPipelineSd-->>SyslogStart: return shared structured data
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: enabling mutual TLS client certificate presentation.
Description check ✅ Passed The description follows the required template and covers the stage summary, checklist, files, and run status.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch stage/mtls

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/syslog/Syslog.c`:
- Line 54: Update the server-only TLS fallback associated with
SYSLOG_COLLECTOR_PORT so incomplete credentials do not target port 6515, which
rejects certificate-less clients. Select the configured reachable server-only
port when either credential is missing, or fail startup explicitly instead of
attempting an unusable connection.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b849178c-d41d-49e0-a55a-69a7fdb7c9bf

📥 Commits

Reviewing files that changed from the base of the PR and between 8cd4b90 and 38819f6.

⛔ Files ignored due to path filters (2)
  • measurements/mtls.csv is excluded by !**/*.csv
  • measurements/stages.tsv is excluded by !**/*.tsv
📒 Files selected for processing (6)
  • README.md
  • app/AppConfig.h
  • app/syslog/Syslog.c
  • app/syslog/SyslogPipelineSd.c
  • app/syslog/SyslogPipelineSd.h
  • run-report.md

Comment thread app/syslog/Syslog.c
@DavidCozens
DavidCozens merged commit 1b67e28 into main Jul 30, 2026
2 checks passed
@DavidCozens
DavidCozens deleted the stage/mtls branch July 30, 2026 13:55
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