Compare commits

..

5 Commits

Author SHA1 Message Date
BigBodyCobain c8fdfbdceb fix(secret-scan): exempt SSH known_hosts entries from leaked-key detection
PR #331 introduced github.com host fingerprints pinned in
.gitlab-ci.yml's mirror-to-github before_script. The scanner flagged
them as embedded secrets and blocked CI:

  BLOCKED: Embedded secrets/tokens found in:
    .gitlab-ci.yml
      133: github.com ssh-ed25519 AAAA...
      135: github.com ssh-rsa AAAA...

These are PUBLIC host keys — the whole point of pinning known_hosts is
to publish the fingerprint widely so a MITM is detectable. They are
documented at https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints
and committing them is the correct, secure practice.

Fix: add a KNOWN_HOSTS_LINE regex to the content-scan block that
recognizes `<host-or-ip> [salt] <algo> AAAA...` shape lines (the
exact format used in ~/.ssh/known_hosts) and filters them out before
flagging the file. Bare `ssh-rsa AAAA...` lines without a host prefix
are still caught — only the host-key shape is exempt.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 03:16:01 -06:00
BigBodyCobain bb7d7f6f83 feat(ci): switch mirror-to-github job from PAT to per-repo SSH deploy key
GitHub fine-grained PATs are capped at 366 days, classic PATs would
need 'public_repo' (broader scope than needed). Per-repo SSH deploy
keys are tighter:
- Can ONLY push to BigBodyCobain/Shadowbroker (no access to anything
  else, not even other repos owned by the same account).
- Never expire.
- Rotating == one-click delete on github.com/.../settings/keys.

Changes:
- New CI/CD variable GITHUB_MIRROR_SSH_KEY (File, Protected) holding
  the ed25519 private half. Public half lives on the repo's deploy
  keys with write access enabled.
- mirror-to-github before_script writes the key to ~/.ssh/id_ed25519,
  pins github.com host fingerprints (ed25519 + ecdsa + rsa from the
  2023-03-24 rotation) into ~/.ssh/known_hosts so we never trust a
  MITM, then pushes via git@github.com:... instead of HTTPS.
- Job rule now gates on GITHUB_MIRROR_SSH_KEY (the new var) instead
  of GITHUB_MIRROR_TOKEN (which never existed).

