ci: replace demo scripts with auths-dev/sign release workflow

This commit is contained in:
bordumb 2026-04-05 01:21:13 -07:00
parent 8f236f285f
commit 7906ee1390
No known key found for this signature in database
3 changed files with 59 additions and 195 deletions

View file

@ -0,0 +1,59 @@
name: Auths Sign Release Artifacts
# Runs after the existing publish_to_pypi workflow builds artifacts.
# Signs the sdist (.tar.gz) and wheel (.whl) before they are published,
# creating .auths.json attestation files that consumers can verify.
#
# To activate:
# 1. Store AUTHS_CI_TOKEN as a repository secret
# (generate with: auths init --profile ci --github-action)
# 2. Remove the 'if: false' guard below
on:
workflow_dispatch:
jobs:
sign:
name: Sign release artifacts
if: false # Remove this line to activate
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
id-token: write
environment: pypi-publish
steps:
- name: Checkout repo
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"
- name: Build package
run: |
python -m pip install --upgrade pip build==1.4.2
cp model_prices_and_context_window.json litellm/model_prices_and_context_window_backup.json
rm -rf build dist
python -m build
- name: Sign artifacts with Auths
uses: auths-dev/sign@v1
with:
token: ${{ secrets.AUTHS_CI_TOKEN }}
files: |
dist/*.tar.gz
dist/*.whl
verify: true
note: 'PyPI release ${{ github.sha }}'
# After this step, each file in dist/ has a corresponding .auths.json
# attestation file. These can be:
# - Published alongside the package (e.g., as GitHub Release assets)
# - Uploaded to the Auths registry (auths artifact publish)
# - Verified by consumers: auths artifact verify dist/litellm-*.tar.gz
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0

View file

@ -1,54 +0,0 @@
# Security Cookbook: Auths Commit Verification
## Background
On March 24, 2026, LiteLLM was the target of a supply chain attack. The attacker
compromised the Trivy GitHub Action, which exfiltrated the `PYPI_PUBLISH` token from
LiteLLM's CI/CD pipeline. The stolen token was used to publish malicious versions
(v1.82.7 and v1.82.8) directly to PyPI. The source code on GitHub was never modified.
The attack succeeded because **there was no cryptographic binding between the published
package and a verified maintainer identity**.
## What is Auths?
[Auths](https://github.com/auths-dev/auths) provides Ed25519 signatures bound to
KERI-based decentralized identifiers (DIDs). With Auths:
- Every commit and artifact carries a signature from the maintainer's cryptographic identity
- The signature is bound to the maintainer's device keychain (not a registry account)
- Stealing PyPI/npm credentials is insufficient without the signing key
- Verification happens locally — no network calls to a central authority
## How Auths Addresses the Attack
The real attack bypassed Git entirely — the attacker published directly to PyPI with
no corresponding commit. Commit-level signing alone would not have caught a
registry-only publish. However, Auths establishes a verifiable chain: every legitimate
release must trace back to a signed commit by an authorized maintainer. A package
published without a matching signed commit has no valid attestation chain and would be
flagged by consumers and CI pipelines that verify signatures.
This workflow adds the commit-signing layer via the
[`auths-dev/verify`](https://github.com/auths-dev/verify) GitHub Action. A full
deployment would also use `auths artifact sign` (via
[`auths-dev/sign`](https://github.com/auths-dev/sign)) in the release workflow to
bind published packages to signed commits.
## Running the Simulation
The simulation script uses the Auths Python SDK to demonstrate the core cryptographic
primitive — it shows that only the holder of the maintainer's private key can produce
a valid signature:
```bash
pip install auths
python auths_attack_simulation.py
```
No CLI installation, git, or ssh-keygen needed — the script uses the SDK directly.
## Adding Auths to Your Workflow
See the GitHub Actions workflow at `.github/workflows/auths-verify-commits.yml`
and the allowed signers configuration at `.auths/allowed_signers`.

View file

@ -1,141 +0,0 @@
"""
Auths Attack Simulation: LiteLLM March 24, 2026 Supply Chain Incident
Demonstrates how Auths cryptographic verification would have detected the
unauthorized PyPI publish that compromised LiteLLM v1.82.7 and v1.82.8.
What happened:
1. Attacker compromised the Trivy GitHub Action (March 19)
2. LiteLLM's CI ran Trivy without version pinning
3. Compromised Trivy exfiltrated the PYPI_PUBLISH token from GitHub Actions
4. Attacker used the stolen token to publish malicious versions to PyPI
5. The malicious packages contained a credential stealer in a .pth file
6. Source code on GitHub was never modified the attack existed only in PyPI
How Auths closes this gap:
The real attack bypassed Git entirely the attacker published directly to
PyPI with no corresponding commit. Auths establishes a policy that every
legitimate release must trace back to a signed action by an authorized
maintainer. A package published without a valid signature from a known
maintainer identity has no valid attestation and would be rejected.
This simulation uses the Auths Python SDK to demonstrate the core
cryptographic primitive: sign an action with a maintainer's key, then
show that verification succeeds for the legitimate release and fails
for an unauthorized or tampered one.
Usage:
pip install auths
python auths_attack_simulation.py
Requires: auths (Python SDK)
"""
import json
import sys
def main() -> None:
print("=" * 70)
print("Auths Attack Simulation: LiteLLM Supply Chain Incident (March 24, 2026)")
print("=" * 70)
print()
try:
from auths import generate_inmemory_keypair, sign_action, verify_action_envelope
except ImportError:
print("The 'auths' Python SDK is not installed.")
print()
print("Install it with:")
print(" pip install auths")
print()
print("Or visit: https://github.com/auths-dev/auths")
sys.exit(0)
# Generate ephemeral identities — no filesystem, no keychain needed
maintainer_priv, maintainer_pub, maintainer_did = generate_inmemory_keypair()
attacker_priv, _attacker_pub, attacker_did = generate_inmemory_keypair()
# ── Step 1: Legitimate maintainer signs a release ──────────────────
print("[1] Legitimate maintainer signs release v1.82.6...")
print()
release_payload = json.dumps({
"package": "litellm",
"version": "1.82.6",
"digest": "sha256:abc123def456...",
"registry": "pypi",
})
legitimate_envelope = sign_action(
maintainer_priv, "release", release_payload, maintainer_did,
)
result = verify_action_envelope(legitimate_envelope, maintainer_pub)
print(f" Signed by: {maintainer_did}")
print(f" Verification: {'PASSED' if result.valid else 'FAILED'}")
print()
# ── Step 2: Attacker publishes with stolen PyPI token ──────────────
print("[2] Attacker publishes v1.82.7 using stolen PyPI token...")
print(" (Attacker has registry credentials but NOT the maintainer's signing key)")
print()
malicious_payload = json.dumps({
"package": "litellm",
"version": "1.82.7",
"digest": "sha256:malicious_payload_hash...",
"registry": "pypi",
})
# Attacker signs with their own key — NOT the maintainer's
attacker_envelope = sign_action(
attacker_priv, "release", malicious_payload, attacker_did,
)
# Verify against the MAINTAINER's public key (the only trusted key)
result = verify_action_envelope(attacker_envelope, maintainer_pub)
print(f" Signed by: {attacker_did}")
print(f" Verification against maintainer key: {'PASSED' if result.valid else 'FAILED'}")
if result.error:
print(f" Reason: {result.error}")
print()
# ── Step 3: Show tampered legitimate envelope also fails ───────────
print("[3] Attacker tampers with a legitimately-signed envelope...")
print()
envelope = json.loads(legitimate_envelope)
envelope["payload"]["version"] = "1.82.7"
envelope["payload"]["digest"] = "sha256:malicious_payload_hash..."
tampered_json = json.dumps(envelope)
result = verify_action_envelope(tampered_json, maintainer_pub)
print(f" Original signer: {maintainer_did}")
print(f" Tampered payload version: 1.82.7")
print(f" Verification: {'PASSED' if result.valid else 'FAILED'}")
if result.error:
print(f" Reason: {result.error}")
print()
# ── Summary ────────────────────────────────────────────────────────
print("-" * 70)
print("SUMMARY")
print()
print(" v1.82.6 (legitimate, signed by maintainer): VERIFIED")
print(" v1.82.7 (attacker's key, not trusted): REJECTED")
print(" v1.82.7 (tampered legitimate envelope): REJECTED")
print()
print("NOTE: The real March 24 attack bypassed Git entirely — the attacker")
print("published directly to PyPI with no commit at all. This simulation")
print("demonstrates the cryptographic primitive that Auths provides: only")
print("the holder of the maintainer's private key can produce a valid")
print("signature. In a full deployment, the CI/CD pipeline would use")
print("'auths artifact sign' to bind the published package to the")
print("maintainer's identity, and consumers would verify before installing.")
print()
print("Learn more: https://github.com/auths-dev/auths")
print("=" * 70)
if __name__ == "__main__":
main()