Kong / Kong/kongctl

Task(docker): harden kongctl image with distroless

Open
#2,002 1 comment 1 reaction 0 assignees View on GitHub
code improvement release Task triaged
Dominant language
Go
Stars
17
Forks
24
Avg merge
8h 13m
Merged PRs (30d)
196

Description

## Context

deck moved its runtime container from Alpine to
`gcr.io/distroless/static-debian12:nonroot` to reduce inherited packages and
security findings: Kong/deck#2164.

kongctl also uses Alpine as its final runtime image. Its released Linux binaries
are built with `CGO_ENABLED=0` and are statically linked, so the core CLI is a
good fit for a distroless static image. Distroless provides the CA certificate
bundle, timezone data, `/tmp`, non-root account data, and writable non-root home
directory needed by the core CLI.

This is not completely drop-in compatible with all currently documented
container behavior. kongctl supports executable extensions, documents universal
shell-script extensions, and includes an example using `#!/bin/sh`. A normal
distroless image has no shell or common utilities. Distroless static also cannot
run dynamically linked extension binaries.

Other external executable paths include declarative `_deck` operations and the
Git-based extension source fallback. The current published Alpine image does not
bundle either `deck` or `git`, so those paths already require additional tooling.

## Proposed approach

- Make a digest-pinned
`gcr.io/distroless/static-debian13:nonroot` image the default runtime image.
Debian 13 is preferred because the distroless Debian 12 image line approaches
end of life in September 2026.
- Set `HOME=/home/nonroot` explicitly. kongctl resolves its default configuration
directory with `os.UserHomeDir()`, while the distroless static image does not
set `HOME`.
- Define the official distroless container contract as supporting self-contained,
statically linked extension executables.
- If shell-script or broader executable compatibility must remain supported,
publish a separately named Alpine/tools image rather than adding a shell to the
hardened production image.
- Document the non-root UID (`65532`), home path, lack of shell, and static-only
external executable requirement.

The expected Dockerfile shape is:

```dockerfile
FROM gcr.io/distroless/static-debian13:nonroot@sha256:

ARG TARGETPLATFORM

ENV HOME=/home/nonroot

COPY --chown=nonroot:nonroot $TARGETPLATFORM/kongctl /kongctl

USER nonroot
ENTRYPOINT ["/kongctl"]
```

## Acceptance criteria

- [ ] Decide and document the supported extension contract for the official
container image.
- [ ] Replace the final Alpine runtime with a digest-pinned distroless static
Debian 13 non-root image.
- [ ] Preserve multi-platform publication for `linux/amd64` and `linux/arm64`.
- [ ] Set and verify a writable home directory for configuration, logs, tokens,
and extension state.
- [ ] Add container smoke coverage for version/help output, non-root execution,
configuration persistence, HTTPS/CA validation, `/tmp`, file I/O, and the
detached audit-log listener.
- [ ] Test a statically linked extension and document or test the expected
behavior for shell-script and dynamically linked extensions.
- [ ] Test supplied `deck` and `git` executables if those workflows are part of
the supported official-container contract.
- [ ] Compare the Alpine and distroless images using the same vulnerability
scanner, SBOM generator, and compressed-size measurement.
- [ ] Document debugging guidance using the corresponding `debug-nonroot` image
without publishing it as the production default.

## References

- Kong/deck#2164
- https://github.com/GoogleContainerTools/distroless
- https://github.com/GoogleContainerTools/distroless/blob/main/SUPPORT_POLICY.md
- `Dockerfile`
- `.goreleaser.yml`
- `docs/extensions.md`

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with Dockerfile, .goreleaser.yml, and docs/extensions.md to understand the current image, platform publication, and extension contract. Review existing container checks before adding coverage for non-root execution, configuration persistence, HTTPS, /tmp, file I/O, and extension behavior. Done means the digest-pinned distroless image is published for both Linux platforms with documented supported behavior and verification results.

Written by the indexing model from the issue text.

Assessment

Tech stack
docker, go
Domain
cli, devops, infrastructure
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.