- Escape single quotes in `.sql.gz` file paths before interpolating into
the `file()` SQL function, preventing injection via crafted filenames
in `/docker-entrypoint-initdb.d/`
- Install `SIGTERM`/`SIGINT` handlers before `initClickHouseDB` so that
`docker stop` during the init phase triggers graceful shutdown instead
of being silently dropped (Linux ignores unhandled signals for PID 1).
The handler forwards the signal to the temporary init server and sets
a flag that breaks the health-check polling loop.
- When `CLICKHOUSE_DO_NOT_CHOWN=1` and running as root, fork a child
that drops to the target UID/GID before calling `fs::create_directories`.
This matches the bash entrypoint behavior (`clickhouse su … mkdir`) and
fixes directory creation on NFS mounts where root is mapped to nobody.
Distroless has no `mkdir` binary, so the child uses the C++ filesystem
library directly after `setuid`/`setgid`.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Validate `CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT` is exactly "0" or "1"
before embedding in XML, preventing injection via the env variable
- Handle EINTR in all `waitpid` calls so signals don't cause spurious
child-process failure reports
- Use `ends_with` instead of manual `substr` for init-script file
extension matching
- Use CTAD for `clickhouse_tools` array to avoid hardcoded size
- Remove deprecated `version` field from distroless docker-compose.yml
Co-Authored-By: Claude Opus 4.6 <[email protected]>
ClickHouse server uses Poco for CLI parsing. Options before "--" must
be registered; unregistered options trigger "Unknown option specified".
The "--" separator marks positional args, which ClickHouse treats as
config.xml overrides (as documented in the server --help output).
Without "--", starting the initdb temp server fails immediately:
Unknown option specified: listen_host=127.0.0.1
This matches the pattern used in entrypoint.sh (line 191):
clickhouse su USER:GROUP clickhouse-server --config-file=... -- --listen_host=127.0.0.1
Fixes: clickhouse-initdb-skip-user-setup [4/6] and
clickhouse-override-tcp-port [6/6] Docker server image tests.
When do_chown is false (running as uid 101, non-root), createDirectoryAndChown
was delegating to 'clickhouse su UID:GID mkdir -p DIR'. The distroless image
has no mkdir binary, so this always failed, silently leaving coordination
subdirectories and server runtime paths uncreated.
Since we are already running as the target user, fs::create_directories works
directly — no user-switch or external binary is needed. The NFS root-squash
concern that motivated the clickhouse-su-mkdir approach does not apply when
the process is already non-root.
The docker-library `override-cmd` test runs:
docker run "$image" echo "Hello $random"
With the ENTRYPOINT `["/usr/bin/clickhouse", "docker-init"]`, docker-init
receives `["echo", "Hello ..."]` as extra_args. Previously, `echo` was not
in the explicit passthrough list, so docker-init started the full server,
which ran indefinitely — causing a ~4.5-hour timeout in CI.
Fix: mirror `entrypoint.sh` behaviour: if the first extra argument does not
start with `--`, exec it directly without server startup. For recognized
ClickHouse tool names (client, local, etc.) the bin_dir path is used for
correct argv[0]-based dispatch; for generic commands (echo, date, bash, …)
PATH resolution via execvp handles it.
Two bugs addressed:
1. `Dockerfile.distroless` — `cp -r` without `-p` preserves mode bits but
resets ownership to root:root. `clickhouse install` sets `config.xml` to
mode 0400 owned by `clickhouse:clickhouse`; after the copy the file becomes
`root:root 0400`, which uid 101 cannot read (errno 13 EACCES). Fix: expand
the `chown -R clickhouse:clickhouse` target from just `users.d` to the
entire `/output/etc/clickhouse-server` tree.
2. `docker-init.cpp` — the official Docker-library test suite runs client
queries by spawning a second container from the same image:
`docker run "$image" clickhouse-client --host ... --query ...`
Because the distroless ENTRYPOINT is `clickhouse docker-init`, the
client tool name and its arguments land in `extra_args` and were being
forwarded to `clickhouse-server`, causing "Unknown option specified" errors.
Fix: before any server-startup logic, detect a recognised client-side
subcommand as the first extra arg (`clickhouse-client`, `clickhouse-local`,
`clickhouse-keeper-client`, and their short forms) and exec it directly
via its `/usr/bin/` symlink, bypassing server initialisation entirely.
- Replace `#include <signal.h>` with `#include <csignal>` (deprecated C header)
- Remove `#include <sstream>`; replace `std::istringstream` with manual newline split
- Wrap all `close()`/`dup2()` calls with `(void)` (return value must be acknowledged)
- Split `int lhs_status = 0, rhs_status = 0;` into two declarations
(readability-isolate-declaration)
- Replace loop-runs-once `has_init_files` detection with iterator comparison
(clang-diagnostic-unreachable-code-loop-increment)
- Add `// NOLINT(concurrency-mt-unsafe)` to `strerror` and `sleep` call sites
- Rename `initClickHouseDb` → `initClickHouseDB` (abbreviations must be all-caps)
- Add `clickhouse docker-init` C++ subcommand that replaces `entrypoint.sh`
for shell-free distroless environments (fork/exec, POSIX-only, no shell deps)
- Add `docker/server/Dockerfile.distroless` and `docker/keeper/Dockerfile.distroless`
as parallel build targets alongside existing ubuntu/alpine variants
- Add `--no-distroless` CI flag and fix package selector to use `.deb` for distroless
- Add structural smoke-test harness under `docker/tests/distroless/`
The distroless images use `gcr.io/distroless/cc-debian12:nonroot` as base,
expose the same ports as ubuntu, run as uid/gid 101, and include a debug
variant (`distroless-debug`) with a busybox shell for troubleshooting.