Checkpoint Lab — Signing, Trust, Secret Removal, fsck, Safe Directories, and Repository Integrity
Checkpoint a complete repository-security incident using fake secret material, ephemeral signatures, evidence capture, current filter-repo remediation, post-rewrite integrity checks, re-signing, and publication/prevention planning.
Learning objectives
- Predict and verify commit-ID, signature, and reachability consequences of a sensitive-history rewrite.
- Detect/document a fake leaked marker and simulate immediate rotation before cleanup.
- Preserve refs, reflogs, fsck evidence, and a controlled pre-rewrite bundle.
- Verify sanitized reachable history and establish a new signed attestation when a backend is available.
- Write force-update, cache/fork/mirror cleanup, collaborator recovery, and prevention procedures.
1. Checkpoint scenario — a fake credential appears in a partially signed release history
You will create an unsigned baseline, a signed release candidate,
and an unsigned commit containing the harmless marker
FAKE_TOKEN_DO_NOT_USE_2026. You will detect the
exposure, simulate immediate rotation, preserve evidence, analyze
fsck output, rewrite in a fresh clone with the current recommended
tool, then verify the sanitized history and create a new signed
attestation.
2. Predict before running the checkpoint
- If a historical blob changes, will descendant commit IDs remain the same?
- If a real token is still active, does completing a Git rewrite make that token cryptographically unusable?
- Will an old commit/tag signature continue to attest to a rewritten object with a different payload?
- Can an unreachable commit appear in fsck output without repository corruption?
3. Create an isolated source and signing environment
Git Bash / Bash / zsh
mkdir git-security-checkpoint
cd git-security-checkpoint
export GNUPGHOME="$PWD/gnupg-home"
mkdir -m 700 "$GNUPGHOME"
git init -b trunk source
cd source
git config user.name "Learner Example"
git config user.email "learner@example.invalid"
printf "service=checkout\nstatus=baseline\n" > app.conf
git add app.conf
git commit -m "baseline: unsigned"
UNSIGNED=$(git rev-parse HEAD)
PowerShell
New-Item -ItemType Directory git-security-checkpoint | Out-Null
Set-Location git-security-checkpoint
$env:GNUPGHOME = Join-Path (Get-Location) 'gnupg-home'
New-Item -ItemType Directory $env:GNUPGHOME | Out-Null
git init -b trunk source
Set-Location source
git config user.name 'Learner Example'
git config user.email 'learner@example.invalid'
4. Signing preflight — use a free local backend if present
gpg --version
gpg --batch --passphrase '' --quick-generate-key \
'Learner Example <learner@example.invalid>' ed25519 sign 0
KEY=$(gpg --batch --with-colons --list-secret-keys \
'learner@example.invalid' |
awk -F: '$1=="fpr" {print $10; exit}')
git config gpg.format openpgp
git config user.signingKey "$KEY"
If GPG is unavailable or this noninteractive key-generation form is unsupported, record “signing backend unavailable” and continue with integrity/secret-removal exercises. Do not import a real personal key merely to satisfy the lab.
5. Create signed and unsigned objects
printf "status=release-candidate\n" >> app.conf
git add app.conf
git commit -S -m "release: signed candidate"
SIGNED=$(git rev-parse HEAD)
git tag -s signed-v1 -m "Signed training release"
git verify-commit "$SIGNED"
git verify-tag signed-v1
git log -1 --show-signature --format='%H %G? %GF %GS %s'
Record the fingerprint and exit statuses in the evidence report. The key is disposable but the verification workflow mirrors production.
6. Commit the harmless marker, then remove it only from the current snapshot
printf "api_token=FAKE_TOKEN_DO_NOT_USE_2026\n" >> app.conf
git add app.conf
git commit -m "incident: fake credential committed"
LEAK=$(git rev-parse HEAD)
sed -i 's/FAKE_TOKEN_DO_NOT_USE_2026/ROTATED_FAKE_VALUE/' app.conf
git add app.conf
git commit -m "containment: current snapshot no longer has fake token"
TIP_BEFORE=$(git rev-parse HEAD)
7. Detect and document the exposure
grep -n 'FAKE_TOKEN_DO_NOT_USE_2026' app.conf || echo "current snapshot clean"
git log -S'FAKE_TOKEN_DO_NOT_USE_2026' --all --oneline -- app.conf
git grep 'FAKE_TOKEN_DO_NOT_USE_2026' $(git rev-list --all)
printf 'unsigned=%s\nsigned=%s\nleak=%s\ntip-before=%s\n' \
"$UNSIGNED" "$SIGNED" "$LEAK" "$TIP_BEFORE" \
> ../incident-oids-before.txt
Prediction check: history search still finds the marker even though the current file is clean.
8. Simulate immediate credential rotation before any rewrite
printf "credential=FAKE_TOKEN_DO_NOT_USE_2026\nrotation=SIMULATED_REVOKED\n" \
> ../rotation-record.txt
9. Preserve pre-rewrite evidence and backup
git status --short --branch
git show-ref --head > ../refs-before.txt
git reflog --all --date=iso > ../reflog-before.txt
git log --graph --decorate --oneline --all > ../graph-before.txt
git fsck --strict --no-progress > ../fsck-before.txt 2>&1
git bundle create ../before-rewrite.bundle --all
git bundle verify ../before-rewrite.bundle
Do not publish the bundle: it intentionally preserves the fake leaked marker as evidence/recovery data.
10. Create and inspect an unreachable evidence object
TREE=$(git write-tree)
ORPHAN=$(printf 'checkpoint orphan evidence\n' | git commit-tree "$TREE")
git fsck --unreachable --no-reflogs --no-progress | tee ../unreachable-before.txt
grep "$ORPHAN" ../unreachable-before.txt
Prediction check: an unreachable commit can be reported without indicating corruption. Preserve it until the checkpoint cleanup.
11. Create a local no-account remote and fresh cleanup clone
cd ..
git init --bare -b trunk origin.git
git -C source remote add training-origin ../origin.git
git -C source push training-origin --all
git -C source push training-origin --tags
git clone --no-local origin.git cleanup
cd cleanup
git log --all --oneline --decorate --graph --max-count=20
12. Preflight the current recommended rewrite tool
git filter-repo --version
printf 'literal:FAKE_TOKEN_DO_NOT_USE_2026==>REMOVED_FAKE_TOKEN\n' \
> ../replacement-expressions.txt
13. Rewrite the disposable fresh clone
git filter-repo \
--sensitive-data-removal \
--replace-text ../replacement-expressions.txt
Read filter-repo's report. Sensitive-data mode is designed to help identify additional cleanup work for other copies/refs; do not treat local completion as incident closure.
14. Verify reachable sanitized history
git log -S'FAKE_TOKEN_DO_NOT_USE_2026' --all --oneline -p
git grep 'FAKE_TOKEN_DO_NOT_USE_2026' $(git rev-list --all) \
&& echo "FAIL: marker remains" \
|| echo "PASS: marker absent from reachable rewritten history"
git fsck --strict --no-progress
git log --graph --decorate --oneline --all --max-count=30
TIP_AFTER=$(git rev-parse HEAD)
printf 'tip-after=%s\n' "$TIP_AFTER"
Prediction check: TIP_AFTER should differ from the
pre-rewrite tip because an ancestor's content changed.
15. Verify signature impact honestly
Filter-repo rewrites commit/tag payloads through fast-export/import and strips signatures that cannot remain valid. Inspect the resulting objects:
git log --show-signature --oneline --all --max-count=10
git tag --list --format='%(refname:short) %(objecttype) %(objectname)'
Do not claim the old signed release still authenticates the rewritten history. Instead create a new attestation after verification.
16. Create and verify a new sanitized signed tag
If the ephemeral OpenPGP key is available in this shell:
git config user.name "Learner Example"
git config user.email "learner@example.invalid"
git config gpg.format openpgp
git config user.signingKey "$KEY"
git tag -s sanitized-v1 -m "Sanitized checkpoint history"
git verify-tag sanitized-v1
This new tag signs the new sanitized payload. It does not erase the incident; it establishes a new provenance point after cleanup.
17. Write the force-update coordination plan before publishing anywhere
Publication coordination
1. Freeze writes and announce exact maintenance window.
2. Record authoritative remote branch/tag OIDs immediately before update.
3. Confirm every contaminated ref that must be replaced/deleted.
4. Use narrow refspecs and expected-old/force-with-lease protection where applicable.
5. Handle protected/hidden hosting refs through provider-specific administration.
6. Invalidate or replace contaminated CI caches, artifacts, mirrors, backups, and forks as policy permits.
7. Tell collaborators not to merge/push old history back; provide a clean re-clone/recovery procedure.
8. Re-run secret scans and integrity/signature checks after publication.
18. Add prevention to the incident closure checklist
- Use an approved credential helper/SSO instead of embedding secrets in files/URLs.
- Add local/CI/server secret detection appropriate to the organization.
- Protect release/signing keys and map signer identities to policy.
- Keep repository ownership and safe.directory exceptions narrow.
- Enable inbound object-integrity checks where cost/threat model justify them.
- Train developers to rotate first and preserve evidence before destructive cleanup.
19. Verification checklist
- The repository and signing environment were disposable and used only fake secret material.
- Unsigned and signed objects were distinguished by verification commands.
- The fake marker was absent from the current snapshot but present in older reachable history before rewrite.
- Credential rotation/revocation was simulated before rewriting.
- Refs, reflog, fsck output, graph, and a controlled bundle were preserved.
- An unreachable commit was interpreted as reachability evidence rather than corruption.
- The rewrite path used git-filter-repo sensitive-data mode, not filter-branch.
- Reachable rewritten history no longer contained the fake marker.
- fsck passed after the rewrite.
- Old signatures were not misrepresented as surviving the rewrite; a new sanitized tag was signed/verified when backend availability permitted.
- The publication plan addressed force coordination, protected refs, caches, forks, mirrors, and re-cloning.
20. Cleanup
cd ../..
pwd
rm -rf git-security-checkpoint
PowerShell equivalent:
Set-Location ../..; Remove-Item -Recurse -Force
git-security-checkpoint.
21. Knowledge check
Question 1. Why must a real credential be revoked before history cleanup?
Question 2. Why did descendant commit IDs change after replacing one historical string?
Question 3. Why can an unreachable commit appear in a healthy repository?
Question 4. What happened to signatures associated with rewritten commits/tags?
Question 5. A teammate has an old contaminated clone after the server rewrite. What is the risk?
22. What Chapter 21 adds to a production Git operating model
You can now separate object integrity from signer authenticity, identity trust, authorization, credential secrecy, repository ownership trust, and history reachability. The resulting incident model is evidence-first: rotate credentials, preserve state, use the current rewrite tool carefully, coordinate every copy, then establish new provenance on sanitized history.
23. Chapter checkpoint summary
A trustworthy repository is not defined by one green check. It needs valid objects, explicit signer/identity policy where signatures are used, correct repository ownership, protected credentials, controlled history remediation, and clear evidence that the sanitized state is what delivery systems will consume.
Authoritative references
Git signature formats
git-fsck
filter-branch warning
git-filter-repo
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.