This is designed to be run from scriptworker, but runs perfectly fine as a standalone script.
Consult https://github.com/mozilla-releng/scriptworker-scripts/blob/master/signingscript/docker.d/passwords.yml and the sops repo used to deploy signingscript for this.
Last updated: 2024-04-26
This is a best effort list of supported signing formats and what they correspond to.
autograph_apk,autograph_focus,autograph_apk_mozillaonline: sign apk or aab files (with different keys)autograph_stage_aab,autograph_stage_apk,autograph_stage_apk_mozillaonline,autograph_stage_focus: sign apk or aab files using stage autographautograph_stage_apk_v3,autograph_stage_focus_v3,autograph_stage_apk_mozillaonline_v3: sign apk or aab file using v3 signingautograph_authenticode_sha2_rfc3161_stub: sign windows binary (PE, MSI, MSIX) using autograph and sha2 hash, adding a dummy certificate in the chain for attribution purposes, and using the rfc3161 protocol for timestampingautograph_authenticode_202404: sign windows binary (PE, MSI, MSIX) using autograph and sha2 hash, using the certificate issued 2024-04-02autograph_authenticode_202404_stub: sign windows binary (PE, MSI, MSIX) using autograph and sha2 hash, using the certificate issued 2024-04-02, and adding a dummy certificate in the chain for attribution purposesautograph_authenticode_ev: sign windows binary using autograph, using the EV (extended validation) code signing certificate, necessary for windows kernel modulesautograph_debsign: gpg-sign a debian changes file and associated dsc and/or buildinfo, using autographautograph_gpg: get a detached PGP signature for a file, using autograph's data signing endpointautograph_hash_only_mar384: sign a mar file, using autograph's hash signing endpointautograph_stage_mar384: sign a mar file, using autograph's hash signing endpoint. This uses autograph stage, so is intended for testing only (no production certificates)autograph_langpack: sign xpi file using autographautograph_omnija: sign omni.ja files contained in a tarball or zip file using autographprivileged_webextension: sign xpi file using autograph and the privileged "extension_rsa" certificatesystem_addon: sign xpi file using autograph and the privileged "systemaddon_rsa" certificateautograph_xpi,autograph_xpi_*: sign xpi file using autograph, with different signing parameters; should not be used in production, that flow should go through addons.mozilla.orgmacapp: [UNUSED] mac app signing is currently handled by iscriptautograph_widevine: get a detached signature for widevine verification purposeswidevine: [UNUSED] same asautograph_widevineautograph_rsa: get a detached signature for a file using autograph's hash signing endpointapple_notarization: notarize and staple a mac pkg or tarballapple_notarization_geckodriver: notarize a mac binary (without stapling)apple_notarize_openh264_plugin: notarize a mac openh264 plugin (without stapling)
Testing takes a few steps to set up. Here's how:
To test, you'll most likely need a local autograph instance.
You can start one with the following command: docker run -p 8000:8000 mozilla/autograph:latest
You also need to create an autograph_config.json, for a shippable-l10n-signing-linux64 task it should look like this:
{
"project:releng:signing:cert:release-signing": [
["http://127.0.0.1:8002", "alice", "fs5wgcer9qj819kfptdlp8gm227ewxnzvsuj9ztycsx08hfhzu", ["gcp_prod_autograph_omnija", "gcp_prod_autograph_langpack"], "extensions-ecdsa"],
["http://127.0.0.1:8002", "alice", "fs5wgcer9qj819kfptdlp8gm227ewxnzvsuj9ztycsx08hfhzu", ["gcp_prod_autograph_gpg"], "randompgp"],
]
}The config json looks like this (comments are not valid json, but I'm inserting comments for clarity. Don't include the comments in the file!):
{
// path to the password json you created above
"autograph_configs": "autograph.json",
// the work directory path. task.json will live here, as well // as downloaded binaries. You can use `uv run create_test_workdir // <task-id>` to create it
"work_dir": "work",
// the artifact directory path. the signed binaries will be copied here for scriptworker to upload
"artifact_dir": "artifact_dir",
// how many seconds should the signing token be valid for?
"token_duration_seconds": 1200,
// enable debug logging
"verbose": true,
// This should be the path to a random file, it doesn't matter for testing. An empty file is fine.
"gpg_pubkey": "foo",
}Running the script itself is fairly easy, uv run signingscript config.json
If you aren't running through scriptworker, you need to manually create the directories that work_dir and artifact_dir point to. It's better to use new directories for these rather than cluttering and potentially overwriting an existing directory. Once you set up scriptworker, the work_dir and artifact_dir will be regularly wiped and recreated.
Scriptworker will expect to find a config.json for the scriptworker config, so I name the signingscript config json script_config.json. You can name it whatever you'd like.
Put the file(s) to sign somewhere where they can be reached via the web; you'll point to their URL(s) in the task.json below. Alternately, point to the artifacts of a TaskCluster task, and add the taskId to your dependencies in the task.json below.
Ordinarily, scriptworker would get the task definition from TaskCluster, and write it to a task.json in the work_dir. Since you're initially not going to run through scriptworker, you need to put this file on disk yourself.
It will look like this:
{
"created": "2016-05-04T23:15:17.908Z",
"deadline": "2016-05-05T00:15:17.908Z",
"dependencies": [
"VALID_TASK_ID"
],
"expires": "2017-05-05T00:15:17.908Z",
"extra": {},
"metadata": {
"description": "Markdown description of **what** this task does",
"name": "Example Task",
"owner": "name@example.com",
"source": "https://tools.taskcluster.net/task-creator/"
},
"payload": {
"upstreamArtifacts": [{
"taskId": "upstream-task-id1",
"taskType": "build",
"paths": ["public/artifact/path1", "public/artifact/path2"],
"formats": []
}],
"maxRunTime": 600
},
"priority": "normal",
"provisionerId": "test-dummy-provisioner",
"requires": "all-completed",
"retries": 0,
"routes": [],
"schedulerId": "-",
"scopes": [
"project:releng:signing:cert:dep-signing",
],
"tags": {},
"taskGroupId": "CRzxWtujTYa2hOs20evVCA",
"workerType": "dummy-worker-aki"
}
The important entries to edit are the upstreamArtifacts, the dependencies, and the scopes.
The upstreamArtifacts point to the file(s) to sign. Because scriptworker downloads and verifies their shas, signingscript expects to find the files under $work_dir/cot/$upstream_task_id/$path
The scope, project:releng:signing:cert:dep-signing, matches the scope in your password json that you created.
Write this to task.json in your work_dir.
You're ready to run signingscript!
signingscript CONFIG_FILE
where CONFIG_FILE is the config json you created above.
This should download the file(s) specified in the payload, download a token from the docker-signing-server, upload the file(s) to the docker-signing-server to sign, download the signed bits from the docker-signing-server, and then copy the signed bits into the artifact_dir.
Invalid json is a common error. Validate your json with this command:
python -mjson.tool JSON_FILE
Your docker-signing-server shell should be able to read the signing.log, which should help troubleshoot.
Scriptworker can deal with the TaskCluster specific parts, and run signingscript.
Follow the scriptworker readme to set up scriptworker, and use ["path/to/signingscript", "path/to/script_config.json"] as your task_script.
Make sure your work_dir and artifact_dir point to the same directories between the scriptworker config and the signingscript config!
This project uses pip-compile-multi for hard-pinning dependencies versions.
Please see its documentation for usage instructions.
In short, requirements/base.in contains the list of direct requirements with occasional version constraints (like Django<2)
and requirements/base.txt is automatically generated from it by adding recursive tree of dependencies with fixed versions.
The same goes for test.
To upgrade dependency versions and hashes, run pip-compile-multi -g base -g test.
To add a new dependency without upgrade, add it to requirements/base.in and run pip-compile-multi --no-upgrade -g base -g test.
For installation always use .txt files. For example, command pip install -Ue . -r requirements/test.txt will install
this project in test mode, testing requirements and development tools.
Another useful command is pip-sync requirements/test.txt, it uninstalls packages from your virtualenv that aren't listed in the file.
Alternatively, you can run pin.sh:
./maintenance/pin.sh iscript