Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
226 changes: 226 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
## Signing Releases with Minisign

Starting from **version 2.0.3**, all Volta release artifacts must be cryptographically signed using [Minisign](https://jedisct1.github.io/minisign/) to ensure authenticity and integrity.

### Prerequisites

Maintainers must have Minisign installed:

```bash
# macOS
brew install minisign

# Ubuntu/Debian
sudo apt install minisign

# Fedora
sudo dnf install minisign

# Or download from: https://jedisct1.github.io/minisign/
```

---

### One-Time Setup: Generate Release Signing Keys

**This only needs to be done once** (or when rotating keys):

```bash
# Generate a key pair for signing Volta releases
minisign -G -p volta-release.pub -s volta-release.key

# You'll be prompted to create a password - use a strong one!
# This creates:
# volta-release.pub - Public key (will be embedded in install.sh)
# volta-release.key - Private key (keep this SECRET and secure)
```

**IMPORTANT:**

- **Never commit `volta-release.key` to the repository**
- Store the private key in a secure, encrypted location
- Share the password securely among authorized maintainers only
- Commit `volta-release.pub` to the repository for reference
- Update the `Volta_PUBLIC_KEY` constant in `dev/unix/volta-install.sh` with the public key

---

### Signing a Release

After building release artifacts for all platforms, sign each one:

#### 1. Build Release Artifacts

```bash
# Example for v2.0.3
# (Adjust based on your actual build process)
./build-release.sh 2.0.3
```

This should produce tarballs for all supported platforms:

- `volta-2.0.3-macos.tar.gz`
- `volta-2.0.3-linux.tar.gz`
- `volta-2.0.3-linux-arm.tar.gz`

#### 2. Sign Each Artifact

```bash
# Navigate to the directory with your release artifacts
cd target/release # or wherever your builds are

# Sign each platform's tarball
minisign -Sm volta-2.0.3-macos.tar.gz
minisign -Sm volta-2.0.3-linux.tar.gz
minisign -Sm volta-2.0.3-linux-arm.tar.gz

# You'll be prompted for the private key password for each file
# This creates .minisig files:
# volta-2.0.3-macos.tar.gz.minisig
# volta-2.0.3-linux.tar.gz.minisig
# volta-2.0.3-linux-arm.tar.gz.minisig
```

#### 3. Verify Signatures Locally

**Always verify signatures before uploading** to catch any issues:

```bash
# Verify each signature
for file in volta-2.0.3-*.tar.gz; do
echo "Verifying $file..."
if minisign -Vm "$file" -p volta-release.pub; then
echo " $file verified successfully"
else
echo " FAILED: $file"
exit 1
fi
done
```

#### 4. Upload to GitHub Releases

1. Go to https://github.com/volta-cli/volta/releases
2. Click "Draft a new release"
3. Tag: `v2.0.3`
4. Upload **BOTH** the tarballs and their signatures:
- `volta-2.0.3-macos.tar.gz`
- `volta-2.0.3-macos.tar.gz.minisig`
- `volta-2.0.3-linux.tar.gz`
- `volta-2.0.3-linux.tar.gz.minisig`
- `volta-2.0.3-linux-arm.tar.gz`
- `volta-2.0.3-linux-arm.tar.gz.minisig`

#### 5. Verify Public Accessibility

Before publishing the release, verify signatures are downloadable:

```bash
# Test each signature URL (replace v2.0.3 with your version)
curl -I https://github.com/volta-cli/volta/releases/download/v2.0.3/volta-2.0.3-macos.tar.gz.minisig
curl -I https://github.com/volta-cli/volta/releases/download/v2.0.3/volta-2.0.3-linux.tar.gz.minisig
curl -I https://github.com/volta-cli/volta/releases/download/v2.0.3/volta-2.0.3-linux-arm.tar.gz.minisig

# All should return: HTTP/2 200
```

#### 6. Publish the Release

Once verified, publish the GitHub release. Users will now get automatic signature verification!

---

### Key Management

#### Storing the Private Key

**Option 1: Local Secure Storage (Recommended for individual maintainers)**

- Store in an encrypted drive/partition
- The key is password-protected by default (minisign feature)
- Keep offline backups in secure locations

**Option 2: CI/CD Secrets (Recommended for automated releases)**

- Store the private key in GitHub Secrets
- Automate signing in the release workflow
- Limit access to authorized maintainers only

#### Key Rotation

If the private key is compromised or as part of regular security hygiene:

1. Generate new keys (follow "One-Time Setup" above)
2. Update `Volta_PUBLIC_KEY` in `dev/unix/volta-install.sh`
3. Create a new release with the updated install script
4. Announce the key rotation to users
5. Consider re-signing recent releases with the new key

#### Public Key Location

- **Embedded**: The public key is hardcoded in `dev/unix/volta-install.sh` as `Volta_PUBLIC_KEY`
- **Repository**: Also stored as `volta-release.pub` in the repo for reference
- **Documentation**: Listed in README.md security section

---

### Troubleshooting

#### "Signature verification failed" during local testing

```bash
# Make sure you're using the correct public key
cat volta-release.pub

# Verify the key in install.sh matches
grep Volta_PUBLIC_KEY dev/unix/volta-install.sh
```

#### "Permission denied" when signing

```bash
# Make sure the private key has correct permissions
chmod 600 volta-release.key
```

#### Forgot the private key password

Unfortunately, there's no way to recover it. You'll need to:

1. Generate new keys
2. Update the public key in `install.sh`
3. Re-sign all future releases with the new key

---

### Quick Reference

```bash
# Generate keys (one-time)
minisign -G -p volta-release.pub -s volta-release.key

# Sign a release
minisign -Sm volta-VERSION-PLATFORM.tar.gz

# Verify a signature
minisign -Vm volta-VERSION-PLATFORM.tar.gz -p volta-release.pub

# Verify all signatures
for f in volta-*.tar.gz; do minisign -Vm "$f" -p volta-release.pub || echo "Failed: $f"; done
```

---

### Security Notes

- The private key password adds an extra layer of security
- Signatures prove authenticity (from Volta maintainers) and integrity (not modified)
- Users' install script automatically verifies signatures before installation
- If verification fails, installation is aborted for security
- Old releases (< v2.0.3) don't have signatures and skip verification

---

### Questions?

If you have questions about the signing process, please reach out to the core maintainers or open a discussion in the repository.
126 changes: 124 additions & 2 deletions dev/unix/volta-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
# has, fetch and install the appropriate build of Volta, and modify the user's
# profile.

readonly Volta_PUBLIC_KEY="RWTf5is8+3rdT2AIPYiQqEkdqqbIJGnRZzoq6ztC6mQaoTDIxfiSSozL"

# NOTE: to use an internal company repo, change how this determines the latest version
get_latest_release() {
curl --silent "https://volta.sh/latest-version"
Expand All @@ -22,8 +24,20 @@ download_release_from_repo() {
local filename="volta-$version-$os_info.tar.gz"
local download_file="$tmpdir/$filename"
local archive_url="$(release_url)/download/v$version/$filename"
local signature_url="${archive_url}.minisig"
local signature_file="${download_file}.minisig"

curl --progress-bar --show-error --location --fail "$archive_url" --output "$download_file" --write-out "$download_file" || return 1

#download the signature file
info 'Fetching' "Signature file"
if ! curl --silent --show-error --location --fail "$signature_url" --output "$signature_file"; then
error "Could not download signature file from $signature_url"
eprintf "Signature verification cannot proceed without the signature file."
return 1
fi

curl --progress-bar --show-error --location --fail "$archive_url" --output "$download_file" --write-out "$download_file"
echo "$download_file"
}

usage() {
Expand Down Expand Up @@ -70,6 +84,85 @@ bold() {
command printf '\033[1m%s\033[0m' "$1"
}

# check if Minisign is available
check_minisign() {
if command -v minisign >/dev/null 2>&1; then
return 0
fi

warning "minisign not found. Attempting to install"

if [[ "$OSTYPE" == "darwin"* ]]; then
#macOS
if command -v brew >/dev/null 2>&1; then
brew install minisign
else
error "Homebrew not found. Please install minsign manually"
eprintf " Visit: https://jedisct1.github.io/minisign/"
return 1
fi
elif [[ "$OSTYPE" == "linux"* ]]; then
#Linux
if command -v apt >/dev/null 2>&1; then
sudo apt update && sudo apt install -y minisign
elif command -v dnf >/dev/null 2>&1; then
sudo dnf install -y minisign
elif command -v yum >/dev/null 2>&1; then
sudo yum install -y minisign
else
error "Unsupported package manager. Please install minisign manually."
eprintf " Visit: https://jedisct1.github.io/minisign/"
return 1
fi
else
error "Unsupported OS ($OSTYPE). Please install minisign manually."
eprintf " Visit: https://jedisct1.github.io/minisign/"
return 1
fi

#Verify Installation succeeded
if ! command -v minisign >/dev/null 2>&1; then
error "Failed to install minisign. Please install it manually."
return 1
fi

info "Minisign successfully Installed"
return 0

}

#Verify the signature of a download release
verify_release_signature() {
local archive_file="$1"
local signature_file="${archive_file}.minisig"

info 'Verifying' "release signature"

#check if signature file exists
if [ ! -f "$signature_file" ]; then
error "Signature file not found: $signature_file"
eprintf "Cannot verify the release without a signature file."
return 1
fi

#verify using minisign
if minisign -Vm "$archive_file" -P "$Volta_PUBLIC_KEY" 2>/dev/null; then
info 'Verified' "release signature successfully"
return 0
else
error "Signature verification failed for $(basename "$archive_file")"
eprintf ""
eprintf "This could mean:"
eprintf " . The file was corrupted during download"
eprintf " . The file has been tampered with"
eprintf " . You are using an outdated installer script"
eprintf ""
eprintf "Please try again or report this issue at:"
eprintf " https://github.com/volta-cli/volta/issues"
return 1
fi
}

# check for issue with VOLTA_HOME
# if it is set, and exists, but is not a directory, the install will fail
volta_home_is_ok() {
Expand Down Expand Up @@ -259,14 +352,43 @@ install_release() {
info 'Checking' "for existing Volta installation"
if upgrade_is_ok "$version" "$install_dir" "$is_dev_install"
then
#Only verify signatures for versions that have them
##Supposing signatures start from v2.0.3 (can be adjusted)
local should_verify=false

#parse version number
local version_number="${version#v}"

#check if version >= 2.0.3
if printf '%s\n2.0.3\n' "$version_number" | sort -V -C 2>/dev/null; then
should_verify=true
fi
if [ "$should_verify" = true ]; then

#ensure mnisign is available
if ! check_minisign; then
return 1
fi
else
warning "Version $version predates signature verification"
info 'Skipping' "signature verification for older releases."
fi

download_archive="$(download_release "$version"; exit "$?")"
exit_status="$?"
if [ "$exit_status" != 0 ]
then
error "Could not download Volta version '$version'. See $(release_url) for a list of available releases"
return "$exit_status"
fi

if [ "$should_verify" = true]; then
#verify signature before installing
if ! verify_release_signature "$download_archive"; then
#remove the download file if verification fails
rm -f "$download_archive" "${download_archive}.minisig"
return 1
fi
fi
install_from_file "$download_archive" "$install_dir"
else
# existing legacy install, or upgrade problem
Expand Down