Website: Add API to send signed CSR emails (#8408)
This pull request relies on the `mdm-gen-cert` command from https://github.com/fleetdm/fleet/pull/8884. Closes: https://github.com/fleetdm/fleet/issues/8223 Changes: - Updated the deploy Fleet website workflow to: - Add Go as a dependency - Build the mdm-gen-cert binary in `/website/.tools/` - add the `/.tools/` folder to the Heroku app - Added `deliver-apple-csr.js` - an API that: - can be called by making a `POST` request to `/api/v1/deliver-apple-csr` - accepts `csr` as an input - runs the `mdm-gen-cert` command with the `csr` set as an environment variable - returns an `invalidEmailDomain` response if the user's email domain is in the array of banned email domains. - saves the users organization and email address to the website's database - Sends an email to the requesting user's email address with the signed CSR attached as a text file named `apple-apns-request.txt` - Posts a message to a channel in the Fleet Slack. - Added a new model: `CertificateSigningRequests` that contains two required attributes: `emailAddress` and `organization` - Added a new email template `email-signed-csr-for-apns` - Updated routes, policies, eslintrc, and rebuilt cloud-sdk Before this can be merged, we will need to: - [x] Add new config variables in Heroku - [x] `sails.config.custom.mdmVendorCertPem` - [x] `sails.config.custom.mdmVendorKeyPem` - [x] `sails.config.custom.mdmVendorKeyPassphrase` - [x] `sails.config.custom.slackWebhookUrlForMDMSignups` - [x] Add the `CertificateSigningRequests` model to the website's database
This commit is contained in:
@@ -51,6 +51,13 @@ jobs:
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
|
||||
# Install the right version of Go for the Golang child process that we are currently using for CSR signing
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
|
||||
# Download top-level dependencies and build Storybook in the website's assets/ folder
|
||||
- run: npm install && npm run build-storybook -- -o ./website/assets/storybook --loglevel verbose
|
||||
|
||||
@@ -79,10 +86,14 @@ jobs:
|
||||
# Compile browser assets & markdown content into generated collateral
|
||||
- run: cd website/ && BUILD_SCRIPT_ARGS="--githubAccessToken=${{ secrets.GITHUB_TOKEN }}" npm run build-for-prod
|
||||
|
||||
# Build the go binary we use to sign APNS certificates in the website/.tools/ folder.
|
||||
- run: cd ee/tools/mdm/ && GOOS=linux GOARCH=amd64 go build -o ../../../website/.tools/mdm-gen-cert .
|
||||
|
||||
# Commit newly-generated collateral locally so we can push them to Heroku below.
|
||||
# (This commit will never be pushed to GitHub- only to Heroku.)
|
||||
# > The local config flags make this work in GitHub's environment.
|
||||
- run: git add website/.www
|
||||
- run: git add website/.tools
|
||||
- run: git add -f website/views/partials/built-from-markdown > /dev/null 2>&1 || echo '* * * WARNING - Silently ignoring the fact that there are no HTML partials generated from markdown to include in automated commit...'
|
||||
- run: git -c "user.name=Fleetwood" -c "user.email=github@example.com" commit -am 'AUTOMATED COMMIT - Deployed the latest, including generated collateral such as compiled documentation, modified HTML layouts, and a .sailsrc file that references minified client-side code assets.'
|
||||
|
||||
|
||||
Vendored
+1
@@ -47,6 +47,7 @@
|
||||
"Subscription": true,
|
||||
"NewsletterSubscription": true,
|
||||
"VantaConnection": true,
|
||||
"CertificateSigningRequest": true,
|
||||
|
||||
// …and any others.
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
@@ -106,6 +106,10 @@ module.exports = {
|
||||
nextBillingAt: Date.now() + (1000 * 60 * 60 * 24 * 7),
|
||||
};
|
||||
break;
|
||||
case 'email-signed-csr-for-apns':
|
||||
layout = 'layout-email';
|
||||
fakeData = {};
|
||||
break;
|
||||
default:
|
||||
layout = 'layout-email-newsletter';
|
||||
fakeData = {
|
||||
|
||||
+2
-2
@@ -39,13 +39,13 @@ module.exports = {
|
||||
markdownEmailPaths = markdownEmailPaths.map((templatePath)=>{
|
||||
let relativePath = path.relative(path.join(sails.config.paths.views, 'emails/'), templatePath);
|
||||
let extension = path.extname(relativePath);
|
||||
return _.trimRight(relativePath, extension);
|
||||
return relativePath.split(extension)[0];
|
||||
});
|
||||
|
||||
templatePaths = templatePaths.map((templatePath)=>{
|
||||
let relativePath = path.relative(path.join(sails.config.paths.views, 'emails/'), templatePath);
|
||||
let extension = path.extname(relativePath);
|
||||
return _.trimRight(relativePath, extension);
|
||||
return relativePath.split(extension)[0];
|
||||
});
|
||||
|
||||
// Respond with view.
|
||||
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
module.exports = {
|
||||
|
||||
|
||||
friendlyName: 'Deliver Apple CSR',
|
||||
|
||||
|
||||
description: 'Generate and deliver a signed certificate signing request to a requesting user\'s email address.',
|
||||
|
||||
extendedDescription: 'Uses the mdm-gen-cert binary to generate a signed CSR for the user and sends the result to the requesting user\'s email address',
|
||||
|
||||
|
||||
inputs: {
|
||||
unsignedCsrData: {
|
||||
required: true,
|
||||
type: 'string',
|
||||
description: 'Base64 encoded CSR submitted from the Fleet server or `fleetctl` on behalf of the user.'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
exits: {
|
||||
|
||||
success: {
|
||||
description: 'Delivered email to specified email address with certificate signing request attached.'
|
||||
},
|
||||
|
||||
invalidEmailDomain: {
|
||||
description: 'This email address is on a denylist of domains and was not delivered.'
|
||||
},
|
||||
|
||||
badRequest: {
|
||||
responseType: 'badRequest'
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
fn: async function({unsignedCsrData}) {
|
||||
let path = require('path');
|
||||
|
||||
let signingToolExists = await sails.helpers.fs.exists(path.resolve(sails.config.appPath, '.tools/mdm-gen-cert'));
|
||||
|
||||
if(!signingToolExists) {
|
||||
throw new Error('Could not generate signed CSR: The mdm-gen-cert binary is missing.');
|
||||
}
|
||||
|
||||
// Throw an error if we're missing any config variables.
|
||||
if(!sails.config.custom.mdmVendorCertPem) {
|
||||
throw new Error('Could not generate signed CSR: The vendor certificate PEM (sails.config.custom.mdmVendorCertPem) is missing.');
|
||||
}
|
||||
|
||||
if(!sails.config.custom.mdmVendorKeyPem) {
|
||||
throw new Error('Could not generate signed CSR: The vendor key PEM (sails.config.custom.mdmVendorKeyPem) is missing.');
|
||||
}
|
||||
|
||||
if(!sails.config.custom.mdmVendorKeyPassphrase) {
|
||||
throw new Error('Could not generate signed CSR: The vendor key passphrase (sails.config.custom.mdmVendorKeyPassphrase) is missing.');
|
||||
}
|
||||
|
||||
const bannedEmailDomainsForCSRSigning = [
|
||||
'gmail.com','yahoo.com','hotmail.com','aol.com','hotmail.co.uk','hotmail.fr','msn.com',
|
||||
'yahoo.fr','wanadoo.fr','orange.fr','comcast.net','yahoo.co.uk','yahoo.com.br','yahoo.co.in',
|
||||
'live.com','rediffmail.com','free.fr','gmx.de','web.de','yandex.ru','ymail.com','libero.it',
|
||||
'outlook.com','uol.com.br','bol.com.br','mail.ru','cox.net','hotmail.it','sbcglobal.net',
|
||||
'sfr.fr','live.fr','verizon.net','live.co.uk','googlemail.com','yahoo.es','ig.com.br','live.nl',
|
||||
'bigpond.com','terra.com.br','yahoo.it','neuf.fr','yahoo.de','alice.it','rocketmail.com',
|
||||
'att.net','laposte.net','facebook.com','bellsouth.net','yahoo.in','hotmail.es','charter.net',
|
||||
'yahoo.ca','yahoo.com.au','rambler.ru','hotmail.de','tiscali.it','shaw.ca','yahoo.co.jp',
|
||||
'sky.com','earthlink.net','optonline.net','freenet.de','t-online.de','aliceadsl.fr','virgilio.it',
|
||||
'home.nl','qq.com','telenet.be','me.com','yahoo.com.ar','tiscali.co.uk','yahoo.com.mx','voila.fr',
|
||||
'gmx.net','mail.com','planet.nl','tin.it','live.it','ntlworld.com','arcor.de','yahoo.co.id',
|
||||
'frontiernet.net','hetnet.nl','live.com.au','yahoo.com.sg','zonnet.nl','club-internet.fr',
|
||||
'juno.com','optusnet.com.au','blueyonder.co.uk','bluewin.ch','skynet.be','sympatico.ca',
|
||||
'windstream.net','mac.com','centurytel.net','chello.nl','live.ca','aim.com','bigpond.net.au',
|
||||
'icloud.com','protonmail.com','zoho.com','proton.me','pm.me','protonmail.ch','tmmbt.net',
|
||||
];
|
||||
|
||||
// Use sails.helpers.process.executeCommand to run the mdm-gen-cert binary.
|
||||
let generateCertificateCommand = await sails.helpers.process.executeCommand.with({
|
||||
command: `./.tools/mdm-gen-cert`,
|
||||
dir: sails.config.appPath,
|
||||
timeout: 10000,
|
||||
environmentVars: {
|
||||
VENDOR_CERT_PEM: sails.config.custom.mdmVendorCertPem,
|
||||
VENDOR_KEY_PEM: sails.config.custom.mdmVendorKeyPem,
|
||||
VENDOR_KEY_PASSPHRASE: sails.config.custom.mdmVendorKeyPassphrase,
|
||||
CSR_BASE64: unsignedCsrData
|
||||
},
|
||||
}).intercept((err)=>{
|
||||
return new Error(`When trying to generate a signed CSR for a user, an error occured while running the mdm-gen-cert command. Full error: ${err}`);
|
||||
});
|
||||
|
||||
// Parse the JSON result from the mdm-gen-cert command
|
||||
let generateCertificateResult = JSON.parse(generateCertificateCommand.stdout);
|
||||
// Throw an error if the result from the mdm-gen-cert command is missing an email value.
|
||||
if(!generateCertificateResult.email) {
|
||||
throw new Error('When trying to generate a signed CSR for a user, the result from the mdm-gen-cert command did not contain a email.');
|
||||
}
|
||||
// Throw an error if the result from the mdm-gen-cert command is missing an org value.
|
||||
if(!generateCertificateResult.org) {
|
||||
throw new Error('When trying to generate a signed CSR for a user, the result from the mdm-gen-cert command did not contain an organization name');
|
||||
}
|
||||
// Throw an error if the result from the mdm-gen-cert command is missing an request value.
|
||||
if(!generateCertificateResult.request) {
|
||||
throw new Error('When trying to generate a signed CSR for a user, the result from the mdm-gen-cert command did not contain a certificate');
|
||||
}
|
||||
|
||||
// Check to make sure that the email included in the result is a valid email address.
|
||||
try {
|
||||
CertificateSigningRequest.validate('emailAddress', generateCertificateResult.email);
|
||||
} catch (err) {
|
||||
if (err.code === 'E_VIOLATES_RULES') {
|
||||
throw 'badRequest';
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the domain from the provided email
|
||||
let emailDomain = generateCertificateResult.email.split('@')[1];
|
||||
|
||||
// If the email domain is in the list of banned email domains list, we'll return the invalidEmailDomain response to the user.
|
||||
if(_.includes(bannedEmailDomainsForCSRSigning, emailDomain.toLowerCase())){
|
||||
throw 'invalidEmailDomain';
|
||||
}
|
||||
|
||||
// Create a new CertificateSigningRequest record in the database.
|
||||
await CertificateSigningRequest.create({
|
||||
emailAddress: generateCertificateResult.email,
|
||||
organization: generateCertificateResult.org,
|
||||
});
|
||||
|
||||
// Send an email to the user, with the result from the mdm-gen-cert command attached as a plain text file.
|
||||
await sails.helpers.sendTemplateEmail.with({
|
||||
to: generateCertificateResult.email,
|
||||
subject: 'Your certificate signing request from Fleet',
|
||||
from: sails.config.custom.fromEmailAddress,
|
||||
fromName: sails.config.custom.fromName,
|
||||
template: 'email-signed-csr-for-apns',
|
||||
templateData: {},
|
||||
attachments: [{
|
||||
contentBytes: generateCertificateResult.request,
|
||||
name: 'apple-apns-request.txt',
|
||||
type: 'text/plain',
|
||||
}],
|
||||
}).intercept((err)=>{
|
||||
return new Error(`When trying to send a signed CSR to a user (${generateCertificateResult.email}), an error occured. Full error: ${err}`);
|
||||
});
|
||||
|
||||
// Send a message to Slack.
|
||||
await sails.helpers.http.post(sails.config.custom.slackWebhookUrlForMDMSignups, {
|
||||
text: `An MDM CSR was generated for ${generateCertificateResult.org} - ${generateCertificateResult.email}`
|
||||
});
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
};
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* CertificateSigningRequest.js
|
||||
*
|
||||
* @description :: A model definition represents a database table/collection.
|
||||
* @docs :: https://sailsjs.com/docs/concepts/models-and-orm/models
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
|
||||
attributes: {
|
||||
|
||||
// ╔═╗╦═╗╦╔╦╗╦╔╦╗╦╦ ╦╔═╗╔═╗
|
||||
// ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗
|
||||
// ╩ ╩╚═╩╩ ╩╩ ╩ ╩ ╚╝ ╚═╝╚═╝
|
||||
|
||||
organization: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'The organization that requested a signed certificate',
|
||||
},
|
||||
|
||||
emailAddress: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'The email address used when the user requested a signed certificate',
|
||||
}
|
||||
|
||||
// ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗
|
||||
// ║╣ ║║║╠╩╗║╣ ║║╚═╗
|
||||
// ╚═╝╩ ╩╚═╝╚═╝═╩╝╚═╝
|
||||
|
||||
|
||||
// ╔═╗╔═╗╔═╗╔═╗╔═╗╦╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
|
||||
// ╠═╣╚═╗╚═╗║ ║║ ║╠═╣ ║ ║║ ║║║║╚═╗
|
||||
// ╩ ╩╚═╝╚═╝╚═╝╚═╝╩╩ ╩ ╩ ╩╚═╝╝╚╝╚═╝
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
Vendored
+2
-1
@@ -60,7 +60,8 @@
|
||||
"Quote": false,
|
||||
"Subscription": false,
|
||||
"NewsletterSubscription": false,
|
||||
"VantaConnection": true,
|
||||
"VantaConnection": false,
|
||||
"CertificateSigningRequest": false,
|
||||
// ...and any other backend globals (e.g. `"Organization": false`)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 521 B |
Vendored
+1
-1
@@ -13,7 +13,7 @@
|
||||
Cloud.setup({
|
||||
|
||||
/* eslint-disable */
|
||||
methods: {"downloadSitemap":{"verb":"GET","url":"/sitemap.xml","args":[]},"receiveUsageAnalytics":{"verb":"POST","url":"/api/v1/webhooks/receive-usage-analytics","args":["anonymousIdentifier","fleetVersion","licenseTier","numHostsEnrolled","numUsers","numTeams","numPolicies","numLabels","softwareInventoryEnabled","vulnDetectionEnabled","systemUsersEnabled","hostStatusWebhookEnabled","numWeeklyActiveUsers","numWeeklyPolicyViolationDaysActual","numWeeklyPolicyViolationDaysPossible","hostsEnrolledByOperatingSystem","hostsEnrolledByOrbitVersion","hostsEnrolledByOsqueryVersion","storedErrors","numHostsNotResponding","organization"]},"receiveFromGithub":{"verb":"GET","url":"/api/v1/webhooks/github","args":["botSignature","action","sender","repository","changes","issue","comment","pull_request","label"]},"deliverContactFormMessage":{"verb":"POST","url":"/api/v1/deliver-contact-form-message","args":["emailAddress","topic","firstName","lastName","message"]},"sendPasswordRecoveryEmail":{"verb":"POST","url":"/api/v1/entrance/send-password-recovery-email","args":["emailAddress"]},"signup":{"verb":"POST","url":"/api/v1/customers/signup","args":["emailAddress","password","organization","firstName","lastName","signupReason"]},"updateProfile":{"verb":"POST","url":"/api/v1/account/update-profile","args":["firstName","lastName","organization","emailAddress"]},"updatePassword":{"verb":"POST","url":"/api/v1/account/update-password","args":["oldPassword","newPassword"]},"updateBillingCard":{"verb":"POST","url":"/api/v1/account/update-billing-card","args":["stripeToken","billingCardLast4","billingCardBrand","billingCardExpMonth","billingCardExpYear"]},"login":{"verb":"POST","url":"/api/v1/customers/login","args":["emailAddress","password","rememberMe"]},"logout":{"verb":"GET","url":"/api/v1/account/logout","args":[]},"createQuote":{"verb":"POST","url":"/api/v1/customers/create-quote","args":["numberOfHosts"]},"saveBillingInfoAndSubscribe":{"verb":"POST","url":"/api/v1/customers/save-billing-info-and-subscribe","args":["quoteId","organization","firstName","lastName","paymentSource"]},"updatePasswordAndLogin":{"verb":"POST","url":"/api/v1/entrance/update-password-and-login","args":["password","token"]},"deliverDemoSignup":{"verb":"POST","url":"/api/v1/deliver-demo-signup","args":["emailAddress"]},"createOrUpdateOneNewsletterSubscription":{"verb":"POST","url":"/api/v1/create-or-update-one-newsletter-subscription","args":["emailAddress","subscribeTo"]},"unsubscribeFromAllNewsletters":{"verb":"GET","url":"/api/v1/unsubscribe-from-all-newsletters","args":["emailAddress"]},"generateLicenseKey":{"verb":"POST","url":"/api/v1/admin/generate-license-key","args":["numberOfHosts","organization","expiresAt"]},"createVantaAuthorizationRequest":{"verb":"POST","url":"/api/v1/create-vanta-authorization-request","args":["emailAddress","fleetInstanceUrl","fleetApiKey"]},"deliverMdmBetaSignup":{"verb":"POST","url":"/api/v1/deliver-mdm-beta-signup","args":["fullName","jobTitle","emailAddress"]}}
|
||||
methods: {"downloadSitemap":{"verb":"GET","url":"/sitemap.xml","args":[]},"receiveUsageAnalytics":{"verb":"POST","url":"/api/v1/webhooks/receive-usage-analytics","args":["anonymousIdentifier","fleetVersion","licenseTier","numHostsEnrolled","numUsers","numTeams","numPolicies","numLabels","softwareInventoryEnabled","vulnDetectionEnabled","systemUsersEnabled","hostStatusWebhookEnabled","numWeeklyActiveUsers","numWeeklyPolicyViolationDaysActual","numWeeklyPolicyViolationDaysPossible","hostsEnrolledByOperatingSystem","hostsEnrolledByOrbitVersion","hostsEnrolledByOsqueryVersion","storedErrors","numHostsNotResponding","organization"]},"receiveFromGithub":{"verb":"GET","url":"/api/v1/webhooks/github","args":["botSignature","action","sender","repository","changes","issue","comment","pull_request","label"]},"receiveFromStripe":{"verb":"POST","url":"/api/v1/webhooks/receive-from-stripe","args":["id","type","data","webhookSecret"]},"deliverContactFormMessage":{"verb":"POST","url":"/api/v1/deliver-contact-form-message","args":["emailAddress","topic","firstName","lastName","message"]},"sendPasswordRecoveryEmail":{"verb":"POST","url":"/api/v1/entrance/send-password-recovery-email","args":["emailAddress"]},"signup":{"verb":"POST","url":"/api/v1/customers/signup","args":["emailAddress","password","organization","firstName","lastName","signupReason"]},"updateProfile":{"verb":"POST","url":"/api/v1/account/update-profile","args":["firstName","lastName","organization","emailAddress"]},"updatePassword":{"verb":"POST","url":"/api/v1/account/update-password","args":["oldPassword","newPassword"]},"updateBillingCard":{"verb":"POST","url":"/api/v1/account/update-billing-card","args":["stripeToken","billingCardLast4","billingCardBrand","billingCardExpMonth","billingCardExpYear"]},"login":{"verb":"POST","url":"/api/v1/customers/login","args":["emailAddress","password","rememberMe"]},"logout":{"verb":"GET","url":"/api/v1/account/logout","args":[]},"createQuote":{"verb":"POST","url":"/api/v1/customers/create-quote","args":["numberOfHosts"]},"saveBillingInfoAndSubscribe":{"verb":"POST","url":"/api/v1/customers/save-billing-info-and-subscribe","args":["quoteId","organization","firstName","lastName","paymentSource"]},"updatePasswordAndLogin":{"verb":"POST","url":"/api/v1/entrance/update-password-and-login","args":["password","token"]},"deliverDemoSignup":{"verb":"POST","url":"/api/v1/deliver-demo-signup","args":["emailAddress"]},"createOrUpdateOneNewsletterSubscription":{"verb":"POST","url":"/api/v1/create-or-update-one-newsletter-subscription","args":["emailAddress","subscribeTo"]},"unsubscribeFromAllNewsletters":{"verb":"GET","url":"/api/v1/unsubscribe-from-all-newsletters","args":["emailAddress"]},"generateLicenseKey":{"verb":"POST","url":"/api/v1/admin/generate-license-key","args":["numberOfHosts","organization","expiresAt"]},"createVantaAuthorizationRequest":{"verb":"POST","url":"/api/v1/create-vanta-authorization-request","args":["emailAddress","fleetInstanceUrl","fleetApiKey"]},"deliverMdmBetaSignup":{"verb":"POST","url":"/api/v1/deliver-mdm-beta-signup","args":["emailAddress","fullName","jobTitle"]},"deliverAppleCsr":{"verb":"POST","url":"/api/v1/deliver-apple-csr","args":["csr"]}}
|
||||
/* eslint-enable */
|
||||
|
||||
});
|
||||
|
||||
Vendored
+1
@@ -49,4 +49,5 @@ module.exports.policies = {
|
||||
'create-vanta-authorization-request': true,
|
||||
'view-fleet-mdm': true,
|
||||
'deliver-mdm-beta-signup': true,
|
||||
'deliver-apple-csr': true,
|
||||
};
|
||||
|
||||
Vendored
+1
@@ -402,4 +402,5 @@ module.exports.routes = {
|
||||
'POST /api/v1/admin/generate-license-key': { action: 'admin/generate-license-key' },
|
||||
'POST /api/v1/create-vanta-authorization-request': { action: 'create-vanta-authorization-request' },
|
||||
'POST /api/v1/deliver-mdm-beta-signup': { action: 'deliver-mdm-beta-signup' },
|
||||
'POST /api/v1/deliver-apple-csr ': { action: 'deliver-apple-csr', csrf: false},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<% /* Note: This is injected into `views/layouts/layout-email.ejs` */ %>
|
||||
<p style="margin-bottom: 32px;">Your certificate signing request (CSR) for Apple Push Notification Service is attached to this email.</p>
|
||||
|
||||
<p style="margin-bottom: 32px; font-weight: 700;">What to do next</p>
|
||||
|
||||
<ol style="margin-bottom: 32px">
|
||||
<li style="margin-bottom: 16px;">
|
||||
Sign in to <a style="color: #6A67FE; text-decoration: none;" href="https://identity.apple.com/pushcert" target="_blank">Apple Push Certificates Portal</a> using a Managed Apple ID (recommended). Refer to <a style="color: #6A67FE; text-decoration: none;" href="https://support.apple.com/guide/apple-business-manager/use-managed-apple-ids-axm78b477c81/web" target="_blank">this guide</a> to learn more about Managed Apple IDs and how to set one up.
|
||||
</li>
|
||||
<li style="margin-bottom: 16px;">
|
||||
In Apple Push Certificates Portal, select <span style="font-style: italic;">Create a Certificate</span>, upload your CSR, and download your APNS certificate.
|
||||
</li>
|
||||
<li style="margin-bottom: 0px;">
|
||||
Deploy Fleet using this certificate. <a style="color: #6A67FE; text-decoration: none;" href="https://fleetdm.com/docs/deploying/configuration#apple-apns-cert" target="_blank">Click here to see how</a>.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div style="position: relative; padding: 12px 16px 12px 40px; margin-bottom: 32px; background-color: #F1F0FF; border: 1px solid #d9d9fe; border-radius: 6px;">
|
||||
<img alt="A lightbulb" style="height: 16px; width: 16px; position: absolute; top: 16px; left: 12px;" src="<%= url.resolve(sails.config.custom.baseUrl,'/images/icon-lightbulb-16x16@2x.png') %>">
|
||||
<p style="margin: 0">
|
||||
<strong>Tip: </strong>Keep a note of the Managed Apple ID you use when creating APNS certificates for use with MDM. Certificates expire annually, and you will need your Managed Apple ID when you renew the certificate.
|
||||
</p>
|
||||
</div>
|
||||
Reference in New Issue
Block a user