fbshipit-source-id: b14273e883aba6de7b817801a1b04e54a29a6366

This commit is contained in:
Daniel Buchele
2018-06-15 02:23:48 -07:00
parent c6dd46db99
commit 6f95ad512f
12 changed files with 217 additions and 130 deletions

View File

@@ -33,6 +33,7 @@ const minCertExpiryWindowSeconds = 24 * 60 * 60;
const appNotDebuggableRegex = /debuggable/;
const allowedAppNameRegex = /^[a-zA-Z0-9.\-]+$/;
const allowedAppDirectoryRegex = /^\/[ a-zA-Z0-9.\-\/]+$/;
const logTag = 'CertificateProvider';
export type SecureServerConfig = {|
key: Buffer,
@@ -124,7 +125,7 @@ export default class CertificateProvider {
}
generateClientCertificate(csr: string): Promise<string> {
this.logger.warn('Creating new client cert', 'CertificateProvider');
this.logger.warn('Creating new client cert', logTag);
const csrFile = this.writeToTempFile(csr);
// Create a certificate for the client, using the details in the CSR.
return openssl('x509', {
@@ -145,40 +146,66 @@ export default class CertificateProvider {
contents: string,
csr: string,
os: string,
) {
): Promise<void> {
if (os === 'Android') {
this.extractAppNameFromCSR(csr).then(app => {
const client = adb.createClient();
client.listDevices().then((devices: Array<{id: string}>) => {
devices.forEach(d =>
// To find out which device requested the cert, search them
// all for a matching csr file.
// It's not important to keep these secret from other apps.
// Just need to make sure each app can find it's own one.
this.androidDeviceHasMatchingCSR(destination, d.id, app, csr)
.catch(e =>
this.logger.error(
`Unable to check for matching CSR in ${d.id}:${app}`,
'CertificateProvider',
),
)
.then(isMatch => {
if (isMatch) {
this.pushFileToAndroidDevice(
d.id,
app,
destination + filename,
contents,
);
}
}),
);
});
});
const appNamePromise = this.extractAppNameFromCSR(csr);
const deviceIdPromise = appNamePromise.then(app =>
this.getTargetDeviceId(app, destination, csr),
);
return Promise.all([deviceIdPromise, appNamePromise]).then(
([deviceId, appName]) =>
this.pushFileToAndroidDevice(
deviceId,
appName,
destination + filename,
contents,
),
);
}
if (os === 'iOS') {
fs.writeFileSync(destination + filename, contents);
return Promise.resolve();
}
return Promise.reject(new Error(`Unsupported device os: ${os}`));
}
getTargetDeviceId(
appName: string,
deviceCsrFilePath: string,
csr: string,
): Promise<string> {
const client = adb.createClient();
return client.listDevices().then((devices: Array<{id: string}>) => {
const deviceMatchList = devices.map(device =>
// To find out which device requested the cert, search them
// all for a matching csr file.
// It's not important to keep these secret from other apps.
// Just need to make sure each app can find it's own one.
this.androidDeviceHasMatchingCSR(
deviceCsrFilePath,
device.id,
appName,
csr,
)
.then(isMatch => {
return {id: device.id, isMatch};
})
.catch(e => {
this.logger.error(
`Unable to check for matching CSR in ${device.id}:${appName}`,
logTag,
);
return {id: device.id, isMatch: false};
}),
);
return Promise.all(deviceMatchList).then(devices => {
const matchingIds = devices.filter(m => m.isMatch).map(m => m.id);
if (matchingIds.length == 0) {
throw new Error(`No matching device found for app: ${appName}`);
}
return matchingIds[0];
});
});
}
androidDeviceHasMatchingCSR(
@@ -191,14 +218,19 @@ export default class CertificateProvider {
deviceId,
processName,
`cat ${directory + csrFileName}`,
).then(deviceCsr => {
return (
deviceCsr
.toString()
.replace(/\r/g, '')
.trim() === csr.replace(/\r/g, '').trim()
);
});
)
.then(deviceCsr => {
return (
deviceCsr
.toString()
.replace(/\r/g, '')
.trim() === csr.replace(/\r/g, '').trim()
);
})
.catch(err => {
this.logger.error(err, logTag);
return false;
});
}
pushFileToAndroidDevice(
@@ -207,10 +239,7 @@ export default class CertificateProvider {
filename: string,
contents: string,
): Promise<void> {
this.logger.warn(
`Deploying sonar certificate to ${deviceId}:${app}`,
'CertificateProvider',
);
this.logger.warn(`Deploying ${filename} to ${deviceId}:${app}`, logTag);
return this.executeCommandOnAndroid(
deviceId,
app,
@@ -245,6 +274,13 @@ export default class CertificateProvider {
throw e;
}
return output;
})
.catch(err => {
this.logger.error(
`Error executing command on android device ${deviceId}:${user}. Command: ${command}`,
logTag,
);
this.logger.error(err, logTag);
});
}
@@ -306,10 +342,7 @@ export default class CertificateProvider {
})
.then(output => undefined)
.catch(e => {
this.logger.warn(
`Certificate will expire soon: ${filename}`,
'CertificateProvider',
);
this.logger.warn(`Certificate will expire soon: ${filename}`, logTag);
throw e;
});
}
@@ -331,7 +364,7 @@ export default class CertificateProvider {
if (!fs.existsSync(getFilePath(''))) {
fs.mkdirSync(getFilePath(''));
}
this.logger.info('Generating new CA', 'CertificateProvider');
this.logger.info('Generating new CA', logTag);
return openssl('genrsa', {out: caKey, '2048': false})
.then(_ =>
openssl('req', {
@@ -364,7 +397,7 @@ export default class CertificateProvider {
generateServerCertificate(): Promise<void> {
return this.ensureCertificateAuthorityExists()
.then(_ => {
this.logger.warn('Creating new server cert', 'CertificateProvider');
this.logger.warn('Creating new server cert', logTag);
})
.then(_ => openssl('genrsa', {out: serverKey, '2048': false}))
.then(_ =>