From 3c9dd27d75cd8edb7868f7ae1d79608de7ecbd4d Mon Sep 17 00:00:00 2001 From: Michal Nicpon <39177923+michalnicp@users.noreply.github.com> Date: Tue, 13 Dec 2022 16:24:50 +0100 Subject: [PATCH] add fleectl generate mdm-apple-bm command (#8979) --- changes/8724-generate-mdm-apple-bm | 1 + cmd/fleetctl/apple_mdm.go | 169 ++++++++++------------------- 2 files changed, 59 insertions(+), 111 deletions(-) create mode 100644 changes/8724-generate-mdm-apple-bm diff --git a/changes/8724-generate-mdm-apple-bm b/changes/8724-generate-mdm-apple-bm new file mode 100644 index 000000000..3a4369890 --- /dev/null +++ b/changes/8724-generate-mdm-apple-bm @@ -0,0 +1 @@ +- Add fleetctl command to generate DEP public and private keys diff --git a/cmd/fleetctl/apple_mdm.go b/cmd/fleetctl/apple_mdm.go index ac00059bf..cb3760149 100644 --- a/cmd/fleetctl/apple_mdm.go +++ b/cmd/fleetctl/apple_mdm.go @@ -24,9 +24,11 @@ import ( ) const ( - apnsKeyPath = "fleet-mdm-apple-apns.key" - scepCACertPath = "fleet-mdm-apple-scep.crt" - scepCAKeyPath = "fleet-mdm-apple-scep.key" + apnsKeyPath = "fleet-mdm-apple-apns.key" + scepCACertPath = "fleet-mdm-apple-scep.crt" + scepCAKeyPath = "fleet-mdm-apple-scep.key" + bmPublicKeyCertPath = "fleet-apple-mdm-bm-public-key.crt" + bmPrivateKeyPath = "fleet-apple-mdm-bm-private.key" ) func appleMDMCommand() *cli.Command { @@ -41,7 +43,6 @@ func appleMDMCommand() *cli.Command { debugFlag(), }, Subcommands: []*cli.Command{ - appleMDMSetupCommand(), appleMDMEnrollmentProfilesCommand(), appleMDMEnqueueCommandCommand(), appleMDMDEPCommand(), @@ -64,6 +65,7 @@ func generateCommand() *cli.Command { }, Subcommands: []*cli.Command{ generateMDMAppleCommand(), + generateMDMAppleBMCommand(), }, } } @@ -111,6 +113,7 @@ func generateMDMAppleCommand() *cli.Command { c.App.Writer, `Sending certificate signing request (CSR) for Apple Push Notification service (APNs) to %s... Generating APNs key, Simple Certificate Enrollment Protocol (SCEP) certificate, and SCEP key... + `, email, ) @@ -178,126 +181,70 @@ Next, use the generated certificates to deploy Fleet with `+"`mdm`"+` configurat } } -func appleMDMSetupCommand() *cli.Command { +func generateMDMAppleBMCommand() *cli.Command { return &cli.Command{ - Name: "setup", - Usage: "Setup commands for Apple MDM", - Subcommands: []*cli.Command{ - appleMDMSetupDEPCommand(), - }, - } -} - -func appleMDMSetupDEPCommand() *cli.Command { - return &cli.Command{ - Name: "dep", - Usage: "Configure DEP token", - Subcommands: []*cli.Command{ - appleMDMSetDEPTokenInitCommand(), - appleMDMSetDEPTokenFinalizeCommand(), - }, - } -} - -func appleMDMSetDEPTokenInitCommand() *cli.Command { - return &cli.Command{ - Name: "init", - Usage: "Start DEP token configuration", + Name: "mdm-apple-bm", + Aliases: []string{"mdm_apple_bm"}, + Usage: "Generate Apple Business Manager public and private keys to enable automatic enrollment for macOS hosts.", Flags: []cli.Flag{ - configFlag(), - contextFlag(), + &cli.StringFlag{ + Name: "public-key", + Usage: "The output path for the Apple Business Manager public key certificate.", + Value: bmPublicKeyCertPath, + }, + &cli.StringFlag{ + Name: "private-key", + Usage: "The output path for the Apple Business Manager private key.", + Value: bmPrivateKeyPath, + }, }, Action: func(c *cli.Context) error { - const ( - cn = "fleet" - // Setting validityDays to 10 in case user doing the init command - // is different than user uploading to Apple. - // (Though we've heard from other users that Apple doesn't really check - // the expiration of this public key.) - validityDays = 10 - pemCertPath = "fleet-mdm-apple-dep.crt" - pemKeyPath = "fleet-mdm-apple-dep.key" + publicKeyPath := c.String("public-key") + privateKeyPath := c.String("private-key") + + const cn = "FleetDM" + + // Apple doesn't check the expiry of the public key certificate. It does not matter what value we use. + const validityDays = 30 + + fmt.Fprintf( + c.App.Writer, + `Generating Apple Business Manager public key and private key... + +`, ) + key, cert, err := tokenpki.SelfSignedRSAKeypair(cn, validityDays) if err != nil { return fmt.Errorf("generate encryption keypair: %w", err) } - pemCert := tokenpki.PEMCertificate(cert.Raw) - pemKey := tokenpki.PEMRSAPrivateKey(key) - if err := os.WriteFile(pemCertPath, pemCert, defaultFileMode); err != nil { - return fmt.Errorf("write certificate: %w", err) + + publicKeyPEM := tokenpki.PEMCertificate(cert.Raw) + if err := os.WriteFile(publicKeyPath, publicKeyPEM, defaultFileMode); err != nil { + return fmt.Errorf("write public key: %w", err) } - if err := os.WriteFile(pemKeyPath, pemKey, defaultFileMode); err != nil { + + privateKeyPEM := tokenpki.PEMRSAPrivateKey(key) + if err := os.WriteFile(privateKeyPath, privateKeyPEM, defaultFileMode); err != nil { return fmt.Errorf("write private key: %w", err) } - fmt.Printf("Successfully generated DEP public and private key: %s, %s\n", pemCertPath, pemKeyPath) - fmt.Printf("Upload %s to your Apple Business MDM server. (Don't forget to click \"Save\" after uploading it.)", pemCertPath) - return nil - }, - } -} -func appleMDMSetDEPTokenFinalizeCommand() *cli.Command { - var ( - pemCertPath string - pemKeyPath string - encryptedTokenPath string - ) - return &cli.Command{ - Name: "finalize", - Usage: "Finalize DEP token configuration for an automatic enrollment", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "certificate", - Usage: "Path to the certificate generated in the init step", - Destination: &pemCertPath, - Required: true, - }, - &cli.StringFlag{ - Name: "private-key", - Usage: "Path to the private key file generated in the init step", - Destination: &pemKeyPath, - Required: true, - }, - &cli.StringFlag{ - Name: "encrypted-token", - Usage: "Path to the encrypted token file downloaded from Apple Business (*.p7m)", - Destination: &encryptedTokenPath, - Required: true, - }, - }, - Action: func(c *cli.Context) error { - pemCert, err := os.ReadFile(pemCertPath) - if err != nil { - return fmt.Errorf("read certificate: %w", err) - } - depCert, err := tokenpki.CertificateFromPEM(pemCert) - if err != nil { - return fmt.Errorf("parse certificate: %w", err) - } - pemKey, err := os.ReadFile(pemKeyPath) - if err != nil { - return fmt.Errorf("read private key: %w", err) - } - depKey, err := tokenpki.RSAKeyFromPEM(pemKey) - if err != nil { - return fmt.Errorf("parse private key: %w", err) - } - encryptedToken, err := os.ReadFile(encryptedTokenPath) - if err != nil { - return fmt.Errorf("read encrypted token: %w", err) - } - token, err := tokenpki.DecryptTokenJSON(encryptedToken, depCert, depKey) - if err != nil { - return fmt.Errorf("decrypt token: %w", err) - } - //nolint:gosec // G101: no credentials, just the file name. - tokenPath := "fleet-mdm-apple-dep.token" - if err := os.WriteFile(tokenPath, token, defaultFileMode); err != nil { - return fmt.Errorf("write token file: %w", err) - } - fmt.Printf("Successfully generated token file: %s.\n", tokenPath) - fmt.Printf("Set FLEET_MDM_APPLE_DEP_TOKEN=$(cat %s) when running Fleet.\n", tokenPath) + fmt.Fprintf( + c.App.Writer, + `Success! + +Generated your public key at %s + +Generated your private key at %s + +Visit https://business.apple.com/ and create a new MDM server with the public key. Then, download the new MDM server's token. + +Next, deploy Fleet with with `+"`mdm`"+` configuration: https://fleetdm.com/docs/deploying/configuration#mdm-mobile-device-management-in-progress +`, + publicKeyPath, + privateKeyPath, + ) + return nil }, }