Skip to content

Commit 688cc02

Browse files
authored
chore(build): sign all artifacts COMPASS-7549 (#5349)
* sign all artifacts * fix and tests * sign archive at one place * sign back linux where its build * use abs path * fix tests and sign linux correctly * use abs path to sign archive * use tasks instead of task_group * rewrite function * use remote signing server for everything
1 parent 8a50418 commit 688cc02

File tree

11 files changed

+369
-225
lines changed

11 files changed

+369
-225
lines changed

.evergreen/functions.yml

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,85 @@ functions:
310310
# and be able to find the binary that is used for the tests
311311
file: src/packages/compass/expansions.yml
312312

313+
spawn-signing-server:
314+
# spawn
315+
- command: host.create
316+
type: setup
317+
params:
318+
provider: ec2
319+
distro: ubuntu2004-large
320+
security_group_ids:
321+
- sg-097bff6dd0d1d31d0 # Magic string that's needed for SSH'ing.
322+
# write host info (this file will be read by signingtool when connection to ssh server)
323+
- command: host.list
324+
params:
325+
num_hosts: 1
326+
path: spawned_hosts.json
327+
timeout_seconds: 1200
328+
wait: true
329+
# copy ssh key (this key will be used to connect to ssh server)
330+
- command: shell.exec
331+
params:
332+
shell: bash
333+
script: |
334+
set -e
335+
{
336+
set +x
337+
echo '${__project_aws_ssh_key_value}' > ~/.ssh/mcipacker.pem
338+
chmod 0600 ~/.ssh/mcipacker.pem
339+
set -x
340+
}
341+
# wait for host to be ready
342+
- command: shell.exec
343+
params:
344+
exec_as_string: true
345+
shell: bash
346+
script: |
347+
set -e
348+
user=ubuntu
349+
hostname=$(tr -d '"[]{}' < spawned_hosts.json | cut -d , -f 1 | awk -F : '{print $2}')
350+
identity_file=$(echo ~/.ssh/mcipacker.pem)
351+
352+
attempts=0
353+
connection_attempts=25
354+
355+
## Check for remote connectivity
356+
while ! ssh \
357+
-i "$identity_file" \
358+
-o ConnectTimeout=10 \
359+
-o ForwardAgent=yes \
360+
-o IdentitiesOnly=yes \
361+
-o StrictHostKeyChecking=no \
362+
"$(printf "%s@%s" "$user" "$hostname")" \
363+
exit
364+
do
365+
if [ "$attempts" -ge "$connection_attempts" ]; then
366+
echo "SSH connection failed after $connection_attempts attempts. Exiting..."
367+
exit 1
368+
fi
369+
printf "SSH connection attempt %d/%d failed. Retrying...\n" "$((attempts++))" "$connection_attempts"
370+
## sleep for Permission denied (publickey) errors
371+
sleep 20
372+
done
373+
echo "SSH connection established after $attempts attempts"
374+
375+
# Write the host info so that it can be used by the signing tool
376+
if [[ $OSTYPE == "cygwin" ]]; then
377+
identity_file=$(cygpath -wa "$identity_file")
378+
else
379+
identity_file=$(eval echo "$identity_file")
380+
fi
381+
cat <<EOL > signing_host_info.yml
382+
SIGNING_SERVER_HOSTNAME: $hostname
383+
SIGNING_SERVER_PRIVATE_KEY: $identity_file
384+
SIGNING_SERVER_USERNAME: $user
385+
SIGNING_SERVER_PORT: 22
386+
EOL
387+
# Update the expansions
388+
- command: expansions.update
389+
params:
390+
file: signing_host_info.yml
391+
313392
package:
314393
- command: shell.exec
315394
params:
@@ -320,6 +399,10 @@ functions:
320399
DEBUG: ${debug}
321400
npm_config_loglevel: ${npm_loglevel}
322401
COMPASS_DISTRIBUTION: ${compass_distribution}
402+
SIGNING_SERVER_HOSTNAME: ${SIGNING_SERVER_HOSTNAME}
403+
SIGNING_SERVER_PRIVATE_KEY: ${SIGNING_SERVER_PRIVATE_KEY}
404+
SIGNING_SERVER_USERNAME: ${SIGNING_SERVER_USERNAME}
405+
SIGNING_SERVER_PORT: ${SIGNING_SERVER_PORT}
323406
script: |
324407
set -e
325408
@@ -508,12 +591,26 @@ functions:
508591
remote_file: ${project}/${revision}_${revision_order_id}/${windows_zip_filename}
509592
content_type: application/zip
510593
optional: true
594+
- command: s3.put
595+
params:
596+
<<: *save-artifact-params-public
597+
local_file: src/packages/compass/dist/${windows_zip_sign_filename}
598+
remote_file: ${project}/${revision}_${revision_order_id}/${windows_zip_sign_filename}
599+
content_type: application/pgp-signature
600+
optional: true
511601
- command: s3.put
512602
params:
513603
<<: *save-artifact-params-public
514604
local_file: src/packages/compass/dist/${windows_nupkg_full_filename}
515605
remote_file: ${project}/${revision}_${revision_order_id}/${windows_nupkg_full_filename}
516606
optional: true
607+
- command: s3.put
608+
params:
609+
<<: *save-artifact-params-public
610+
local_file: src/packages/compass/dist/${windows_nupkg_full_sign_filename}
611+
remote_file: ${project}/${revision}_${revision_order_id}/${windows_nupkg_full_sign_filename}
612+
content_type: application/pgp-signature
613+
optional: true
517614
- command: s3.put
518615
params:
519616
<<: *save-artifact-params-public
@@ -534,20 +631,41 @@ functions:
534631
remote_file: ${project}/${revision}_${revision_order_id}/${osx_zip_filename}
535632
content_type: application/zip
536633
optional: true
634+
- command: s3.put
635+
params:
636+
<<: *save-artifact-params-public
637+
local_file: src/packages/compass/dist/${osx_zip_sign_filename}
638+
remote_file: ${project}/${revision}_${revision_order_id}/${osx_zip_sign_filename}
639+
content_type: application/pgp-signature
640+
optional: true
537641
- command: s3.put
538642
params:
539643
<<: *save-artifact-params-public
540644
local_file: src/packages/compass/dist/${linux_rpm_filename}
541645
remote_file: ${project}/${revision}_${revision_order_id}/${linux_rpm_filename}
542646
content_type: application/x-redhat-package-manager
543647
optional: true
648+
- command: s3.put
649+
params:
650+
<<: *save-artifact-params-public
651+
local_file: src/packages/compass/dist/${linux_rpm_sign_filename}
652+
remote_file: ${project}/${revision}_${revision_order_id}/${linux_rpm_sign_filename}
653+
content_type: application/pgp-signature
654+
optional: true
544655
- command: s3.put
545656
params:
546657
<<: *save-artifact-params-public
547658
local_file: src/packages/compass/dist/${rhel_tar_filename}
548659
remote_file: ${project}/${revision}_${revision_order_id}/${rhel_tar_filename}
549660
content_type: application/x-gzip
550661
optional: true
662+
- command: s3.put
663+
params:
664+
<<: *save-artifact-params-public
665+
local_file: src/packages/compass/dist/${rhel_tar_sign_filename}
666+
remote_file: ${project}/${revision}_${revision_order_id}/${rhel_tar_sign_filename}
667+
content_type: application/pgp-signature
668+
optional: true
551669
- command: s3.put
552670
params:
553671
<<: *save-artifact-params-public

.evergreen/tasks.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ tasks:
105105
- func: apply-compass-target-expansion
106106
vars:
107107
compass_distribution: compass
108+
- func: spawn-signing-server
108109
- func: package
109110
vars:
110111
debug: 'hadron*,mongo*,compass*,electron*'
@@ -124,6 +125,7 @@ tasks:
124125
- func: apply-compass-target-expansion
125126
vars:
126127
compass_distribution: compass-readonly
128+
- func: spawn-signing-server
127129
- func: package
128130
vars:
129131
debug: 'hadron*,mongo*,compass*,electron*'
@@ -143,6 +145,7 @@ tasks:
143145
- func: apply-compass-target-expansion
144146
vars:
145147
compass_distribution: compass-isolated
148+
- func: spawn-signing-server
146149
- func: package
147150
vars:
148151
debug: 'hadron*,mongo*,compass*,electron*'

package-lock.json

Lines changed: 0 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/hadron-build/commands/release.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const packager = require('electron-packager');
2828
const createApplicationZip = require('../lib/zip');
2929
const run = require('./../lib/run');
3030
const rebuild = require('@electron/rebuild').rebuild;
31+
const { signArchive } = require('./../lib/signtool');
3132

3233
const ui = require('./ui');
3334
const verify = require('./verify');
@@ -541,6 +542,7 @@ exports.run = (argv, done) => {
541542
!noAsar && task('create application asar', createApplicationAsar),
542543
!skipInstaller && task('create branded installer', createBrandedInstaller),
543544
task('create application zip', createApplicationZip),
545+
task('sign zip', signArchive),
544546
task('store build configuration as json', writeConfigToJson)
545547
].filter(Boolean));
546548

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,67 @@
1-
const debug = require('debug')('hadron-build:signtool');
2-
const { execFileSync } = require('child_process');
31
const path = require('path');
2+
const debug = require('debug')('hadron-build:target');
3+
const { sign: _garasign } = require('@mongodb-js/signing-utils');
44

