Docker network toolbox: DNS, HTTP, TCP, TLS, proxy, PostgreSQL & Java 17/21 diagnostics on UBI 10
835
UTB provides Linux, network, PostgreSQL, and Java 17/21 diagnostics on Red Hat UBI 10.
docker run --rm thelazyengineer/utb:1.3.0 http https://example.com
docker run --rm thelazyengineer/utb:1.3.0 dns example.com
docker run --rm thelazyengineer/utb:1.3.0 tools
Use UTB for disposable checks, CI JSON reports, or a persistent administration workspace. The Docker socket is needed only for Docker API inspection.
| You need to… | Start with… |
|---|---|
| Check an HTTP health endpoint | http --json https://example.com |
| Reach an explicit service port | tcp postgres.internal 5432 --timeout 3 |
| Resolve a service name | dns service.internal |
| Diagnose a proxy or corporate CA | proxy, certs, keystore |
| Check database authentication and TLS | psql -h postgres.internal |
| Discover the installed commands | tools or help |
Use an existing Compose network to reach its service names, or share a running container's network to check its localhost endpoint:
# Replace myproject_default, postgres, and my-app with your existing resources.
docker run --rm --network myproject_default \
thelazyengineer/utb:1.3.0 tcp postgres 5432
docker run --rm --network container:my-app \
thelazyengineer/utb:1.3.0 http http://127.0.0.1:8080/health
On an existing Kubernetes pod running on an amd64 node, from your workstation:
kubectl debug -it pod/my-app --image=thelazyengineer/utb:1.3.0 \
--container=utb-debug -- /usr/local/bin/utb shell
The debug container shares the pod network and remains until pod deletion.
Workstation kubectl and appropriate permissions are required. The image is
amd64 only; these examples have not been validated on a live cluster.
mkdir -p reports
docker run --rm --user "$(id -u):$(id -g)" \
--cap-drop ALL --security-opt no-new-privileges \
-v "$(pwd)/reports:/workspace:z" \
thelazyengineer/utb:1.3.0 http https://example.com \
--timeout 10 --expect-status 200 --json --report /workspace/http.json
HTTP uses GET, verifies TLS, respects proxies/NO_PROXY, and succeeds on 2xx by
default. --follow enables bounded redirects; --expect-status 200,204 selects
accepted codes. HTTPS redirects cannot downgrade to HTTP; downloads are capped
at 10 MiB. DNS, connect, TLS, first-byte, and total timings are cumulative
milliseconds; proxies and connection reuse affect their interpretation. Reports
show the URL origin and omit its path, query, userinfo, fragment, body, and headers.
Use --ca-file /workspace/roots.pem to select an HTTP trust bundle.
TCP checks connect without sending application data. Specify up to 16 ports:
tcp host 443 8443 --timeout 3. The timeout applies to each port, including DNS
and address attempts. An open port establishes reachability, not service health.
These explicit checks are separate from the configured utb all audit.
Failures return nonzero even in JSON mode; reports use mode 0600. CI should
collect the report on failure too. More examples cover restricted non-root runs,
packet capture, corporate trust, and service networks in the bundled recipe guide:
docker run --rm thelazyengineer/utb:1.3.0 cat /opt/utb/docs/RECIPES.md
| Tag | Purpose |
|---|---|
thelazyengineer/utb:1.3.0 | Versioned release, 2026-09-20 |
thelazyengineer/utb:latest | Current release |
thelazyengineer/utb:ubi10 | Current release based on UBI 10 |
thelazyengineer/utb:1.2.0 | Previous release, retained for rollback |
thelazyengineer/utb:1.1.0 | Earlier release |
thelazyengineer/utb:1.0.0 | Legacy release |
Published and tested platform: Linux amd64. Pin a version or digest for repeatable deployments. Rolling tags can change.
Version 1.3.0 adds a complete build context and host helpers for local UBI 10 builds without Docker Hub dependencies. Both JDKs and all diagnostics remain included. Validation covers offline regressions, lint, Compose, local rebuilds, isolated containers/networks, and disposable PostgreSQL tests.
Choose Method 1 to generate the workspace automatically, or Method 2 to
create both docker-compose.yaml and .env manually. Both methods use the
published image initially; the optional local-build workflow below needs no source checkout.
init (Recommended)Create a workspace directory, then run the image's init command:
mkdir -p utb-workspace
cd utb-workspace
docker pull thelazyengineer/utb:1.3.0
docker run --rm --user "$(id -u):$(id -g)" \
-v "$(pwd):/workspace:z" thelazyengineer/utb:1.3.0 init
This creates docker-compose.yaml, .env, .env.example, host-scripts/, and
a workspace README.md, and the complete utb-source/ build context in the mounted directory. New .env files have mode
0600; --user makes the generated files belong to your host user.
Edit the generated .env for your proxy, database, or keystore. Leave optional
settings blank when unused. init already creates .env; no copy step is
needed. The generated Compose file follows latest; set its image: to
thelazyengineer/utb:1.3.0 if you want to pin this release.
utb init [directory] defaults to /workspace. Inside a running toolbox,
utb init /workspace performs the same initialization. Re-running it creates
only missing files and preserves existing configuration, helpers, symlinks,
and modes. To obtain updated samples, initialize an empty directory and merge
the changes you want.
.envCreate a workspace directory and place both files below in the same directory. The environment file is part of this setup method, not a separate method.
docker-compose.yamlservices:
utb:
image: thelazyengineer/utb:1.3.0
container_name: utb-toolbox
stdin_open: true
tty: true
network_mode: host
env_file:
- .env
environment:
# STACK_* overrides inherited host-shell proxy settings.
HTTP_PROXY: ${STACK_HTTP_PROXY:-${HTTP_PROXY:-${http_proxy:-}}}
HTTPS_PROXY: ${STACK_HTTPS_PROXY:-${HTTPS_PROXY:-${https_proxy:-}}}
NO_PROXY: ${STACK_NO_PROXY:-${NO_PROXY:-${no_proxy:-}}}
http_proxy: ${STACK_HTTP_PROXY:-${HTTP_PROXY:-${http_proxy:-}}}
https_proxy: ${STACK_HTTPS_PROXY:-${HTTPS_PROXY:-${https_proxy:-}}}
no_proxy: ${STACK_NO_PROXY:-${NO_PROXY:-${no_proxy:-}}}
volumes:
- .:/workspace:z
# Host inspection and RHEL CA discovery; omit for remote-only checks.
- /:/host:ro
# Docker API access grants host-level control even with :ro.
- /var/run/docker.sock:/var/run/docker.sock:ro
# Optional extra CA certificates; create ./certs before enabling.
# - ./certs:/custom-certs:ro
Other toolbox settings are loaded from .env by env_file; they do not need
duplicate entries under environment. This manual example does not require
host-scripts/. Method 1 also supplies those optional convenience helpers.
.envSave this alongside docker-compose.yaml, then fill in only the settings you need:
# Corporate proxy: leave blank for direct connections.
# Example URL: http://proxy.corp.example:8080
HTTP_PROXY=
HTTPS_PROXY=
NO_PROXY=localhost,127.0.0.1,::1
# Nonempty STACK_* values override inherited proxy settings.
STACK_HTTP_PROXY=
STACK_HTTPS_PROXY=
STACK_NO_PROXY=
PROXY_TEST_URL=https://registry-1.docker.io/v2/
# Optional database: leave POSTGRES_HOST blank to skip aggregate DB checks.
POSTGRES_HOST=
POSTGRES_PORT=5432
POSTGRES_USER=postgres
POSTGRES_PASSWORD=
POSTGRES_DB=postgres
PGSSLMODE=prefer
# For authenticated TLS, select verify-full and the appropriate CA file.
PGSSLROOTCERT=
# DNS and active JDK (17 or 21).
DNS_TARGETS="registry-1.docker.io access.redhat.com google.com"
JAVA_VERSION=17
# Optional Java keystore: use a path visible inside the container.
TLS_KEYSTORE_FILE=
TLS_KEYSTORE_PASSWORD=
TLS_KEY_PASSWORD=
TLS_KEYSTORE_TYPE=
FQDN=
VERIFY_TLS_HOST=
UTB_CA_BUNDLE=
Keep .env private and validate the two files together:
chmod 600 .env
docker compose config --quiet
The stack uses Linux host networking, the host filesystem, and the Docker socket
for host administration. A socket bind marked :ro still permits mutating Docker
API calls. Omit the host/socket mounts for remote-only diagnostics. Keep the
container's /etc/pki/ca-trust writable so custom CA imports can work.
After either setup method, you can switch to a locally built image. init exports
utb-source/, containing the Dockerfile, .dockerignore, version, diagnostics,
templates, and bundled guides needed for a complete build. The image keeps the
same context at /opt/utb/source/, including /opt/utb/source/Dockerfile.
No source repository checkout is required.
On the host, with Bash, Python 3, Docker Engine, and Docker Compose 2.24.4 or newer, run from your initialized workspace:
# Build utb:local and select it for this workspace.
./host-scripts/use-local-image.sh
# Recreate the toolbox when ready, then confirm its version.
docker compose up -d --no-build utb
docker compose exec utb utb version
The helper builds first and then creates docker-compose.override.yaml, which
ordinary docker compose commands load automatically. It sets image: utb:local
and pull_policy: never, and removes any inherited build setting. The original
Compose file and .env stay intact. The helper does not restart any container.
Existing overrides and alternate Compose filenames are preserved; if one exists,
merge the local image selection manually. Unset COMPOSE_FILE when using this
automatic workflow. An absent local image fails instead of being pulled from Hub.
To build without switching, customize the image tag, or refresh upstream packages:
./host-scripts/build-local.sh --tag localhost/utb:custom
./host-scripts/use-local-image.sh --skip-build --tag localhost/utb:custom
# Edit utb-source/Dockerfile or utb-source/bin/ before rebuilding.
./host-scripts/build-local.sh --tag localhost/utb:custom --pull --no-cache
docker compose up -d --no-build --force-recreate utb
Use the selected tag when rebuilding. To select a different tag later, remove
the helper-generated override first, then run use-local-image.sh again. Removing
that override also restores the original Compose image selection; recreate the
toolbox to apply it. Current run-utb.sh and diagnose-container.sh follow the
local selection and disable image pulls. An explicit UTB_IMAGE still wins.
The final image is UBI 10. Java 17 is assembled from signed Red Hat UBI 9 RPMs in
a separate build stage; Java 21 comes from UBI 10. Builds no longer need Docker
Hub for UTB, Java, or a Dockerfile frontend. They still need Red Hat's registry
and package repositories plus download.docker.com for the Docker CLI. This is
not an offline build unless those dependencies are cached or mirrored. Both
JDKs now use Red Hat OpenJDK; JAVA_VERSION=17 and 21 work as before.
UBI_IMAGE and JAVA17_BASE_IMAGE environment variables select alternate base
references, including digests or internal mirrors:
UBI_IMAGE=registry.internal/ubi10:approved \
JAVA17_BASE_IMAGE=registry.internal/ubi9:approved \
./host-scripts/build-local.sh
Base mirrors must provide compatible UBI repositories. Configure build proxies
in Docker's build configuration; the helpers do not read the runtime .env.
To forward exported host proxy variables explicitly, build with:
docker build --build-arg HTTP_PROXY --build-arg HTTPS_PROXY \
-t utb:local ./utb-source
For an older or manually created workspace, first export the missing files from 1.3.0 or newer:
docker run --rm --user "$(id -u):$(id -g)" \
-v "$(pwd):/workspace:z" thelazyengineer/utb:1.3.0 init
This preserves existing files, including edited build sources and helpers.
To refresh existing sources or launchers, initialize an empty directory and merge
the desired updates. Once the source is exported, future local builds and runs
need no prebuilt UTB image. Copy utb-source/ and host-scripts/ to another host
if that host cannot access Hub for the initial export.
For a one-off check:
docker compose run --rm utb utb help
docker compose run --rm utb utb all
For a persistent toolbox:
docker compose up -d
docker compose exec utb utb info
docker compose exec utb utb all --json --report /workspace/audit-report.json
docker compose exec utb utb shell
The utb command initializes settings for fresh docker exec sessions too.
Reports under /workspace persist in the mounted workspace.
Edit .env generated by init. A minimal example is:
HTTP_PROXY=http://proxy.corp.example:8080
HTTPS_PROXY=http://proxy.corp.example:8080
NO_PROXY=localhost,127.0.0.1,::1,.corp.internal
PROXY_TEST_URL=https://registry-1.docker.io/v2/
JAVA_VERSION=17
POSTGRES_HOST=
TLS_KEYSTORE_FILE=
Keep .env private (chmod 600 .env). Supply actual passwords through the
environment or private configuration rather than command arguments. Percent-
encode reserved characters inside proxy URL credentials. Credentials are hidden
in normal diagnostics but remain visible to sufficiently privileged operators.
Startup loads an explicit ENV_FILE, otherwise /workspace/.env, otherwise a
local .env. It treats dotenv values literally and loads only supported toolbox
settings: shell commands, variable interpolation, and multiline expressions are
not executed. Existing container environment variables win, including explicitly
empty values. Docker Compose separately performs its own interpolation; single-
quote values containing a literal $ in Compose dotenv files.
Proxy variables are mirrored between uppercase and lowercase. Nonempty
STACK_HTTP_PROXY, STACK_HTTPS_PROXY, and STACK_NO_PROXY override inherited
proxy values, useful when the host shell already sets corporate proxies.
Proxy credentials are not written into curl, wget, dnf, or shell profiles.
docker compose run --rm utb utb proxy --timeout 5 --json
docker compose run --rm utb utb dns --timeout 2 internal-db.corp.example
utb proxy deliberately tests the selected proxy even when NO_PROXY would
bypass the target; the report separately states the bypass decision. Registry
HTTP 401 is reachable, while proxy authentication errors (407), server errors
(5xx), and TLS verification errors fail the check. If no HTTP(S) proxy is
configured, the command checks direct connectivity.
Set DNS_TARGETS to space- or comma-separated hosts for environment-specific
DNS checks. utb all also tests default public TLS endpoints; use individual
commands with explicit targets when public access is intentionally restricted.
Mount extra PEM/DER certificates under /custom-certs:
docker run --rm -v "$(pwd)/certs:/custom-certs:ro" \
thelazyengineer/utb:1.3.0 certs app.corp.example:443
For a RHEL-family host trust store:
docker run --rm -v /etc/pki/ca-trust:/host-ca-trust:ro \
thelazyengineer/utb:1.3.0 certs app.corp.example:443
The host-aware Compose stack also discovers roots through /host/etc/pki/ca-trust.
Startup imports certificate blocks into the writable container trust store;
source mounts stay untouched and private keys are excluded. Both included JDKs
use the system Java trust store. Certificate imports are serialized and only
changed UTB-managed anchors cause trust regeneration.
Do not mount /etc/pki/ca-trust inside the container read-only when you need
imports. With a non-root user or read-only trust store, native tools can use an
already extracted host bundle, but new system/Java roots cannot be installed.
UTB_CA_BUNDLE or SSL_CERT_FILE selects an explicit native-tool PEM bundle;
this does not replace Java's system trust. Explicit per-tool trust settings and
database-specific PGSSLROOTCERT are preserved.
Select either included JDK:
docker run --rm -e JAVA_VERSION=17 thelazyengineer/utb:1.3.0 java -version
docker run --rm -e JAVA_VERSION=21 thelazyengineer/utb:1.3.0 java -version
Configure the optional database in the workspace .env:
POSTGRES_HOST=db.corp.example
POSTGRES_PORT=5432
POSTGRES_USER=observer
POSTGRES_PASSWORD='replace-with-your-password'
POSTGRES_DB=app
PGSSLMODE=verify-full
PGSSLROOTCERT=/workspace/certs/database-root.pem
docker compose run --rm utb utb psql --timeout 5 --json
The check reports TCP reachability, authentication, bounded diagnostic queries,
and active TLS status. Use a suitably scoped monitoring account. Leave
POSTGRES_HOST blank to skip the aggregate database check; psql -h specifies
the host, while psql --help displays help.
For a Java keystore, set TLS_KEYSTORE_FILE, TLS_KEYSTORE_PASSWORD, and
optionally TLS_KEYSTORE_TYPE=JKS or PKCS12 in .env:
docker compose run --rm utb utb keystore \
-f /workspace/certs/app.p12 --fqdn app.corp.example --json
The check validates listing/password integrity, certificate dates, and leaf
subject alternative names. It explicitly marks private-key decryption and
system-CA chain trust as NOT_TESTED. Successful listing alone does not prove
that an application can use the key or serve a trusted certificate chain.
| Command | Purpose |
|---|---|
utb all | Aggregate DNS, proxy, TLS, ports, and configured optional checks |
utb dns [hosts...] | Resolver settings, forward/reverse lookup, latency |
utb proxy [url] | Proxy TCP, HTTP(S), TLS trust, or direct connectivity |
utb certs [host:port...] | Remote TLS trust, hostname, and certificate validity |
utb keystore -f file | Keystore listing, certificate dates, and leaf SANs |
utb psql -h host | Database TCP, authentication, SQL, and TLS |
utb host | Mounted Docker host proxy and CA configuration |
utb ports | Current-namespace listeners, forwarding, available Docker mappings |
utb app container | Container state, JVM, logs, sockets, and DB correlation |
utb http URL | HTTP status, TLS validation, and request timings; JSON/report support |
utb tcp host port [port ...] | Bounded DNS/TCP reachability for explicit ports |
utb tools [--json] | Installed utility inventory grouped by task |
utb version | Release identification |
utb info | Configuration summary with proxy credentials hidden |
utb init [directory] | Create missing workspace files |
utb shell | Interactive shell |
docker run IMAGE dns ... and docker run IMAGE utb dns ... are both supported.
Use utb <command> --help for complete options. Included tools also run directly,
including curl, wget, dig, openssl, psql, ip/ss, tcpdump, mtr, ncat, jq, Python,
keytool, jcmd, jstat, jstack, and jmap. For raw utilities that share a diagnostic
name, use their full path: utb /usr/bin/psql or utb /usr/bin/host.
Checks exit 0 on success, 1 on failure, and 2 on invalid arguments. JSON
checks also exit nonzero on failure; enable set -o pipefail when piping them
through jq. utb app is an evidence collector: exit 0 means its report was
collected, not that the application is healthy. An inaccessible target fails.
docker compose run --rm -T utb utb all --json --check-timeout 60 \
--report /workspace/audit-report.json
docker compose run --rm utb utb all --report /workspace/audit-report.txt
Aggregate JSON includes per-check status, duration, exit code, and details.
Unconfigured database/keystore checks and unavailable host mounts are skipped.
Reports are written atomically with mode 0600. Without host networking, socket
listeners describe the toolbox's network namespace, not the host's listeners.
Disabled IP forwarding is an observation, not automatically a failed audit.
The generated host helpers support noninteractive pipelines and an explicit image:
UTB_IMAGE=thelazyengineer/utb:1.3.0 ./host-scripts/run-utb.sh utb all --json
UTB_IMAGE=thelazyengineer/utb:1.3.0 ./host-scripts/diagnose-container.sh my-app --json
./host-scripts/check-docker-host.sh
Inspect a proposed daemon proxy change before explicitly applying it:
./host-scripts/setup-docker-proxy.sh --dry-run .env
sudo ./host-scripts/setup-docker-proxy.sh .env
The helper escapes systemd settings, hides credentials, backs up existing
configuration, and writes privately and atomically. It reloads systemd after
writing. --restart additionally restarts Docker; --clear explicitly clears
proxy configuration. These options are administrative actions, not audit steps.
Use utb app <container> --tail 200 --json to gather evidence. Interpret it with
application logs and metrics:
OOMKilled: true
records Docker's OOM evidence. Compare host logs and memory measurements.Logs, JVM arguments, and database query text may contain sensitive information. Review reports before sharing them, even though known credentials are redacted.
Pull thelazyengineer/utb:1.3.0, change your Compose image: to that version,
and recreate only the toolbox with docker compose up -d --no-build utb.
Confirm with docker compose exec utb utb version. Existing commands and
workspace files remain compatible. init continues to preserve existing files;
extract fresh samples into an empty directory when you want updated helpers.
The 1.2.0 and 1.1.0 tags remain available for rollback.
thelazyengineer/utb:1.3.0 and change the existing Compose image: to that
version. For a source-build Compose file, use --no-build when starting the
published image./etc/pki/ca-trust when
imports are needed. Use /host-ca-trust:ro or the existing /host:ro mount..env with literal values; remove unused sample
database or keystore settings. Review nonempty STACK_* overrides.success.docker compose pull utb
docker compose up -d --no-build --force-recreate utb
docker compose exec utb utb info
utb init preserves existing helpers and Compose files. To obtain updated
samples, initialize an empty directory and merge the desired changes:
mkdir -p utb-1.3.0-samples
docker run --rm --user "$(id -u):$(id -g)" \
-v "$(pwd)/utb-1.3.0-samples:/workspace:z" thelazyengineer/utb:1.3.0 init
Keep your private .env and customized files. The 1.0.0 image remains available
for rollback but lacks the fixes introduced in 1.1.0. If a credential from an earlier copied
test suite was ever used operationally, rotate it; removing it from a file does
not revoke it.
The image includes the user guide, recipes, troubleshooting guide, and release notes:
docker run --rm thelazyengineer/utb:1.3.0 cat /opt/utb/docs/USER_GUIDE.md
docker run --rm thelazyengineer/utb:1.3.0 cat /opt/utb/docs/TROUBLESHOOTING.md
docker run --rm thelazyengineer/utb:1.3.0 cat /opt/utb/docs/RELEASES.md
docker run --rm thelazyengineer/utb:1.3.0 cat /opt/utb/docs/RECIPES.md
The image defaults to a root shell for administration. Choose mounts, network access, user privileges, and credentials to match the diagnostic task. This toolbox collects evidence and does not replace application-specific monitoring.
Content type
Image
Digest
sha256:33d1a9a32…
Size
270.2 MB
Last updated
22 days ago
docker pull thelazyengineer/utb