After this lands, every commit pushed directly to GitLab main will
mirror back to GitHub main automatically — closing the loop on
bi-directional sync.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 02:47:02 -06:00
Shadowbroker a1af9c3595 fix(ci): wrap GitLab dind TLS env in docker context so buildx accepts it (#330)
The build-backend and build-frontend jobs were failing immediately after
identity verification finally allocated runners:

    $ docker buildx create --use --name multiarch --driver docker-container
    ERROR: could not create a builder instance with TLS data loaded from
    environment. Please use `docker context create <context-name>` to create
    a context for current environment and then create a builder instance
    with context set to <context-name>

The dind service exports DOCKER_HOST=tcp://docker:2376 +
DOCKER_TLS_CERTDIR=/certs, but buildx --driver docker-container doesn't
read TLS from those env vars directly. Documented GitLab fix: create an
empty `docker context` (which inherits the current TLS env), then bind
buildx to that context name as a positional arg.

After this lands, the multi-arch buildx jobs should actually build and
push amd64 + arm64 images to
  registry.gitlab.com/bigbodycobain/shadowbroker/backend:latest
  registry.gitlab.com/bigbodycobain/shadowbroker/frontend:latest

Surfaced by the post-verification pipeline at
  https://gitlab.com/bigbodycobain/Shadowbroker/-/pipelines/2550501798

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 02:04:53 -06:00
Shadowbroker c8a8fc56f8 chore(ci): bump comment in .gitlab-ci.yml to verify post-verification runner allocation (#329)
Pipelines on the GitLab mirror have been instant-failing with 0 jobs and
no started_at since the project was created — classic "shared runners
not allocated to unverified free-tier accounts" pattern. The account is
now identity-verified; this trivial comment bump exists solely to fire a
fresh pipeline that confirms runners now pick up the build-backend and
build-frontend jobs.

If the resulting pipeline produces real jobs that build the multi-arch
images and push them to registry.gitlab.com/bigbodycobain/shadowbroker/{backend,frontend},
the GitLab install path is at full parity with the GitHub one.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 01:54:08 -06:00
Shadowbroker e6aba86ce1 chore(release): update v0.9.81 SHA256 digests after rebuild (#328)
Re-cut v0.9.81 binaries from current main (which now includes the
private gate + DM hashchain spool from #326 and the gate-directory
test from #327). All three artifacts were signed with the same
minisign updater key as the original v0.9.81 release, so existing
v0.9.81 installs on Tauri auto-update accept the new bundles.

Updated hashes (verified against released assets):
- ShadowBroker_v0.9.81.zip      f81f454bdc88e9a32c351df38212b8cfa624704d65764b971bb091eef62259c6
- ShadowBroker_0.9.81_x64-setup.exe   25e9a95d0d8ce959a7d08fe8e7406772ae24b596652793e81d1de5d02510a5a6
- ShadowBroker_0.9.81_x64_en-US.msi   34e655fc0c0f195ee4ac978f228a4b2b9d5565253b8771aca9ef4693409e9e70

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 01:16:12 -06:00
2 changed files with 70 additions and 21 deletions
+42 -12
View File
@@ -13,13 +13,22 @@
# 2. Reverse-mirrors main back to GitHub (only if commits land directly
# on GitLab) so the two sources stay in sync.
#
# Pipelines on this repo were instant-failing for free-tier accounts until
# identity verification was added — the May 2026 bump in this comment is
# the marker commit that confirms runner allocation after verification.
#
# Auth notes:
# - The image build/push uses $CI_JOB_TOKEN, which GitLab provides
# automatically. No credentials need to be configured.
# - The reverse mirror requires a GitHub personal access token stored
# as the GitLab CI/CD variable GITHUB_MIRROR_TOKEN (Protected + Masked).
# Scope: public_repo (or repo for private). If the variable isn't
# set the mirror job is skipped — image builds still run.
# - The reverse mirror authenticates to GitHub via a per-repo SSH
# deploy key. The private half is stored as the File-type GitLab
# CI/CD variable GITHUB_MIRROR_SSH_KEY (Protected). The matching
# public key is added to github.com/BigBodyCobain/Shadowbroker/
# settings/keys with write access. This is a tighter-scoped
# replacement for a personal access token: it can ONLY push to
# Shadowbroker, never expires, and rotating it is a one-click
# delete on GitHub's deploy-keys page. If the variable isn't set,
# the mirror job is skipped — image builds still run.
stages:
- build
@@ -48,7 +57,11 @@ variables:
- docker info
- docker login -u "$CI_REGISTRY_USER" -p "$CI_JOB_TOKEN" "$CI_REGISTRY"
- docker run --privileged --rm tonistiigi/binfmt --install all
- docker buildx create --use --name multiarch --driver docker-container
# buildx --driver docker-container can't read TLS from the env vars
# the GitLab dind service exports. Wrap them in a docker context and
# bind buildx to it. See https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-buildx
- docker context create tls-env
- docker buildx create --use --name multiarch --driver docker-container tls-env
# ── Backend image ────────────────────────────────────────────────────────
build-backend:
@@ -93,18 +106,35 @@ build-frontend:
- .gitlab-ci.yml
# ── Reverse mirror to GitHub ─────────────────────────────────────────────
# Pushes refs/heads/main to github.com/BigBodyCobain/Shadowbroker.
# Fast-forward-only — if GitLab main and GitHub main have diverged, this
# fails loudly rather than silently overwriting either side.
# Pushes refs/heads/main to github.com/BigBodyCobain/Shadowbroker via SSH
# using a per-repo deploy key. Fast-forward-only by default — if GitLab
# main and GitHub main have diverged, the push fails loudly rather than
# silently overwriting either side.
#
# Only runs if GITHUB_MIRROR_TOKEN is set as a CI/CD variable. See the
# header comment of this file for setup instructions.
# Only runs if GITHUB_MIRROR_SSH_KEY is set as a File-type CI/CD variable.
# See the header comment of this file for setup instructions.
mirror-to-github:
stage: mirror
image: alpine:3.20
needs: []
before_script:
- apk add --no-cache git openssh-client ca-certificates
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
# Install the deploy key. File-type CI variable exposes the path; copy
# to ~/.ssh/id_ed25519 with restrictive perms so ssh accepts it.
- cp "$GITHUB_MIRROR_SSH_KEY" ~/.ssh/id_ed25519
- chmod 600 ~/.ssh/id_ed25519
# Pin github.com's current host keys so we never trust a man-in-the-
# middle. Sourced from https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints
# (rotated 2023-03-24 after the previous RSA key leak).
- |
cat > ~/.ssh/known_hosts <<'EOF'
github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
EOF
- chmod 644 ~/.ssh/known_hosts
script:
- git config --global user.email "ci-mirror@gitlab.com"
- git config --global user.name "GitLab CI Mirror"
@@ -115,7 +145,7 @@ mirror-to-github:
- cd repo
- >
git push
"https://x-access-token:${GITHUB_MIRROR_TOKEN}@github.com/BigBodyCobain/Shadowbroker.git"
"git@github.com:BigBodyCobain/Shadowbroker.git"
"${CI_COMMIT_SHA}:refs/heads/main"
rules:
- if: $CI_COMMIT_BRANCH == "main" && $GITHUB_MIRROR_TOKEN
- if: $CI_COMMIT_BRANCH == "main" && $GITHUB_MIRROR_SSH_KEY
+28 -9
View File
@@ -92,18 +92,37 @@ SECRET_REGEX+='pypi-[0-9a-zA-Z-]{50,}' # PyPI token
TEXT_FILES=$(grep -ivE '\.(png|jpg|jpeg|gif|ico|svg|woff2?|ttf|eot|pbf|zip|tar|gz|db|sqlite|xlsx|pdf|mp[34]|wav|ogg|webm|webp|avif)$' "$FILELIST" | grep -v 'scan-secrets\.sh$' || true)
if [[ -n "$TEXT_FILES" ]]; then
# Known-public exclusions: lines matching `<host-or-ip> ssh-<algo> <key>`
# are SSH known_hosts entries — the host's PUBLIC fingerprint, which is
# by definition safe to commit (the whole point of pinning known_hosts
# is to publish the fingerprint widely so MITM is detectable). Filter
# these out before flagging the file.
KNOWN_HOSTS_LINE='^[[:space:]]*[a-zA-Z0-9._:,*-]+([[:space:]]+[a-zA-Z0-9._:,*-]+)?[[:space:]]+(ssh-rsa|ssh-ed25519|ssh-dss|ecdsa-sha2-nistp256|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521)[[:space:]]+AAAA'
# Use grep with file list, skip missing/binary, limit output
CONTENT_HITS=$(echo "$TEXT_FILES" | xargs grep -lE "$SECRET_REGEX" 2>/dev/null || true)
if [[ -n "$CONTENT_HITS" ]]; then
echo -e "\n${RED}BLOCKED: Embedded secrets/tokens found in:${NC}"
echo "$CONTENT_HITS" | while read -r f; do
echo -e " ${RED}$f${NC}"
# Show first matching line for context
grep -nE "$SECRET_REGEX" "$f" 2>/dev/null | head -2 | while read -r line; do
echo -e " ${YELLOW}$line${NC}"
done
done
FOUND=1
REAL_HITS=""
REAL_REPORT=""
while IFS= read -r f; do
[[ -z "$f" ]] && continue
# Re-grep this file, but filter out known_hosts-style lines.
FILE_HITS=$(grep -nE "$SECRET_REGEX" "$f" 2>/dev/null | grep -vE "$KNOWN_HOSTS_LINE" || true)
if [[ -n "$FILE_HITS" ]]; then
REAL_HITS+="$f"$'\n'
REAL_REPORT+=" ${RED}$f${NC}"$'\n'
# Show first 2 matching lines for context
while IFS= read -r line; do
[[ -z "$line" ]] && continue
REAL_REPORT+=" ${YELLOW}$line${NC}"$'\n'
done < <(echo "$FILE_HITS" | head -2)
fi
done <<< "$CONTENT_HITS"
if [[ -n "$REAL_HITS" ]]; then
echo -e "\n${RED}BLOCKED: Embedded secrets/tokens found in:${NC}"
echo -en "$REAL_REPORT"
FOUND=1
fi
fi
fi