5-
async function signtool(fileToSign) {
6-
const signtoolPath = path.resolve(__dirname, '..', 'signtool/signtool.exe');
7-
8-
const execArgs = [signtoolPath, [path.resolve(fileToSign)], { stdio: 'inherit' }];
9-
debug(`Running signtool.exe to sign '${signtoolPath}'`, {
10-
execArgs: execArgs,
11-
env: {
12-
NOTARY_SIGNING_COMMENT: process.env.NOTARY_SIGNING_COMMENT,
13-
NOTARY_URL: process.env.NOTARY_URL,
14-
NOTARY_SIGNING_KEY: process.env.NOTARY_SIGNING_KEY,
15-
}
16-
});
17-
18-
// eslint-disable-next-line no-sync
19-
await execFileSync(...execArgs);
5+
const canSign = () => (
6+
process.env.GARASIGN_USERNAME &&
7+
process.env.GARASIGN_PASSWORD &&
8+
process.env.ARTIFACTORY_USERNAME &&
9+
process.env.ARTIFACTORY_PASSWORD
10+
);
11+
12+
/**
13+
* When using gpg to sign a file, it creates a signature file
14+
* with same name as the original file and adds `.sig` to it.
15+
*
16+
* @param {string} filename
17+
* @returns string
18+
*/
19+
function getSignedFilename(filename) {
20+
return `${filename}.sig`;
21+
}
22+
23+
/**
24+
* Currently, windows and macos zip are created from `zip` step
25+
* of the release process and we sign them here. For linux, we
26+
* create and sign the archive when creating corresponding deb/rpm file.
27+
*
28+
* @param {import('./Target')} target
29+
*/
30+
function signArchive(target, cb) {
31+
const { app_archive_name, platform } = target;
32+
if (platform === 'linux') {
33+
debug('linux archive is signed when creating deb/rpm');
34+
return cb();
35+
}
36+
sign(target.dest(app_archive_name)).then(cb).catch(cb);
37+
}
38+
39+
/**
40+
* We are signing the file using `gpg` or `jsign` depending on the
41+
* file extension. If the extension is `.exe` or `.msi`, we use `jsign`
42+
* otherwise we use `gpg`.
43+
*
44+
* @param {string} src
45+
* @returns {Promise<void>}
46+
*/
47+
async function sign(src, garasign = _garasign) {
48+
debug('Signing %s ...', src);
49+
50+
if (!canSign()) {
51+
debug('Skipping signing. Missing credentials.');
52+
return;
53+
}
54+
55+
const clientOptions = {
56+
client: 'remote',
57+
host: process.env.SIGNING_SERVER_HOSTNAME,
58+
username: process.env.SIGNING_SERVER_USERNAME,
59+
port: process.env.SIGNING_SERVER_PORT,
60+
privateKey: process.env.SIGNING_SERVER_PRIVATE_KEY,
61+
signingMethod: path.extname(src) === '.exe' || path.extname(src) === '.msi' ? 'jsign' : 'gpg'
62+
};
63+
64+
return await garasign(src, clientOptions);
2065
}
2166

22-
module.exports = { signtool };
67+
module.exports = { sign, signArchive, getSignedFilename };

0 commit comments

Comments
 (0)