forked from apache/superset
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(releasing): adding SHA512 and RSA signature validation script to…
… verify releases (apache#26278)
- Loading branch information
Showing
4 changed files
with
172 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -388,8 +388,23 @@ The script will generate the email text that should be sent to [email protected] | |
|
||
## Validating a release | ||
|
||
Official instructions: | ||
https://www.apache.org/info/verification.html | ||
|
||
We now have a handy script for anyone validating a release to use. The core of it is in this very folder, `verify_release.py`. Just make sure you have all three release files in the same directory (`{some version}.tar.gz`, `{some version}.tar.gz.asc` and `{some version}tar.gz.sha512`). Then you can pass this script the path to the `.gz` file like so: | ||
`python verify_release.py ~/path/tp/apache-superset-{version/candidate}-source.tar.gz` | ||
|
||
|
||
If all goes well, you will see this result in your terminal: | ||
```bash | ||
SHA-512 verified | ||
RSA key verified | ||
``` | ||
|
||
There are also additional support scripts leveraging this to make it easy for those downloading a release to test it in-situ. You can do either of the following to validate these release assets: | ||
* `cd` into `superset-frontend` and run `npm run validate-release` | ||
* `cd` into `RELEASES` and run `./validate_this_release.sh` | ||
|
||
## Publishing a successful release | ||
|
||
Upon a successful vote, you'll have to copy the folder into the non-"dev/" folder. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# Licensed to the Apache Software Foundation (ASF) under one | ||
# or more contributor license agreements. See the NOTICE file | ||
# distributed with this work for additional information | ||
# regarding copyright ownership. The ASF licenses this file | ||
# to you 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. | ||
|
||
#!/bin/bash | ||
|
||
# Function to determine Python command | ||
get_python_command() { | ||
if command -v python3 &>/dev/null; then | ||
echo "python3" | ||
else | ||
echo "python" | ||
fi | ||
} | ||
|
||
# Function to determine Pip command | ||
get_pip_command() { | ||
if command -v pip3 &>/dev/null; then | ||
echo "pip3" | ||
else | ||
echo "pip" | ||
fi | ||
} | ||
|
||
PYTHON=$(get_python_command) | ||
PIP=$(get_pip_command) | ||
|
||
# Get the release directory's path. If you unzip an Apache release and just run the npm script to validate the release, this will be a file name like `apache-superset-x.x.xrcx-source.tar.gz` | ||
RELEASE_DIR_NAME="../../$(basename "$(dirname "$(pwd)")").tar.gz" | ||
|
||
# Install dependencies from requirements.txt if the file exists | ||
if [ -f "path/to/requirements.txt" ]; then | ||
echo "Installing Python dependencies..." | ||
$PYTHON -m $PIP install -r path/to/requirements.txt | ||
fi | ||
|
||
# echo $PYTHON | ||
# echo $RELEASE_DIR_NAME | ||
|
||
# Run the Python script with the parent directory name as an argument | ||
$PYTHON ../RELEASING/verify_release.py "$RELEASE_DIR_NAME" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import re | ||
import subprocess | ||
import sys | ||
from typing import Optional | ||
|
||
import requests | ||
|
||
# Part 1: Verify SHA512 hash - this is the same as running `shasum -a 512 {release}` and comparing it against `{release}.sha512` | ||
|
||
|
||
def get_sha512_hash(filename: str) -> str: | ||
"""Run the shasum command on the file and return the SHA512 hash.""" | ||
result = subprocess.run(["shasum", "-a", "512", filename], stdout=subprocess.PIPE) | ||
sha512_hash = result.stdout.decode().split()[0] | ||
return sha512_hash | ||
|
||
|
||
def read_sha512_file(filename: str) -> str: | ||
"""Read the corresponding .sha512 file and process its contents.""" | ||
sha_filename = filename + ".sha512" | ||
with open(sha_filename) as file: | ||
lines = file.readlines() | ||
processed_sha = "".join(lines[1:]).replace(" ", "").replace("\n", "").lower() | ||
return processed_sha | ||
|
||
|
||
def verify_sha512(filename: str) -> str: | ||
"""Verify if the SHA512 hash of the file matches with the hash in the .sha512 file.""" | ||
sha512_hash = get_sha512_hash(filename) | ||
sha512_file_content = read_sha512_file(filename) | ||
|
||
if sha512_hash == sha512_file_content: | ||
return "SHA verified" | ||
else: | ||
return "SHA failed" | ||
|
||
|
||
# Part 2: Verify RSA key - this is the same as running `gpg --verify {release}.asc {release}` and comparing the RSA key and email address against the KEYS file | ||
|
||
|
||
def get_gpg_info(filename: str) -> tuple[Optional[str], Optional[str]]: | ||
"""Run the GPG verify command and extract RSA key and email address.""" | ||
asc_filename = filename + ".asc" | ||
result = subprocess.run( | ||
["gpg", "--verify", asc_filename, filename], capture_output=True | ||
) | ||
output = result.stderr.decode() | ||
|
||
rsa_key = re.search(r"RSA key ([0-9A-F]+)", output) | ||
email = re.search(r'issuer "([^"]+)"', output) | ||
|
||
rsa_key_result = rsa_key.group(1) if rsa_key else None | ||
email_result = email.group(1) if email else None | ||
|
||
# Debugging: print warnings if rsa_key or email is not found | ||
if rsa_key_result is None: | ||
print("Warning: No RSA key found in GPG verification output.") | ||
if email_result is None: | ||
print("Warning: No email address found in GPG verification output.") | ||
|
||
return rsa_key_result, email_result | ||
|
||
|
||
def verify_rsa_key(rsa_key: str, email: Optional[str]) -> str: | ||
"""Fetch the KEYS file and verify if the RSA key and email match.""" | ||
url = "https://downloads.apache.org/superset/KEYS" | ||
response = requests.get(url) | ||
if response.status_code == 200: | ||
if rsa_key not in response.text: | ||
return "RSA key not found on KEYS page" | ||
|
||
# Check if email is None or not in response.text | ||
if email and email in response.text: | ||
return "RSA key and email verified against Apache KEYS file" | ||
elif email: | ||
return "RSA key verified, but Email not found on KEYS page" | ||
else: | ||
return "RSA key verified, but Email not available for verification" | ||
else: | ||
return "Failed to fetch KEYS file" | ||
|
||
|
||
def verify_sha512_and_rsa(filename: str) -> None: | ||
"""Verify SHA512 hash and RSA key.""" | ||
sha_result = verify_sha512(filename) | ||
print(sha_result) | ||
|
||
rsa_key, email = get_gpg_info(filename) | ||
if rsa_key: | ||
rsa_result = verify_rsa_key(rsa_key, email) | ||
print(rsa_result) | ||
else: | ||
print("GPG verification failed: RSA key or email not found") | ||
|
||
|
||
if __name__ == "__main__": | ||
if len(sys.argv) != 2: | ||
print("Usage: python script.py <filename>") | ||
else: | ||
filename = sys.argv[1] | ||
verify_sha512_and_rsa(filename) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters