sia: include x-principals in the rsa ssh host certificate csr - #3448
sia: include x-principals in the rsa ssh host certificate csr#3448havetisyan wants to merge 2 commits into
Conversation
The certsign-based CSR path (SshHostKeyType=rsa) only ever requested the ZTS cloud domain hostnames, so hosts using an RSA SSH host key could not get the hostname, the operator-configured SshPrincipals or the provider's additional principals into their host certificate the way the ECDSA SSHCertRequest path can. Add GenerateSSHHostCSRWithXPrincipals, which keeps the cloud domain hostnames in the principals field (used to derive the key id) and puts the full requested set - hostname, SshPrincipals, provider principals and the private ip - in the new xprincipals field that ZTS validates against in InstanceCertManager.validPrincipals. Hoist the additional-provider- principals lookup in generateSshRequest out of the ECDSA branch so both key types share it, and switch the RSA branch to the new function. GenerateSSHHostCSR is left in place for existing callers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Henry Avetisyan <hga@yahooinc.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the Go SIA (Service Identity Agent) SSH host certificate CSR generation so the RSA CSR path can request the full intended set of SSH principals (hostname, configured SshPrincipals, provider additional principals, and private IP) using a new xprincipals field, aligning it with the richer ECDSA SSHCertRequest behavior and ZTS validation expectations.
Changes:
- Add
XPrincipalssupport to the CSR JSON model and introduceGenerateSSHHostCSRWithXPrincipalsfor RSA SSH host key CSRs. - Refactor
generateSshRequestso provider additional principals are applied for both RSA and ECDSA branches. - Add/extend unit tests covering both the legacy CSR behavior and the new x-principals CSR behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| libs/go/sia/util/util.go | Adds xprincipals to SSH CSR model and implements new RSA CSR generator with XPrincipals payload. |
| libs/go/sia/util/util_test.go | Adds unit tests validating legacy CSR output and new x-principals CSR output. |
| libs/go/sia/agent/agent.go | Refactors SSH request generation to include provider principals for RSA and switches RSA to new CSR generator. |
| libs/go/sia/agent/agent_test.go | Extends tests to validate RSA CSR includes principals vs x-principals as expected. |
Suppressed comments (1)
libs/go/sia/util/util.go:443
sshPrincipalsis split on commas without trimming/empty filtering. Values like"host1, host2"or a trailing comma will generate principals with leading spaces / empty strings, which can cause ZTS principal validation failures. Trim whitespace and skip empty entries when populatingxprincipals.
if sshPrincipals != "" {
xprincipals = append(xprincipals, strings.Split(sshPrincipals, ",")...)
}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Requesting x-principals changes what ZTS validates - the field is what InstanceCertManager.validPrincipals checks the hostname against, and an absent field means pass-through - so make the new behavior opt-in via Options.SshIncludePrincipals rather than changing every rsa host key deployment at once. When the flag is not set the rsa path keeps calling GenerateSSHHostCSR and produces exactly the payload it did before. Mark xprincipals as omitempty/optional so the legacy CSR stays byte-identical and so an empty list is never sent, since the server validates against an empty list instead of skipping the check. Drop the unused instanceId argument from GenerateSSHHostCSRWithXPrincipals - SSHKeyReq has no instance id field to carry it. Tests cover both sides of the flag in generateSshRequest, and pin the presence and absence of the xprincipals field in the marshalled CSR, including the no-cloud-domains case where it must be omitted rather than emitted as an empty list. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Henry Avetisyan <hga@yahooinc.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Suppressed comments (4)
libs/go/sia/util/util_test.go:1505
- This comment says an empty
xprincipalslist would trigger hostname validation. In ZTSvalidateSSHHostnames, an empty list leads tocnames.isEmpty()and returnstrue(no validation performed). Consider rewording to explain the omission here is simply because there are no values to send and the field isomitempty.
// finally with no optional arguments and no zts cloud domains there are no
// x-principals to report, and the field must be omitted rather than sent as
// an empty list which the server would validate the hostname against
libs/go/sia/agent/agent.go:568
GetAdditionalSshHostPrincipalsis now called unconditionally whenever SSH is enabled, even for the RSA legacy CSR path where the value is unused (SshIncludePrincipalsis false). Since provider implementations may hit instance metadata, this introduces unnecessary network/latency and work on the common RSA legacy path.
sshPrincipals := opts.SshPrincipals
// additional ssh host principals are added on best effort basis, hence error below is ignored.
additionalSshHostPrincipals, _ := opts.Provider.GetAdditionalSshHostPrincipals(opts.MetaEndPoint)
if additionalSshHostPrincipals != "" {
if sshPrincipals != "" {
libs/go/sia/config/config.go:218
SshIncludePrincipalswas added to the runtimeOptionsstruct, but it is not present in the JSONConfigstruct and is not populated from env/config in the options loaders. As a result the feature cannot be enabled by users in production (it will always remain the defaultfalseunless set programmatically/tests).
HostnameSuffix string //hostname suffix in case we need to auto-generate hostname
SshPrincipals string //ssh additional principals
SshIncludePrincipals bool //optional flag to include additional principals in host rsa key certs
libs/go/sia/util/util_test.go:1421
- These test comments claim that an empty
xprincipalslist is treated differently from an absent field and that ZTS would validate the hostname against an empty list. In ZTSInstanceCertManager.validPrincipals/validateSSHHostnames, an empty list results in no hostname/cname validation and returnstrue; the only special case is whenxPrincipalsisnull(missing field), which short-circuits validation and logs an error. Updating the comments will avoid misleading future readers.
This issue also appears on line 1503 of the same file.
// the legacy csr only includes the zts cloud domain based principals and
// must not carry an x-principals field at all - an empty x-principals list
// is not the same as an absent one since the server only skips hostname
// validation when the field is not present
The certsign-based CSR path (SshHostKeyType=rsa) only ever requested the ZTS cloud domain hostnames, so hosts using an RSA SSH host key could not get the hostname, the operator-configured SshPrincipals or the provider's additional principals into their host certificate the way the ECDSA SSHCertRequest path can.
Add GenerateSSHHostCSRWithXPrincipals, which keeps the cloud domain hostnames in the principals field (used to derive the key id) and puts the full requested set - hostname, SshPrincipals, provider principals and the private ip - in the new xprincipals field that ZTS validates against in InstanceCertManager.validPrincipals. Hoist the additional-provider- principals lookup in generateSshRequest out of the ECDSA branch so both key types share it, and switch the RSA branch to the new function.
GenerateSSHHostCSR is left in place for existing callers.
Description
Contribution Checklist:
Attach Screenshots (Optional)