Author SHA1 Message Date
rmcguire 6ceb78b00c rm kubeconform
Build Images / Select images (push) Successful in 11s
Lint / Lint (push) Successful in 37s
Build Images / Build changed images (push) Successful in 2m1s
2026-08-10 17:15:13 -04:00
rmcguire f79a175021 rm kubeconform 2026-08-10 17:12:47 -04:00
rmcguire 552d3dd289 add kubeconform 2026-08-10 17:06:34 -04:00
3 changed files with 28 additions and 3 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ RUN apk add --no-cache \
coreutils findutils diffutils gawk grep sed \
file less patch tree \
curl wget \
jq \
jq yamllint \
bind-tools iputils-ping netcat-openbsd socat rsync \
git git-lfs openssh-client-default \
ripgrep fd \
+23
View File
@@ -24,6 +24,7 @@ Base: `node:22-alpine` · ~400 MB uncompressed (`kubectl` and `helm` are about
| Text & search | `jq`, `yq`, `ripgrep` (`rg`), `fd`, `less`, `file`, `patch`, `tree` |
| Network | `curl`, `wget`, `dig`/`nslookup`/`host` (bind-tools), `nc`, `socat`, `rsync`, `ping`, `openssl` |
| Kubernetes | `helm`, `kubectl` |
| Linting | `yamllint` |
| Source control | `git`, `git-lfs`, `ssh`/`scp`/`sftp` |
| Archives | `tar`, `gzip`, `xz`, `zip`, `unzip` |
| System | `ps`/`top`/`free` (procps-ng), `make`, `su-exec`, `tini`, `tzdata`, `ca-certificates` |
@@ -35,6 +36,28 @@ is pinned. Run `<tool> --version` in the image for what a given tag actually shi
`yq` comes from upstream releases because Alpine does not package it; everything
else is an Alpine package.
### Manifest validation
`yamllint` backs the `yamllint` MCP server in `50W/kube-manifests`, which pairs it
with `kubectl` for Kubernetes manifests:
```sh
yamllint -f parsable -d relaxed manifest.yaml
kubectl create --dry-run=client --validate=strict -f manifest.yaml
```
Worth knowing what that second command does **not** catch. Client-side validation
rejects malformed YAML, a document that is not a Kubernetes object, and a missing
`apiVersion`/`kind` — but it does **not** reject an unknown or renamed field on a CRD
(Flux's `HelmRelease` sets `x-kubernetes-preserve-unknown-fields`), nor a wrong scalar
type. Only `--dry-run=server` catches those, and server-side dry-run is authorized as
a **write**, so it needs `create`/`patch` RBAC that this image's consumers deliberately
do not have.
Client-side validation needs no RBAC at all — it only fetches the OpenAPI schema,
which every authenticated identity may do — but it does need to reach the API server.
With no API access it fails closed on valid manifests (`failed to download openapi`).
## Environment
| Variable | Value | Why |
+4 -2
View File
@@ -42,8 +42,8 @@ gnu() {
}
echo "-- present on PATH"
for t in bash sh curl wget jq yq helm kubectl git git-lfs ssh scp dig nslookup host \
nc socat rsync ping rg fd tar gzip xz zip unzip less file patch tree make \
for t in bash sh curl wget jq yq yamllint helm kubectl git git-lfs ssh scp dig nslookup \
host nc socat rsync ping rg fd tar gzip xz zip unzip less file patch tree make \
ps top free tini su-exec node npm npx openssl base64 env xargs; do
have "$t"
done
@@ -63,6 +63,8 @@ works 'fd finds' bash -c 'fd -1 . /tmp >/dev/null'
works 'dig runs' dig -v
works 'openssl runs' openssl version
works 'node runs' node -e 'process.exit(0)'
works 'yamllint flags bad' bash -c '! printf "a: [\n" | yamllint - >/dev/null 2>&1'
works 'yamllint passes ok' bash -c 'printf "a: 1\n" | yamllint -d relaxed -'
echo "-- deployment contract"
[ "$(id -u)" = 1000 ] && ok 'runs as uid 1000' || bad "runs as uid $(id -u), want 1000"