From 669c36b6980c468f0e3b05749385a11792110f17 Mon Sep 17 00:00:00 2001 From: Facundo Tuesca Date: Thu, 24 Nov 2022 13:43:36 +0100 Subject: [PATCH] tools: add automation for updating OpenSSL dependency Add a Github Action that checks for new versions of the `OpenSSL` library, and creates a PR to update it if a newer version than the one present in the repo is found. Refs: https://github.com/nodejs/security-wg/issues/828 PR-URL: https://github.com/nodejs/node/pull/45605 Reviewed-By: Michael Dawson Reviewed-By: Rafael Gonzaga Reviewed-By: Marco Ippolito --- .github/workflows/update-openssl.yml | 70 ++++++++++++++++++++ tools/dep_updaters/README.md | 41 ++++++++++++ tools/dep_updaters/update-openssl.sh | 99 ++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 .github/workflows/update-openssl.yml create mode 100755 tools/dep_updaters/update-openssl.sh diff --git a/.github/workflows/update-openssl.yml b/.github/workflows/update-openssl.yml new file mode 100644 index 00000000000000..c2f919c60f6dcb --- /dev/null +++ b/.github/workflows/update-openssl.yml @@ -0,0 +1,70 @@ +name: OpenSSL update +on: + schedule: + # Run once a week at 00:05 AM UTC on Sunday. + - cron: 5 0 * * 0 + + workflow_dispatch: + +permissions: + contents: read + +jobs: + openssl-update: + if: github.repository == 'nodejs/node' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + persist-credentials: false + - name: Check if update branch already exists + run: | + BRANCH_EXISTS=$(git ls-remote --heads origin actions/tools-update-openssl) + echo "BRANCH_EXISTS=$BRANCH_EXISTS" >> $GITHUB_ENV + - name: Check and download new OpenSSL version + # Only run rest of the workflow if the update branch does not yet exist + if: ${{ env.BRANCH_EXISTS == '' }} + run: | + NEW_VERSION=$(gh api repos/quictls/openssl/releases -q '.[].tag_name|select(contains("openssl-3"))|ltrimstr("openssl-")' | head -n1) + NEW_VERSION_NO_RELEASE_1=$(case $NEW_VERSION in *quic1) echo ${NEW_VERSION%1};; *) echo $NEW_VERSION;; esac) + VERSION_H="./deps/openssl/config/archs/linux-x86_64/asm/include/openssl/opensslv.h" + CURRENT_VERSION=$(grep "OPENSSL_FULL_VERSION_STR" $VERSION_H | sed -n "s/^.*VERSION_STR \"\(.*\)\"/\1/p") + if [ "$NEW_VERSION_NO_RELEASE_1" != "$CURRENT_VERSION" ]; then + echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV + echo "HAS_UPDATE=true" >> $GITHUB_ENV + ./tools/dep_updaters/update-openssl.sh download "$NEW_VERSION" + fi + env: + GITHUB_TOKEN: ${{ secrets.GH_USER_TOKEN }} + - name: Create PR with first commit + if: env.HAS_UPDATE + uses: gr2m/create-or-update-pull-request-action@v1 + # Creates a PR with the new OpenSSL source code committed + env: + GITHUB_TOKEN: ${{ secrets.GH_USER_TOKEN }} + with: + author: Node.js GitHub Bot + body: This is an automated update of OpenSSL to ${{ env.NEW_VERSION }}. + branch: actions/tools-update-openssl # Custom branch *just* for this Action. + commit-message: 'deps: upgrade openssl sources to quictls/openssl-${{ env.NEW_VERSION }}' + labels: dependencies + title: 'deps: update OpenSSL to ${{ env.NEW_VERSION }}' + path: deps/openssl + - name: Regenerate platform specific files + if: env.HAS_UPDATE + run: | + sudo apt install -y nasm libtext-template-perl + ./tools/dep_updaters/update-openssl.sh regenerate + env: + GITHUB_TOKEN: ${{ secrets.GH_USER_TOKEN }} + - name: Add second commit + # Adds a second commit to the PR with the generated platform-dependent files + if: env.HAS_UPDATE + uses: gr2m/create-or-update-pull-request-action@v1 + env: + GITHUB_TOKEN: ${{ secrets.GH_USER_TOKEN }} + with: + author: Node.js GitHub Bot + branch: actions/tools-update-openssl # Custom branch *just* for this Action. + commit-message: 'deps: update archs files for openssl-${{ env.NEW_VERSION }}' + path: deps/openssl diff --git a/tools/dep_updaters/README.md b/tools/dep_updaters/README.md index bae6d2be2d0943..7e6e47199cc82b 100644 --- a/tools/dep_updaters/README.md +++ b/tools/dep_updaters/README.md @@ -61,6 +61,45 @@ been created with the changes), do the following: 4. Create a commit for the update and in the commit message include the important/relevant items from the changelog. +## OpenSSL + +The `update-openssl.sh` script automates the steps described in +[`maintaining-openssl.md`][]. The main difference is that the script downloads +the release tarball from GitHub, instead of cloning the repo and using that as +the source code. This is useful since the release tarball does not include +development-specific files and directories (e.g the `.github` folder). + +The script has to be run in two steps. The first one (using the `download` +sub-command) replaces the OpenSSL source code with the new version. The second +one (using the `regenerate` sub-command) regenerates the platform-specific +files. This makes it easier to create two separate git commits, making the git +history more descriptive. + +For example, in order to update to version `3.0.7+quic1`, the following commands +should be run: + +```bash +./tools/dep_updaters/update-openssl.sh download 3.0.7+quic1 +git add -A deps/openssl/openssl +git commit -m "deps: upgrade openssl sources to quictls/openssl-3.0.7+quic1" + +./tools/dep_updaters/update-openssl.sh regenerate 3.0.7+quic1 +git add -A deps/openssl/config/archs deps/openssl/openssl +git commit -m "deps: update archs files for openssl" +``` + +Once the script has run (either manually, or by CI in which case a PR will have +been created with the changes), do the following: + +1. Check the `CHANGES.md` file in the [repo](https://github.com/quictls/openssl) + for things that might require changes in Node.js. +2. Check the diffs to ensure the changes are right. Even if there are no changes + in the source, `buildinf.h` files will be updated because they have timestamp + data in them. +3. Check that Node.js compiles without errors and the tests pass. +4. Create a commit for the update and in the commit message include the + important/relevant items from the changelog. + ## postject The `update-postject.sh` script downloads postject from the [npm package](http://npmjs.com/package/postject) @@ -80,3 +119,5 @@ been created with the changes), do the following: 2. Check that Node.js compiles without errors and the tests pass. 3. Create a commit for the update and in the commit message include the important/relevant items from the changelog. + +[`maintaining-openssl.md`]: https://github.com/nodejs/node/blob/main/doc/contributing/maintaining-openssl.md diff --git a/tools/dep_updaters/update-openssl.sh b/tools/dep_updaters/update-openssl.sh new file mode 100755 index 00000000000000..753120e54839bc --- /dev/null +++ b/tools/dep_updaters/update-openssl.sh @@ -0,0 +1,99 @@ +#!/bin/sh +set -e +# Shell script to update OpenSSL in the source tree to a specific version +# Based on https://github.com/nodejs/node/blob/main/doc/contributing/maintaining-openssl.md + +cleanup() { + EXIT_CODE=$? + [ -d "$WORKSPACE" ] && rm -rf "$WORKSPACE" + exit $EXIT_CODE +} + +download() { + if [ -z "$1" ]; then + echo "Error: please provide an OpenSSL version to update to" + echo " e.g. ./$0 download 3.0.7+quic1" + exit 1 + fi + + OPENSSL_VERSION=$1 + echo "Making temporary workspace..." + WORKSPACE=$(mktemp -d 2> /dev/null || mktemp -d -t 'tmp') + + + cd "$WORKSPACE" + + echo "Fetching OpenSSL source archive..." + curl -sL "https://api.github.com/repos/quictls/openssl/tarball/openssl-$OPENSSL_VERSION" | tar xzf - + mv quictls-openssl-* openssl + + echo "Replacing existing OpenSSL..." + rm -rf "$DEPS_DIR/openssl/openssl" + mv "$WORKSPACE/openssl" "$DEPS_DIR/openssl/" + + echo "All done!" + echo "" + echo "Please git add openssl, and commit the new version:" + echo "" + echo "$ git add -A deps/openssl/openssl" + echo "$ git commit -m \"deps: upgrade openssl sources to quictls/openssl-$OPENSSL_VERSION\"" + echo "" +} + +regenerate() { + command -v perl >/dev/null 2>&1 || { echo >&2 "Error: 'Perl' required but not installed."; exit 1; } + command -v nasm >/dev/null 2>&1 || { echo >&2 "Error: 'nasm' required but not installed."; exit 1; } + command -v as >/dev/null 2>&1 || { echo >&2 "Error: 'GNU as' required but not installed."; exit 1; } + perl -e "use Text::Template">/dev/null 2>&1 || { echo >&2 "Error: 'Text::Template' Perl module required but not installed."; exit 1; } + + echo "Regenerating platform-dependent files..." + + make -C "$DEPS_DIR/openssl/config" clean + # Needed for compatibility with nasm on 32-bit Windows + # See https://github.com/nodejs/node/blob/main/doc/contributing/maintaining-openssl.md#2-execute-make-in-depsopensslconfig-directory + sed -i 's/#ifdef/%ifdef/g' "$DEPS_DIR/openssl/openssl/crypto/perlasm/x86asm.pl" + sed -i 's/#endif/%endif/g' "$DEPS_DIR/openssl/openssl/crypto/perlasm/x86asm.pl" + make -C "$DEPS_DIR/openssl/config" + + echo "All done!" + echo "" + echo "Please commit the regenerated files:" + echo "" + echo "$ git add -A deps/openssl/config/archs deps/openssl/openssl" + echo "$ git commit -m \"deps: update archs files for openssl\"" + echo "" +} + +help() { + echo "Shell script to update OpenSSL in the source tree to a specific version" + echo "Sub-commands:" + printf "%-23s %s\n" "help" "show help menu and commands" + printf "%-23s %s\n" "download" "download and replace OpenSSL source code with new version" + printf "%-23s %s\n" "regenerate" "regenerate platform-specific files" + echo "" + exit "${1:-0}" +} + +main() { + if [ ${#} -eq 0 ]; then + help 0 + fi + + trap cleanup INT TERM EXIT + + BASE_DIR=$(cd "$(dirname "$0")/../.." && pwd) + DEPS_DIR="$BASE_DIR/deps" + + case ${1} in + help | download | regenerate ) + $1 "${2}" + ;; + * ) + echo "unknown command: $1" + help 1 + exit 1 + ;; + esac +} + +main "$@"