Skip to content

Insert host_mdm_managed_certificates rows from non-proxied cert ingestion (PR 5/8)#45037

Merged
mostlikelee merged 6 commits into
40639-cert-renewfrom
pr-2.2-ingestion-insert
May 11, 2026
Merged

Insert host_mdm_managed_certificates rows from non-proxied cert ingestion (PR 5/8)#45037
mostlikelee merged 6 commits into
40639-cert-renewfrom
pr-2.2-ingestion-insert

Conversation

@mostlikelee
Copy link
Copy Markdown
Contributor

@mostlikelee mostlikelee commented May 8, 2026

Related issue: Resolves #44345

What this PR does

Adds an INSERT pass to UpdateHostCertificates that creates host_mdm_managed_certificates rows when an ingested cert's Subject contains the fleet-<profile_uuid> marker for a profile installed on the host. Rows are created with type = NULL. Also relaxes the matcher's SupportsRenewalID() skip so NULL-type rows can be updated on subsequent ingestion.

Why this is needed

Phase 2 (#40639) auto-renews certs from non-proxied CAs. The renewal cron acts on host_mdm_managed_certificates rows that today only get created at proxy issuance. This PR creates them from cert ingestion instead, so non-proxied flows (e.g. customer-cisneros-a's Hydrant ACME, Okta SCEP) can be renewed by the existing mechanism.

Checklist for submitter

  • Input data is properly validated, SELECT * is avoided, SQL injection is prevented (using placeholders for values in statements)

Testing

  • Added/updated automated tests
  • Where appropriate, automated tests simulate multiple hosts and test for host isolation

…tion

Extend UpdateHostCertificates with two changes that activate auto-renewal
for non-proxied SCEP/ACME flows where Fleet wasn't in the issuance path:

1. Loosen the matcher's SupportsRenewalID() guard so empty/NULL Type
   rows are not silently skipped — without this, ingestion-created rows
   would never have their not_valid_after advanced after a renewal
   completes.

2. New INSERT pass: for each profile installed on the host without an
   existing host_mdm_managed_certificates row, scan the toInsertBySHA1
   pool for a cert whose Subject CN/OU contains "fleet-<profile_uuid>"
   and create the row. Type is left NULL; ca_name is derived from the
   issuer's CN.

A dedicated insertHostMDMManagedCertDB helper handles empty-Type → NULL
conversion (the column's enum doesn't accept empty strings) and uses
INSERT IGNORE so a concurrent proxy issuance doesn't get clobbered.
@mostlikelee
Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 8, 2026

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 8, 2026

Review Change Stack

Walkthrough

This PR extends the UpdateHostCertificates function to insert host_mdm_managed_certificates rows when ingested certificates carry a fleet-<profile_uuid> marker in their Subject field but no corresponding managed-cert row exists. The implementation collects installed profile UUIDs lacking existing rows, matches incoming certificates via the profile UUID marker while enforcing certificate validity windows, derives CAName from the issuer CN (or a sentinel), and inserts new rows with Type set to NULL via a transaction-safe helper. The matching logic now explicitly skips Type values that cannot support renewal markers while treating empty/NULL Type as eligible. A new comprehensive subtest validates the insertion behavior alongside proxied row updates and non-matching profile handling.

Possibly related PRs

  • fleetdm/fleet#44691: Extends the managed-certificate matcher in UpdateHostCertificates to recover stuck rows, operating alongside this PR's non-proxied insert path changes to host_mdm_managed_certificates matching and backfill logic.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The PR description covers the main change (INSERT for non-proxied certs), rationale, and testing. However, it's incomplete: missing Changes file entry, database migration checklist, and doesn't address all required sections. Add a Changes file entry, complete the full checklist including database migration considerations, and address any schema or collation concerns for the host_mdm_managed_certificates table.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly summarizes the main change: inserting host_mdm_managed_certificates rows from non-proxied cert ingestion, which is the core purpose of this PR.
Linked Issues check ✅ Passed The PR fulfills all coding requirements from #44345: extends UpdateHostCertificates to insert host_mdm_managed_certificates rows when cert Subject contains fleet-<profile_uuid> marker with no existing row, verifies profile_uuid exists on host, and sets type=NULL with ca_name from Issuer CN.
Out of Scope Changes check ✅ Passed All changes are scoped to host certificate processing and directly support the linked objective of non-proxied cert ingestion. No unrelated modifications detected beyond the required implementation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 pr-2.2-ingestion-insert

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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 `@server/datastore/mysql/host_certificates.go`:
- Around line 236-297: The code only attempts to create missing
host_mdm_managed_certificates when toInsertBySHA1 is non-empty and searches that
map for matches, which misses matches present only in incomingBySHA1; change the
condition and loop to use the full incoming set: replace the outer if
len(toInsertBySHA1) > 0 check with len(incomingBySHA1) > 0 (or remove the check
and rely on candidateProfileUUIDs), and when searching for bestMatch inside the
profileUUID loop iterate over incomingBySHA1 (or the variable name that holds
the full incoming certs) instead of toInsertBySHA1; keep existingProfileUUIDs,
candidateProfileUUIDs, hostMDMManagedCertsToInsert, and the matching/validity
logic the same so duplicate inserts are still prevented.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4d170025-27b7-4c3f-8a4a-1b79d3a2b606

📥 Commits

Reviewing files that changed from the base of the PR and between 85ee5c6 and be9ff23.

📒 Files selected for processing (2)
  • server/datastore/mysql/host_certificates.go
  • server/datastore/mysql/host_certificates_test.go

Comment thread server/datastore/mysql/host_certificates.go Outdated
@mostlikelee mostlikelee removed their assignment May 8, 2026
The previous gate and inner loop only saw certs that were new in the
current call. A cert already in host_certificates from a prior call
(so present in incomingBySHA1 but not in toInsertBySHA1) would be
missed when its matching profile was installed afterward — no INSERT
would fire even though a valid marker-bearing cert existed for the
profile.

Switch the gate and inner-loop pool to incomingBySHA1; the
existingProfileUUIDs / candidateProfileUUIDs check still prevents
duplicate inserts.
@mostlikelee mostlikelee marked this pull request as ready for review May 8, 2026 16:56
@mostlikelee mostlikelee requested a review from a team as a code owner May 8, 2026 16:56
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@mostlikelee mostlikelee linked an issue May 8, 2026 that may be closed by this pull request
@codecov
Copy link
Copy Markdown

codecov Bot commented May 8, 2026

Codecov Report

❌ Patch coverage is 85.71429% with 8 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (40639-cert-renew@b35b491). Learn more about missing BASE report.

Files with missing lines Patch % Lines
server/datastore/mysql/host_certificates.go 85.71% 4 Missing and 4 partials ⚠️
Additional details and impacted files
@@                 Coverage Diff                 @@
##             40639-cert-renew   #45037   +/-   ##
===================================================
  Coverage                    ?   66.81%           
===================================================
  Files                       ?     2698           
  Lines                       ?   217601           
  Branches                    ?    10142           
===================================================
  Hits                        ?   145392           
  Misses                      ?    59035           
  Partials                    ?    13174           
Flag Coverage Δ
backend 68.68% <85.71%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@sgress454 sgress454 left a comment

Choose a reason for hiding this comment

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

couple questions but nothing blocking if you've already thought them through

Comment on lines +701 to +703
if c.Type != "" {
typeArg = string(c.Type)
}
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.

Isn't the expectation here that this will always be null? so we could just set NULL directly in the INSERT statement, rather than checking cert type at all?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

True, there's no other callers to insertHostMDMManagedCertDB so it simplified things a bit

Comment on lines +279 to +284
// Surface the issuer's CN as ca_name for support visibility;
// fall back to a sentinel since the column is NOT NULL.
caName := bestMatch.IssuerCommonName
if caName == "" {
caName = "non_proxied"
}
Copy link
Copy Markdown
Contributor

@sgress454 sgress454 May 8, 2026

Choose a reason for hiding this comment

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

Not sure about this one -- CN can change, right? It looks like we have WHERE clauses that match against ca_name, which could fail if it changes between renewals. 🤖 says that the proxied certs use a fixed string here that won't change; seems like we should consider doing the same here (e.g. just always use non_proxied).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

good catch, switched to a sentinel here

Stop deriving ca_name from the cert's Issuer CN. Use the fixed sentinel
"non_proxied" instead, matching the proxied-flows convention of
Fleet-controlled ca_name values and removing drift risk if the upstream
CA ever renames. The cert's actual issuer remains available in
host_certificates for support visibility.
@sgress454
Copy link
Copy Markdown
Contributor

👍 , +1 still stands

@mostlikelee mostlikelee merged commit 46cbf05 into 40639-cert-renew May 11, 2026
35 checks passed
@mostlikelee mostlikelee deleted the pr-2.2-ingestion-insert branch May 11, 2026 19:09
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.

Ingestion-driven managed-cert row creation (sub-task of #40639)

2 participants