From d5dadb5f4bc8219cac3ff53e0ea4787abd30c89d Mon Sep 17 00:00:00 2001 From: cyberops7 <18562612+cyberops7@users.noreply.github.com> Date: Tue, 30 Jun 2026 21:34:34 -0600 Subject: [PATCH 1/2] ci: lint and secret-scan pull requests Add a GitHub Actions workflow that runs the project's pre-commit hooks against the files changed in each PR, plus a gitleaks secret scan over the PR's commits. Both block on findings. Enrich the existing .pre-commit-config.yaml with general hygiene checks and four linters (shellcheck, yamllint, actionlint, gitleaks), add a lenient yamllint config tuned for the repo's existing YAML, and add a gitleaks config that keeps the full default ruleset while baselining the repo's pre-existing intentional dummy credentials. Linting is scoped to changed files, so the checks pass on the current tree and only hold new or edited files to standard. - .github/workflows/lint.yml: pull_request workflow, read-only token, two jobs (pre-commit on changed files + gitleaks on the commit range) - .pre-commit-config.yaml: keep existing hooks; add hygiene checks, shellcheck (--severity=warning), yamllint, actionlint, gitleaks - .yamllint.yaml: lenient ruleset - .gitleaks.toml: default rules + allowlist for .env/example placeholders - .gitleaksignore: baseline of pre-existing intentional dummy credentials --- .github/workflows/lint.yml | 56 ++++++++++++++++++++++++++++++++++++++ .gitleaks.toml | 49 +++++++++++++++++++++++++++++++++ .gitleaksignore | 41 ++++++++++++++++++++++++++++ .pre-commit-config.yaml | 29 +++++++++++++++++++- .yamllint.yaml | 21 ++++++++++++++ 5 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/lint.yml create mode 100644 .gitleaks.toml create mode 100644 .gitleaksignore create mode 100644 .yamllint.yaml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..9f8d869 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,56 @@ +--- +name: Lint + +on: + pull_request: + +# Read-only: this workflow only inspects code and reports status, so it works +# from fork PRs (which receive a read-only token and no secrets). +permissions: + contents: read + +concurrency: + group: lint-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + pre-commit: + name: pre-commit (changed files) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Run pre-commit on changed files + # gitleaks is handled by the dedicated job below (it scans git history, + # not a file list), so skip it here to avoid a redundant no-op run. + env: + SKIP: gitleaks + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + python -m pip install --upgrade pre-commit + pre-commit run \ + --from-ref "$BASE_SHA" \ + --to-ref "$HEAD_SHA" \ + --show-diff-on-failure + + gitleaks: + name: gitleaks (secret scan) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Scan PR commits for secrets + env: + GITLEAKS_VERSION: 8.30.1 + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \ + | tar -xz gitleaks + ./gitleaks git . --redact --no-banner --log-opts="${BASE_SHA}..${HEAD_SHA}" diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..2d4c385 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,49 @@ +# Gitleaks configuration for JimsGarage. +# +# These are homelab tutorial configs: many directories intentionally ship +# throwaway/dummy credentials so viewers can copy a working example (see +# issue #127). This config keeps the full default secret-detection ruleset +# but allowlists the paths and placeholder values that are dummy *by +# convention*, so the scanner only fires on something that looks like a +# genuine accidental leak. +# +# Tradeoff worth knowing: because `.env` files here are demo placeholders by +# design, they are allowlisted wholesale — gitleaks will NOT catch a real +# secret accidentally dropped into a `.env`. The scanner's value in this repo +# is catching real secrets pasted into scripts, compose files, and manifests. + +title = "JimsGarage gitleaks config" + +[extend] +# Inherit all of gitleaks' built-in rules (AWS keys, JWTs, private keys, +# provider tokens, high-entropy strings, etc.). +useDefault = true + +[[allowlists]] +description = "Intentional throwaway credentials in example/placeholder files" +# `paths` is matched against the file path of the candidate finding. +paths = [ + # Tracked .env files are demo placeholders by convention in this repo. + '''(^|/)\.env$''', + '''\.env$''', + # Files that are explicitly named as examples/samples/templates. + '''(^|/)[^/]*example[^/]*''', + '''(^|/)[^/]*sample[^/]*''', + '''\.(example|sample|dist|template|tmpl)$''', +] + +[[allowlists]] +description = "Common placeholder secret values" +# `regexes` is matched against the detected secret value itself, so a +# realistic-looking value anywhere still gets flagged — only obvious +# placeholders are exempted. +regexTarget = "match" +regexes = [ + '''(?i)changeme''', + '''(?i)your[-_].*(key|token|secret|password)''', + '''(?i)example[-_.]?(key|token|secret|password|value)?''', + '''(?i)supersecret''', + '''(?i)redacted''', + '''(?i)<[^>]+>''', # angle-bracket placeholders like + '''(?i)^(test|dummy|placeholder|password|secret)$''', +] diff --git a/.gitleaksignore b/.gitleaksignore new file mode 100644 index 0000000..bd198ad --- /dev/null +++ b/.gitleaksignore @@ -0,0 +1,41 @@ +# gitleaks baseline — pre-existing intentional dummy credentials kept here +# instead of an inline `# gitleaks:allow` annotation. A finding is baselined +# (rather than annotated inline) when its file cannot or should not be edited: +# - JSON files have no comment syntax (e.g. Netbird/management.json) +# - multi-line PEM private-key blocks can't carry a reliable inline marker +# (Authelia/Authelia/configuration.yml) +# - files with pre-existing lint debt: editing them would pull that file into +# the changed-files lint scope and fail unrelated yamllint/hygiene checks. +# +# New intentional dummies in clean, comment-capable files should use an inline +# `# gitleaks:allow` instead of being added here. See CONTRIBUTING.md. + +Authelia/Authelia/configuration.yml:generic-api-key:19 +Authelia/Authelia/configuration.yml:generic-api-key:226 +Authelia/Authelia/configuration.yml:private-key:369 +Authelia/Authelia/configuration.yml:generic-api-key:681 +Authelia/Authelia/configuration.yml:private-key:790 +Authelia/Authelia/configuration.yml:generic-api-key:872 +Authelia/Authelia/configuration.yml:private-key:962 +Authelia/Authelia/configuration.yml:private-key:1068 +Authelia/Authelia/configuration.yml:private-key:1225 +Authelia/Authelia/configuration.yml:generic-api-key:1267 +Authelia/Authelia/configuration.yml:private-key:1314 +Grafana-Monitoring/Part-2/telegraf.conf:generic-api-key:398 +Home-Assistant/docker-compose.yaml:generic-api-key:43 +Netbird/docker-compose.yml:generic-api-key:15 +Netbird/docker-compose.yml:generic-api-key:16 +Netbird/management.json:generic-api-key:19 +Netbird/management.json:generic-api-key:30 +Netbird/management.json:generic-api-key:35 +Netbird/management.json:generic-api-key:52 +Paperless-ngx/docker-compose.yaml:generic-api-key:83 +Popup-Homelab/docker-compose.yaml:generic-api-key:87 +Pterodactyl/config.yml:generic-api-key:3 +Pterodactyl/config.yml:generic-api-key:4 +Synapse/docker-compose.yaml:generic-api-key:57 +Synapse/homeserver.yaml:generic-api-key:29 +Synapse/mautrix-discord-bridge/docker-compose.yaml:generic-api-key:36 +Terraform/providers.tf:generic-api-key:14 +Vikunja/docker-compose.yaml:generic-api-key:13 +Zitadel/docker-compose.yaml:generic-api-key:10 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 615bc75..4a1000e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,40 @@ --- repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v5.0.0 hooks: + # Pre-existing checks - id: check-symlinks - id: destroyed-symlinks - id: detect-aws-credentials args: [--allow-missing-credentials] + # General hygiene + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-merge-conflict + - id: check-added-large-files + - id: mixed-line-ending + - id: check-yaml + args: [--allow-multiple-documents] - repo: https://github.com/IamTheFij/docker-pre-commit rev: v3.0.1 hooks: - id: docker-compose-check + - repo: https://github.com/koalaman/shellcheck-precommit + rev: v0.10.0 + hooks: + - id: shellcheck + # Block on real warnings/errors; skip style/info noise on legacy scripts. + args: [--severity=warning] + - repo: https://github.com/adrienverge/yamllint + rev: v1.35.1 + hooks: + - id: yamllint + - repo: https://github.com/rhysd/actionlint + rev: v1.7.7 + hooks: + - id: actionlint + - repo: https://github.com/gitleaks/gitleaks + rev: v8.30.1 + hooks: + - id: gitleaks diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..5fe29e9 --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,21 @@ +--- +# Lenient ruleset: this repo has a large body of pre-existing YAML (compose +# stacks, k8s manifests, Ansible). The goal is to catch real problems on +# changed files without drowning contributors in stylistic noise. +extends: default + +rules: + # Long lines are common and harmless in compose/k8s/Ansible YAML. + line-length: disable + # Many files intentionally omit the leading '---'. + document-start: disable + # compose/Ansible use yes/no/on/off as values, not just true/false. + truthy: + check-keys: false + # Indentation styles vary across the repo; only require internal consistency. + indentation: + spaces: consistent + indent-sequences: whatever + comments: + min-spaces-from-content: 1 + comments-indentation: disable From 6c94afa975ea78af0676450bb5d22199d8c1c72b Mon Sep 17 00:00:00 2001 From: cyberops7 <18562612+cyberops7@users.noreply.github.com> Date: Tue, 30 Jun 2026 21:34:52 -0600 Subject: [PATCH 2/2] docs: document dummy-credential convention; annotate examples Add CONTRIBUTING.md explaining the linting setup and how to mark the repo's intentional dummy credentials so gitleaks does not flag them: inline `# gitleaks:allow` for lint-clean, comment-capable files, and a .gitleaksignore baseline otherwise. Annotate the dummy credentials in four already-lint-clean example files inline, as the preferred convention going forward. - DIUN/docker-compose.yaml - Headscale/Tailscale-Client/docker-compose,yaml - Homepage/Homepage/services.yaml - Kubernetes/Traefik-PiHole/Helm/Traefik/Dashboard/secret-dashboard.yaml --- CONTRIBUTING.md | 72 +++++++++++++++++++ DIUN/docker-compose.yaml | 2 +- .../Tailscale-Client/docker-compose.yaml | 2 +- Homepage/Homepage/services.yaml | 4 +- .../Traefik/Dashboard/secret-dashboard.yaml | 2 +- 5 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..cd11318 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,72 @@ +# Contributing + +Thanks for contributing to JimsGarage! This guide covers the automated checks +that run on pull requests and — importantly for this repo — how to handle the +**dummy credentials** that many of these tutorials intentionally ship. + +## Linting + +A `pre-commit` configuration (`.pre-commit-config.yaml`) and a GitHub Actions +workflow (`.github/workflows/lint.yml`) run a set of checks. CI lints **only the +files changed in your PR**, so you are not responsible for pre-existing issues in +files you do not touch. + +To run the checks locally before pushing: + +```bash +pip install pre-commit +pre-commit install # optional: run automatically on every commit +pre-commit run --all-files # or scope to changed files (CI behavior) +``` + +The hooks include general hygiene (trailing whitespace, end-of-file newline), +`yamllint`, `shellcheck`, `actionlint`, and `gitleaks` (secret scanning). + +## Credentials and secrets + +**Never commit a real credential.** If you accidentally commit one, treat it as +compromised: rotate it, then remove it from history. + +Many tutorials here include **throwaway / dummy credentials** so a config works +out of the box when copied. That is fine — but because they live next to real +config, the `gitleaks` scanner cannot tell a deliberate dummy from a genuine +leak. You therefore need to mark intentional dummies explicitly. There are two +ways to do this; prefer the first. + +### 1. Inline annotation (preferred) + +For a dummy value in a **comment-capable file that is already lint-clean**, add a +trailing `# gitleaks:allow` on the offending line: + +```yaml +environment: + - "ADMIN_TOKEN=not-a-real-token-1234567890" # gitleaks:allow +``` + +This is self-documenting at the point of use and is the convention for any +**new** dummy credential you add. + +### 2. Baseline in `.gitleaksignore` + +Some findings cannot or should not be annotated inline. For these, add the +finding's fingerprint to `.gitleaksignore`. Use this when the file is: + +- **JSON** — it has no comment syntax (e.g. `Netbird/management.json`). +- A **multi-line PEM private-key block** — an inline marker is unreliable across + the block (e.g. `Authelia/Authelia/configuration.yml`). +- **Burdened with pre-existing lint debt** — editing it would pull the whole file + into the changed-files lint scope and fail unrelated `yamllint`/hygiene checks. + +To get a fingerprint, scan and copy the `Fingerprint` field for the finding: + +```bash +gitleaks dir . --report-format json --report-path /tmp/gitleaks.json +# copy the "Fingerprint" value into .gitleaksignore, with a brief comment +``` + +### Allowlisted paths + +`.gitleaks.toml` already allowlists conventional placeholder locations — tracked +`.env` files and `*example*` / `*sample*` files — since those are dummy by +convention in this repo. Secrets placed there are not scanned, so do not rely on +them to hold anything real. diff --git a/DIUN/docker-compose.yaml b/DIUN/docker-compose.yaml index 6c30657..9d86d9a 100644 --- a/DIUN/docker-compose.yaml +++ b/DIUN/docker-compose.yaml @@ -19,7 +19,7 @@ services: - "DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT=true" - "DIUN_NOTIF_GOTIFY_ENDPOINT=https://gotify.jimsgarage.co.uk" - - "DIUN_NOTIF_GOTIFY_TOKEN=AYgfdfQaRk3Pb1x" # get your token from Gotify UI + - "DIUN_NOTIF_GOTIFY_TOKEN=AYgfdfQaRk3Pb1x" # get your token from Gotify UI # gitleaks:allow - "DIUN_NOTIF_GOTIFY_PRIORITY=1" - "DIUN_NOTIF_GOTIFY_TIMEOUT=10s" diff --git a/Headscale/Tailscale-Client/docker-compose.yaml b/Headscale/Tailscale-Client/docker-compose.yaml index 0691331..71d5b9b 100644 --- a/Headscale/Tailscale-Client/docker-compose.yaml +++ b/Headscale/Tailscale-Client/docker-compose.yaml @@ -14,5 +14,5 @@ services: - TS_STATE_DIR=/var/lib/tailscale - TS_EXTRA_ARGS=--login-server=https://headscale.jimsgarage.co.uk --advertise-exit-node --advertise-routes=192.168.0.0/16 --accept-dns=true - TS_NO_LOGS_NO_SUPPORT=true - # - TS_AUTHKEY=e6f46b99f2ddsfsf3easdf125590e415db007 # generate this key inside your headscale server container + # - TS_AUTHKEY=e6f46b99f2ddsfsf3easdf125590e415db007 # generate this key inside your headscale server container # gitleaks:allow restart: unless-stopped diff --git a/Homepage/Homepage/services.yaml b/Homepage/Homepage/services.yaml index 1bf065d..4c093f8 100644 --- a/Homepage/Homepage/services.yaml +++ b/Homepage/Homepage/services.yaml @@ -17,7 +17,7 @@ type: traefik url: https://traefik.jimsgarage.co.uk username: admin - password: gT8ni3iX6QkKreWfAdYKe4xqVsaMRUQ4GG7xn59Q + password: gT8ni3iX6QkKreWfAdYKe4xqVsaMRUQ4GG7xn59Q # gitleaks:allow - PiHole: icon: pi-hole.png @@ -28,7 +28,7 @@ widget: type: pihole url: http://192.168.8.2 - key: 73T8oBs9MFKLVAC3mAs2KQbWSsqA7oe2PN9r9H4TQWg2TXNAdq4ZPzvy8oEv + key: 73T8oBs9MFKLVAC3mAs2KQbWSsqA7oe2PN9r9H4TQWg2TXNAdq4ZPzvy8oEv # gitleaks:allow - My Second Group: - My Second Service: diff --git a/Kubernetes/Traefik-PiHole/Helm/Traefik/Dashboard/secret-dashboard.yaml b/Kubernetes/Traefik-PiHole/Helm/Traefik/Dashboard/secret-dashboard.yaml index 48f570c..86603e7 100644 --- a/Kubernetes/Traefik-PiHole/Helm/Traefik/Dashboard/secret-dashboard.yaml +++ b/Kubernetes/Traefik-PiHole/Helm/Traefik/Dashboard/secret-dashboard.yaml @@ -1,6 +1,6 @@ --- apiVersion: v1 -kind: Secret +kind: Secret # gitleaks:allow metadata: name: traefik-dashboard-auth namespace: traefik