From 97433a5de6149126122c6c4653afcf4c44983f5e Mon Sep 17 00:00:00 2001 From: Jordan Montgomery Date: Thu, 19 Mar 2026 15:37:22 -0400 Subject: [PATCH] Update PEM header type per hydrant spec (#42052) **Related issue:** Resolves #40910 # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. - [x] 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. - [x] If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes ## Testing - [x] Added/updated automated tests - [x] Where appropriate, [automated tests simulate multiple hosts and test for host isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing) (updates to one hosts's records do not affect another) - [x] QA'd all new/changed functionality manually --- changes/40910-correct-request-certificate-pem | 1 + ee/server/service/request_certificate.go | 7 ++++--- ee/server/service/request_certificate_test.go | 10 +++++----- 3 files changed, 10 insertions(+), 8 deletions(-) create mode 100644 changes/40910-correct-request-certificate-pem diff --git a/changes/40910-correct-request-certificate-pem b/changes/40910-correct-request-certificate-pem new file mode 100644 index 0000000000..73c3e8965d --- /dev/null +++ b/changes/40910-correct-request-certificate-pem @@ -0,0 +1 @@ +* Updated the Request Certificate API to return the proper PEM header for PKCS #7 certificates returned by EST CAs diff --git a/ee/server/service/request_certificate.go b/ee/server/service/request_certificate.go index 308d629ea2..14129a3da4 100644 --- a/ee/server/service/request_certificate.go +++ b/ee/server/service/request_certificate.go @@ -16,7 +16,6 @@ import ( "github.com/fleetdm/fleet/v4/server/contexts/authz" "github.com/fleetdm/fleet/v4/server/contexts/ctxerr" "github.com/fleetdm/fleet/v4/server/fleet" - "github.com/fleetdm/fleet/v4/server/ptr" ) // This code largely adapted from fleet/website/api/controllers/get-est-device-certificate.js @@ -135,8 +134,10 @@ func (svc *Service) RequestCertificate(ctx context.Context, p fleet.RequestCerti return nil, &fleet.BadRequestError{Message: fmt.Sprintf("EST certificate request failed: %s", err.Error())} } svc.logger.InfoContext(ctx, "Successfully retrieved a certificate from EST", "ca_id", ca.ID, "idp_username", idpUsername) - // Wrap the certificate in a PEM block for easier consumption by the client - return ptr.String("-----BEGIN CERTIFICATE-----\n" + string(certificate.Certificate) + "\n-----END CERTIFICATE-----\n"), nil + // Wrap the certificate in a PEM block for easier consumption by the client. TODO: If we ever + // support CAs other than Hydrant/EST in this API, this may need to be modified to be aware of + // their formats. + return new("-----BEGIN PKCS7-----\n" + string(certificate.Certificate) + "\n-----END PKCS7-----\n"), nil } func (svc *Service) introspectIDPToken(ctx context.Context, idpClientID, idpToken, idpOauthURL string) (*oauthIntrospectionResponse, error) { diff --git a/ee/server/service/request_certificate_test.go b/ee/server/service/request_certificate_test.go index db972ba1ff..3004e0df55 100644 --- a/ee/server/service/request_certificate_test.go +++ b/ee/server/service/request_certificate_test.go @@ -191,7 +191,7 @@ func TestRequestCertificate(t *testing.T) { }) require.NoError(t, err) require.NotNil(t, cert) - require.Equal(t, "-----BEGIN CERTIFICATE-----\n"+hydrantSimpleEnrollResponse+"\n-----END CERTIFICATE-----\n", *cert) + require.Equal(t, "-----BEGIN PKCS7-----\n"+hydrantSimpleEnrollResponse+"\n-----END PKCS7-----\n", *cert) }) t.Run("Request a certificate - Happy path, no IDP", func(t *testing.T) { @@ -203,7 +203,7 @@ func TestRequestCertificate(t *testing.T) { }) require.NoError(t, err) require.NotNil(t, cert) - require.Equal(t, "-----BEGIN CERTIFICATE-----\n"+hydrantSimpleEnrollResponse+"\n-----END CERTIFICATE-----\n", *cert) + require.Equal(t, "-----BEGIN PKCS7-----\n"+hydrantSimpleEnrollResponse+"\n-----END PKCS7-----\n", *cert) }) t.Run("Request a certificate - Happy path, no IDP, http sig auth", func(t *testing.T) { @@ -223,7 +223,7 @@ func TestRequestCertificate(t *testing.T) { }) require.NoError(t, err) require.NotNil(t, cert) - require.Equal(t, "-----BEGIN CERTIFICATE-----\n"+hydrantSimpleEnrollResponse+"\n-----END CERTIFICATE-----\n", *cert) + require.Equal(t, "-----BEGIN PKCS7-----\n"+hydrantSimpleEnrollResponse+"\n-----END PKCS7-----\n", *cert) }) t.Run("Request a certificate - Happy path, no IDP, UPN does not match IDP info(should pass)", func(t *testing.T) { @@ -235,7 +235,7 @@ func TestRequestCertificate(t *testing.T) { }) require.NoError(t, err) require.NotNil(t, cert) - require.Equal(t, "-----BEGIN CERTIFICATE-----\n"+hydrantSimpleEnrollResponse+"\n-----END CERTIFICATE-----\n", *cert) + require.Equal(t, "-----BEGIN PKCS7-----\n"+hydrantSimpleEnrollResponse+"\n-----END PKCS7-----\n", *cert) }) t.Run("Request a certificate - CA returns error", func(t *testing.T) { @@ -310,7 +310,7 @@ func TestRequestCertificate(t *testing.T) { }) require.NoError(t, err) require.NotNil(t, cert) - require.Equal(t, "-----BEGIN CERTIFICATE-----\n"+hydrantSimpleEnrollResponse+"\n-----END CERTIFICATE-----\n", *cert) + require.Equal(t, "-----BEGIN PKCS7-----\n"+hydrantSimpleEnrollResponse+"\n-----END PKCS7-----\n", *cert) }) t.Run("Request certificate - non-Hydrant and non-EST CA", func(t *testing.T) {