Compare commits
1 Commits
fd78aeb003
...
533fc61817
| Author | SHA1 | Date | |
|---|---|---|---|
| 533fc61817 |
@@ -1,10 +1,8 @@
|
|||||||
# Docker matches this file with moby/patternmatcher: Go filepath.Match
|
# .dockerignore uses Go filepath.Match, NOT .gitignore semantics: `*`
|
||||||
# semantics plus a `**` extension, compiled to a regexp. Plain
|
# does not cross `/`, and a pattern without a leading `**/` is anchored
|
||||||
# filepath.Match has no `**` at all. What follows from that: `*` does not
|
# at the build-context root. Every depth-independent pattern therefore
|
||||||
# cross `/`, and a pattern without a leading `**/` is anchored at the
|
# needs the `**/` prefix — without it `config/.env` and
|
||||||
# build-context root. Every depth-independent pattern therefore needs the
|
# `certs/server.key` still ship while the file reads as solved.
|
||||||
# `**/` prefix — without it `config/.env` and `certs/server.key` still
|
|
||||||
# ship while the file reads as solved.
|
|
||||||
#
|
#
|
||||||
# Root-anchored entries are for paths that occur exactly once, at the
|
# Root-anchored entries are for paths that occur exactly once, at the
|
||||||
# context root. A host-built binary is the usual case, and it must be
|
# context root. A host-built binary is the usual case, and it must be
|
||||||
@@ -13,18 +11,11 @@
|
|||||||
# context.
|
# context.
|
||||||
#
|
#
|
||||||
# Matching is case-sensitive, so `**/*.key` does not match
|
# Matching is case-sensitive, so `**/*.key` does not match
|
||||||
# `certs/SERVER.KEY`, which is reachable on the case-insensitive
|
# `certs/SERVER.KEY`. The secret-material extensions below use character
|
||||||
# filesystems most laptops use. Adding an ALL-CAPS twin per pattern is
|
# classes, which cover every capitalisation in one line — a doubled
|
||||||
# not the fix: it still misses `Server.Key` while reading as though case
|
# ALL-CAPS pattern would still miss `Server.Key` while reading as though
|
||||||
# were handled. Character ranges cover every spelling in one line, so
|
# case were handled. Names that only ever exist in one spelling because
|
||||||
# every secret name below is written that way — including the
|
# a tool writes them (`.env`, `.envrc`, `id_rsa`) stay literal.
|
||||||
# extensionless SSH keys and `.envrc`, because on those same
|
|
||||||
# case-insensitive filesystems direnv reads `.ENVRC` and ssh reads
|
|
||||||
# `ID_RSA`.
|
|
||||||
#
|
|
||||||
# `**/*.[eE][nN][vV]` also excludes a committed env template such as
|
|
||||||
# `example.env`. If the build genuinely needs one, re-include it with a
|
|
||||||
# negation after the pattern: `!docs/example.env`.
|
|
||||||
#
|
#
|
||||||
# Extend this file with the repo's own host-built artifacts (compiled
|
# Extend this file with the repo's own host-built artifacts (compiled
|
||||||
# binaries, test binaries, coverage output); those are per-repo and
|
# binaries, test binaries, coverage output); those are per-repo and
|
||||||
@@ -34,11 +25,13 @@
|
|||||||
# Repository metadata: exactly one, at the context root.
|
# Repository metadata: exactly one, at the context root.
|
||||||
.git
|
.git
|
||||||
|
|
||||||
# Environment files. `*.env` covers both the bare `.env` name (`*` matches
|
# Environment files. `*.env` covers the `prod.env` / `local.env`
|
||||||
# the empty string) and the `prod.env` convention.
|
# convention; the `.env` and `.env.*` spellings are listed explicitly
|
||||||
|
# because they are what most tooling writes.
|
||||||
|
**/.env
|
||||||
|
**/.env.*
|
||||||
**/*.[eE][nN][vV]
|
**/*.[eE][nN][vV]
|
||||||
**/.[eE][nN][vV].*
|
**/.envrc
|
||||||
**/.[eE][nN][vV][rR][cC]
|
|
||||||
|
|
||||||
# Private keys and the bundles that carry them. Public certificates
|
# Private keys and the bundles that carry them. Public certificates
|
||||||
# (*.crt, *.cer) are deliberately absent: they are not secrets and are
|
# (*.crt, *.cer) are deliberately absent: they are not secrets and are
|
||||||
@@ -47,10 +40,10 @@
|
|||||||
**/*.[kK][eE][yY]
|
**/*.[kK][eE][yY]
|
||||||
**/*.[pP]12
|
**/*.[pP]12
|
||||||
**/*.[pP][fF][xX]
|
**/*.[pP][fF][xX]
|
||||||
**/[iI][dD]_[rR][sS][aA]
|
**/id_rsa
|
||||||
**/[iI][dD]_[dD][sS][aA]
|
**/id_dsa
|
||||||
**/[iI][dD]_[eE][cC][dD][sS][aA]
|
**/id_ecdsa
|
||||||
**/[iI][dD]_[eE][dD]25519
|
**/id_ed25519
|
||||||
|
|
||||||
# Dependencies: restored inside the image, never copied in.
|
# Dependencies: restored inside the image, never copied in.
|
||||||
**/node_modules
|
**/node_modules
|
||||||
|
|||||||
22
TODO.md
22
TODO.md
@@ -25,17 +25,17 @@ fmt-check, and commit.
|
|||||||
developer's local `.env`, `*.pem` or `*.key` was reaching the Docker build
|
developer's local `.env`, `*.pem` or `*.key` was reaching the Docker build
|
||||||
context under `COPY . .`, invisible to every git-based check because
|
context under `COPY . .`, invisible to every git-based check because
|
||||||
`.gitignore` covers it. The patterns are written to `.dockerignore`'s own
|
`.gitignore` covers it. The patterns are written to `.dockerignore`'s own
|
||||||
`moby/patternmatcher` semantics — `**/`-prefixed so they hold at every depth,
|
`filepath.Match` semantics — `**/`-prefixed so they hold at every depth, which
|
||||||
which also fixes nested `node_modules` — rather than transplanted from
|
also fixes nested `node_modules` — rather than transplanted from `.gitignore`,
|
||||||
`.gitignore`, whose unprefixed form protects only the repository root while
|
whose unprefixed form protects only the repository root while reading as
|
||||||
reading as solved. Coverage extends past the `.env`/`.pem`/`.key` trio to the
|
solved. Coverage extends past the `.env`/`.pem`/`.key` trio to the `prod.env`
|
||||||
`prod.env` convention, `.envrc`, PKCS#12 bundles and extensionless SSH keys,
|
convention, `.envrc`, PKCS#12 bundles and extensionless SSH keys, with
|
||||||
every one of them case-folded with character ranges because matching is
|
capitalisation handled by character classes because matching is case-sensitive
|
||||||
case-sensitive and an ALL-CAPS twin per pattern still misses `Server.Key`.
|
and an ALL-CAPS twin per pattern still misses `Server.Key`. `REPO_POLICIES.md`
|
||||||
`REPO_POLICIES.md` and both repo checklists now state that asymmetry and
|
and both repo checklists now state that asymmetry and require verification by
|
||||||
require verification by enumerating the image rather than by reading the
|
enumerating the image rather than by reading the patterns. Verified with a
|
||||||
patterns. Verified with a probe image before, against three naive forms
|
probe image before, against three naive forms (unprefixed, lowercase-only,
|
||||||
(unprefixed, lowercase-only, ALL-CAPS-doubled), and after.
|
ALL-CAPS-doubled), and after.
|
||||||
- 2026-08-09: Made the pinned golangci-lint actually propagate: REPO_POLICIES.md
|
- 2026-08-09: Made the pinned golangci-lint actually propagate: REPO_POLICIES.md
|
||||||
now carries the canonical `script/bootstrap` snippet for Go repos, which
|
now carries the canonical `script/bootstrap` snippet for Go repos, which
|
||||||
installs when the installed version does not match the pin (the old
|
installs when the installed version does not match the pin (the old
|
||||||
|
|||||||
@@ -338,17 +338,14 @@ style conventions are in separate documents:
|
|||||||
|
|
||||||
- **`.dockerignore` does not use `.gitignore` semantics, and copying patterns
|
- **`.dockerignore` does not use `.gitignore` semantics, and copying patterns
|
||||||
across unmodified leaves secrets in the build context.** Docker matches with
|
across unmodified leaves secrets in the build context.** Docker matches with
|
||||||
`moby/patternmatcher`: Go `filepath.Match` semantics plus a `**` extension,
|
Go `filepath.Match`: `*` does not cross `/`, and a pattern without a leading
|
||||||
compiled to a regexp — plain `filepath.Match` has no `**` at all. So `*` does
|
`**/` is anchored at the build-context root. A `.dockerignore` listing `.env`,
|
||||||
not cross `/`, and a pattern without a leading `**/` is anchored at the
|
`*.pem` and `*.key` therefore excludes only the copies at the repository root;
|
||||||
build-context root. A `.dockerignore` listing `.env`, `*.pem` and `*.key`
|
`config/.env` and `certs/server.key` still reach the context and can land in
|
||||||
therefore excludes only the copies at the repository root; `config/.env` and
|
an image layer. That file is more dangerous than a short one with no secret
|
||||||
`certs/server.key` still reach the context and can land in an image layer.
|
patterns at all, because it reads as solved and stops anyone looking. Give
|
||||||
That file is more dangerous than a short one with no secret patterns at all,
|
every depth-independent pattern the `**/` prefix — `**/.env`, `**/.env.*`,
|
||||||
because it reads as solved and stops anyone looking. Give every
|
`**/*.pem`, `**/*.key`, `**/node_modules` — and leave only genuinely
|
||||||
depth-independent pattern the `**/` prefix — `**/node_modules`,
|
|
||||||
`**/.DS_Store`, and the secret patterns in the canonical file, which are
|
|
||||||
additionally case-folded per the rule below — and leave only genuinely
|
|
||||||
root-anchored entries such as `.git` unprefixed. The inverse move is equally
|
root-anchored entries such as `.git` unprefixed. The inverse move is equally
|
||||||
wrong: never apply `**/` to `.gitignore`, where it is redundant and produces a
|
wrong: never apply `**/` to `.gitignore`, where it is redundant and produces a
|
||||||
file that is wrong in a way that looks careful. Each file is written to its
|
file that is wrong in a way that looks careful. Each file is written to its
|
||||||
@@ -367,19 +364,10 @@ style conventions are in separate documents:
|
|||||||
filesystems most laptops use. Adding an ALL-CAPS twin for each pattern is not
|
filesystems most laptops use. Adding an ALL-CAPS twin for each pattern is not
|
||||||
the fix: it still misses `Server.Key` and `Ca.Pem` while reading as though
|
the fix: it still misses `Server.Key` and `Ca.Pem` while reading as though
|
||||||
case were handled — the same manufactured confidence as the root-anchored
|
case were handled — the same manufactured confidence as the root-anchored
|
||||||
form. The matcher supports character ranges, so one line covers every
|
form. `filepath.Match` supports character ranges, so one line covers every
|
||||||
spelling: `**/*.[kK][eE][yY]`, `**/*.[pP][eE][mM]`. Apply this to every secret
|
spelling: `**/*.[kK][eE][yY]`, `**/*.[pP][eE][mM]`. Apply this to
|
||||||
name, not only to extensions: the extensionless SSH keys and `.envrc` need it
|
secret-material extensions; names that exist in exactly one spelling because a
|
||||||
for the same reason, since on the very filesystems that make `SERVER.KEY`
|
tool writes them (`.env`, `.envrc`, `id_rsa`) stay literal.
|
||||||
reachable, direnv reads `.ENVRC` and ssh reads `ID_RSA`. Note that `*` matches
|
|
||||||
the empty string, so `**/*.[eE][nN][vV]` already covers a bare `.ENV` and no
|
|
||||||
separate literal `.env` entry is needed.
|
|
||||||
|
|
||||||
- **A pattern that also catches something the build needs is re-included with a
|
|
||||||
negation, not deleted.** The canonical `**/*.[eE][nN][vV]` excludes a
|
|
||||||
committed env template such as `example.env`; a repo whose build genuinely
|
|
||||||
reads one adds `!docs/example.env` after the pattern. Deleting the pattern
|
|
||||||
instead reopens the exposure for every other file it covers.
|
|
||||||
|
|
||||||
- **Verify `.dockerignore` by enumerating the image, not by reading the
|
- **Verify `.dockerignore` by enumerating the image, not by reading the
|
||||||
patterns.** Plant files at the root _and_ at least two directories deep, build
|
patterns.** Plant files at the root _and_ at least two directories deep, build
|
||||||
|
|||||||
Reference in New Issue
Block a user