-
-
Notifications
You must be signed in to change notification settings - Fork 7.2k
ci: add Auths cryptographic commit verification (warn-only) #25124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
bordumb
wants to merge
6
commits into
BerriAI:main
Choose a base branch
from
bordumb:auths/add-commit-verification
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 1 commit
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
73f6b82
ci: add Auths cryptographic commit verification (warn-only)
bordumb cd508eb
fix: pin auths-verify-github-action to full SHA
bordumb c24d69b
fix: pin action to SHA, add contributor key to allowed_signers
bordumb adaf293
fix: clarify that simulation shows commit-signing layer, not full art…
bordumb 8f236f2
fix: use auths-dev/verify action and SDK-based demo scripts
bordumb 7906ee1
ci: replace demo scripts with auths-dev/sign release workflow
bordumb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or 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,23 @@ | ||
| # Auths Allowed Signers | ||
| # | ||
| # This file lists the Ed25519 public keys of maintainers authorized to sign | ||
| # commits for this repository using Auths (https://github.com/auths-dev/auths). | ||
| # | ||
| # With Auths, every commit carries an Ed25519 signature bound to the | ||
| # maintainer's KERI-based decentralized identifier (DID). This creates a | ||
| # cryptographic chain of custody that cannot be forged with stolen registry | ||
| # credentials alone. | ||
| # | ||
| # Format: | ||
| # <DID>@auths.local namespaces="git" ssh-ed25519 <base64-public-key> | ||
| # | ||
| # To add your key: | ||
| # 1. Install Auths: pip install auths | ||
| # 2. Create identity: auths init | ||
| # 3. Configure Git: auths git setup | ||
| # 4. Export signers: auths git allowed-signers --output .auths/allowed_signers | ||
| # 5. Commit and push this file | ||
| # | ||
| # Documentation: https://github.com/auths-dev/auths/blob/main/docs/guides/platforms/ci-cd.md | ||
| # | ||
| # Maintainers — add your keys below this line: | ||
This file contains hidden or 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,37 @@ | ||
| name: Auths Commit Verification | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| pull_request: | ||
| branches: [main] | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| permissions: {} | ||
|
|
||
| jobs: | ||
| verify: | ||
| name: Verify commit signatures | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 5 | ||
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 | ||
| with: | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
|
|
||
| - name: Verify commits with Auths | ||
| uses: auths-dev/auths-verify-github-action@v1 # TODO: pin to SHA once stable | ||
|
||
| with: | ||
| allowed-signers: .auths/allowed_signers | ||
| fail-on-unsigned: 'false' | ||
| skip-merge-commits: 'true' | ||
| post-pr-comment: 'true' | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
This file contains hidden or 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,36 @@ | ||
| # 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 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 | ||
|
|
||
| ## Running the Simulation | ||
|
|
||
| The simulation script recreates the attack scenario and demonstrates how Auths | ||
| verification catches the unauthorized commit: | ||
|
|
||
| ```bash | ||
| pip install auths | ||
| python auths_attack_simulation.py | ||
| ``` | ||
|
|
||
| ## 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`. |
This file contains hidden or 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,173 @@ | ||||||||
| """ | ||||||||
| Auths Attack Simulation: LiteLLM March 24, 2026 Supply Chain Incident | ||||||||
|
|
||||||||
| Demonstrates how Auths cryptographic commit 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 | ||||||||
|
|
||||||||
| Why Auths prevents this: | ||||||||
| With Auths, every release artifact carries an Ed25519 signature from the | ||||||||
| maintainer's cryptographic identity (KERI-based DID). Stealing the PyPI | ||||||||
| token is insufficient — the attacker cannot produce a valid signature | ||||||||
| without the maintainer's private key stored in their device keychain. | ||||||||
|
|
||||||||
| Usage: | ||||||||
| pip install auths | ||||||||
| python auths_attack_simulation.py | ||||||||
| """ | ||||||||
| import os | ||||||||
| import shutil | ||||||||
| import subprocess | ||||||||
| import sys | ||||||||
| import tempfile | ||||||||
|
|
||||||||
|
|
||||||||
| def check_auths_cli() -> bool: | ||||||||
| """Check if the auths CLI is available.""" | ||||||||
| return shutil.which("auths") is not None | ||||||||
|
|
||||||||
|
|
||||||||
| def run(cmd: list[str], cwd: str, check: bool = True) -> subprocess.CompletedProcess: | ||||||||
| """Run a command and return the result.""" | ||||||||
| return subprocess.run( | ||||||||
| cmd, | ||||||||
| cwd=cwd, | ||||||||
| capture_output=True, | ||||||||
| text=True, | ||||||||
| check=check, | ||||||||
| ) | ||||||||
|
|
||||||||
|
|
||||||||
| def setup_test_repo(tmpdir: str) -> str: | ||||||||
| """Create a temporary git repo with signed and unsigned commits.""" | ||||||||
| repo = os.path.join(tmpdir, "litellm-simulation") | ||||||||
| os.makedirs(repo) | ||||||||
|
|
||||||||
| # Initialize repo | ||||||||
| run(["git", "init"], cwd=repo) | ||||||||
| run(["git", "config", "user.email", "maintainer@example.com"], cwd=repo) | ||||||||
| run(["git", "config", "user.name", "LiteLLM Maintainer"], cwd=repo) | ||||||||
|
|
||||||||
| # Generate a test Ed25519 keypair for the "legitimate maintainer" | ||||||||
| key_path = os.path.join(tmpdir, "test_key") | ||||||||
| run( | ||||||||
| ["ssh-keygen", "-t", "ed25519", "-f", key_path, "-N", "", "-q"], | ||||||||
| cwd=tmpdir, | ||||||||
| ) | ||||||||
|
|
||||||||
| # Configure git to sign with this key | ||||||||
| run(["git", "config", "gpg.format", "ssh"], cwd=repo) | ||||||||
| run(["git", "config", "user.signingkey", key_path], cwd=repo) | ||||||||
|
|
||||||||
| # Create allowed_signers file | ||||||||
| pub_key_content = open(f"{key_path}.pub").read().strip() | ||||||||
|
||||||||
| pub_key_content = open(f"{key_path}.pub").read().strip() | |
| with open(f"{key_path}.pub") as fh: | |
| pub_key_content = fh.read().strip() |
Fixed
Show fixed
Hide fixed
bordumb marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
bordumb marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
allowed_signersmeans zero cryptographic enforcement at merge timeThe file contains no public keys. Until at least one maintainer adds their key, the Auths verification step has no authorized identities to validate commits against. Depending on the Auths CLI implementation, this will either:
Either outcome means the "cryptographic chain of custody" described in the PR description doesn't exist the moment this merges. The workflow will run but provide no actual signal until keys are populated.
Consider making this a follow-up requirement: the workflow should only be merged once at least one maintainer key is present, otherwise it creates the impression of coverage without providing any.