From 53d6b2375851b9a07785c8ecc57fc85421da1dc4 Mon Sep 17 00:00:00 2001 From: rishuranjan Date: Tue, 4 Aug 2026 14:11:28 +0530 Subject: [PATCH 1/2] fix: verify Node.js SHASUMS256.txt.asc GPG signature before trusting checksums Signed-off-by: rishuranjan --- knife.d/update_node_archives.js | 139 ++++++++++++++++++++++++++------ 1 file changed, 116 insertions(+), 23 deletions(-) diff --git a/knife.d/update_node_archives.js b/knife.d/update_node_archives.js index b4ee2fef9..7d53f38f3 100755 --- a/knife.d/update_node_archives.js +++ b/knife.d/update_node_archives.js @@ -2,6 +2,9 @@ const crypto = require("crypto"); const https = require("https"); const fs = require("fs"); +const os = require("os"); +const path = require("path"); +const { execSync } = require("child_process"); if (process.argv.length < 3) { console.error("Usage: node nodeChecksum.js "); @@ -13,51 +16,141 @@ const architectures = ["amd64", "arm64", "arm", "ppc64le", "s390x"]; const nodeVersions = {}; -const calculateChecksum = (url) => { +// Node.js release team GPG key fingerprints. +// Keep in sync with https://github.com/nodejs/node#release-keys +const NODE_RELEASE_KEYS = [ + "4ED778F539E3634C779C87C6D7062848A1AB005C", // Beth Griggs + "141F07595B7B3FFE74309A937405533BE57C7D57", // Bryan English + "74F12602B6F1C4E913FAA37AD3A89613643B6201", // Danielle Adams + "DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7", // Juan José Arboleda + "CC68F5A3106FF448322E48ED27F5E38D5B0A215F", // Marco Ippolito + "8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600", // Michaël Zasso + "890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4", // Richard Lau + "C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C", // Ruben Bridgewater + "108F52B48DB57BB0CC439B2997B01419BD92F80A", // Ruy Adorno + "A363A499291CBBC940DD62E41F10027AF002F8B0", // Ulises Gascón +]; + +/** + * Import Node.js release team GPG keys into an isolated temporary keyring. + * Returns the path to the temporary GNUPGHOME directory. + * Caller is responsible for deleting it when done. + */ +const importReleaseKeys = () => { + const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "gpg-distroless-")); + fs.chmodSync(tmpHome, 0o700); // gpg requires 700 on its home directory + const env = { ...process.env, GNUPGHOME: tmpHome }; + const keyList = NODE_RELEASE_KEYS.join(" "); + try { + execSync( + `gpg --keyserver hkps://keys.openpgp.org --recv-keys ${keyList}`, + { stdio: "pipe", env } + ); + } catch (_) { + // Fall back to a secondary keyserver if the primary is unavailable. + execSync( + `gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys ${keyList}`, + { stdio: "pipe", env } + ); + } + return tmpHome; +}; + +/** + * Fetch a URL and return the response body as a string. + */ +const fetchText = (url) => { return new Promise((resolve, reject) => { https .get(url, (res) => { - const hash = crypto.createHash("sha256"); - res.on("data", (data) => { - hash.update(data); + let body = ""; + res.on("data", (chunk) => { + body += chunk; }); res.on("end", () => { - resolve(hash.digest("hex")); + resolve(body); }); }) .on("error", (err) => { - reject(`Error downloading file: ${err.message}`); + reject(`Error fetching ${url}: ${err.message}`); }); }); }; +/** + * Download SHASUMS256.txt and its detached GPG signature for the given Node.js + * version, verify the signature against the imported release keys, and return + * a map of { filename → sha256 }. + * + * Throws if GPG verification fails — no checksums are returned in that case. + */ +const fetchVerifiedShasums = async (nodeVersion, gpgHome) => { + const base = `https://nodejs.org/dist/v${nodeVersion}`; + const [shasumsText, shasumsAsc] = await Promise.all([ + fetchText(`${base}/SHASUMS256.txt`), + fetchText(`${base}/SHASUMS256.txt.asc`), + ]); + + // Write both files to a temp dir for gpg --verify + const tmpDir = fs.mkdtempSync( + path.join(os.tmpdir(), `node-shasums-${nodeVersion}-`) + ); + const shasumsFile = path.join(tmpDir, "SHASUMS256.txt"); + const ascFile = path.join(tmpDir, "SHASUMS256.txt.asc"); + try { + fs.writeFileSync(shasumsFile, shasumsText); + fs.writeFileSync(ascFile, shasumsAsc); + const env = { ...process.env, GNUPGHOME: gpgHome }; + execSync(`gpg --verify ${ascFile} ${shasumsFile}`, { stdio: "pipe", env }); + } finally { + fs.rmSync(tmpDir, { recursive: true }); + } + + // Parse "sha256hex filename" lines from the verified SHASUMS file. + const checksums = {}; + for (const line of shasumsText.split("\n")) { + const parts = line.trim().split(/\s+/); + if (parts.length === 2) { + checksums[parts[1]] = parts[0]; + } + } + return checksums; +}; + const fetchChecksums = async () => { - for (const nodeVersion of versions) { - const major = parseInt(nodeVersion.split(".")[0]); - nodeVersions[nodeVersion] = {}; - await Promise.all( - architectures.map(async (key) => { + // Import Node.js release GPG keys once into an isolated keyring. + const gpgHome = importReleaseKeys(); + try { + for (const nodeVersion of versions) { + const major = parseInt(nodeVersion.split(".")[0]); + nodeVersions[nodeVersion] = {}; + + // Fetch and GPG-verify the official SHASUMS before trusting any checksum. + const shasums = await fetchVerifiedShasums(nodeVersion, gpgHome); + + for (const key of architectures) { let arch = key; if (major > 22 && key === "arm") { - return; + continue; } if (key === "amd64") { arch = "x64"; } else if (key === "arm") { arch = "armv7l"; } - const url = `https://nodejs.org/dist/v${nodeVersion}/node-v${nodeVersion}-linux-${arch}.tar.gz`; - try { - const checksum = await calculateChecksum(url); - nodeVersions[nodeVersion][key] = { - checksum, - suffix: arch, - }; - } catch (error) { - console.error(error); + const filename = `node-v${nodeVersion}-linux-${arch}.tar.gz`; + const checksum = shasums[filename]; + if (!checksum) { + throw new Error( + `No checksum found for ${filename} in verified SHASUMS256.txt` + ); } - }) - ); + nodeVersions[nodeVersion][key] = { checksum, suffix: arch }; + } + } + } finally { + // Always clean up the temporary GPG home directory. + fs.rmSync(gpgHome, { recursive: true }); } }; From 14401bb9c4b9be0bbd63a830da213465b8085ae3 Mon Sep 17 00:00:00 2001 From: rishuranjan Date: Fri, 28 Aug 2026 00:10:07 +0530 Subject: [PATCH 2/2] gpg: verify Node.js checksums against committed release keys + daily key sync --- .github/workflows/update-node-keys.yml | 53 ++++ knife.d/nodejs_keys.asc | 405 +++++++++++++++++++++++++ knife.d/update_node_archives.js | 39 +-- knife.d/update_node_keys.sh | 25 ++ 4 files changed, 491 insertions(+), 31 deletions(-) create mode 100644 .github/workflows/update-node-keys.yml create mode 100644 knife.d/nodejs_keys.asc create mode 100755 knife.d/update_node_keys.sh diff --git a/.github/workflows/update-node-keys.yml b/.github/workflows/update-node-keys.yml new file mode 100644 index 000000000..bf6324125 --- /dev/null +++ b/.github/workflows/update-node-keys.yml @@ -0,0 +1,53 @@ +name: update-node-keys +on: + schedule: + - cron: "30 6 * * *" + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + persist-credentials: false + + - name: Update node release keys + run: ./knife.d/update_node_keys.sh + + - name: Check for changes + run: | + if [[ $(git status --porcelain) ]]; then + echo "DISTROLESS_DIFF=true" >> "$GITHUB_ENV" + else + echo "No changes detected"; exit 0 + fi + + - name: Create commits + if: env.DISTROLESS_DIFF + env: + ACTIONS_TOKEN: ${{ secrets.ACTIONS_TOKEN }} + run: | + git checkout -b update-node-keys + git config --global user.email "distroless-bot@google.com" + git config --global user.name "Distroless Bot" + git add knife.d/nodejs_keys.asc + git commit -s -m "Bumping Node.js release keys" + git remote set-url origin https://x-access-token:${ACTIONS_TOKEN}@github.com/${{ github.repository }}.git + git push --force origin HEAD + + - name: Create Pull Request + if: env.DISTROLESS_DIFF + env: + GH_TOKEN: ${{ secrets.ACTIONS_TOKEN }} + run: | + if ! OUTPUT=$(gh pr create -B main -H update-node-keys --fill 2>&1) ; then + echo "$OUTPUT" + [[ "$OUTPUT" =~ "already exists" ]] && exit 0 || exit 1 + fi diff --git a/knife.d/nodejs_keys.asc b/knife.d/nodejs_keys.asc new file mode 100644 index 000000000..a5443f4ef --- /dev/null +++ b/knife.d/nodejs_keys.asc @@ -0,0 +1,405 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEaGA63BYJKwYBBAHaRw8BAQdAo/yU+MutacFmmn0CEX495goNrBxR24235XLM +cvHYjfq0L0FudG9pbmUgZHUgSGFtZWwgPGR1aGFtZWxhbnRvaW5lMTk5NUBnbWFp +bC5jb20+iI4EExYKADYWIQRb6KP2yKXAHRBsCtggsaOQsWjTVgUCaGA63AIbAwQL +CQgHBBUKCQgFFgIDAQACHgUCF4AACgkQILGjkLFo01afgwEA/sLHqsj7ml2vyDoT +KDPE8n9a80ZOh14OfnlOe0cCZA8BAMEOOk7QFI69DIlV1nMiqcFCqQFoSzBU2LkI +R17p/j4NtDNBbnRvaW5lIGR1IEhhbWVsIDxhbnRvaW5lLmR1aGFtZWxAcGxhdGZv +cm1hdGljLmRldj6IjgQTFgoANhYhBFvoo/bIpcAdEGwK2CCxo5CxaNNWBQJpsCMx +AhsDBAsJCAcEFQoJCAUWAgMBAAIeAQIXgAAKCRAgsaOQsWjTVr/sAPwIBsG8g6ND +zoNRTX1wPKBvfZg1NP7tYCyM5sxQfrpuLAEA05AhG4xBILfhL/f0pqR5jXfxg6gz +T6WfeVeS6zeHZwe4OARoYDrcEgorBgEEAZdVAQUBAQdAQVmtih8AO3ryBQMR/22x +WHVKLjAbCiH2cMxNH+iy1RQDAQgHiHgEGBYKACAWIQRb6KP2yKXAHRBsCtggsaOQ +sWjTVgUCaGA63AIbDAAKCRAgsaOQsWjTVu8oAP9Bc+QY+9FikX3YvMgWAqiDlVOy +o0y6UIZGBMSQlF80wAD/d34LqtVIVe9oe5NO3xA75+6Ew8tGeAjUq/ovagr5dAWY +MwRkAW20FgkrBgEEAdpHDwEBB0D1RRA1yUWnSsiuC1Cc3DoGCMMg2QU+hVu5Lgex +A0R/57QsSnVhbiBKb3PDqSBBcmJvbGVkYSA8c295anVhbmFyYm9sQGdtYWlsLmNv +bT6IkwQTFgoAOxYhBN15L1lzxt5SxDLL2sd6v6AN2/K3BQJkAW20AhsDBQsJCAcC +AiICBhUKCQgLAgQWAgMBAh4HAheAAAoJEMd6v6AN2/K3c9gA/0Dxdu9lhP43uT3b +GdEx+sBR6npMt9ZvH25/uT+Gb/4oAQCapRhXVp9y7BTbFcTkjybblUycIQoUUQXn +a1bZtpaRBbg4BGQBbbQSCisGAQQBl1UBBQEBB0C3CxyBvFvLe97DaGf0X4qVU8u8 +UE+Oe84NyeyqU7aZdAMBCAeIeAQYFgoAIBYhBN15L1lzxt5SxDLL2sd6v6AN2/K3 +BQJkAW20AhsMAAoJEMd6v6AN2/K3PFoBAO9hfijVjpHfRtjAhd6YaBs7waUKSCTm +K3syN5KTy9TKAQCkTnMiLk60/xOU+IyC7YyglsiW2qoMIpZO0c8hBE0jCZkCDQRm +BaI9ARAApuFCmHMpCAm+aMWUEMUmUWMM5Ep4KXhXK67h1R34No3EPlZkVQtza24U +qwULidKTMf86v8mQeiuCUvhDH+RqWl9mbFbI/zD6EilR3Ml0eHJSLpQsTa1MimEu +/GsJd2xPncH/VPiGNnzKDFS8DS/JE9mK9vHSNruO2zj0asQ08SfFZETEduDb/FWx +Z9pGVZuLv2ROJVZQK/Nr7jPHTnaPkQOjk35yUQbbsKfGJki0J6BPgXkfoi4G5qjh +UqgLEOM+bZej5cFDEJ+5PxomHxhis4KWfXqtuucbu7u39nF5N0gJ8X9k5r9yS7HS +3d8G8RjkgHfQosvlTZoVtOpIXfSRq4lcrJWqUjbtGXR+EXqT6o0xCLRyxHZ1MJXJ +iRip2xN5+0Opk3s9qTLTWDQHgOCytuqh5B6pIAJZICGSht0jJQ/cHNxSn0qTHREM +PUagGYrztAqIhUQvlZJ0VRcuDfxdJ67wuLhwxKJiLnibMDdq9C1oTA5pVY9Ahnw0 +NUg3lNEKVbbkOcI6ad1Lpi7Y/rs31OmNNZq7cR9oTW1ylomBZQ0h9e8K2s2BshFg +58+6MC28RDdXO7EIB1U1YBc0LP80Y0tuxxN21itjvr11g/pnHlzB1C9KJtOFdkxY +oORmhj69hkg+HmORJuBthc5sDxoYwfpz/Jx5rECwLjFISEeO8jcAEQEAAbQqbWFy +Y28taXBwb2xpdG8gPG1hcmNvaXBwb2xpdG81NEBnbWFpbC5jb20+iQJXBBMBCABB +FiEEzGj1oxBv9EgyLkjtJ/XjjVsKIV8FAmYFoj0CGwMFCRLMAwAFCwkIBwICIgIG +FQoJCAsCBBYCAwECHgcCF4AACgkQJ/XjjVsKIV+5TA/+MgjKSasRimkrHOYQ26+b +mtLkEZ84hQVD8/0RRekZkgwwmfhrnJNgPvHe1h4f25l4c/bFCEadOKC6Z7CQJG23 +TSWnj5kcRDqItWdclIS5X6avWQdWPM/R4LJOH82wXKb8hq36ZrkcdQfBeeEDtNlR +j6fovyXpPjzwqcl0khVVXuNUTh3gBaAHQLJPz59iv8T/LBVn9smSQ8WrhDVD+u2i +u6vODeIgvd4qFWlv7XVRnTiisEgqNY8RJ1qjAh3uOPwTMw84zc3BjRTTUtlqecdQ +c5rI+Egx41qX4Hzczrls23R9B+diokFuSP0w8P1pwDAwJvxAntVdJ//jjYJgjAn/ +4kZmJS1kX9u2hiuTpR/nKRf37LlnteEL0myyoDund52d5Cem3a5HiNPnjLJz1TLX +PUn6NQXoJeFzDjC16BgOUNLWaQrnvqOx3ydoRLWEFS9xtsiIFsC3MP/2bfzfbGDi +QBrzIthkLm6k68gZaLJFfwwE/HB97baJDlhfxGWq1pCZrTjKRiE/raZ8rWJ6Vf0e +rAKk9MIasaOAODHvJ3BSIW74iTQXcv4YtKicR71Q1ERh2jBQnGBs8345R/51zhgn +yJ8xJpmi1JMqqDPpnCzr1gWnKa5V6ZvFXfjqa4JY3gSOicgmWJyCcnDUKlNnax4x +64BZdiRJrx8As7j9Faq/xsW5Ag0EZgWiPQEQAOI6LTUvTiivLU9zkaqahDbOI9TD +B0FvbSfStjN66iy7FMe9jDdzSBM4nRMxsq+xynQUPPFwtuU0arSoZY1Bad5t7MO4 +2rmXEwhxf72luVZH4sfai+QQ4qkO0+eKNOZ5gsVwVig2uuCRQNBnEecWXmDkYvxL +o6HlNsVml2UAblNESyLa2hiZyCFAprD8a0nXtBAU6YzSdMxCqnVFsMdiQ+bMbNi5 +9aJSRQOISgrhp2IY6I0MWqQexymp5qjCQ+pr0FQo1JsNRTWjQy3xav4cfchdr0zW +dQc1L0H2AairMJw+v0PgbraY2g8hj/MNvjVVgkJT8VV4h3DgQzYfCUskjBEh4ub5 +bV7oymQJK/quuzmayqWudzLcNDwBLjOcJfg79lLd2d5liGvvIfADv0l5wEV887DO +jPLBzIhddiCM8QLX73vHiDaK/oBIQfSyXaGzqvqPLbupWvVDqLWPnSTy9acukrmQ +WzkqSUYqOz8Lg8+O/D2238g9x0okXL/VFglIKXX4jzSyc/DQgmMMDNAsDZSjr3NK +RAUMbnxzjA8RFr+0cTncVojg8C2a5chOni4eQWsTPwDMDliR+UXhmgANFQ0Y1x0Z +vrzglyxDXh/mZU6pyiBx5igzyXcm85ZbMWhadNvnOrH+xo/QT3QNK8ya3aa526C/ +KAa/Byc7w8OptNpDABEBAAGJAjwEGAEIACYWIQTMaPWjEG/0SDIuSO0n9eONWwoh +XwUCZgWiPQIbDAUJEswDAAAKCRAn9eONWwohX17oD/9YmDKjDOzEYnxXvxyMIQZl +BgTl70aznWBJ6EDkDOQlfq6NZoFS692pR6OsXOWWMCDatwL30tF0yxtM9YO80KYn +LaSuJqNrE0bo3iaX+kz4Ce2iPfn2A536vkpoQ4CiJUdtDVMSzICvLtngXBBfIKqh +gZXR1em+k2wwWRlusQFRl3uPTS3fENaxzwlFQOErAxdFoJ/YGomqzKpZjrXK7GpU +7xq27A88h2PKfjpLWHiTwlctImok+UU8JBo8oSJ0sx9EMm/dWmFyiXR++HOgTxY6 +F3hDP5/6R93LuYeMd5vvGe1+Zff1K1fZATOU8cIp08BlLkAy3rNJo8LKVHN5IDxo +Dxs0aefsE3EX4hdNJbhBVMUbQSwiHejykh92YQ40QNuTNGKeKj/NcILukqpWwfoK +CGbWxACXiMVjXqzFXgtRaG7hWvUurfTwFJb1+5jJQQDAzr+nMxZK5e60o8kMOJXG +L9t8/6ONshRvhHJxEN5eyt49appBQ4hH6KW0FQTaROvbLY6TtbEFeH1WK3rtGoL3 +dfsima3KBKPp6V8s1z3JEbB3IUuqW4XJNbxbYCsF6Two0gtHuiMKw4ABsNLJnV3i +CMDaDFWmN6JLFgPA3UXTQmh6mGpVvvC8rYjjnAb4BQjHFLK9DXC7LVsMomVn9Gt1 +T+hnKOW5gz4bxqCxLPVcOZkBjQRig9zkAQwAtxEsTXe3R3zOx5d7BO159qLEuFGZ +CbZlwO4XW38m839euMAmy3GBAqtBqxn4HZyhDFjc69ktPCpFFkMCWXe9Cbdb6Faa +llV3bQR/ysRsIn4sTJExMpbtHj7yTOy+RzTiGT+Z43AFNN4SoCIm2EFvklo/xt+8 +n3cjaazdrl/KeAlMiW5wXGDmdltbVMzbHXb01dNyZQ5n0eZMB6IubEwqr0Fk87CE +J/7xh8TKmt142m30r4ptQMrRNSe1SIruz/YVtaaxH1FWOfoILMFAnyZeAOFnAsBS +yGiHTq3ompAIsex5+QA+A5crJE8w8ukEacxVZumMqys1M4IKmvm0Of/WqvC3SlYp +v3/y5hLkkHcJBLdKzloQF2CUK5yU/CBS6poJyRMoBpMHHVzgRkREUQfDHrNK364G +B2FtFqhiOXzMci9U4vcPtAB+kBatSKFmWPG8xpNWyXue5YCa7IWr6UnNX7Sk6rBQ +KC2A7DBepcZDcku/jmTCMVPYW77iYHT4+/5VABEBAAG0I1JhZmFlbEdTUyA8cmFm +YWVsLm51bnVAaG90bWFpbC5jb20+iQHUBBMBCgA+AhsDBQsJCAcCBhUKCQgLAgQW +AgMBAh4BAheAFiEEiQwI24V5Fi/uDfnbi+q0389VXvQFAmaMCmEFCQfKlH0ACgkQ +i+q0389VXvTiOwv/ZkMXHIP6GcZ+EJcVmjY6qyy4w7eQ+uATBVpagCIPvTogARd3 +QAGHZES70OFsatsR2rn9s3btaWwnSsTuJYRg1WzIyoAr34x9tX6DkR/2Uf3WjrfZ +XN8sBRoPVjDeFZbffnDuH3RDTjDDaQ1T1oiGdFTyWXBTV7R5gYE9wpio1WUFA1Hq +4aeqmkcZDKlHVZ2H4nlCl5Q3b4jCkKFR5s2g5XsYL7Csgl+jc4wgSrIoDkHgvoar +dwHcjdyBtrAQFQazBr6jdjF34Hm69WQyZwtGNIQOswHssyq0eb68we6A0AJdic3v +XtIYp47XAhjqO/l52ug5thj9ho9OITUpOiVwEVNNymVpzK8kakAgzcX3R8vUoZjw +A9z7h/4P1QkDHkLAdUtR/pv11adiPhv32Y+i+4maUeVEW2EBUki+1CFY8hU8plPH +vD5JEalwyNJ26GmQVYpG5RZmACDbbFSSx2O9JQGftASQhrPUCBE4LsRbbKH0cN6a +p7svdxSldv09sCVGuQGNBGKD3OQBDADfmeg19TtHy5LdJavje9IJjvgJRSPZeC9h +ygYjwjDDwlz/4l/9OM1c5up965ecqTaPkOLpunhsC/VBq81CUsHA9J7LyvFPklcg +6afHt4uPqYdl0YguQZJLX4Mntr0wIBPXJapw6UBp2kOvPYDnfZz0AFKjV54m7mbJ +xE7rG7v/h0A6jOMHUsUxl0OD2qo5sgE3lHE1cIfG83QTi2fDtnopRYDYWN9HMnMX +ryOvEpy8z8+64l00KNKSm3+obyuZJ951C5+Ze2/kBbblW333vb6Nydzk/JRF/Qr3 +e98tNX4A0V7OY9WW4OTmjjF6jLYW53avnp5YZA+L8ghdOy5LhwZPJTF9clFcrDMf +4qje2Ev9mRDJCbdPeUP+g3DYoBFKU74LTyWMR2ptbl0G9xJZYWqf2HgpJIDrNhTn +srWiEfJ2vwO04z8DjOr/7Wq/hDUAxdZwoa6oZS4p/Y/pMosJE8qVidJJslOfH2Rr +5UodSX41TWjLjOH+saBUBW/PALuKfLMAEQEAAYkBvAQYAQoAJgIbDBYhBIkMCNuF +eRYv7g3524vqtN/PVV70BQJmjAqMBQkHypSoAAoJEIvqtN/PVV70kAUMALDL4zE4 +clNeG/wWt83wuH6rQXde5URMU5cgqYyY+yrfLYhUjNWKtDN9J3ULWWNvraYl/Iso +4Fj2KXGzYJuk94zhVdbYLadpu22Ihk6mu6NFUBHHbvudhqjZqy7+2i2RqAjPJDVm +kRsot9uN9xFnc+m/tXH0/B+5SKyyrwXcKzleatVzEC39gwzS0FIS6hm+qhOhBvf0 +cOHvLnNdMN8M9Ma9y3tfhZawtShQtF2h6AQltIDau7StIBvmmgEiF4PKkEv3E903 +9NI6vzO4+FZekMDrDsGcNYZ/8nQUHyt07/h8XtOtL1noSMnP5358PaKOAHrTSkKo +njRY34ewh4QCGk1Q4rHNd9K0FibU5paEQaFwNLsC3OnSLyFotYS+yoBCOec0nl1H +RSKcFo0XDcXYi0OOhmoLVTI/tYc/BDlOyd72NOGURw8mqQkWKEV3fzfDFJ7Gf8kk +XQP0pOe1I5/VB95xRIQ+gB/ctPCusweCRTe7GiYgo9kO1tp17hFIUVbNipkCDQRd +ttnNARAAv8CIiOxGJgO8qazWv9Hp9pXhfgqxdPQZQiLUxx+SII0ixqtrSAYt4SIX ++04rQ5buhqYR5RY74Y+lA4HRJq9IrXKDi0YbREelpZjGl7UvopuXz0i1lrJGUgId +WAt4qjkAhK5AJ1RVFmOZ1G1n8YL9KSq3ORk09kZIg+p4KK+WCAJEWmEPp54h7HpF +GDri5Z9KeJ/ytP4kcOTTKJPUNhcJRI+x9JAnwz7qrIHxKA16Fb/EElSKQdYHTkO3 +tjHjAAelOaNDo6szMT1KyJA5oBiAbwoHAjd95TDTuA1pYa97xfdBISr1jy8uJE2u +BnLbfX+ZCAs2+wGGSRu/hfIa1R6WxdH1804GbDvwQX+BEUzChn4+2V6u3hgEU3w4 +M0fcsVn8X6OO41HfGplZP6o/TveJSD6GxFHj+0ij8toyEU8dOB3HTAzwBaJMyX9X +MAoIXNfZjfq6N9a3J7ndGWtBDO2MavPRh7x4lBM8Y9l1p8t5+fN1eC4enNmwJtLX +KpiZS4aJXhaN6lv5wUgnXtAY8H2pineCgfNLbBfELujnWoRU/Mm2ggNpbHXJLPbg +F16sMk5l4cHttgh5P/dfVG5NAxT6M4I9AGg3/r0M/pSsQ1LmNBWkhKN7D58OyZht +X3c9owMNFLyuMRq2BPJ05RfOZjgYVe848Fue2/cdOzoNM4pamYcAEQEAAbQdUmlj +aGFyZCBMYXUgPHJsYXVAcmVkaGF0LmNvbT6JAjkEEwECACMFAl91/+cCGwMHCwkI +BwMCAQYVCAIJCgsEFgIDAQIeAQIXgAAKCRDEPOxFwXq5PMtIEAC2b/OSA3hyA7G3 +9OQN6uRuteDEM0blN61wZS6UxL+gyaK0JZd4YZr0VOGkM19g9yU8zgG/FrLvnB1F +yb1NdawiLAEDdy+clhBtdYZXf6Axt4+UtLDPv92L6io5bloUKWLzq00FEaCap+VL +yjc2evHFoEAyTkt11cOUi7LQd3LaGt5ntLEpElbOxsvS9kiVJ53N9xhhK1CQqO7v +9Hg+fooTZz+G7oGvsXfMOM6nvYBM4fAWTIvrKAL9pDQuMJTGxKyVIdDLKi0xCSpm +zbFXhl9IMI5SUdLDxm3zVpTeJcOsQJYxXkY0SYOfWfYJOT+flB2m1GSxZPVPfHY/ +/LXJZsaGvXHjFtVVTgXKVbJIVKRewyu49KI48/YkV11iMZJ+XYJwD+fHivlmbUkO +ZQW88ixRZa+XOdfxWaBbq1QAlsphzYsgN14ObbL9nu2t9U9aDwtCvU92cZ2r0oJ8 +dBm9EkSTNltJCcCI3V+tYhzfMorJw+PgzB1hmZjYDzf7VYaUlSlSiQtXaFpQ4GqI +tmoJ0bYHyNBvRJtQje8hwQkaVmlREQGj//rgaIAMFjmRTYEI8FbVOo2WM6C4Gsym +0Fw3/OfXiegugMFsawYjDLHS7LkKBU5gM7WUt3n2y11NKjuDZEOUKRDD//VOvO9x +uEAKXcxB6I08k7dfH35r2Y5M6W4+1rQfUmljaGFyZCBMYXUgPHJpY2xhdUB1ay5p +Ym0uY29tPokCUQQwAQgAOxYhBMgvo64cvtxr5GuTYMQ87EXBerk8BQJg0bfEHR0g +Q2hhbmdlZCBlbXBsb3llciAyMDIwLTEwLTAxAAoJEMQ87EXBerk8nzQP/iHIY78g +oRfH+QhCCr30+OsoNyawpfLBE3+nE0fmWOmZXlYBw+PwDOsmVw4Ogd4H41IGTKad +X7u+jdc2PjkOnzx/YYDhhLHyl//5+QCQgsYRElz5cUO67wJDi6lGvFLDCyPLnodf +Ub/mnxC+Ge5fpiRdx4xmQCJ1YW14OzgafcdmNWIchkHtO+G09YF+1D3X0z5ppE+3 +A/dnnNbsSVUmGxuFM1GW+On2UlJoMkQJc97E54Nsu2p/bf2rszza85vgwkqZbqQ6 +sLtOzK34ilJxnybOgtLcfSxmOikECmUecglaqfm1oUbhXdOHvICF9qqt+L4FPhm7 +pSaM+tzid7btYoKLqFZ0uzsKGKxCIlmSN2oM5G9Fb6QJalSJzq15mbbxZ1d8C1u0 +X13+rDt7Eo4FQRgrp8TWrG2kwrVJNY6YjECmaYNS8EhHCxAF7ecCA2blpfNu/wh0 +hLi6iTZCbE0WF2Xv4V71ptPThkj0IJXAHkR/X8rVsQQTHfNi0XLLmMP2+nccPapM +GixGSjdr7b3SWLtwa0uD53VOWdk+d9K3aYoJ9O2aSAz78RJSfCrhUYIpDRboyngH +DEPj2mVVlwUxF7JqlSTT6S3Lp/kW/rn6YsIXoZam54XcqheNEcYr39qZPoMtGyqw +pfgBGIqifnSqVLOdD2AKb0AqCqunlY6BJ7AAiQJOBBMBCgA4FiEEyC+jrhy+3Gvk +a5NgxDzsRcF6uTwFAl222c0CGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ +xDzsRcF6uTxovw/+Mqq3Zn5BqiQQx13xOsJ25tvq7aa9BefOwhbSnguz/ZScwwVx +r1Ntp3+KWAdXPlLC0bzPpd6UAH8SXvJYNX3Oj4bKNXG/Vlarqh4ZDw0/nJYJBlfH +NDPU9pUaQzi15p+1H6KlaumhgiO36lX+pqtGAnKa7AMenDxXehuoxeqhKjGsO2Mf +uoBEkcpiNhW4nxBwBDlmR3Evx/orUTejkBu+p+Vvz5lTvG7AqsU0JU6i0gEV+2zB +VSPyfxdNAzY6iNrFszcdhS7BG3xvjUq5xprp/w89DP/008KQDkU3UOcRhR3MTQrL +jXkR1b2wKz+zVyMnTJeZgQrPFvXwGZDVXIYlVYDKL5/gDwNP84Qo8P/iOhMBpz7T +mMxDXK4ijxf+WU67/5qBlVpLX3YEzvGIG8dmePed1NCTAEHPPAsmNUZ8O+Uv7Y5k +/ocJRjG/mCosKvfj92aOfvXTx8/mi+DOYLJGQwT9iLvnNIsYLpKCnWH/HZo3u1LL +hdkb1tMtGf8CITUqOvzf4WK+ouXDKA3meszFWWRMhT8fyZ7wF+ge7fuwazDRUbY7 +xvUzbeT+AZKActHy+QHZonf6FpQy8+qaLI+Wsn42+IoQfdQSNTkWqhvKCwFAugtb +2/HZrwjxiMjNKZiZwC3Of4JZwzasjlUa2AEF6MDa5VT3W193WG2dKKBgHsG0IVJp +Y2hhcmQgTGF1IDxyaWNoYXJkLmxhdUBpYm0uY29tPokCVAQTAQoAPgIbAwULCQgH +AgIiAgYVCgkICwIEFgIDAQIeBwIXgBYhBMgvo64cvtxr5GuTYMQ87EXBerk8BQJo +XCvUAhkBAAoJEMQ87EXBerk86HMP/RzDIYXXTaCXqjgBZykinCpubsNXHIE3s4s7 +q4+luxhipiQL8yx8/YA/sc904wHvhOGQJEDRdsdDprhIhzdUk95arZhdSX89wtf1 +r7U05tN4ereTUEbQcwi4V1u5h+kG6H/MiN6aMzPbf5oZr5JK+xYycQvHDgPFWzVy +FQoWa0Qa0GV369Oj5Iw4O6hM0imYAwNdKEgSXsWZLH/INS9A+zbhFLt3S4MKy9vh +ZU90QphBIRHov/1eIE28ANg144K6Oeer1b9UpYCVjrGg99tvF09OY0rWu4yrMDMo +Uv1Pv3u67dwVoZv7MXR6WVzp0NLagfP73ruGiO/QxUz//6AyOOccF1xUsGcBfRhL +zpV9auWVAOHktpsWNYR64+y6PVWcFtXGaLuZrKil6Xuvgi3Eoz2yD0+dVVRU25sf +abPtLMQmtnGzbvw7zpcQJmY9nPXHluH7AIkuJvCuGdJzPMsvrftiR4fHePxqZCzD +FnWBqt8U8UUuJgnpBGdvC1r8I6Pf19OlL0hkrvmeO1cZke6FXkiLfID8NO94AGM+ +IG/nF03wJvizpOYuI+euCMaoeaSJI8GrDEEub5bMFhE6tYZg7AgwjTICyG20GJVE +IAhQpT5fouE131nO29CtwFEo3k4RuGabIw4YQJf7g+spDTLoGbeulxCaAzr8yO+j +MD9K8Ll9uQINBF222c0BEAC/WAOdFl3fogitq05Q5OD7/KYhpAbvdgifGZWz9+wK +4ktMSmJu/LFhakcH5RaHq1MHT2wuz1eRbrJUQt2O09k+7iTlNPGBLnuZwl4CycZ2 +7aYwiqC5YA1oC4C3JrX0J0K2ZCGZmEOs5UxR6yWfjINvZLvyXmexxwdxD3UKBXdB +CCSScuSFISQNdMZ4zQ90a0mgKUtEO7MLovC1zTrS2v7aRlxf9DgOjoyFwcbk/JYE +01LZOs8/hEsNAYCPhp2GZi+uZvz0n01Xe/T7o1oT93vzpg7YvCcVlbL44bW5ktmT +bkBCgLxiUv2aDK2qdnLgOzqwzNPzXaKvXX7ecoVmW5MATXrz8F0qPWVYLSvpmcla +0Dyk3z7rIxdux8D8Nca0r49YdW9WwJYNfRSU0wL+Rqjnj5Z24FJT7yWLfUd4IqQc +tmSMFlfXX0WXvOPHc93UXg//qtXzZECSQu+qb+09Am6BDB7yq8in7Cjaekx8h1kR +OO9pJEGe6hdrLMbt3fiRxb5/ihAJiG0flH0uYZxDVWpZR10VOILRAGnvHAZVzZz/ +LMMzugZIBqrBEbbwE03W/MY0UlCHKj8b+xCERue5ZxB7hq+jdV2uhQMU5oTstuwJ +Zp20MmiZqQzHubOVxwKVG0QODKtakm3DhTZpd81FSpwl2l2NAejDORd0T4UZ8fSx +0QARAQABiQI2BBgBCgAgFiEEyC+jrhy+3Gvka5NgxDzsRcF6uTwFAl222c0CGwwA +CgkQxDzsRcF6uTy5Rw//ZCOGxDWM5vyBNRCtM6ePC2QrGnmRmf4cltKifZZy8lyk +ZopRwHi5g/rUVxJwjs5hcKn7SaUCXYrce/bqMlIk4jgP8nsYmuP4ayLWfPhO5V0J +rwOrxBUTDWfufk7ZTjDEvAS9ZHciSAufPZw2aKwPJfONNXvZwBlbaMIZP41qRd7b +kX2+7FSr71qc3nvxCdZ7ZlF4vBa4X5TIQSWn0GkXSxZ6xk4694sIdOUwxAG6cM03 +2UJI+mJYyQBc/kMDWVfZivYRs6tRMRqNwexF3WO6npUCGExAcj15EV2vY7VGCWTi +l9docgmHzjSBRXGXDa8sWxfl+1LQp064MivC2dy9bdsSjsbpr6Hi53zql1nYs5tu +Xrtb2gSkpnWMMAvc/Q2GaiJmLTpLC0KKJahIdC3KiQSibiLTCpg7AVuRAeSBYp26 +Zs8Wtl+K71+6jTUOOpMtLCcy1vm6JZbK5+NEtNtSFMl6vxbIrCU8hc+zXXk7xIK/ +LrzmSX/vq/aBGcOPLImTao7z7Ho0I2nLvVMlzNHQpyUmjpvDp2cvE6UQf4m2CoNe +PU9WNvia8B2l3SojNFOJN2vV/k4wPAQldPpt3mPe3LhalyLWhBoP9xScn/ZeRUtW +5o26J3X05N/qwbGf7cw3bPicNoq9pp3DtY14FZbPMYngRqEVrx9Te50kjDQRtheZ +Ag0EWW0CCAEQAMdiZyAE5bAlNVEkKeoSkLwi85t6vjryKRKA2iVAcVvJSQnL+R1z +H5vIGe+qz6zTnQ3oShwiSY+9pr6m01h/bW4OcYo/IWo8wqfryIBfBUNqxQrVJJOV +BKLF/irIeNwPujIOb7Vt6HDQn4oSeMuP3jX9DhM5O73ee1G2AEpfS6tdO8aO7GSI +fIyI9KKKy5XhLRymLlew84+sK9EWvHrFQZEA9/n4xUmRtLkCSeYT9oVoxrvJ9d6F +kRgjAnMcWBhhFNkfhEH5rp87dWpV0QQ7age1HfhzhsSfbyu2Bysm//etNrXJdnFZ +/+oYe2FFRdj54fBX4iHfM7GVD6cYErmxquqk4sXaK5Vhua0l5ijpvQtBFXE0Q6iS +bEQ4kSmQol+rWdZF3eFjzQpGc0LAJsaMzmLazwhvIO2+bJUJkC2kGRA6kvbhR6M7 +XkRqolQwtsDBuuPe81lkFFxYLj8xYvyx5hifTlwhEqn21/z4mwkWmrVVhyC0yMlu +7XX4H+WG43AOjD6IERN8eDA2XrUH3/YCXIyjaoaecn1D98HEkNX4X9xK6f/PKvmS +znAi7Njx8NjOapphE6Bk5sYPCpsQ43nCX+JAoVIx8sHXfwA/VNjvIMu5666iGksL +ESJWe2vr53sUhYgN8lLsUtAm8LquJR+n1NZeOeo+MhA96toY2WopJQh5ABEBAAG0 +IlJ1eSBBZG9ybm8gPHJ1eWFkb3Jub0Bob3RtYWlsLmNvbT6JAjkEEwEIACMFAllt +AggCGwMHCwkIBwMCAQYVCAIJCgsEFgIDAQIeAQIXgAAKCRCXsBQZvZL4CooHD/9p +Ans5dYpciIivjmiN9MnshoVuFpETdrBHLOuedoaZlKK/hE3pTNvHsWvM49Uqv9LN +6VRViVG366ZkIhwHd+/Rs6prfYZcc03qZU9AvU5YU99Lo2pX20SkTdt0aZ99hyTW +vcey3+Qvk55OnpVXtg/VszLWS+gY3M5YqdnHWxXkUo5VaYnTmYbpAaP6sU4QTkL+ +ZBuzrHz9qghVcw100kwY9wD/6U8prPW+TbIi3WvlhO1OXPPQiKGW1hSKG6iLbYZW +LBI6qO0rpzoeF95Q5/PnetkqOC1yZA0WXKlUksJva1RlBJNq46JpjblCzeOS9Nce +L5CohYfeyUMG0X8KymkMVvTaNIXIdhs29aeU48cByNgfAwY9d00kJUMH1rxdAdSV +glAW/LW4N5/JxZhmT3O95p4cSIH/MdfmrXrJPaNDGm3Wg+NnNVT9+Y+0jDm8eacc +wbWU7oDbaYpcCuYf5HAutIhghP1ViljmeSEqKQI97iYAveNCaQ5sAmHqrz5xUYbz +4KMRBcxhD/q6Lz7a+pQ19qTM4J3NQNz6lMib3eQZeQxpQ5tL3ihwzGoFmAWmIZKL +HWjEX2CADMjJK74UR+XHPI0GpfIKD8LBCXmEGRaFMnxx0FYIJscpm8PCAXSi1Icf +nEme9zg4oXjoIKtd7NP22fdmcTv95mx71/SBIq15c7kCDQRZbQIIARAA6NLh9aFn +p2YCziy5PJ/bqL6ukOIvxJGhHa7EfgbZ96nEVYc8+yJgR0bJcV5jzsjzSVr1snXs +uNip1Oxw2rWNA9+R5XrB35c6bygNcezO+5kws8ClzsEE4Rwym8bhfyIAFWfj8Cpi +o4AfLO2mH0VqLVN5+qAHGUsk0ZdQMKu6onEhCkCUQ+BX+RKa9N6fiJ6JoQJa6lqe +jHpFj/TXYeGmTA21UG5VDBN3ff8UcLKxlm+/3atnIWoh+Qs/lqbXFSZtKGyaZpfR +U5SOPKsxoIet3ACcbT2oNFGW0pCJKMFa/iofYRmXoTso9BB0gh5QEIpTa1JAvDh9 +pCPkyCOdu3ntdgDxQAu5BDGDmsgquCqD3rTZKn4mU/XFs175GAhHMrl+hHXSsBOg +NhawMsO0UQzTzM1YCyCd23UFu0U5LfyiItgWmxwUC8qR7xnpIbrxuGq8sr6OH5Jw +GsJNzZUJQD4yQaClZtSO8RisGCOkKGaP4IdOE4ZETU883PuWkcV2AWQiwMNLKtYV +om6pLj+hbPXOmK8oOKbwABOJHGEKJUN36X1tH1gocdNoFxnDwUlPZhSwfr91Mpl4 +6MVo0fqdj8DRLhABcjZM8iXutPAIeb34bGom1M8iod++hnaF8vc3cuC5nGO4QSnj +yYOv4++JvYd2hNhzop6oEnrGX6EKza/ATYUAEQEAAYkCHwQYAQgACQUCWW0CCAIb +DAAKCRCXsBQZvZL4CgpUEACPxK7BgcbdzRyByArFXpZxgvI+5nLCxRkDTYIDvQGB +JmLqXKuE9vZqRercyuHQ1fQbF+AMeiSCBoy5dm26W6P/0WFYKqW9wbixveC6Ode6 +fPNgFnwq2A8MsVXjMBV0cGxCdIUoV56ZW8pgPW+WQY+Qeg7wkeX2BnP+Z6LQ4RFz +JhvLDi59QSBIQZop/UF8e95d4zkXZq22G2Ni+GyrlGytQH1BrMb9Qq7R8wKh//rd +aTCUmiLTRI20xvCoWSGePFmRn1L5+UuPhNdyIa6ik2TNnCdS0GNOqtKOB51Mqkfx +Lm8+KoE5FHyFxveUoodIjIgEMeR/ROTmwM2OhuZHOnQtDuEqHceAJ80ws7B99M7D +xkwj8HXhUeeyvOtQiazgazyWiFPgV1sL5LFb1t03yJ8Y9WiMPoizFBemBNcZ11Nk +kO4Ws1kmys+C6KdlLNcEgDWyosd/iNzqIf+qR06veOYNaIW1ckPmYfyHL5rY1n1U +2wjKCG8R53DLrWCs+CEUOuQko3+eZtOyhLnNUiQHAK291jv/xxd1jhAVdZERYC7O +9PT3XKgOIQWoVzQdRMQfMkQfAOIE1pG9a+ymHf/B6PCs/PohnyOH92+FMfW54Xuf +65ao671ijbh3fPyNGimnt1Omieg9hNAqYfy7M16q87cMXGbt8ntNKtdpgmd05DcA +jpgzBGpCI3MWCSsGAQQB2kcPAQEHQGERVrwQgHfXQ4yk3j7gL78wFsYb6IwF3KNJ +uNgDIfzAtB9TdGV3YXJ0IFggQWRkaXNvbiA8c3hhQGlibS5jb20+iJkEExYKAEEW +IQRlXztcH7P6jRoMpr3kp9IyuTbS/QUCakIjcwIbAwUJA8JnAAULCQgHAgIiAgYV +CgkICwIEFgIDAQIeBwIXgAAKCRDkp9IyuTbS/RThAQDKSI5J5uH5xEL05iJc8tJA +7laZReB0e/PpQXn9ouOfdAD/YvNsLT+KR9f78p6EgTJAmrhSD5hdXPTGCxRdH+d8 +6gK4OARqQiNzEgorBgEEAZdVAQUBAQdAFWtVieD/Iq/zOzWghq1+zw7NEFPas6c5 +nmuU2eOG/2IDAQgHiH4EGBYKACYWIQRlXztcH7P6jRoMpr3kp9IyuTbS/QUCakIj +cwIbDAUJA8JnAAAKCRDkp9IyuTbS/RM2APwK6+IWyi2lOjlIwFWx+xxixJdB0r8R +POZRIFn7jW6jrQEAhfVE9MEBH4iI9kd+fAnuts1eN8Nsmg/F7tDP0voc/AGZAg0E +XRWougEQAKI8lgpf9RPk4We5ywFCpKYU0+xEPoloKVGo17zBB85koU0vJq2O02PY +G+6rbANN0pKLBxPc9a08IRURDmLgwzPCad+UBXgpO2PyYsIZNHeKCGd/2QWEF1f4 +bgjeVDasgBzuJ76wjrvMWawALvAVPDToOsHXy/GXm5fsflSoNcuw5AoTf4NDZKzK +Aj4mwJqdpn6STqnGB+U0OVaPm5Eg97zYLDGDSBLRpApfmb/92Jq9+GU+a6hhpCKh +7z9voGj2t7uilaG0QV9jIZTFpHFVMugxHBZxpyynCodwZ76unzxiYsxRcOE8uaAb +DxoCx/5vpZyPXbM+6yfLWwmgbvPCbftr3xuXIKhgXPH/FMbqCr9Eu53/F1Q446E8 +Ywf3BAr2eBVgbdav4tZTQAGtQFrJjXATdAGPvBiSgroERiSfOTObvLi07aOu//13 +FXyS3zNQQPBSrTVSPdMN7dgBttiFdNUhcXBVqsIzgCZ0iu8pslnPQlYIgD3ozc1X +IgkepjYpiyb9haxYwOqJ4tByLkzvyjfBJ2vi+xu9NFEaeakRfDlxTXqpj7+elabd +HYfijchX2pGutxhOAx1LpCiEJXeoF5NEJ3LufdgHpfvwhMij8j2iAYbaeuyL6aPR +HoXf1jpj8pDMMzUQJaOyTinF0rvUkZdZlZ6uyYjyvKZu139ZP5QrABEBAAG0LnVs +aXNlcyBHYXNjb24gPHVsaXNlc2dhc2NvbmdvbnphbGV6QGdtYWlsLmNvbT6JAlQE +EwEIAD4CGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQSjY6SZKRy7yUDdYuQf +EAJ68AL4sAUCaD2XngUJEMuJZAAKCRAfEAJ68AL4sAT+D/sGa9gg25oV5BceMZFd +V+RxeiOs0oXn9yjlem/Fo2XnRQ6ur60idSDbf9DYRLvpUyA41QiAhSo/vV3/T0P9 +16EWcR4cImF+UIyMBu5k9Oi+PJ9M8/CopBQwuX4/jPtACrgcqK6o/qFBbSsgOxGy +I4OYMYxeDEDc7y0imFSaBAmLUaGJldFmHyQ0Cjd3iluSGgFddeWCb68lW7qFx/Yo +KNyI2uyrD5X2g2rwvJnK2koF1imumHp4K9qIaUZSTo8zMz7q00E9tgk+3S/JVBMa +99msd7Vq4SNuH+gUKiU79SAd/IAp7fJz4Ccq5RbIt6L6KbPR9OKjs6Pl3kLkdNRA +JpZPAQzSw4NvTnMj0QeOSnNEfJPAS5kz2A75vLkz0zynEnrTCtSt+06AMx4qbz6U +hebPqZiB+g4UcDOjPBAON9IJPUWrfSlZwJvdSIFv/NhX83UJOqOegSYdeIHl1KsT +M0MGZIhW8mltjWyiQ5/EPw82LSRy82JmfFmD6iAQKM8q0/HlVsSvQVhkRbtB10Az +XreN/Gact1h22y30LFKoHq4yLP0XXJsynwfhATjBuZ0bOKqp5tFXSbgQ6ImslaYm +dYXNpJwbV0ZT4TPcgIvdDgkd8eJCLy27qtFRrucIG0DbyHjcf64igepqk5k7D9k9 +m0UCmAaCcvI3pVPfyZOAi5GrlokCVAQTAQgAPgIbAwULCQgHAgYVCgkICwIEFgID +AQIeAQIXgBYhBKNjpJkpHLvJQN1i5B8QAnrwAviwBQJkeZ79BQkLJl1DAAoJEB8Q +AnrwAviwVv4QAI5zxgByXoje4adECGK7QXi1Qj6WzRb7UWAxNki2QIftiDkEuy7l +qvGz8mfli0S5XZDZGKkLZEm01/8wq0n3i5uuWNlDcdrdQCnCriqiOIibiYwGUr6A +qd7BfvprHJq5dT5YLRyalNMCFNg+kUU0bzQndpYglrqBu4y+qd++4DdKnDH9I6yZ +R+ekpUVzXVN/WkT7Upy++gqUA3ZfH/E0ZPoZOyDE1Y0IWkNWiZJXm2c13qAdPX+T +qed5+sLXU2rOpLDLoMs2OKMVnSOfq0kVlOGDavZFALlFm9egzjjz5p4E3iTOvcPX +qiNte8Iymoe5TY+RlSAuPDvBVJpRt7XFS/k6ax6mtX7hHu3oVEhxzbJ9ED0tR2Hj +huZ0YEnXLGI/54VrL42o2Irstijdn9Ftx1z8ou21HrAHqXgEtnfVU/QObslKTlUr +0uWwGV6J9iSV0jXh7gaQDepH0LbdgvKIAR5SL5t3/JbXCKsIXGimXHSnAKseL4av +kSAtYOJr84N9CHdkl9Ejft59oyZGqM3gXIa9BojKoK739C/0w0T0llLD3eb3/aQr +ziUf4vIKioOYU3VaVGz4VJzs+lHjTVua2G2u6PXMOa/DFk4H/GZJ08TZHpHck71L +QOq9W42Fu/iye16Atdj200nN8EoWRw+bzhYmQ6DtPNiQ4Tl7Is3hobuYiQJUBBMB +CAA+AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAFiEEo2OkmSkcu8lA3WLkHxAC +evAC+LAFAmC2G7wFCQdi2gIACgkQHxACevAC+LD1Kw//RWdtWzYENL0HPNHyaMXv +FwYZeBQmiy6xoNGY0d8YGCcfRIofxM6qXb3lPIbDMnIjjJR/+yerlgvq1R/ms8MV +/5ioh9aQglnSWrEbMOoVfFXRDUjGvExosMpnSwWxXZ/FcVGGm6/MZaSfsZt7QSNZ +weUjVreC90OldXQeIfRnAPVUCrnuaigKvZRAfkDkgqB3krPLvPqDEF43DnCr+CfH +CSo43takCDgJeUSV0v7GlLqwtN8Hhqk8I45+Ngvd1BO9q0pmzCblR1hOCT8Y7pmq +TLY9nNG5eegna8QM1OM0q0ffeFEYsGtAN9h4CyupAKWvAlMrtw3jQILsOHlnbepz +K5GjlJlk/qMLxUt/n6y8W/49BRmZn6+iOnxfN0a5fEJrt55Ogck5yWDIDjUx/Qsm +Ym5oxP/dXUUeXbprCuMHhEsGcOjRQu0AwUuE/b2y5zzwEQ7Ue4OVXZKgm/Ga4vI9 +3ID6LSvnMfFE7By1DKAcFodUVfxlLAbn5kfsNrPTz+0eT0nya3e1HiNoFXyufxFd +qqDmU1ydcvbxhJ7d/u+xbOQ4M8S59HV3Hi290Ioz9ClzgpRX1Qiihf9AB4NTRRFz +73HT0JTtR7uuHzu/5rauIbez181EYrmb8jw3/Bs0Git/sntNXnI2QNezjQB3STs+ +ZlgwofGKiyPXJZ6U9FCOiS+JAlQEEwEIAD4WIQSjY6SZKRy7yUDdYuQfEAJ68AL4 +sAUCXRWougIbAwUJA8JnAAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRAfEAJ6 +8AL4sM2aD/9oOriKbZUkub7fBjQM1hieTIFxG6mCqQVQ9AP6eFsou8JZ4u2s1maL +bQ4Nlg1+FmqG/J+Shqro0EI9JqBWFT/Yf+f9otMz7LhV9hu04WNYDwmtTIHCw+Ag +zE2gOp7Wlk0CAV2XLr/BL05RuDgfISG9NTovMxtcdnplkN9yAwzA9Xf7+zCB7iCa +FM2QAX6N6LAzT3tDbMty0B4fmTFdaaFR7HihZb+Y/5nYpOnvIpzUWDd/LbXNDgIL +IMTyVazML5xrEvpCDBFyY7kUYoa+RhMaSQZYU5rpJ+byskcjleM/Ne2r/4TJSuu9 +Xd/F0MEliyozkdm3S7JoHs1NTtrhUYbpoiqOVq6NlJeYezQtlYWflKlWbBxDb8fu +Ur6VbNzM4wxOalA+Ko95G7RYgZmetzZ3yBVRNUok4KUJ9BMV4E9LDADWoWg9tnh0 +1eD9NwBYQh23Ao9WrjjSEfXRnJh1bY84TDW0y2HL/nyanFzT5bquLGmBP4NIQ+YF +1uCTj9dk5iTqwImensvsIEFFezfoNMQtkzOuW9ONHfB5H0Pfb8bjdSzpJ7nI12Mq +uQGya0ditK/C2iRekI7i27eFWwlD2O7OzH0Ox/VZNXzZUNPn3YrgdKHkA+nUonZn +mAQuWqo455/1BEgr0QSA3XmhKNcSQhEmSrabsscyKeCa+b5JPeojELkCDQRoem0b +ARAAq/OOHNHyMEvkTLUKwPaRK0yHI0wQYv7mbXlXNyHTqOa23MlfbEwMUvL9Wbe9 +0zVicz6BCF5FmnNFtgFG0mNrbsIG79gk/T8++4hb3jQu+n4wa5Z2D1UnseouNPUZ +BInhKCuQL6nvBQy1bn1J4J2K3NFflIhiwyvJ2z9PwdgfyfdjK46fTPlWMobArDDv +uZQmnpAD0I203F6IQslei+DS5in+Q98A5VyTxa0IdcoWby1n/jr38+2dPeyMUrf+ +fJrXMFSLY6VCGGlDBGCT62JIpZudASZIiHu/llF9imHel2mdhZ/t4cvMX6RksreQ +d/RL0vRBH/GtoJG9WJAh5XLJ/dDmxeCB/G0cgX2Zc91eQw+idPxPwGnQgZKoVUrC +9jyixD3jn9ZYQTTqmZfpfRu4Tq2ydVlUc10fvqygWHcwqWD6GXENOK+G2Tm4iPxL +QEbtnRjGRUzh2htQay6bkxuSXnZmr/5EeQUY9S+nQEvdfDQGA6TW6Pk1U5BL9VU3 +gUOSOryI9v3sm6axvFs8PeZoJwztnwFkTUcmXSZSypJuUsvL5wBFY2VZJ+uMw3Y5 +TGEX7prkB+d0m/U+d0CUZukR3ThRZo4Ntn4Pb0OhyY0opU64I1/Rs5ZmaKugFAXM +ezUKitSODYJJlaSOgL8/UEbBajKCxgbLVAtd7b+SfEJM6BUAEQEAAYkEcgQYAQoA +JhYhBKNjpJkpHLvJQN1i5B8QAnrwAviwBQJoem0bAhsCBQkDwmcAAkAJEB8QAnrw +AviwwXQgBBkBCgAdFiEEwrJdm0Jy2ylWW6h/PHgk85qJV1gFAmh6bRsACgkQPHgk +85qJV1iBYw/+LZMBO7ez7zIvvhDZbsf7zlCsLecYY3wVV4OyRfBu7992ZMWin/oD +ac9I/Th/fH46ghj4n93UoZ2TQcYSZ/wMBUUk4mFzWND2BS14csZ+PAdeqnjOHy2z +Oz8dnbPC+A7d9U3o2WGExfkDiwfQ5vPGKH8qGTCVFwFtdhi+5PpalozVLsGj2O2G +ytjasvSVVocvijd0IJFVHkUp3LaZg2WBywTJYOYZrxKzYKoJNoivE3F9lLd4BLEO +hXfzS6JHaL8pX0Eu0SXP9gBdKGZAnwJiYViDMIMaVN9kGhCidGebl3WD3cc40Huf +1W1rqkEsO8N0QmIH87LUbwtKjBbmO5AHlst/o9mSyhiZuGNwjBf6CmeSVmtfE5Nh +7RLTXLxrmUP3DHQv5aIqKfARNGeflegwyvH1zWd86qLGQ8ArL+fCeg5hI9jAtNjU +ZdPzu1pl9zcFEaSZHFyu/exTiqvMdpvyMnQ5rHne+9wcj2qZ1xEEO1vxfyHu5cdl +O75tVEG2IU7oeEcSFjmyq4bM0/RimFlJ0vt1eP6sFnrXUwDR2YHCnbt3CUBXbooQ +XwNanvzRqQLuU5vWDzdhqDiUSh8J25ZIcx5otFyQPsfAOM55wtcAt8gk1JKE9Y+v +lY/2Q2cGOufNTjy0LF+fHx4lQyzLrvdL/wNK1PIofWaTYEEhtDqRjNi4OQ/+Pt20 +KFxq1c2mO1XQgNy1ura09Zvrtw+lMQFNqICOgLXMViy1JuVBTWH1IMUzIxS60qVN ++6qWZuI8eZLJpnK9pJVmXHVjHzID3ourv0BN34KdiXwJZANpBDgT6LimwmmtKfte +CcX5EGG6NmI/+NxG5PAnVFmba/ik20GYBpbllc/JHh8pksWxbbYUkM3Mu1lwEHPw +c8PcgYfSylRp/k9qVig2kGCHp64JY/xbR8IrIg/LpsgvnJwe6jcStbMgct6Q72AZ +5pI3+3VUuB0TXoZqjO7w7KnHa7Rz1+FHtqphM7E99yt6S4ER68cArE57VsgSpyCp +3GG3dtcW996rHqS+afxj0SEaMDxb89atpWgKspV0ddV7juXiL/VBlTdTGcR4CP3s +Ctbof86zVyMr0rvvHMKkDPwUxPsBo9Cre5F5RoOqg0gxM760IUHZ1hBh5fxgaAgt +7jRsykKe9jrPELtS+U75jGFlJCQXzRi2T/Z4e6gt4SIz0hb/T7PGM/pI2ad+GWQt +6+52UUXGhiWDm5YfoEgotJQXWAJEgUqPdPU6cysOUQCQ1isDdYBPEdYRgBk2xnjB +LgjSg9jZtnxJxL7arCZytR/hmir+mtoElj0ZiyUCHb0yZea9Iy5W3RKVhhYp0x6m +fzr9XS8liksqY0euFOjJxppUg3enr0CnmYjO1Yu5Ag0EXRWougEQAL+U+bFC99fZ +LnbRc6c4PQae+qcoVS8gJz6F9T29bujsPL1l0YUtGCaStltcI2jDA+UYjrpVI5R/ +C54S+YmHkFBFci/uCS8uELvI7s6nKoCItWu7HYrr1vsnD/L3UVllnJo7IfyRd1U0 +7uoBDho5NOWf4NshdrZZbV+WNSS1sWFLOMrcJG1Uvs1SAesV48Z8RNmKYghThnGO +1fnY0tCnKiHnpagalb36hd7bAQZhhhwcmzMoP2JnI0KlJQtPulE0XBlDfJBc2m06 +i1gLDQvyACZLAnpJSB74x0uTUEalcSOLusS9TGP33KaHexNapzN0oCBha3CKKzQo +OkusK0OiGtzQknFzYPZwjUVtwX5vgYDX4oZQBH3J3f4QLnJctg9TR3cE3lpeMJpc +Kv+9YtLj8txcEAiPMYXUl2vVrm5AEPskG//gnEsJ32v1qp9T6lJSAzT7igHU/Gqj +7R+bubjYAu971z63lirur0H94dLHqxpx+rsMP4Sjo27EhkX2muXxvze48T9ivQg0 +T4mb8M8s70V9Bi64tmzJvdFxDxFoCtV1Yy15gGR7a1sDGVpYrZf6q8OqOxtE3ONA +pkQz6Jvy5ZwvFBAgXyD/o4Bjolc80mthMzICRGzLyt6AjJlwKz0pXDr6MCcc5cDI +VRGqnINqWVIZ3axQVRGJ1OqCtkhUEnrJABEBAAGJBHIEGAEIACYCGy4WIQSjY6SZ +KRy7yUDdYuQfEAJ68AL4sAUCZHme/QUJCyZdQwJACRAfEAJ68AL4sMF0IAQZAQgA +HRYhBKYCNTD8U0Yf7JH5nATNPy/eB5V4BQJdFai6AAoJEATNPy/eB5V4UcQQAKwp +Dv1U0ooKOHu1xERYAjPVH/ZjpZDEHED3izCjRPGs1EpSBF0cKpD36EjR6t9qYHha +vf7r+oF2b3epOPzFn0/70TIogQGkrW/tfRcOXbVeliO28EizkBxBic9Suz9mkZvn +JdNjW8jlwmDbto/CR6oQGmcUjPYmyA9BWcMuTSW1T/Wka05shYfgmRGXrFgUW4YS +3F0X7RcF5o4W8vl7wOzVsuv7BFcJhrRvWjRRLy3y6pbBD8qDh9J9PzoaOR9T0201 +7YtSOgpFmTiaJZsLlm9wKZLxTf+o3f4AUblRFGrzp9BJJa8So9CqZ2DwT203Loph +cEIdTvmGXD3M9qx1zu/DdRQioKmSiE4jN0sCu36wxWSVMqWGY2MKRumC4Er8R/sr +XYnJUedRSHLGbTewoosAufmoGWTCVepIuAIo9hJhIWFX25fhl7NK1x5XjB7HWahx +TQQllfNuFCI+FohAUVfIim5u8WnDiW+s1nTzlCK3Ql264SvgxQMMG3QdxA5Llqk+ +lGQ1hTtkViAg1wZu32JRYutOxWbqRBUBep4gozu4nkulLUAPAM51YAriV6gWoJF5 +v2tw8DbatoMt7xDU8XxwAO20HBfWZf2wCfo11wEG816ZRKbz5GUvhbaDkQj8tugk +Q/mx47fa9BG5rQw7vT8bZKXOqJ1tV1OL/jHgFILkSNcP/2+V+7/LYHaUdl8cZDkU +lgUGxOGPKgHEZTVooTru8dmV6LfzgHvgnR1621ygMgqh2aTtoT+QcdV3GFlkAztT +c1SnwHhPIVAenFFe2pU/bq2pOeGy8eNBLBQh781VJXOfdu5JWh9fL1WIbTBOMggV +icxSqTtpS9klrWWp1nbNa5KY06NDVRwM1GP9+nlJTwrWqI5gLIefbfRUZVN/ZRQ0 +mfXXLn8S/YfDeHMPCQAr4LteFEgoTzdT61ykSzbGU9M3WCVM+YqVyOP/y+uuCIzq +dzY8oXSIdsl1quAoQAlkF8QKzrJQnqqp08Yu1g95zDxdWC31mAW9jUm95kdPDFg+ +26T7daYvda71Sp7DaaZUVjo3R1hQTcD9FBY1OitF10AaFhVN5I5T6dIpQbe+H3f0 +ktzrCEMczBHZay8qMSgvxKILTYl/59L9BUOTgbJvRpjGl7QEqKMZZ38C8ZlNoIbX +DXxNbr3N5kq51S/VTiTZIHYNr63HbeDvcZJb6BFk4uco+0dvRP36LZTxgqC1z8WW +S+MXUlGW015NuT5wHRhP5QQd6C+Vfu6YwqpQ8oPnhWuZp9PbwHWxNsiHWKv2fa+I ++AYr4g11Wn7s3obKbSPhN0Kc+DZfn8pfkxUEQZCmREkLImhJGHlBVuPBd/vmDPb3 +Kb7eSBvBLoXBSnU/UEHGsFNOuQINBGh6bfwBEADAEESrYyW7HjgOHY5KTdyNuq09 +Ge8926bwmSg0wdSdPc7swX/RSz/5mU7et2NdPnB/Hg6iticfyZ+E8UDAJTFPuWDJ +Azrs3Y+7AcgSJIop2DhRu7XEB7uY0lmMTaNuq4yfvNqJlTI8D9DZmB8mFaTxmjdP +FI1eh8LKuFcWX/KSh1XELZOLQLgbDvHayeEdpBl9wBcZmeQzB+0NqMQctDuJBtg0 +LEpe5Be5sksDcD8NRKacdlAh7T2ta+gyC972lCUIaxFv89hzEuCPx7Kn6ynP/4XR +LlliMdz3C6BRqkBzXz4sCzomakE7t1SwsGs6zX0DWaWboFsKPmCt5LjdlDKeAk8m +xVuKO5Z8m4VzfPPkI9baXX99CLRwyLNj/IvXjg0+krMZ/fJ5oSMmUq8ZYL2kw1f3 +yag7FRuOgUnR0vIe98sZUThhBLxOoP4qe4UmxR4qpfE650J0R9lMx/jyW5nU1Nen +CsUdFWeesFdDl4R/4kiXTVf4ygLfx4j8/iZ+iNQt+XlCCbZXDCBxrnuCQMZm6xif +vhma4Buwz1Dc/OVWaPkzc5g6RuYB1fExum4q7bbKlnx8sruHHGKnb+XTgQbKygdN +K+OWpRzFyXWe5n9TOjkA6mP6UuTGM2Uo/ogwLON4LL1rkE3lNZWA2yDevbfy5oHU +oU64+dnU0JxUwQJ/NQARAQABiQI8BBgBCgAmFiEEo2OkmSkcu8lA3WLkHxACevAC ++LAFAmh6bfwCGwwFCQPCZwAACgkQHxACevAC+LC7cQ//RBnYdkYlgd1g3gsvulRN +nxNWtY9qhm9SPTM2moBiIzXzS0GtGUXeAvBdj8TKuOIPaSU9eE4v08nVvFJ2r3Dt +noOQD/pqzj+GxfCPprcdWLZ8gzzgT7WsRV3NYdahe9Cg8s0CZfQt54vza27yPE08 +oeyYxRKn8oOT1PptkwcKCvnEKAFW+jRAPWwMyrGsqlYOfehOAN1RJ/C5Il7IXvej +Bl8BDQmW1CYVNfguTPqtl1IuAcH523HIFt3+ux8jxGLdXcmLkA9fETNiOtcr1Hwk +vVDI2bgb90ChEQ2qeOsen4O536uzWagQeCrW9Kaf/zSSXT/Hj+87PrW7ixA7yxd/ +kYYgVK0dnVse6mSzNSUah3fObbbwdcSu3/9/ExOydkk6SKR5QYFq6KllGG08N4Yi +HzfaTUopHjPISo2fsOfB6168fIMTdLautHPbQLMcu8KWZr7n59Ok/QGqjxpwK34d +5gM5OBvYxteSXBjuWCqxNTPctKasgYHZLvs6fOw/ExLlgWtxjxzFapMM+U5v6L6W +uprNiorjSvalWG/CWlIOQMTp5MSUo56JkQlX03+snHHsXZhOBBJFDYRY/+mPlAOF +gsYQEuBq2iwwEVFpyj5MNmGOJu3Fjwx81xfqTdyhy3o/rtTvtMtavTpqUNMqIKNX +585niB7yWImJkjbEqvRVM08= +=M4ZP +-----END PGP PUBLIC KEY BLOCK----- diff --git a/knife.d/update_node_archives.js b/knife.d/update_node_archives.js index 7d53f38f3..86d3b5c3b 100755 --- a/knife.d/update_node_archives.js +++ b/knife.d/update_node_archives.js @@ -16,43 +16,20 @@ const architectures = ["amd64", "arm64", "arm", "ppc64le", "s390x"]; const nodeVersions = {}; -// Node.js release team GPG key fingerprints. -// Keep in sync with https://github.com/nodejs/node#release-keys -const NODE_RELEASE_KEYS = [ - "4ED778F539E3634C779C87C6D7062848A1AB005C", // Beth Griggs - "141F07595B7B3FFE74309A937405533BE57C7D57", // Bryan English - "74F12602B6F1C4E913FAA37AD3A89613643B6201", // Danielle Adams - "DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7", // Juan José Arboleda - "CC68F5A3106FF448322E48ED27F5E38D5B0A215F", // Marco Ippolito - "8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600", // Michaël Zasso - "890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4", // Richard Lau - "C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C", // Ruben Bridgewater - "108F52B48DB57BB0CC439B2997B01419BD92F80A", // Ruy Adorno - "A363A499291CBBC940DD62E41F10027AF002F8B0", // Ulises Gascón -]; +// Committed Node.js release public keys (active keys only), generated from +// https://github.com/nodejs/release-keys by knife.d/update_node_keys.sh. +const NODE_KEYS_FILE = path.join(__dirname, "nodejs_keys.asc"); /** - * Import Node.js release team GPG keys into an isolated temporary keyring. - * Returns the path to the temporary GNUPGHOME directory. - * Caller is responsible for deleting it when done. + * Import the committed Node.js release public keys into an isolated temporary + * keyring. No network access - keys come from the in-repo file. + * Caller is responsible for deleting the returned GNUPGHOME. */ const importReleaseKeys = () => { const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "gpg-distroless-")); - fs.chmodSync(tmpHome, 0o700); // gpg requires 700 on its home directory + fs.chmodSync(tmpHome, 0o700); const env = { ...process.env, GNUPGHOME: tmpHome }; - const keyList = NODE_RELEASE_KEYS.join(" "); - try { - execSync( - `gpg --keyserver hkps://keys.openpgp.org --recv-keys ${keyList}`, - { stdio: "pipe", env } - ); - } catch (_) { - // Fall back to a secondary keyserver if the primary is unavailable. - execSync( - `gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys ${keyList}`, - { stdio: "pipe", env } - ); - } + execSync(`gpg --batch --import ${NODE_KEYS_FILE}`, { stdio: "pipe", env }); return tmpHome; }; diff --git a/knife.d/update_node_keys.sh b/knife.d/update_node_keys.sh new file mode 100755 index 000000000..15360968a --- /dev/null +++ b/knife.d/update_node_keys.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Copyright 2026 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Regenerates knife.d/nodejs_keys.asc from nodejs/release-keys (active keys only). +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +OUT="${SCRIPT_DIR}/nodejs_keys.asc" +WORKDIR="$(mktemp -d)"; GNUPGHOME="$(mktemp -d)"; chmod 700 "${GNUPGHOME}"; export GNUPGHOME +trap 'rm -rf "${WORKDIR}" "${GNUPGHOME}"' EXIT +git clone --depth 1 https://github.com/nodejs/release-keys "${WORKDIR}/release-keys" +cp "${WORKDIR}/release-keys/gpg-only-active-keys/"* "${GNUPGHOME}/" +gpg --armor --export > "${OUT}" +echo "Wrote ${OUT}"