Repository navigation
infra: hardened Dockerfile, modern k8s manifest, .gitignore & .dockerignore - #27
Merged
Merged
Conversation
…st (securityContext, probes, configmap/secret), richer .gitignore, .dockerignore
# Conflicts: # Dockerfile
…cr image ghcr.io refuses anonymous pulls of rhysd/actionlint: even the token endpoint answers 403 DENIED, so 'docker run ghcr.io/rhysd/actionlint' exits 125 on every runner and the lint job could never pass. Instead download the pinned v1.7.12 release archive from github.com for the runner architecture and verify it against the published SHA-256 before executing it: same check, no container-registry dependency, no :latest, no third-party action code.
…action 'uses: helm/kubeconform-action@v1.6.0' cannot resolve -- that repository does not exist, so the job died in 'Set up job' before any manifest was ever validated (it was, in effect, a check that always passed nothing). Fetch the pinned v0.8.0 release binary from github.com, verify the published SHA-256, and run it in strict mode against kubernetes/ with a pinned schema version. The manifest itself is valid: kubeconform 0.8.0 reports 4/4 resources valid in strict mode.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Dockerfile
python:3.12.2-alpine3.19(Feb 2024, nearly EOL) →python:3.13.5-alpine3.22(verified tag).appuser (uid 10001, matching the k8srunAsUser) +USER 10001.sh -cwrapper removed →ENTRYPOINT ["python","-m","bot"]: python is PID 1, gets SIGTERM → graceful shutdown (the new code handles it).PYTHONDONTWRITEBYTECODE/PYTHONUNBUFFERED, deps layer cached beforeCOPY bot/./app/stateheartbeat dir for the liveness probe; new.dockerignorekeeps secrets, tests and.gitout of the build context.Kubernetes manifest (current-cluster best practices, validated with kubeconform strict)
BOT_TOKEN/CHANNEL_IDwere missing/hardcodable →Secret(stringData placeholder, rotate via SOPS/sealed-secrets) +ConfigMapfor non-secret settings viaenvFrom.securityContext—runAsNonRoot: 10001,readOnlyRootFilesystem,capabilities.drop: [ALL],allowPrivilegeEscalation: false,seccompProfile: RuntimeDefault,automountServiceAccountToken: false.:latest→ pinnedv2.0.0+imagePullPolicy: IfNotPresent+ commentedimagePullSecretsfor the private registry.cpu 0.1 / mem 20M(under-provisioned, invalid-ish unit style) → requests50m/64Mi+ limits250m/192Mi.ls /appliveness (always true) → real heartbeat freshness probe (age < 3× cycle) + readiness probe; state on a size-limitedemptyDir.Recreateinstead of 25%/25% surge — prevents two pods posting the same news during rollouts;revisionHistoryLimit: 3."quote characters into the env var; added standardapp.kubernetes.io/*labels..gitignore
Added:
.env.*(+.env.examplenegation),*.pem/*.key/*.p12,secrets.yaml(example negated),configuration.*.toml,.ruff_cache/, IDE & OS noise,sbom.json/coverage.xmlartifacts,docker-compose.override.yml.Together with #1 (heartbeat-aware app code) and #2 (kubeconform gate).
Merged
master(conflict resolution)master(PR #26 + #28) is now merged into this branch. OnlyDockerfileconflicted: both sidesrewrote the tail of the image. The hardened version from this PR is kept —
python:3.13.5-alpine3.22,non-root uid
10001, writable/app/state,HEARTBEAT_FILE=/app/state/heartbeatand the exec-formENTRYPOINT ["python", "-m", "bot"]. It is a superset ofmaster'sCMD ["python", "-m", "bot"](same entry point, plus signal handling as PID 1), so nothing from
masteris lost.Verified on the merged tree:
ruff check+ruff format --checkclean,mypyclean,82 tests pass (81 unit + 1 integration).
Also in this PR: the
lintjob was unfixable as writtenLint & type-checkfailed here for a reason unrelated to this PR's content. The step randocker run ghcr.io/rhysd/actionlint:latest, and ghcr.io refuses anonymous pulls of that image —its token endpoint answers
403 DENIED— so the job exits125on every branch;masteris redfor exactly this reason. It now downloads the pinned
v1.7.12release archive from github.com andverifies the published SHA-256 before executing it: same check, no container-registry dependency,
no mutable
:latest, no third-party action code. Both pinned digests were checked against upstreamactionlint_1.7.12_checksums.txt, and actionlint 1.7.12 passes against all three workflows locally.It is one isolated commit (
ci(actionlint): …) — happy to split it into its own PR if you preferto keep this one purely infra-as-authored.
Also in this PR: the
Kubernetes manifestsjob never ran kubeconformuses: helm/kubeconform-action@v1.6.0referenced a repository that does not exist, so the job diedin
Set up job("Unable to resolve action") without ever validating anything — a green-lookingcheck that checked nothing, and here a red one. It now downloads the pinned
kubeconformv0.8.0release binary, verifies the published SHA-256, and runs
-strict -summaryagainstkubernetes/with a pinned schema version (the same pattern as the actionlint step above).
kubernetes/kube-manifest.yamlis in fact valid:kubeconform 0.8.0 -strictreports4 resources found — Valid: 4, Invalid: 0 against both the 1.33 and 1.34 schemas.
Why both CI commits belong here
Both broken steps came from
ci.ymlonmaster, which this branch now carries, so PR #27 could notgo green without them. Each is a separate, self-contained commit (
ci(actionlint): …,ci(kubeconform): …) and can be cherry-picked out if you would rather keep this PR purely infra.