Add support for SAN in Android certificate templates.#44690
Add support for SAN in Android certificate templates.#44690
Conversation
- End-to-end implementation for `subject_alternative_name` in certificate templates. - Includes schema migration, variable expansion, frontend UI updates, GitOps support, and Android agent integration. - Limits SAN types to `DNS`, `EMAIL`, `UPN`, `IP`, and `URI`.
|
@CodeRabbit full review |
|
/agentic_review |
✅ Actions performedFull review triggered. |
Code Review by Qodo
1. Premium gate contradicts spec
|
WalkthroughAdds end-to-end support for an optional certificate Subject Alternative Name (SAN) on Android certificate templates. Changes include a MySQL migration and schema column, datastore read/write handling, API and service-layer structs and method signatures, SAN format and variable validation, GitOps generation and client-side validation, fleetctl test fixtures, and new unit and integration tests exercising SAN behavior and runtime variable expansion. Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Pull request overview
Adds an OpenSpec (proposal/design/spec/tasks) for implementing subject_alternative_name (SAN) support in Android certificate templates, including backend persistence, GitOps, frontend UI, and Android agent CSR behavior.
Changes:
- Added OpenSpec proposal, design, and detailed acceptance spec for SAN support end-to-end.
- Added an implementation task breakdown covering backend/API/datastore, GitOps apply/generate, frontend modal UX, and Android agent parsing/CSR extension wiring.
- Added
.openspec.yamlto mark the change as spec-driven.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| openspec/changes/android-cert-san-attributes/tasks.md | Step-by-step implementation plan across backend/frontend/GitOps/Android agent. |
| openspec/changes/android-cert-san-attributes/specs/android-cert-san/spec.md | Acceptance requirements and scenarios for SAN persistence/validation/expansion and Android CSR behavior. |
| openspec/changes/android-cert-san-attributes/proposal.md | Problem statement + intended product/engineering changes and rollout considerations. |
| openspec/changes/android-cert-san-attributes/design.md | Detailed design decisions, parsing/encoding rules, rollout plan, and risks. |
| openspec/changes/android-cert-san-attributes/.openspec.yaml | Declares the change as spec-driven with creation date. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Updated schema to use `TEXT` for `subject_alternative_name`, matching existing
Adds the optional subject_alternative_name field end to end on the server side: REST API request and response, persistence, GitOps apply and generate-gitops emit, per-host variable expansion, lightweight server-side validation, and Premium gating. The Android agent and frontend ship in separate PRs (per the OpenSpec migration plan). Changes: - Type changes on CertificateTemplate, CertificateRequestSpec, CertificateTemplateSpec and the response structs. - Migration adds a nullable TEXT column to certificate_templates, matching subject_name. schema.sql updated. - Datastore CRUD reads via COALESCE so absent SAN deserialises to ""; writes normalise whitespace-only input to NULL while preserving non-empty values verbatim for GitOps idempotency. - Service layer validates token shape, KEY allow-list (DNS, EMAIL, UPN, IP, URI), variable allow-list, and length cap (4096 bytes); rejects with BadRequestError. Premium check fires only on SAN-bearing payloads. - GetDeviceCertificateTemplate expands $FLEET_VAR_HOST_* in SAN with the same failure semantics as subject_name. - generate-gitops emits subject_alternative_name when non-empty; the fleetctl client propagates SAN through GitOps apply, including detecting SAN changes for re-creation. - Unit tests cover validation table, Premium gating, format failures, and variable allow-list. The generate-gitops compareDirs test now round-trips a SAN-bearing template. OpenSpec: openspec/changes/android-cert-san-attributes/
Verifies the end-to-end path that the Android Fleet agent will exercise: the fleetd certificate API expands $FLEET_VAR_HOST_END_USER_IDP_USERNAME inside both subject_name and subject_alternative_name when the host has an associated IdP account, and returns the rendered template ready for the agent to wrap in a CSR.
|
@coderabbitai full review |
|
/agentic_review |
✅ Actions performedFull review triggered. |
|
Persistent review updated to latest commit 3cb17de |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
server/datastore/mysql/certificate_templates.go (1)
261-273:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUpdate all mutable fields in the duplicate-key branch.
Reapplying a spec with the same template name but a new
subject_nameorcertificate_authority_idwill keep the old row values, because theON DUPLICATE KEY UPDATEclause only touchesteam_idandsubject_alternative_name.ApplyCertificateTemplateSpecsgoes through this path, so GitOps edits to existing templates won't fully take effect.Suggested fix
ON DUPLICATE KEY UPDATE - name = VALUES(name), - team_id = VALUES(team_id), - subject_alternative_name = VALUES(subject_alternative_name) + certificate_authority_id = VALUES(certificate_authority_id), + subject_name = VALUES(subject_name), + subject_alternative_name = VALUES(subject_alternative_name) `🤖 Prompt for 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. In `@server/datastore/mysql/certificate_templates.go` around lines 261 - 273, The INSERT's ON DUPLICATE KEY UPDATE clause in the sqlInsertCertificate constant only updates team_id and subject_alternative_name, so reapplying a spec via ApplyCertificateTemplateSpecs won't overwrite changed fields; update the clause to also set certificate_authority_id and subject_name (using VALUES(certificate_authority_id) and VALUES(subject_name)) so all mutable fields from the new spec are applied on duplicate-key upserts.
🤖 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/schema.sql`:
- Line 1973: The CREATE TABLE declaration ending with "ENGINE=InnoDB
AUTO_INCREMENT=522 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" has the
wrong AUTO_INCREMENT; update the AUTO_INCREMENT value from 522 to 523 to reflect
the new seeded row (id=522) so the table's CREATE statement follows the file's
convention of last_inserted_id + 1.
In `@server/fleet/certificate_templates.go`:
- Line 27: Change the CertificateTemplateResponseSummary.SubjectAlternativeName
from string to *string so sqlx can scan NULL DB values without panicking; update
any JSON handling or marshaling logic that assumes a non-nil string (the struct
tag json:"subject_alternative_name,omitempty" will already omit nils, but ensure
callers and code that dereference SubjectAlternativeName check for nil).
Alternatively, if you prefer DB-side fixes, update the migration to set
subject_alternative_name NOT NULL DEFAULT '' instead of TEXT NULL so the
existing string type remains safe.
In `@server/service/certificate_templates.go`:
- Around line 51-81: The validator currently accepts separator-only SAN strings
like "," because it skips empty tokens; update
validateCertificateTemplateSubjectAlternativeName to detect when no non-empty
key=value tokens were found and reject that case (or normalize to empty before
licensing/storage); specifically, track a count of valid tokens while iterating
the tokens from strings.SplitSeq(san, ",") in
validateCertificateTemplateSubjectAlternativeName and if count==0 after the loop
return an error such as "subject_alternative_name contains no entries" (or
return nil to normalize to empty) so separator-only inputs are not treated as a
valid non-empty SAN; reference the
validateCertificateTemplateSubjectAlternativeName function and the
subjectAlternativeNameAllowedKeys lookup when implementing this change.
In `@server/service/certificates.go`:
- Around line 283-299: When SAN expansion fails in the block handling
certificate.SubjectAlternativeName (and similarly in the earlier subject_name
failure path), clear any active challenges on the returned certificate by
setting certificate.SCEPChallenge = nil and certificate.FleetChallenge = nil
before setting certificate.Status = fleet.CertificateTemplateFailed and
returning; keep the existing UpsertCertificateStatus logic
(svc.ds.UpsertCertificateStatus with fleet.CertificateStatusUpdate) and ensure
you apply the same niling/guard to the subject_name error branch that calls
svc.replaceCertificateVariables so the device cannot receive active challenges
alongside a failed template.
---
Outside diff comments:
In `@server/datastore/mysql/certificate_templates.go`:
- Around line 261-273: The INSERT's ON DUPLICATE KEY UPDATE clause in the
sqlInsertCertificate constant only updates team_id and subject_alternative_name,
so reapplying a spec via ApplyCertificateTemplateSpecs won't overwrite changed
fields; update the clause to also set certificate_authority_id and subject_name
(using VALUES(certificate_authority_id) and VALUES(subject_name)) so all mutable
fields from the new spec are applied on duplicate-key upserts.
🪄 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: 14bc6ab3-da1d-4cb8-8260-36aace2bd42e
⛔ Files ignored due to path filters (4)
openspec/changes/android-cert-san-attributes/design.mdis excluded by!**/*.mdopenspec/changes/android-cert-san-attributes/proposal.mdis excluded by!**/*.mdopenspec/changes/android-cert-san-attributes/specs/android-cert-san/spec.mdis excluded by!**/*.mdopenspec/changes/android-cert-san-attributes/tasks.mdis excluded by!**/*.md
📒 Files selected for processing (19)
cmd/fleetctl/fleetctl/generate_gitops.gocmd/fleetctl/fleetctl/generate_gitops_test.gocmd/fleetctl/fleetctl/testdata/generateGitops/expectedTeamControls.yamlcmd/fleetctl/fleetctl/testdata/generateGitops/teamConfig.jsoncmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_premium/fleets/team-a-thumbsup.ymlopenspec/changes/android-cert-san-attributes/.openspec.yamlpkg/spec/gitops.goserver/datastore/mysql/certificate_templates.goserver/datastore/mysql/migrations/tables/20260504193725_AddSubjectAlternativeNameToCertificateTemplates.goserver/datastore/mysql/schema.sqlserver/fleet/app.goserver/fleet/certificate_templates.goserver/fleet/service.goserver/mock/service/service_mock.goserver/service/certificate_templates.goserver/service/certificate_templates_test.goserver/service/certificates.goserver/service/client.goserver/service/integration_android_certificate_templates_test.go
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 23 out of 23 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ogic. - Added stricter validation for `subject_alternative_name`, including disallowing empty values, separator-only inputs, and malformed tokens. - Updated schema to enforce UTF-8 compliance for `subject_alternative_name` field. - Clarified Premium gating behavior for SAN-bearing templates. - Fixed propagation of challenges when setting cert status to "failed." - Aligned validation checks and migrations with spec updates.
|
@claude review once |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #44690 +/- ##
========================================
Coverage 66.68% 66.68%
========================================
Files 2651 2652 +1
Lines 213533 213676 +143
Branches 9734 9644 -90
========================================
+ Hits 142398 142494 +96
- Misses 58171 58209 +38
- Partials 12964 12973 +9
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
2/3rds of this PR is OpenSpec and tests. Use OpenSpec files as a reference (if needed). They're there to help the review, and not to be a review surface themselves.
subject_alternative_namein certificate templates.DNS,EMAIL,UPN,IP, andURI.Related issue: Resolves #41472
Checklist for submitter
If some of the following don't apply, delete the relevant line.
Changes file added for user-visible changes in
changes/,orbit/changes/oree/fleetd-chrome/changes.See Changes files for more information.
Input data is properly validated,
SELECT *is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters.Timeouts are implemented and retries are limited to avoid infinite loops
If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes
Testing
Added/updated automated tests
Where appropriate, automated tests simulate multiple hosts and test for host isolation (updates to one hosts's records do not affect another)
QA'd all new/changed functionality manually
For unreleased bug fixes in a release candidate, one of:
Database migrations
COLLATE utf8mb4_unicode_ci).New Fleet configuration settings
If you didn't check the box above, follow this checklist for GitOps-enabled settings:
fleetctl generate-gitopsfleetd/orbit/Fleet Desktop
runtime.GOOSis used as needed to isolate changesSummary by CodeRabbit