diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MacOSMdmPage/components/content/ApplePushCertSetup.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MacOSMdmPage/components/content/ApplePushCertSetup.tsx index 8eb0ea88d5..5cf157b47b 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MacOSMdmPage/components/content/ApplePushCertSetup.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MacOSMdmPage/components/content/ApplePushCertSetup.tsx @@ -7,6 +7,7 @@ import mdmAppleApi from "services/entities/mdm_apple"; import CustomLink from "components/CustomLink"; import FileUploader from "components/FileUploader"; import DownloadCSR from "../../../../../../components/DownloadFileButtons/DownloadCSR"; +import { ms } from "date-fns/locale"; interface IApplePushCertSetupProps { baseClass: string; @@ -32,7 +33,10 @@ const ApplePushCertSetup = ({ onSetupSuccess(); } catch (e) { const msg = getErrorReason(e); - if (msg.toLowerCase().includes("invalid certificate")) { + if ( + msg.toLowerCase().includes("invalid certificate") || + msg.toLowerCase().includes("required private key") + ) { renderFlash("error", msg); } else { renderFlash("error", "Couldn’t connect. Please try again."); @@ -46,7 +50,10 @@ const ApplePushCertSetup = ({ const onDownloadError = useCallback( (e: unknown) => { const msg = getErrorReason(e); - if (msg.toLowerCase().includes("email address")) { + if ( + msg.toLowerCase().includes("email address") || + msg.toLowerCase().includes("required private key") + ) { renderFlash("error", msg); } else { renderFlash("error", "Something’s gone wrong. Please try again."); diff --git a/server/service/apple_mdm.go b/server/service/apple_mdm.go index 21a3e9227b..0de70d9f3a 100644 --- a/server/service/apple_mdm.go +++ b/server/service/apple_mdm.go @@ -3578,6 +3578,16 @@ func (svc *Service) GenerateABMKeyPair(ctx context.Context) (*fleet.MDMAppleDEPK if err := svc.authz.Authorize(ctx, &fleet.AppleBM{}, fleet.ActionWrite); err != nil { return nil, err } + + privateKey := svc.config.Server.PrivateKey + if testSetEmptyPrivateKey { + privateKey = "" + } + + if len(privateKey) == 0 { + return nil, ctxerr.New(ctx, "Couldn't download public key. Missing required private key. Learn how to configure the private key here: https://fleetdm.com/learn-more-about/fleet-server-private-key") + } + var publicKeyPEM, privateKeyPEM []byte assets, err := svc.ds.GetAllMDMConfigAssetsByName(ctx, []fleet.MDMAssetName{ fleet.MDMAssetABMCert, diff --git a/server/service/integration_mdm_test.go b/server/service/integration_mdm_test.go index 126b5160fa..294e0f37cf 100644 --- a/server/service/integration_mdm_test.go +++ b/server/service/integration_mdm_test.go @@ -929,6 +929,14 @@ func (s *integrationMDMTestSuite) TestGetMDMCSR() { t := s.T() ctx := context.Background() + // Validate errors if no private key is set + testSetEmptyPrivateKey = true + s.uploadAPNSCert([]byte("-----BEGIN CERTIFICATE-----\nZm9vCg==\n-----END CERTIFICATE-----"), http.StatusInternalServerError, "Couldn't upload APNs certificate. Missing required private key. Learn how to configure the private key here: https://fleetdm.com/learn-more-about/fleet-server-private-key") + + r := s.Do("GET", "/api/latest/fleet/mdm/apple/request_csr", getMDMAppleCSRRequest{}, http.StatusInternalServerError) + require.Contains(t, extractServerErrorText(r.Body), "Couldn't download signed CSR. Missing required private key. Learn how to configure the private key here: https://fleetdm.com/learn-more-about/fleet-server-private-key") + testSetEmptyPrivateKey = false + // ensure we leave everything in a clean state for other tests t.Cleanup(s.appleCoreCertsSetup) @@ -8667,6 +8675,13 @@ func (s *integrationMDMTestSuite) TestABMAssetManagement() { // ensure enable ABM again for other tests t.Cleanup(s.enableABM) + // Validate error when server private key not set + testSetEmptyPrivateKey = true + + r := s.Do("GET", "/api/latest/fleet/mdm/apple/abm_public_key", generateABMKeyPairResponse{}, http.StatusInternalServerError) + require.Contains(t, extractServerErrorText(r.Body), "Couldn't download public key. Missing required private key. Learn how to configure the private key here: https://fleetdm.com/learn-more-about/fleet-server-private-key") + testSetEmptyPrivateKey = false + // grab the current public key var abmResp generateABMKeyPairResponse s.DoJSON("GET", "/api/latest/fleet/mdm/apple/abm_public_key", nil, http.StatusOK, &abmResp) diff --git a/server/service/mdm.go b/server/service/mdm.go index b631f6818d..069a5ecf1d 100644 --- a/server/service/mdm.go +++ b/server/service/mdm.go @@ -2120,6 +2120,9 @@ func (svc *Service) ResendHostMDMProfile(ctx context.Context, hostID uint, profi // GET /mdm/apple/request_csr //////////////////////////////////////////////////////////////////////////////// +// Used for overriding the env var value in testing +var testSetEmptyPrivateKey bool + type getMDMAppleCSRRequest struct{} type getMDMAppleCSRResponse struct { @@ -2143,8 +2146,13 @@ func (svc *Service) GetMDMAppleCSR(ctx context.Context) ([]byte, error) { return nil, err } - if len(svc.config.Server.PrivateKey) == 0 { - return nil, ctxerr.New(ctx, "no private key configured") + privateKey := svc.config.Server.PrivateKey + if testSetEmptyPrivateKey { + privateKey = "" + } + + if len(privateKey) == 0 { + return nil, ctxerr.New(ctx, "Couldn't download signed CSR. Missing required private key. Learn how to configure the private key here: https://fleetdm.com/learn-more-about/fleet-server-private-key") } vc, ok := viewer.FromContext(ctx) @@ -2298,6 +2306,15 @@ func (svc *Service) UploadMDMAppleAPNSCert(ctx context.Context, cert io.ReadSeek return err } + privateKey := svc.config.Server.PrivateKey + if testSetEmptyPrivateKey { + privateKey = "" + } + + if len(privateKey) == 0 { + return ctxerr.New(ctx, "Couldn't upload APNs certificate. Missing required private key. Learn how to configure the private key here: https://fleetdm.com/learn-more-about/fleet-server-private-key") + } + if cert == nil { return ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("certificate", "Invalid certificate. Please provide a valid certificate from Apple Push Certificate Portal.")) }