feat: add Windows code signing infrastructure (bd-14v0)
Implements Authenticode signing for Windows binaries to reduce AV false positives. Changes: - Add scripts/sign-windows.sh for osslsigncode-based signing - Update .goreleaser.yml with post-build signing hook - Update release.yml to install osslsigncode and pass secrets - Update docs/ANTIVIRUS.md with signing verification instructions - Update scripts/README.md with signing script documentation The signing is gracefully degraded - releases continue without signing if the certificate secrets are not configured. Required secrets for signing: - WINDOWS_SIGNING_CERT_PFX_BASE64: base64-encoded PFX certificate - WINDOWS_SIGNING_CERT_PASSWORD: certificate password 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -187,6 +187,56 @@ This script fixes all those issues and is now used by `release.sh`.
|
||||
|
||||
---
|
||||
|
||||
## sign-windows.sh
|
||||
|
||||
Signs Windows executables with an Authenticode certificate using osslsigncode.
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
# Sign a Windows executable
|
||||
./scripts/sign-windows.sh path/to/bd.exe
|
||||
|
||||
# Environment variables required for signing:
|
||||
export WINDOWS_SIGNING_CERT_PFX_BASE64="<base64-encoded-pfx>"
|
||||
export WINDOWS_SIGNING_CERT_PASSWORD="<certificate-password>"
|
||||
```
|
||||
|
||||
### What It Does
|
||||
|
||||
This script is called automatically by GoReleaser during the release process:
|
||||
|
||||
1. **Decodes** the PFX certificate from base64
|
||||
2. **Signs** the Windows executable using osslsigncode
|
||||
3. **Timestamps** the signature using DigiCert's RFC3161 server
|
||||
4. **Replaces** the original binary with the signed version
|
||||
5. **Verifies** the signature was applied correctly
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- `osslsigncode` installed (`apt install osslsigncode` or `brew install osslsigncode`)
|
||||
- EV code signing certificate exported as PFX file
|
||||
- GitHub secrets configured:
|
||||
- `WINDOWS_SIGNING_CERT_PFX_BASE64` - base64-encoded PFX file
|
||||
- `WINDOWS_SIGNING_CERT_PASSWORD` - certificate password
|
||||
|
||||
### Graceful Degradation
|
||||
|
||||
If the signing secrets are not configured:
|
||||
- The script prints a warning and exits successfully
|
||||
- GoReleaser continues without signing
|
||||
- The release proceeds with unsigned Windows binaries
|
||||
|
||||
This allows releases to work before a certificate is acquired.
|
||||
|
||||
### Why This Script Exists
|
||||
|
||||
Windows code signing helps reduce antivirus false positives that affect Go binaries.
|
||||
Kaspersky and other AV software commonly flag unsigned Go executables as potentially
|
||||
malicious due to heuristic detection. See `docs/ANTIVIRUS.md` for details.
|
||||
|
||||
---
|
||||
|
||||
## Future Scripts
|
||||
|
||||
Additional maintenance scripts may be added here as needed.
|
||||
|
||||
Executable
+122
@@ -0,0 +1,122 @@
|
||||
#!/bin/bash
|
||||
# sign-windows.sh - Sign Windows executables using osslsigncode
|
||||
#
|
||||
# This script signs Windows binaries with an Authenticode certificate.
|
||||
# It's designed to be called from GoReleaser hooks or CI/CD pipelines.
|
||||
#
|
||||
# Required environment variables:
|
||||
# WINDOWS_SIGNING_CERT_PFX_BASE64 - Base64-encoded PFX certificate file
|
||||
# WINDOWS_SIGNING_CERT_PASSWORD - Password for the PFX certificate
|
||||
#
|
||||
# Optional environment variables:
|
||||
# TIMESTAMP_SERVER - RFC3161 timestamp server URL (default: DigiCert)
|
||||
#
|
||||
# Usage:
|
||||
# ./sign-windows.sh <path-to-exe>
|
||||
# ./sign-windows.sh dist/bd-windows-amd64_windows_amd64_v1/bd.exe
|
||||
#
|
||||
# For GoReleaser integration, add to .goreleaser.yml:
|
||||
# builds:
|
||||
# - id: bd-windows-amd64
|
||||
# hooks:
|
||||
# post:
|
||||
# - ./scripts/sign-windows.sh "{{ .Path }}"
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration
|
||||
TIMESTAMP_SERVER="${TIMESTAMP_SERVER:-http://timestamp.digicert.com}"
|
||||
CERT_FILE="/tmp/signing-cert.pfx"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1" >&2
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
# Remove temporary certificate file
|
||||
if [[ -f "$CERT_FILE" ]]; then
|
||||
rm -f "$CERT_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
# Check for required argument
|
||||
if [[ $# -lt 1 ]]; then
|
||||
log_error "Usage: $0 <path-to-exe>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
EXE_PATH="$1"
|
||||
|
||||
# Verify file exists
|
||||
if [[ ! -f "$EXE_PATH" ]]; then
|
||||
log_error "File not found: $EXE_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for required environment variables
|
||||
if [[ -z "${WINDOWS_SIGNING_CERT_PFX_BASE64:-}" ]]; then
|
||||
log_warn "WINDOWS_SIGNING_CERT_PFX_BASE64 not set - skipping code signing"
|
||||
log_info "To enable Windows code signing:"
|
||||
log_info " 1. Obtain an EV code signing certificate"
|
||||
log_info " 2. Export it as a PFX file"
|
||||
log_info " 3. Base64 encode: base64 -i cert.pfx"
|
||||
log_info " 4. Add as GitHub secret: WINDOWS_SIGNING_CERT_PFX_BASE64"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -z "${WINDOWS_SIGNING_CERT_PASSWORD:-}" ]]; then
|
||||
log_error "WINDOWS_SIGNING_CERT_PASSWORD not set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for osslsigncode
|
||||
if ! command -v osslsigncode &> /dev/null; then
|
||||
log_error "osslsigncode not found. Install it with:"
|
||||
log_error " Ubuntu/Debian: apt-get install osslsigncode"
|
||||
log_error " macOS: brew install osslsigncode"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Signing Windows executable: $EXE_PATH"
|
||||
|
||||
# Decode certificate from base64
|
||||
echo "$WINDOWS_SIGNING_CERT_PFX_BASE64" | base64 -d > "$CERT_FILE"
|
||||
|
||||
# Sign the executable
|
||||
osslsigncode sign \
|
||||
-pkcs12 "$CERT_FILE" \
|
||||
-pass "$WINDOWS_SIGNING_CERT_PASSWORD" \
|
||||
-n "beads - AI-supervised issue tracker" \
|
||||
-i "https://github.com/steveyegge/beads" \
|
||||
-t "$TIMESTAMP_SERVER" \
|
||||
-in "$EXE_PATH" \
|
||||
-out "${EXE_PATH}.signed"
|
||||
|
||||
# Replace original with signed version
|
||||
mv "${EXE_PATH}.signed" "$EXE_PATH"
|
||||
|
||||
log_info "Successfully signed: $EXE_PATH"
|
||||
|
||||
# Verify the signature
|
||||
log_info "Verifying signature..."
|
||||
osslsigncode verify -in "$EXE_PATH" || {
|
||||
log_warn "Signature verification returned non-zero (may still be valid)"
|
||||
}
|
||||
|
||||
log_info "Windows code signing complete"
|
||||
Reference in New Issue
Block a user